蓝牙鼠标HID设备连接流程

这篇具有很好参考价值的文章主要介绍了蓝牙鼠标HID设备连接流程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

首先我们需要大致知道下HID( The Human Interface Device )是啥,手机和鼠标分别扮演什么角色,这里我们大致了解下即可,然后又在看代码。HID 定义了蓝牙在人机接口设备中的协议、特征和使用规程。典型的应用包括蓝牙鼠标、蓝牙键盘、蓝牙游戏手柄等。该协议改编自USB HID Protocol。所以和USB类似,分为host和device,host这里指手机端,device就是指鼠标、键盘这些HID设备。

蓝牙HID report分为三种:

  1. Input report:从hid device发送给hid host的封包
  2. Output report:从hid host发送给hid device的封包
    总结就是以host为参考,进入host的包就是 Input report,出host的包就是 Output report
  3. Feature report:特性封包,是双向的

HID有两条逻辑链路:

  1. HID Control:这个通道主要用于传输控制封包,在这个通道传输的封包称为同步封包(synchronous reports)
  2. HID Interrupt:在这个通道传输的封包不需要确认,所以称为异步封包(asynchronous reports)

Ok知道这些大致就差不多了,直接看代码流程吧,这里代码选取:Android 13
这里我们比如使用蓝牙鼠标连接手机,在手机界面已经搜索到蓝牙鼠标mac,点击发起连接,我们从这里去看代码,都知道蓝牙显示的界面在这里 http://aospxref.com/android-13.0.0_r3/xref/packages/apps/Settings/src/com/android/settings/bluetooth/
DeviceListPreferenceFragment.java

 public boolean onPreferenceTreeClick(Preference preference) {
     if (KEY_BT_SCAN.equals(preference.getKey())) {
         startScanning();
         return true;
     }

     if (preference instanceof BluetoothDevicePreference) {
         BluetoothDevicePreference btPreference = (BluetoothDevicePreference) preference;
         CachedBluetoothDevice device = btPreference.getCachedDevice();
         mSelectedDevice = device.getDevice();
         mSelectedList.add(mSelectedDevice);
         onDevicePreferenceClick(btPreference);
         return true;
     }
     return super.onPreferenceTreeClick(preference);
 }
 
void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
   btPreference.onClicked();    ---> 其实就是调用 BluetoothDevicePreference.onClicked()
}

http://aospxref.com/android-13.0.0_r3/xref/packages/apps/Settings/src/com/android/settings/bluetooth/
BluetoothDevicePreference.java#onClicked()
void onClicked() {
    Context context = getContext();
    int bondState = mCachedDevice.getBondState();    ---> 获取绑定状态

    final MetricsFeatureProvider metricsFeatureProvider =
            FeatureFactory.getFactory(context).getMetricsFeatureProvider();

    if (mCachedDevice.isConnected()) {  // 已连接,询问是否断开连接
        metricsFeatureProvider.action(context,
                SettingsEnums.ACTION_SETTINGS_BLUETOOTH_DISCONNECT);
        askDisconnect();
    } else if (bondState == BluetoothDevice.BOND_BONDED) {
        metricsFeatureProvider.action(context,
                SettingsEnums.ACTION_SETTINGS_BLUETOOTH_CONNECT);
        mCachedDevice.connect();  // 已绑定,则进行连接
    } else if (bondState == BluetoothDevice.BOND_NONE) {
        metricsFeatureProvider.action(context,
                SettingsEnums.ACTION_SETTINGS_BLUETOOTH_PAIR);
        if (!mCachedDevice.hasHumanReadableName()) {
            metricsFeatureProvider.action(context,
                    SettingsEnums.ACTION_SETTINGS_BLUETOOTH_PAIR_DEVICES_WITHOUT_NAMES);
        }
        pair();  // 如果未绑定,则进行配对
    }
}

private void pair() {
    if (!mCachedDevice.startPairing()) {   ---> 配对失败提示无法配对
        Utils.showError(getContext(), mCachedDevice.getName(), R.string.bluetooth_pairing_error_message);
    }
}

这时候就到了Settinglib里面的代码了,
http://aospxref.com/android-13.0.0_r3/xref/frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/
CachedBluetoothDevice.java#startPairing

public boolean startPairing() {
    // 扫描时配对是不可靠的,因此取消
    if (mLocalAdapter.isDiscovering()) {
        mLocalAdapter.cancelDiscovery();
    }
    if (!mDevice.createBond()) {    ---> 就是调用BluetoothDevice里面的createBond()方法
        return false;
    }
    return true;
}

http://aospxref.com/android-13.0.0_r3/xref/packages/modules/Bluetooth/framework/java/android/bluetooth/
BluetoothDevice.java#createBond
// 在BluetoothDevice里面经过调用流程转换,最终到这里
private boolean createBondInternal(int transport, @Nullable OobData remoteP192Data,
        @Nullable OobData remoteP256Data) {
    if (DBG) log("createBondOutOfBand()");
    final IBluetooth service = sService;
    final boolean defaultValue = false;
    if (service == null || !isBluetoothEnabled()) {
        Log.w(TAG, "BT not enabled, createBondOutOfBand failed");
        if (DBG) log(Log.getStackTraceString(new Throwable()));
    } else if (NULL_MAC_ADDRESS.equals(mAddress)) {
        Log.e(TAG, "Unable to create bond, invalid address " + mAddress);
    } else {
        try {
            final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get();
            // 这里的service 就是Adaptservice,下面看这里调用的方法
            service.createBond(this, transport, remoteP192Data, remoteP256Data, mAttributionSource, recv);
            return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
        } catch (RemoteException | TimeoutException e) {
            Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
        }
    }
    return defaultValue;
}

http://aospxref.com/android-13.0.0_r3/xref/packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/
AdapterService.java#createBond
boolean createBond(BluetoothDevice device, int transport, OobData remoteP192Data,
        OobData remoteP256Data, String callingPackage) {
    DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device);
    if (deviceProp != null && deviceProp.getBondState() != BluetoothDevice.BOND_NONE) {
        return false;
    }

    if (!isPackageNameAccurate(this, callingPackage, Binder.getCallingUid())) {
        return false;
    }

    CallerInfo createBondCaller = new CallerInfo();
    createBondCaller.callerPackageName = callingPackage;
    createBondCaller.user = Binder.getCallingUserHandle();
    mBondAttemptCallerInfo.put(device.getAddress(), createBondCaller);

    mRemoteDevices.setBondingInitiatedLocally(Utils.getByteAddress(device));

    // Pairing is unreliable while scanning, so cancel discovery
    // Note, remove this when native stack improves
    cancelDiscoveryNative();

    Message msg = mBondStateMachine.obtainMessage(BondStateMachine.CREATE_BOND);
    msg.obj = device;
    msg.arg1 = transport;

    Bundle remoteOobDatasBundle = new Bundle();
    boolean setData = false;
    if (remoteP192Data != null) {
        remoteOobDatasBundle.putParcelable(BondStateMachine.OOBDATAP192, remoteP192Data);
        setData = true;
    }
    if (remoteP256Data != null) {
        remoteOobDatasBundle.putParcelable(BondStateMachine.OOBDATAP256, remoteP256Data);
        setData = true;
    }
    if (setData) {
        msg.setData(remoteOobDatasBundle);
    }
    mBondStateMachine.sendMessage(msg);   ---> 其实就是到BondStateMachine里面处理 CREATE_BOND
    return true;
}

http://aospxref.com/android-13.0.0_r3/xref/packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/
BondStateMachine.java#StableState
// 初始状态 setInitialState(mStableState)
 private class StableState extends State {
     @Override
     public void enter() {
         infoLog("StableState(): Entering Off State");
     }
     @Override
     public synchronized boolean processMessage(Message msg) {

         BluetoothDevice dev = (BluetoothDevice) msg.obj;
         switch (msg.what) {
             case CREATE_BOND:
                 OobData p192Data = (msg.getData() != null)
                         ? msg.getData().getParcelable(OOBDATAP192) : null;
                 OobData p256Data = (msg.getData() != null)
                         ? msg.getData().getParcelable(OOBDATAP256) : null;
                 createBond(dev, msg.arg1, p192Data, p256Data, true);
                 break;
                
// 继续跟下 createBond 方法
private boolean createBond(BluetoothDevice dev, int transport, OobData remoteP192Data,
        OobData remoteP256Data, boolean transition) {
    if (dev.getBondState() == BluetoothDevice.BOND_NONE) {
        infoLog("Bond address is:" + dev);
        byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
        boolean result;
        // If we have some data
        if (remoteP192Data != null || remoteP256Data != null) {    ---> 可以看前面,传下来的是null,那现在就应该走下面这个分支
            result = mAdapterService.createBondOutOfBandNative(addr, transport, remoteP192Data, remoteP256Data);
        } else {
            result = mAdapterService.createBondNative(addr, transport);   ---> 看这个
        }
        BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_DEVICE_NAME_REPORTED,
                mAdapterService.getMetricId(dev), dev.getName());
        BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
                mAdapterService.obfuscateAddress(dev), transport, dev.getType(),
                BluetoothDevice.BOND_BONDING,
                remoteP192Data == null && remoteP256Data == null
                        ? BluetoothProtoEnums.BOND_SUB_STATE_UNKNOWN
                        : BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_OOB_DATA_PROVIDED,
                BluetoothProtoEnums.UNBOND_REASON_UNKNOWN);

        if (!result) {
            BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
                    mAdapterService.obfuscateAddress(dev), transport, dev.getType(),
                    BluetoothDevice.BOND_NONE, BluetoothProtoEnums.BOND_SUB_STATE_UNKNOWN,
                    BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS);
            // Using UNBOND_REASON_REMOVED for legacy reason
            sendIntent(dev, BluetoothDevice.BOND_NONE, BluetoothDevice.UNBOND_REASON_REMOVED, false);
            return false;
        } else if (transition) {
            transitionTo(mPendingCommandState);
        }
        return true;
    }
    return false;
}

// mAdapterService.createBondNative(addr, transport)
http://aospxref.com/android-13.0.0_r3/xref/packages/modules/Bluetooth/android/app/jni/
com_android_bluetooth_btservice_AdapterService.cpp#createBondNative
static jboolean createBondNative(JNIEnv* env, jobject obj, jbyteArray address, jint transport) {
  ALOGV("%s", __func__);
  if (!sBluetoothInterface) return JNI_FALSE;
  jbyte* addr = env->GetByteArrayElements(address, NULL);
  if (addr == NULL) {
    jniThrowIOException(env, EINVAL);
    return JNI_FALSE;
  }
  int ret = sBluetoothInterface->create_bond((RawAddress*)addr, transport);    ---> 看这个
  env->ReleaseByteArrayElements(address, addr, 0);
  return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}
//  /packages/modules/Bluetooth/system/btif/src/bluetooth.cc
static int create_bond(const RawAddress* bd_addr, int transport) {
  if (!interface_ready()) return BT_STATUS_NOT_READY;
  if (btif_dm_pairing_is_busy()) return BT_STATUS_BUSY;

  do_in_main_thread(FROM_HERE, base::BindOnce(btif_dm_create_bond, *bd_addr, transport));
  return BT_STATUS_SUCCESS;
}

继续追下 btif_dm_create_bond  //packages/modules/Bluetooth/system/btif/src/btif_dm.cc
void btif_dm_create_bond(const RawAddress bd_addr, int transport) {

  BTIF_TRACE_EVENT("%s: bd_addr=%s, transport=%d", __func__, bd_addr.ToString().c_str(), transport);
  btif_stats_add_bond_event(bd_addr, BTIF_DM_FUNC_CREATE_BOND, pairing_cb.state);
  pairing_cb.timeout_retries = NUM_TIMEOUT_RETRIES;
  btif_dm_cb_create_bond(bd_addr, transport);  ---> 看这个
}

// 
static void btif_dm_cb_create_bond(const RawAddress bd_addr, tBT_TRANSPORT transport) {
  bool is_hid = check_cod(&bd_addr, COD_HID_POINTING);
  bond_state_changed(BT_STATUS_SUCCESS, bd_addr, BT_BOND_STATE_BONDING);

  int device_type = 0;
  tBLE_ADDR_TYPE addr_type = BLE_ADDR_PUBLIC;
  std::string addrstr = bd_addr.ToString();
  const char* bdstr = addrstr.c_str();
  if (transport == BT_TRANSPORT_LE) {
    if (!btif_config_get_int(bdstr, "DevType", &device_type)) {
      btif_config_set_int(bdstr, "DevType", BT_DEVICE_TYPE_BLE);
    }
    if (btif_storage_get_remote_addr_type(&bd_addr, &addr_type) !=
        BT_STATUS_SUCCESS) {
      // Try to read address type. OOB pairing might have set it earlier, but
      // didn't store it, it defaults to BLE_ADDR_PUBLIC
      uint8_t tmp_dev_type;
      tBLE_ADDR_TYPE tmp_addr_type = BLE_ADDR_PUBLIC;
      BTM_ReadDevInfo(bd_addr, &tmp_dev_type, &tmp_addr_type);
      addr_type = tmp_addr_type;

      btif_storage_set_remote_addr_type(&bd_addr, addr_type);
    }
  }
  if ((btif_config_get_int(bdstr, "DevType", &device_type) &&
       (btif_storage_get_remote_addr_type(&bd_addr, &addr_type) ==
        BT_STATUS_SUCCESS) &&
       (device_type & BT_DEVICE_TYPE_BLE) == BT_DEVICE_TYPE_BLE) ||
      (transport == BT_TRANSPORT_LE)) {
    BTA_DmAddBleDevice(bd_addr, addr_type,
                       static_cast<tBT_DEVICE_TYPE>(device_type));
  }

  if (is_hid && (device_type & BT_DEVICE_TYPE_BLE) == 0) {
    const bt_status_t status = btif_hh_connect(&bd_addr);
    if (status != BT_STATUS_SUCCESS)
      bond_state_changed(status, bd_addr, BT_BOND_STATE_NONE);
  } else {
    BTA_DmBond(bd_addr, addr_type, transport, device_type);   ---> 看这个
  }
  /*  Track  originator of bond creation  */
  pairing_cb.is_local_initiated = true;
}

void BTA_DmBond(const RawAddress& bd_addr, tBLE_ADDR_TYPE addr_type,
                tBT_TRANSPORT transport, tBT_DEVICE_TYPE device_type) {
  do_in_main_thread(FROM_HERE, base::Bind(bta_dm_bond, bd_addr, addr_type, transport, device_type));
}
继续看 bta_dm_bond
/** Bonds with peer device */
void bta_dm_bond(const RawAddress& bd_addr, tBLE_ADDR_TYPE addr_type,
                 tBT_TRANSPORT transport, tBT_DEVICE_TYPE device_type) {
  LOG_DEBUG("Bonding with peer device:%s type:%s transport:%s type:%s",
            PRIVATE_ADDRESS(bd_addr), AddressTypeText(addr_type).c_str(),
            bt_transport_text(transport).c_str(),
            DeviceTypeText(device_type).c_str());

  tBTA_DM_SEC sec_event;
  char* p_name;

  tBTM_STATUS status =
      (bluetooth::shim::is_gd_security_enabled())
          ? bluetooth::shim::BTM_SecBond(bd_addr, addr_type, transport, device_type)
          : BTM_SecBond(bd_addr, addr_type, transport, device_type, 0, NULL);

  if (bta_dm_cb.p_sec_cback && (status != BTM_CMD_STARTED)) {
    memset(&sec_event, 0, sizeof(tBTA_DM_SEC));
    sec_event.auth_cmpl.bd_addr = bd_addr;
    p_name = (bluetooth::shim::is_gd_security_enabled())
                 ? bluetooth::shim::BTM_SecReadDevName(bd_addr)
                 : BTM_SecReadDevName(bd_addr);
    if (p_name != NULL) {
      memcpy(sec_event.auth_cmpl.bd_name, p_name, BD_NAME_LEN);
      sec_event.auth_cmpl.bd_name[BD_NAME_LEN] = 0;
    }

    /*      taken care of by memset [above]
            sec_event.auth_cmpl.key_present = false;
            sec_event.auth_cmpl.success = false;
    */
    sec_event.auth_cmpl.fail_reason = HCI_ERR_ILLEGAL_COMMAND;
    if (status == BTM_SUCCESS) {
      sec_event.auth_cmpl.success = true;
    } else {
      /* delete this device entry from Sec Dev DB */
      bta_dm_remove_sec_dev_entry(bd_addr);
    }
    bta_dm_cb.p_sec_cback(BTA_DM_AUTH_CMPL_EVT, &sec_event);
  }
}

tBTM_STATUS BTM_SecBond(const RawAddress& bd_addr, tBLE_ADDR_TYPE addr_type,
                        tBT_TRANSPORT transport, tBT_DEVICE_TYPE device_type,
                        uint8_t pin_len, uint8_t* p_pin) {
  if (bluetooth::shim::is_gd_shim_enabled()) {
    return bluetooth::shim::BTM_SecBond(bd_addr, addr_type, transport,
                                        device_type);
  }

  if (transport == BT_TRANSPORT_AUTO) {
    if (addr_type == BLE_ADDR_PUBLIC) {
      transport =
          BTM_UseLeLink(bd_addr) ? BT_TRANSPORT_LE : BT_TRANSPORT_BR_EDR;
    } else {
      LOG_INFO("Forcing transport LE (was auto) because of the address type");
      transport = BT_TRANSPORT_LE;
    }
  }
  tBT_DEVICE_TYPE dev_type;

  BTM_ReadDevInfo(bd_addr, &dev_type, &addr_type);
  /* LE device, do SMP pairing */
  if ((transport == BT_TRANSPORT_LE && (dev_type & BT_DEVICE_TYPE_BLE) == 0) ||
      (transport == BT_TRANSPORT_BR_EDR &&
       (dev_type & BT_DEVICE_TYPE_BREDR) == 0)) {
    return BTM_ILLEGAL_ACTION;
  }
  return btm_sec_bond_by_transport(bd_addr, addr_type, transport, pin_len, p_pin);  ---> 看这个
}
/*******************************************************************************
 *
 * Function         btm_sec_bond_by_transport
 *
 * Description      this is the bond function that will start either SSP or SMP.
 *
 * Parameters:      bd_addr      - Address of the device to bond
 *                  pin_len      - length in bytes of the PIN Code
 *                  p_pin        - pointer to array with the PIN Code
 *
 *  Note: After 2.1 parameters are not used and preserved here not to change API
 ******************************************************************************/
tBTM_STATUS btm_sec_bond_by_transport(const RawAddress& bd_addr,
                                      tBLE_ADDR_TYPE addr_type,
                                      tBT_TRANSPORT transport, uint8_t pin_len,
                                      uint8_t* p_pin) {
  tBTM_SEC_DEV_REC* p_dev_rec;
  tBTM_STATUS status;
  VLOG(1) << __func__ << " BDA: " << bd_addr;

  BTM_TRACE_DEBUG("%s: Transport used %d, bd_addr=%s", __func__, transport,
                  bd_addr.ToString().c_str());

  /* Other security process is in progress */
  if (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) {
    BTM_TRACE_ERROR("BTM_SecBond: already busy in state: %s",
                    btm_pair_state_descr(btm_cb.pairing_state));
    return (BTM_WRONG_MODE);
  }

  p_dev_rec = btm_find_or_alloc_dev(bd_addr);
  if (p_dev_rec == NULL) {
    return (BTM_NO_RESOURCES);
  }

  if (!controller_get_interface()->get_is_ready()) {
    BTM_TRACE_ERROR("%s controller module is not ready", __func__);
    return (BTM_NO_RESOURCES);
  }

  BTM_TRACE_DEBUG("before update sec_flags=0x%x", p_dev_rec->sec_flags);

  /* Finished if connection is active and already paired */
  if (((p_dev_rec->hci_handle != HCI_INVALID_HANDLE) &&
       transport == BT_TRANSPORT_BR_EDR &&
       (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED)) ||
      ((p_dev_rec->ble_hci_handle != HCI_INVALID_HANDLE) &&
       transport == BT_TRANSPORT_LE &&
       (p_dev_rec->sec_flags & BTM_SEC_LE_AUTHENTICATED))) {
    BTM_TRACE_WARNING("BTM_SecBond -> Already Paired");
    return (BTM_SUCCESS);
  }

  /* Tell controller to get rid of the link key if it has one stored */
  if ((BTM_DeleteStoredLinkKey(&bd_addr, NULL)) != BTM_SUCCESS)
    return (BTM_NO_RESOURCES);

  /* Save the PIN code if we got a valid one */
  if (p_pin && (pin_len <= PIN_CODE_LEN) && (pin_len != 0)) {
    btm_cb.pin_code_len = pin_len;
    p_dev_rec->pin_code_length = pin_len;
    memcpy(btm_cb.pin_code, p_pin, PIN_CODE_LEN);
  }

  btm_cb.pairing_bda = bd_addr;

  btm_cb.pairing_flags = BTM_PAIR_FLAGS_WE_STARTED_DD;

  p_dev_rec->security_required = BTM_SEC_OUT_AUTHENTICATE;
  p_dev_rec->is_originator = true;

  BTM_LogHistory(kBtmLogTag, bd_addr, "Bonding initiated", bt_transport_text(transport));

  if (transport == BT_TRANSPORT_LE) {
    btm_ble_init_pseudo_addr(p_dev_rec, bd_addr);
    p_dev_rec->sec_flags &= ~BTM_SEC_LE_MASK;

    if (SMP_Pair(bd_addr, addr_type) == SMP_STARTED) {     ---> 配对
      btm_cb.pairing_flags |= BTM_PAIR_FLAGS_LE_ACTIVE;
      p_dev_rec->sec_state = BTM_SEC_STATE_AUTHENTICATING;
      btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_AUTH_COMPLETE);
      return BTM_CMD_STARTED;
    }

    btm_cb.pairing_flags = 0;
    return (BTM_NO_RESOURCES);
  }

  p_dev_rec->sec_flags &=
      ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED |
        BTM_SEC_ROLE_SWITCHED | BTM_SEC_LINK_KEY_AUTHED);

  BTM_TRACE_DEBUG("after update sec_flags=0x%x", p_dev_rec->sec_flags);
  if (!controller_get_interface()->supports_simple_pairing()) {
    /* The special case when we authenticate keyboard.  Set pin type to fixed */
    /* It would be probably better to do it from the application, but it is */
    /* complicated */
    if (((p_dev_rec->dev_class[1] & BTM_COD_MAJOR_CLASS_MASK) ==
         BTM_COD_MAJOR_PERIPHERAL) &&
        (p_dev_rec->dev_class[2] & BTM_COD_MINOR_KEYBOARD) &&
        (btm_cb.cfg.pin_type != HCI_PIN_TYPE_FIXED)) {
      btm_cb.pin_type_changed = true;
      btsnd_hcic_write_pin_type(HCI_PIN_TYPE_FIXED);
    }
  }

  BTM_TRACE_EVENT("BTM_SecBond: Remote sm4: 0x%x  HCI Handle: 0x%04x",
                  p_dev_rec->sm4, p_dev_rec->hci_handle);

#if (BTM_SEC_FORCE_RNR_FOR_DBOND == TRUE)
  p_dev_rec->sec_flags &= ~BTM_SEC_NAME_KNOWN;
#endif

  /* If connection already exists... */
  if (BTM_IsAclConnectionUpAndHandleValid(bd_addr, transport)) {
    btm_sec_wait_and_start_authentication(p_dev_rec);

    btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_PIN_REQ);

    /* Mark lcb as bonding */
    l2cu_update_lcb_4_bonding(bd_addr, true);
    return (BTM_CMD_STARTED);
  }

  BTM_TRACE_DEBUG("sec mode: %d sm4:x%x", btm_cb.security_mode, p_dev_rec->sm4);
  if (!controller_get_interface()->supports_simple_pairing() ||
      (p_dev_rec->sm4 == BTM_SM4_KNOWN)) {
    if (btm_sec_check_prefetch_pin(p_dev_rec)) return (BTM_CMD_STARTED);
  }
  if ((btm_cb.security_mode == BTM_SEC_MODE_SP ||
       btm_cb.security_mode == BTM_SEC_MODE_SC) &&
      BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)) {
    /* local is 2.1 and peer is unknown */
    if ((p_dev_rec->sm4 & BTM_SM4_CONN_PEND) == 0) {
      /* we are not accepting connection request from peer
       * -> RNR (to learn if peer is 2.1)
       * RNR when no ACL causes HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT */
      btm_sec_change_pairing_state(BTM_PAIR_STATE_GET_REM_NAME);
      status = BTM_ReadRemoteDeviceName(bd_addr, NULL, BT_TRANSPORT_BR_EDR);
    } else {
      /* We are accepting connection request from peer */
      btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_PIN_REQ);
      status = BTM_CMD_STARTED;
    }
    BTM_TRACE_DEBUG("State:%s sm4: 0x%x sec_state:%d",
                    btm_pair_state_descr(btm_cb.pairing_state), p_dev_rec->sm4,
                    p_dev_rec->sec_state);
  } else {
    /* both local and peer are 2.1  */
    status = btm_sec_dd_create_conn(p_dev_rec);
  }

  if (status != BTM_CMD_STARTED) {
    BTM_TRACE_ERROR(
        "%s BTM_ReadRemoteDeviceName or btm_sec_dd_create_conn error: 0x%x",
        __func__, (int)status);
    btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE);
  }

  return status;
}

这块代码有点多,细节找个时间再细细追下,先为了流程我们可以大致知道这个如果配对上就会回调状态给上层,比如这样的log打印:
09-19 14:49:07.917 17238 17294 I BluetoothBondStateMachine: bondStateChangeCallback: Status: 0 Address: FC:25:4B:6B:40:D0 newState: 1 hciReason: 0 —> 1 表示 BOND_STATE_BONDING
09-19 14:49:11.532 17238 17294 I BluetoothBondStateMachine: bondStateChangeCallback: Status: 0 Address: FC:25:4B:6B:40:D0 newState: 2 hciReason: 0 —> 2 表示 BOND_STATE_BONDED
所以只要我们看到回调的结果是 BOND_STATE_BONDED,就表示连接成功了
看下连接成功后代码:http://aospxref.com/android-13.0.0_r3/xref/packages/modules/Bluetooth/android/app/src/
com/android/bluetooth/btservice/BondStateMachine.java#455

455      void bondStateChangeCallback(int status, byte[] address, int newState, int hciReason) {
456          BluetoothDevice device = mRemoteDevices.getDevice(address);
457          // 配对成功的device
458          if (device == null) {
459              infoLog("No record of the device:" + device);
460              // This device will be added as part of the BONDING_STATE_CHANGE intent processing
461              // in sendIntent above
462              device = mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
463          }
464  		 // hciReason 表示原因,看这里的说明 AdapterService.java#4473
465          infoLog("bondStateChangeCallback: Status: " + status + " Address: " + device + " newState: "
466                  + newState + " hciReason: " + hciReason);
467  
468          Message msg = obtainMessage(BONDING_STATE_CHANGE);
469          msg.obj = device;
470  
471          if (newState == BOND_STATE_BONDED) {
472              msg.arg1 = BluetoothDevice.BOND_BONDED;
473          } else if (newState == BOND_STATE_BONDING) {
474              msg.arg1 = BluetoothDevice.BOND_BONDING;
475          } else {
476              msg.arg1 = BluetoothDevice.BOND_NONE;
477          }
478          msg.arg2 = status;
479  
480          sendMessage(msg);    --->发送这个消息 BONDING_STATE_CHANGE
481      }    

// PendingCommandState 状态里面处理
218                  case BONDING_STATE_CHANGE:
219                      int newState = msg.arg1;
220                      int reason = getUnbondReasonFromHALCode(msg.arg2);
221                      // Bond is explicitly removed if we are in pending command state
222                      if (newState == BluetoothDevice.BOND_NONE
223                              && reason == BluetoothDevice.BOND_SUCCESS) {
224                          reason = BluetoothDevice.UNBOND_REASON_REMOVED;
225                      }
226                      sendIntent(dev, newState, reason, false);  ---> 做些判断,然后发送ACTION_BOND_STATE_CHANGED广播
227                      if (newState != BluetoothDevice.BOND_BONDING) {
228                          // This is either none/bonded, remove and transition, and also set
229                          // result=false to avoid adding the device to mDevices.
230                          mDevices.remove(dev);
231                          result = false;
232                          if (mDevices.isEmpty()) {
233                              transitionTo(mStableState);   ---> 切换到这个状态
234                          }
235                          if (newState == BluetoothDevice.BOND_NONE) {
236                              mAdapterService.setPhonebookAccessPermission(dev,
237                                      BluetoothDevice.ACCESS_UNKNOWN);
238                              mAdapterService.setMessageAccessPermission(dev,
239                                      BluetoothDevice.ACCESS_UNKNOWN);
240                              mAdapterService.setSimAccessPermission(dev,
241                                      BluetoothDevice.ACCESS_UNKNOWN);
242                              // Set the profile Priorities to undefined
243                              clearProfilePriority(dev);
244                          }
245                      } else if (!mDevices.contains(dev)) {
246                          result = true;
247                      }
248                      break;

收到 ACTION_BOND_STATE_CHANGED 广播的地方就会做一些逻辑处理,支持,连接流程大致梳理完毕。文章来源地址https://www.toymoban.com/news/detail-845407.html

到了这里,关于蓝牙鼠标HID设备连接流程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • stm32 USB复合设备 cubeMX库一键生成 多路CDC串口 HID鼠标键盘 Composite Device

    最近有个需求,需要同时用usb键盘鼠标和虚拟串口等,因为平时没怎么研究过usb协议,所以自己写复合设备一直没有成功,然后正巧在github上看到了一个stm32的一个usb复合设备库,可以快速配置usb组合设备,并且支持超级多路串口 Gihub地址 https://github.com/alambe94/I-CUBE-USBD-Compo

    2024年02月09日
    浏览(51)
  • Android连接蓝牙设备问题(android.permission.BLUETOOTH)

            近期遇到一个问题,之前发布的APP连接蓝牙都是正常的,现在有人反映连不上了。经过测试发现:android 12 和 harmonyOS 3.0.0 都会有这个问题,而之前的版本就不会有这个。         经过网上一番查找,原来是因为最近Google发布的Android 12,新引入了 BLUETOOTH_SCAN、

    2024年01月16日
    浏览(31)
  • USB HID转蓝牙&鼠键宏&指纹解锁

    将有线鼠标键盘游戏手柄等USB HID转换为蓝牙设备,附带鼠键宏和指纹解锁功能。 硬件开源地址  前作 使用esp32-c3的GPIO模拟USB HOST,识别低速USB HID设备并读取其报告描述符和报告 TEANSLATE 工作模式: 检测设备类型,尝试对鼠标和键盘的报告描述符进行解析,将其报告翻译成预

    2024年02月06日
    浏览(68)
  • stm32实现hid鼠标

    启动CubelMX 选择芯片(直接输入stm32f103zet6) 设置时钟 如下图 usb设置 配置usb设备 调试端口设置   配置时钟 项目输出设置 打开工程(后记:此工程含有中文不能编译通过) 配置项目  配置调试器 编译无法通过 删除路径中的中文,以及工程名中的中文。再次生成工程(其他设置

    2024年04月17日
    浏览(25)
  • 【Bluetooth蓝牙开发】九、BLE协议之GATT

    个人主页:董哥聊技术 我是董哥,嵌入式领域新星创作者 创作理念:专注分享高质量嵌入式文章,让大家读有所得!   【所有文章汇总】  

    2024年01月22日
    浏览(27)
  • 使用 Web HID API 在浏览器中进行HID设备交互(纯前端)

    最近在搞HID透传 《STM32 USB使用记录:HID类设备(后篇)》 。 市面上的各种测试工具都或多或少存在问题,所以就自己写一个工具进行测试。目前来说纯前端方案编写这个工具应该是最方便的,这里对 Web HID API 相关内容做个记录。 Web HID API 相关内容参考如下: https://develop

    2024年01月19日
    浏览(38)
  • STM32自定义HID(模拟厂家设备)

    与上位机调试软件配合, QT编写的调试助手:QT HID调试助手 源码:源码链接 QT上位机教程:教程地址 笔者这边硬件是TM32F103RCT6 软件使用CubeMX生成HID设备 首先配置单片机的基本时钟,调试方式等 本文主要讲解USBHID配置 勾选如上图所示 勾选 下面是对这几种模式的简要介绍:

    2024年02月06日
    浏览(38)
  • 【Bluetooth蓝牙开发】七、BLE协议之L2CAP

    个人主页:董哥聊技术 我是董哥,嵌入式领域新星创作者 创作理念:专注分享高质量嵌入式文章,让大家读有所得!   【所有文章汇总】  

    2023年04月09日
    浏览(24)
  • STM32 USB使用记录:HID类设备(前篇)

    USB是目前最流行的接口,现在很多个人用的电子设备也都是USB设备。目前大多数单片机都有USB接口,使用USB接口作为HID类设备来使用是非常常用的,比如USB鼠标、键盘都是这一类。这篇文章将简单介绍使用STM32实现相关内容。 一些USB相关最基础的内容可以参考下面文章中 基础

    2024年02月16日
    浏览(24)
  • STM32 USB使用记录:HID类设备(后篇)

    接上篇: 《STM32 USB使用记录:HID类设备(前篇)》 USB HID 类的设备有个比较大的好处是大部分时候接入主机中都是可以免驱使用的。这篇文章将介绍下 STM32 中实现 USB HID 双向透传功能,结合免驱的特点,这在实际工作中是比较常用的。 在上一篇文章中简单了解接触了下HID设

    2024年02月13日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包