精华内容
下载资源
问答
  • Android 9.0 Bluetooth源码分析(三)蓝牙配对流程
    千次阅读
    2020-06-19 11:34:59

    1 UI

    蓝牙配对开始于settings设备列表 /packages/apps/Settings/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java中。
    DeviceListPreferenceFragment是蓝牙扫描到的设备列表,点击其中一个蓝牙设备,调用onPreferenceTreeClick方法开始蓝牙的配对过程。
    @Override
    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
    Preference preference) {
    if (KEY_BT_SCAN.equals(preference.getKey())) {
    mLocalAdapter.startScanning(true);
    return true;
    }

        if (preference instanceof BluetoothDevicePreference) {
            BluetoothDevicePreference btPreference = (BluetoothDevicePreference) preference;
            CachedBluetoothDevice device = btPreference.getCachedDevice();
            mSelectedDevice = device.getDevice();
            //配对连接
            onDevicePreferenceClick(btPreference);
            return true;
        }
    
        return super.onPreferenceTreeClick(preferenceScreen, preference);
    }
    

    在本地onDevicePreferenceClick方法中调用/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,
                      MetricsEvent.ACTION_SETTINGS_BLUETOOTH_DISCONNECT);
              askDisconnect(); // 已连接,询问是否断开连接
          } else if (bondState == BluetoothDevice.BOND_BONDED) {
              metricsFeatureProvider.action(context,
                      MetricsEvent.ACTION_SETTINGS_BLUETOOTH_CONNECT);
              mCachedDevice.connect(true);// 已绑定,则进行连接
          } else if (bondState == BluetoothDevice.BOND_NONE) {
              metricsFeatureProvider.action(context,
                      MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR);
              if (!mCachedDevice.hasHumanReadableName()) {
                  metricsFeatureProvider.action(context,
                      MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR_DEVICES_WITHOUT_NAMES);
              }
              pair();// 如果未绑定,则进行配对
          }
      }
    

    这里先获取mCachedDevice的绑定状态,如果已经连接,则询问是否断开;如果已经绑定未连接,则开始连接;如果未连接也未绑定,则开始配对。这里我们先看配对。配对调用的是本地的pair方法:
    private void pair() {
    if (!mCachedDevice.startPairing()) {
    Utils.showError(getContext(), mCachedDevice.getName(),
    R.string.bluetooth_pairing_error_message);
    }
    }

    pair方法会调用/frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java中的startPairing,启动配对
    2 framework
    public boolean startPairing() {
    // Pairing is unreliable while scanning, so cancel discovery
    // 配对时,如果正在扫描,则取消扫描
    if (mLocalAdapter.isDiscovering()) {
    mLocalAdapter.cancelDiscovery();
    }
    // 开始配对
    if (!mDevice.createBond()) {
    return false;
    }
    // 标识位,配对完成后,自动连接
    mConnectAfterPairing = true; // auto-connect after pairing
    return true;
    }

    createBond调用/frameworks/base/core/java/android/bluetooth/BluetoothDevice.java
    中的createBond方法:
    public boolean createBond(int transport) {
    final IBluetooth service = sService;
    if (service == null) {
    Log.e(TAG, “BT not enabled. Cannot create bond to Remote Device”);
    return false;
    }
    if (TRANSPORT_AUTO > transport || transport > TRANSPORT_LE) {
    throw new IllegalArgumentException(transport + " is not a valid Bluetooth transport");
    }
    try {
    Log.i(TAG, "createBond() for device " + getAddress()
    + " called by pid: " + Process.myPid()
    + " tid: " + Process.myTid());
    return service.createBond(this, transport);
    } catch (RemoteException e) {
    Log.e(TAG, “”, e);
    }
    return false;
    }

    createBond接着调用IBluetooth的createBond方法,通过aidl方式调用蓝牙远程服务。
    3 Bluetooth app

    和蓝牙扫描一样,实现IBluetooth接口的类是AdapterServiceBinder,AdapterServiceBinder实现IBluetooth.Stub接口,是/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService的私有内部类,AdapterServiceBinder收到的操作,都会转交AdapterService处理,所以会调用AdapterService的createBond方法。
    boolean createBond(BluetoothDevice device, int transport) {
    enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    “Need BLUETOOTH ADMIN permission”);
    DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device);
    //属性检查
    if (deviceProp != null && deviceProp.getBondState() != BluetoothDevice.BOND_NONE) {
    return false;
    }

        // Pairing is unreliable while scanning, so cancel discovery
        // Note, remove this when native stack improves
        cancelDiscoveryNative();// 配对过程,取消扫描
        // 给配对的状态机发消息,创建了BondStateMachine.CREATE_BOND
        Message msg = mBondStateMachine.obtainMessage(BondStateMachine.CREATE_BOND);
        msg.obj = device;
        msg.arg1 = transport;
        mBondStateMachine.sendMessage(msg);
        return true;
    }
    

    createBond 方法会检查一下远程设备属性信息,取消蓝牙扫描任务,将配对任务转交mBondStateMachine,由状态机处理该信息。
    @Override
    public boolean processMessage(Message msg) {

            BluetoothDevice dev = (BluetoothDevice)msg.obj;
    
            switch (msg.what) {
                case CREATE_BOND:
                    OobData oobData = null;
                    if (msg.getData() != null) {
                        oobData = msg.getData().getParcelable(OOBDATA);
                    }
    
                    result = createBond(dev, msg.arg1, oobData, false);
                    break;
                    ........................省略.................................
                    }
            }
    

    BondStateMachine处理服务发送过来的BondStateMachine.CREATE_BOND消息 ,在processMessage 中调用 BondStateMachine的createBond 方法:
    private boolean createBond(BluetoothDevice dev, int transport, OobData oobData,
    boolean transition) {
    if (dev.getBondState() == BluetoothDevice.BOND_NONE) {
    infoLog(“Bond address is:” + dev);
    byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
    boolean result;
    if (oobData != null) {// 判断是否借助其他硬件进行无绑定配对
    result = mAdapterService.createBondOutOfBandNative(addr, transport, oobData);
    } else {
    result = mAdapterService.createBondNative(addr, transport);// 调用到JNI层,进行配对
    }

              if (!result) {
                  sendIntent(dev, BluetoothDevice.BOND_NONE, BluetoothDevice.UNBOND_REASON_REMOVED);
                  return false;
              } else if (transition) {
                  transitionTo(mPendingCommandState);
              }
              return true;
          }
          return false;
      }
    

    createBondNative方法实现在/packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterService.cpp中:
    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;
    }
    // 调用到hal层的配对函数
    int ret = sBluetoothInterface->create_bond((RawAddress*)addr, transport);
    env->ReleaseByteArrayElements(address, addr, 0);
    return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
    

    }

    这里通过create_bond这个方法调用到了蓝牙协议栈里面。
    4 蓝牙协议栈

    create_bond方法位于/system/bt/btif/src/bluetooth.cc:
    static int create_bond(const RawAddress* bd_addr, int transport) {
    /* sanity check */
    if (!interface_ready()) return BT_STATUS_NOT_READY;

    return btif_dm_create_bond(bd_addr, transport);
    

    }

    create_bond方法调用/system/bt/btif/src/btif_dm.cc的btif_dm_create_bond方法:
    bt_status_t btif_dm_create_bond(const RawAddress* bd_addr, int transport) {
    btif_dm_create_bond_cb_t create_bond_cb;
    create_bond_cb.transport = transport;
    create_bond_cb.bdaddr = *bd_addr;

    BTIF_TRACE_EVENT("%s: bd_addr=%s, transport=%d", __func__,
                     bd_addr->ToString().c_str(), transport);
    // 如果如果不是未配对状态,则取消配对
    if (pairing_cb.state != BT_BOND_STATE_NONE) return BT_STATUS_BUSY;
    
    btif_stats_add_bond_event(*bd_addr, BTIF_DM_FUNC_CREATE_BOND,
                              pairing_cb.state);// 添加了绑定事件
    
    // 这里create_bond_cb在上面已经传入了要绑定的蓝牙地址,
    // 会分别发送给底层两部分,最后会调用btif_dm_generic_evt
    btif_transfer_context(btif_dm_generic_evt, BTIF_DM_CB_CREATE_BOND,
                          (char*)&create_bond_cb,
                          sizeof(btif_dm_create_bond_cb_t), NULL);
    
    return BT_STATUS_SUCCESS;
    

    }

    btif_dm_create_bond方法最终调用了本地的btif_dm_generic_evt方法,传入BTIF_DM_CB_CREATE_BOND事件:
    static void btif_dm_generic_evt(uint16_t event, char* p_param) {
    BTIF_TRACE_EVENT("%s: event=%d", func, event);
    switch (event) {
    …省略…
    case BTIF_DM_CB_CREATE_BOND: {// 根据传入的事件,走这里进行配对
    pairing_cb.timeout_retries = NUM_TIMEOUT_RETRIES;
    btif_dm_create_bond_cb_t* create_bond_cb =
    (btif_dm_create_bond_cb_t*)p_param;
    btif_dm_cb_create_bond(create_bond_cb->bdaddr, create_bond_cb->transport);
    } break;
    …省略…
    }
    }

    这里又调用本地的btif_dm_cb_create_bond方法:
    static void btif_dm_cb_create_bond(const RawAddress& bd_addr,
    tBTA_TRANSPORT transport) {
    bool is_hid = check_cod(&bd_addr, COD_HID_POINTING);
    // 这里开始回调,将绑定状态变成绑定中
    bond_state_changed(BT_STATUS_SUCCESS, bd_addr, BT_BOND_STATE_BONDING);
    …省略…
    if (is_hid && (device_type & BT_DEVICE_TYPE_BLE) == 0) {
    bt_status_t status;
    status = (bt_status_t)btif_hh_connect(&bd_addr);
    if (status != BT_STATUS_SUCCESS)
    bond_state_changed(status, bd_addr, BT_BOND_STATE_NONE);
    } else {
    BTA_DmBondByTransport(bd_addr, transport);// 第一次调用会走这里
    }
    /* Track originator of bond creation */
    pairing_cb.is_local_initiated = true;
    }

    BTA_DmBondByTransport方法位于\system\bt\bta\dm\bta_dm_api.c:
    void BTA_DmBondByTransport(BD_ADDR bd_addr, tBTA_TRANSPORT transport)
    {
    // 调用bta的bta_dm_bond方法
    do_in_bta_thread(FROM_HERE, base::Bind(bta_dm_bond, bd_addr, transport));
    }

    这里通过do_in_bta_thread调用/system/bt/bta/dm/bta_dm_act.cc里面的bta_dm_bond方法,进入bta进程:
    void bta_dm_bond (tBTA_DM_MSG *p_data)
    {
    tBTM_STATUS status;
    tBTA_DM_SEC sec_event;
    char *p_name;

    if (p_data->bond.transport == BTA_TRANSPORT_UNKNOWN)
        status = BTM_SecBond ( p_data->bond.bd_addr, 0, NULL, 0 );
    else
        status = BTM_SecBondByTransport ( p_data->bond.bd_addr, p_data->bond.transport, 0, NULL, 0 );
    
    
    if (bta_dm_cb.p_sec_cback && (status != BTM_CMD_STARTED))
    {
    
        memset(&sec_event, 0, sizeof(tBTA_DM_SEC));
        bdcpy(sec_event.auth_cmpl.bd_addr, p_data->bond.bd_addr);
        p_name = BTM_SecReadDevName(p_data->bond.bd_addr);
        if (p_name != NULL)
        {
            memcpy(sec_event.auth_cmpl.bd_name, p_name, (BD_NAME_LEN-1));
            sec_event.auth_cmpl.bd_name[BD_NAME_LEN-1] = 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(p_data->bond.bd_addr);
    }
    bta_dm_cb.p_sec_cback(BTA_DM_AUTH_CMPL_EVT, &sec_event);// 配对事件回调
    }
    }

    然后来到\system\bt\stack\btm\btm_sec.c的BTM_SecBondByTransport 方法:
    tBTM_STATUS BTM_SecBondByTransport (BD_ADDR bd_addr, tBT_TRANSPORT transport,
    UINT8 pin_len, UINT8 *p_pin, UINT32 trusted_mask[])
    {
    tBT_DEVICE_TYPE dev_type;
    tBLE_ADDR_TYPE addr_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, transport, pin_len, p_pin, trusted_mask);
    

    }

    调用本地btm_sec_bond_by_transport方法,这个方法内容很多,着重看这段代码:
    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);// 这里就在和hci层打交道
    }
    }

    这里调用system/bt/stack/hcic/hcicmds.cc的btsnd_hcic_write_pin_type方法通过HCI向底层发送命令进行控制
    void btsnd_hcic_write_pin_type (UINT8 type)
    {
    BT_HDR *p = (BT_HDR *)osi_malloc(HCI_CMD_BUF_SIZE);
    UINT8 *pp = (UINT8 *)(p + 1);

    p->len    = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM1;
    p->offset = 0;
    
    UINT16_TO_STREAM (pp, HCI_WRITE_PIN_TYPE);
    UINT8_TO_STREAM  (pp, HCIC_PARAM_SIZE_WRITE_PARAM1);
    
    UINT8_TO_STREAM (pp, type);
    
    btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID,  p);//这里是向hci层发命令,
    

    }

    可以看出,这里是通过和hci层的通信,host告诉controlor蓝牙地址、数据、命令等,从而控制其底层硬件发起配对操作。具体btu如何与hci通信,过程也是很繁琐,可以参考《Android BT STACK BTU 和 HCI之间的消息传递》这篇文章。
    到此绑定的流程就结束了。有一个遗留问题就是绑定状态是如何返回给上层的呢?
    5 配对状态改变的回传

    上文我们在bta里面调用/system/bt/bta/dm/bta_dm_act.cc里面的bta_dm_bond方法,进行配对,这个方法里面有这样一段代码:
    bta_dm_cb.p_sec_cback(BTA_DM_AUTH_CMPL_EVT, &sec_event);

    这个就是bta的回调函数,回调事件是BTA_DM_AUTH_CMPL_EVT,根据这个事件标志,我们找到了 /system/bt/btif/src/btif_dm.cc里面的btif_dm_upstreams_evt方法,这个方法就是用于向上层回调消息的,相关代码是:
    case BTA_DM_AUTH_CMPL_EVT:
    btif_dm_auth_cmpl_evt(&p_data->auth_cmpl);
    break;

    可以看到是调用这个函数,返回配对完成的事件,这个函数代码很多这里就不引用了,无论配对成功还是失败,这里都会用 bond_state_changed这个方法进行处理:
    static void bond_state_changed(bt_status_t status, const RawAddress& bd_addr,
    bt_bond_state_t state) {
    btif_stats_add_bond_event(bd_addr, BTIF_DM_FUNC_BOND_STATE_CHANGED, state);

    // Send bonding state only once - based on outgoing/incoming we may receive
    // duplicates
    if ((pairing_cb.state == state) && (state == BT_BOND_STATE_BONDING)) {
      // Cross key pairing so send callback for static address
      if (!pairing_cb.static_bdaddr.IsEmpty()) {
        auto tmp = bd_addr;
        HAL_CBACK(bt_hal_cbacks, bond_state_changed_cb, status, &tmp, state);
      }
      return;
    }
    
    if (pairing_cb.bond_type == BOND_TYPE_TEMPORARY) state = BT_BOND_STATE_NONE;
    
    BTIF_TRACE_DEBUG("%s: state=%d, prev_state=%d, sdp_attempts = %d", __func__,
                     state, pairing_cb.state, pairing_cb.sdp_attempts);
    
    auto tmp = bd_addr;
    HAL_CBACK(bt_hal_cbacks, bond_state_changed_cb, status, &tmp, state);
    
    if (state == BT_BOND_STATE_BONDING) {
      pairing_cb.state = state;
      pairing_cb.bd_addr = bd_addr;
    } else if ((state == BT_BOND_STATE_NONE) &&
        ((bd_addr == pairing_cb.bd_addr) ||
        (bd_addr == pairing_cb.static_bdaddr))) {
       memset(&pairing_cb, 0, sizeof(pairing_cb));
    }else{
      if ((!pairing_cb.sdp_attempts)&&
            ((bd_addr == pairing_cb.bd_addr) ||
            (bd_addr == pairing_cb.static_bdaddr)))
        memset(&pairing_cb, 0, sizeof(pairing_cb));
      else
        BTIF_TRACE_DEBUG("%s: BR-EDR service discovery active", __func__);
    }
    

    }

    可以发现也是通过HAL_CBACK(bt_hal_cbacks, bond_state_changed_cb, status, &tmp, state);这样的方法进行回调的,bond_state_changed_cb这个函数在bluetooth.h被定义对应的是com_android_bluetooth_btservice_AdapterService.cpp里的bond_state_changed_callback,关键代码如下:
    sCallbackEnv->CallVoidMethod(sJniCallbacksObj, method_bondStateChangeCallback,
    (jint)status, addr.get(), (jint)state);

    这里将bondStateChangeCallback方法对应到jni的method_bondStateChangeCallback方法
    jclass jniCallbackClass =
    env->FindClass(“com/android/bluetooth/btservice/JniCallbacks”);
    …省略…
    method_bondStateChangeCallback =
    env->GetMethodID(jniCallbackClass, “bondStateChangeCallback”, “(I[BI)V”);

    就找到了JniCallbacks.java里面的bondStateChangeCallback方法
    void bondStateChangeCallback(int status, byte[] address, int newState) {
    mBondStateMachine.bondStateChangeCallback(status, address, newState);
    }

    接下来便进入了/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/BondStateMachine.java状态机里面:
    void bondStateChangeCallback(int status, byte[] address, int newState) {
    BluetoothDevice device = mRemoteDevices.getDevice(address);

          if (device == null) {
              infoLog("No record of the device:" + device);
              // This device will be added as part of the BONDING_STATE_CHANGE intent processing
              // in sendIntent above
              device = mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
          }
    
          infoLog("bondStateChangeCallback: Status: " + status + " Address: " + device
                  + " newState: " + newState);
    
          Message msg = obtainMessage(BONDING_STATE_CHANGE);
          msg.obj = device;
    
          if (newState == BOND_STATE_BONDED)
              msg.arg1 = BluetoothDevice.BOND_BONDED;
          else if (newState == BOND_STATE_BONDING)
              msg.arg1 = BluetoothDevice.BOND_BONDING;
          else
              msg.arg1 = BluetoothDevice.BOND_NONE;
          msg.arg2 = status;
    
          sendMessage(msg);
      }
    

    状态机里面通过sendMessage进行配对状态的变更。
    到此,配对流程就分析结束了。

    作者:猫疏
    链接:https://www.jianshu.com/p/0b748b11fa62
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    更多相关内容
  • DeviceListPreferenceFragment是蓝牙扫描到的设备列表,点击其中一个蓝牙设备,调用onPreferenceTreeClick方法开始蓝牙配对过程。 /packages/apps/Settings/src/...
  • 蓝牙的连接流程分为9个步骤,如下图所示,在这9个步骤中,实线的步骤是必选的,虚线是可选的,接下来我们就来分析下。蓝牙A2dp hfp opp rfcomm pbap sdp l2cap源代码流程图,请点击下面链接

    更多蓝牙A2dp hfp opp rfcomm pbap sdp l2cap源代码流程图,请点击此链接

    Android 蓝牙配对连接源码分析文档大全

    在蓝牙核心文档中,蓝牙的连接流程分为9个步骤,如下图所示,在这9个步骤中,实线的步骤是必选的,虚线是可选的,接下来我们就来分析下:

     1、第一个步骤就是create connect,它是必选的,host通过hci发送create connection命令给到controller,这个命令里面包含指定的蓝牙设备地址,通过指定的蓝牙地址去page蓝牙设备,就是建立物理链路;

    2、第二步就是发送获取对方蓝牙设备的特性信息,了解对方蓝牙设备的一些属性,例如是否支持OOB配对,也就是通过别的途径交换配对信息,例如NFC蓝牙设备等,后面会说到

    3、获取完对方的特性后,这时候host就可以发送connection request请求了,建立ACL逻辑链路,这个也是必选的步骤,对端蓝牙设备收到这个请求后,会有三种响应方式,分别是接受连接,拒绝连接,接受连接但是请求自己为master,下面我们分别介绍

     4.1第一种响应方式就是远程主机接收到了连接请求,但是拒绝连接,整个连接过程终止

    4.2第二种就是远程主机接收到了连接请求,并且接受这个连接请求,这个也是最常见的一种情况

    4.3第三种就是,远程主机接收到了连接请求,也同意连接,但是想要自己做老大, 成为master,于是他会发送一个请求,申请自己为master,一般有一些特殊的设备会申请自己做master,这也是一些设备会连接失败的原因

     5、一般情况下,当远端蓝牙设备接受连接请求后,这时候双方就需要交换特性了,我们知道蓝牙的频段是2402~2480MHZ,以1M为间隔,分为79个频道,蓝牙和wifi的频段是重合的,所以蓝牙采用自适应跳频技术,当有一个频段阻塞时,它会经过计算,自动跳到下一个频段,所以这里双方设备交换特性,始终保持频率等一致,为后面数据交换做准备

     6、当前面的准备工作都做完之后,就要开始进行两个蓝牙设备之间的配对了,其实整个配对过程就是获取link key,这个link key在后面传输数据时使用,非常重要,在配对之前还有一件事,就是授权,当两个设备事第一次进行连接时,由于双方都没有保存link key,所以需要进行授权验证,也就是下面我们要说到的配对过程,如果两个设备之前已经有连接过,双方都保存了link key,那么就不需要进行授权,可以跳过配对的过程,这里我们分析第一次连接的情况,控制器向host发送link key请求,如果host回复的是negative,接下来就是配对过程了,如果回复的是positive,那么双方蓝牙设备不需要进行配对

     7、图为整个蓝牙的配对过程,现在蓝牙的配对基本上都使用secure simple pair,也就是ssp配对,首先使能简单配对,OOB(out of band)配对的优先级最高,如果双方都支持,优先使用OOB,接下来就是开始ssp配对的过程了

     8、开始简单配对后,先进行特性交换,交换有关鉴权的需求,以及最重要的双方蓝牙设备具有怎么样的人机交互能力,也就是io capabilities exchange,也就是获取对方设备是不是支持输入,支持显式等功能,根据双方具有的人机交互能力,选择合适的配对方式

     9、接下来就是双方蓝牙设备交换public key了,当收到对方蓝牙设备的公钥,利用它就可以计算出Diffle Hellman密钥,也就是DHKey了,这个DHKey我们后面会看到,就是利用它来生成link key

    10.1  根据前面的feature exchange的结果,有三种鉴权方式,前面说过,OOB鉴权的优先级最高,由配对的双方,在配对过程之外,额外的交互一些信息,并以这些信息为输入,进行后续的配对操作,额外信息称作OOB(out of band),这个也是OOB的由来

     10.2 接下来的鉴权方式叫做MITM(man-in-the-middle)也就是中间人鉴权,方式中带人,也就是说需要人的参与,这种方式有2种,先介绍Numeric comparision方式鉴权,这种方式需要两个蓝牙设备都具备显式的能力,然后两个设备自行产生6位数字,用户比较后确认,最常见的场景就是手机与手机通过蓝牙之间进行的配对连接

     中间人鉴权的第二种方式,叫做passkey entry,这种方式要求一方蓝牙设备具备输入的能力,比如蓝牙键盘,一方蓝牙设备具有显式的功能,比如手机,一端显式配对码,一点输入配对配对码进行配对,这种方式常用在平板或者pc和蓝牙键盘之间的连接

     10.3、最后还有一种鉴权方式,叫做just work,它不需要用户的参与,两个设备自行协商配对,最常用的场景就是手机和蓝牙耳机进行配对。

    使用场景:当两个设备都有输出能力时,将使用数字比较方式(Numeric comparison), 如果两者设备具有输出功能,此步骤需要显示用户确认值。

    如果一个或两个设备没有输出能力,则使用相同的协议,但主机将跳过要求用户确认的步骤。 使用Just Works,主机不会向用户显示数字。

    11、说完了上面的鉴权过程后,之前说的DHKey就该派上用场了,link key就是从DHKey中计算得到,并用作后续交互过程中的输入,这步完成后,LM控制器通过hci发送HCI_Link_Key_Notification事件来通知host得到了link key。

     

     12、完成了鉴权和配对的过程,就会开始利用得到的link key进行加密的连接,利用加密信息,可以相互传送一些私密信息,如encryption information,identity information等,进行最后的连接过程交互

    13、 最后,连接建立完成,双方蓝牙设备LM控制器向各自的host发送LMP_setup_complete事件,将底层连接建立的句柄发送给上层,使用这个连接handle就可以用来发送上层的数据了,那么整个的连接流程就完成了

    更多蓝牙A2dp hfp opp rfcomm pbap sdp l2cap源代码流程图,请点击此链接

    展开全文
  • Android 蓝牙配对连接源码分析文档大全-点击下载 1、此篇文档主要对经典蓝牙的扫描配对连接流程进行了分析,并通过实际的耳机配对连接手表的例子进行梳理。主要目的是通过该文档学习,对蓝牙的扫描配对流程有一个...

    android-蓝牙A2dp-avrcp-hfp-opp-配对流程-ble-rfcomm源码流程图-点击下载

    Android 蓝牙配对连接源码分析文档大全-点击下载

    1、此篇文档主要对经典蓝牙的扫描配对连接流程进行了分析,并通过实际的耳机配对连接手表的例子进行梳理。主要目的是通过该文档学习,对蓝牙的扫描配对流程有一个清晰的认识。在项目中遇到相关问题,知道从哪方面去入手分析。

    2.1 Inquiry概念简介
          搜索Inquiry顾名思义就是检测和收集周围环境中的蓝牙设备,根据询问的方式可分为 One-Time Inquiry(一次性询问)和 Periodic inquiry(周期性询问)两种搜索模式。需要周期性地搜索蓝牙设备的情况下才会采用周期性询问这种方式,但是由于该种模式使用的场景有限且对功耗是个不小的负担,所以现在市面上的蓝牙设备基本不采用这种搜索模式,接下来就主要介绍下一次性询问。
    2.2 One-Time
    Inquiry
    步骤1:蓝牙Host通过命令HCI_Inquiry告知控制器Controller进入查询模式,用于发现周围环境中的其他蓝牙设备。

     步骤2:蓝牙控制器Controller接收到上层开启搜索指令后将以指定的查询访问代码(IAC)和查询时长启动蓝牙基带搜索查询流程,对外发送ID包。周围环境中的蓝牙设备接收到ID包后会将自己的设备信息封装到FHS包中做出响应,控制器解析FHS包获取所需的信息,并使用一个或多个查询结果事件将找到的设备相关信息返回给主机Host。

     步骤3(a):如果主机Host希望停止搜索查询,则使用HCI_Inquiry_Cancel命令通知控制器Controller立即停止查询过程,控制器接收到指令就会停止对外发送ID包执行停止查询流程。

     步骤3(b):如果主机Host没有主动停止查询,则控制器Controller会在查询上报的结果已达数量限制或者查询时长已到而停止查询,并将查询的完成事件上报给Host。

     经过以上三步,一个完整的搜索查询Inquiry流程就完成了。对于流程中涉及到的HCI命令或事件再做如下说明:
    1、HCI_Inquiry

     命令中三个参数的含义如下所示:

    LAP:上述步骤2中的查询访问代码就是从该值派生得到的,具体取值范围为是0x9E8B00 ~ 0x9E8B3F,但是只有 0x9E8B00 和 0x9E8B33 这两个数值是有效的,其他值保留供将来使用。
    0x9E8B33 代表一般/无限制查询访问代码(GIAC)
    0x9E8B00 代表有限的专用查询访问代码(LIAC)
    由于LIAC的使用有其局限性,所以参数LAP基本上都是使用GIAC这一固定参数。
     
    Inquiry_Length:查询模式的总持续时间,当此时间超时后查询将被停止,该参数的取值范围及对应的超时时间如下图: 

     Num_Responses:在查询停止之前可以接收的响应数,当响应数达到该值后,控制器Controller停止当前的查询,并上报Host查询完成事件。该参数的取值范围如下:

     2、上报查询的结果
    在发起inquiry之前也可能发送Set_Event_Filter命令,该命令可以使主机对指定的事件进行过滤,命令格式如下:

             该命令有三个参数:Filter_Type、Filter_Condition_Type、Condition,通过设置这三个参数可以过滤掉搜索到的不感兴趣的设备,使控制器Controller只上报符合设置条件的设备信息。具体的参数如何设置可以参考《蓝牙协议标准 corespec》里“Set Event Filter Command”参数的详细描述,这里不详细讲解。我们的实际产品中一般会设置这个过滤条件,比如我们的手表产品去扫描连接耳机或音箱时候会过滤掉其他手表手机等设备信息。
            在当前查询期间响应的蓝牙设备如果未被上报过且该设备没有被命令Set_Event_Filter过滤掉应始终以如下三种事件之一(Inquiry Result、Inquiry Result with RSSI或者Extended Inquiry Result)上报主机Host查询的结果,不管采用哪个事件上报Host,都是将查询到的设备信息告知主机。主机协议栈依次接收到这些设备信息后,解析出相应的数据保存并上报蓝牙服务层。
            如果搜索到的蓝牙设备在当前查询或查询期间已经被上报过,则该设备这次可能被上报,也可能不被上报,这取决于控制器Controller中的实现(对同一设备是只上报一次,还是只要设备响应就上报)。
            最后随着HCI_Inquiry_Complete事件的上报,查询流程就完结了。

    流程对应的HCI交互如下:

     三、蓝牙连接配对流程分析
    3.1.蓝牙连接流程分析

    在蓝牙核心文档中,连接过程如下图所示:

            上图表示Connection establishment 和 detachment 过程,总共需9个不同的过程。有些过程不是必须的比如授权和加密过程。有些过程是必须的,比如ConnectionRequest 和Setup Complete过程。
     
    接下来将具体介绍每个流程:
    Step 1: create connection(必选)
    Host发送HCI_Create_Connection命令给Controller,然后Controller通过指定的蓝牙地址去page设备。

     Step 2: FeaturesExchange(可选)
      LM将会去进行exchange feature,获取对方的特性信息,Controller之间的交互。

     Step 3: ConnectionRequest(必选)
    主Controller LM发送LMP_host_connection_req请求,远程设备LM确认请求,远程设备可能接受也可能拒绝请求。

     Step 4: OptionalRole Switch
    根据设备情形分类
    a.远程Host拒绝连接,连接将会被终止。

     b、远程设备接受连接。

     c、远程设备接收连接,并请求为Master。

     Step 5: Optional AFH(交换特性)
    和远程设备交换特性,当AFH(Adaptive FrequencyHopping 自适应跳频)被确认可用后。主设备将会发送LMP_set_AFH 和LMP_channel_classification_req请求。

     Step 6: Authentication (鉴权)
    若需要授权,控制器将会为这个连接向host请求Link Key。在HCI中能看到Link key request的事件。

     Step 7: linkkey
    a、如果链接需要授权且没有公用的link key。
            接下来将会是pairing过程。LM会向host请求link key,如果host给的是Negative 回复,LM将会向host请求PIN。Host端请求结束后,随后远程端也会产生PIN请求。链接的授权都是基于请求到的PIN。最后两端都会将link Key通知到各自的host保存,为以后的链接使用。所以配对过程实际上是获取Link Key的过程。
            在HCI中能看到Link key获取结束时,Link key Notification事件。紧跟着的就是授权完成的事件:Authentication Complete。

     b、两端有共用的link key
            如果两端有共用的link key存在,那么不需要配对过程!LM向host请求link key时,如果得到的是positive回复,将会直接使用回复的link key进行授权。

     Step 8: encryption (加密)
    一旦完成配对和授权,就会开始加密。下图显示的是设置一个加密的点对点连接。

     Step 9: LMP_setup_complete(连接建立完成)
    两端的LM都会向各自的host发送LMP_setup_complete事件。通过LMP_setup_complete事件将底层的连接handle发送给上层,到此为止建立的链接才可以用来发送上层的数据。

             至此蓝牙设备已经在物理通道上建立了链接,但上层应用若需要在设备之间通信,那么还需要在L2CAP层次建立连接,L2CAP 的CID(通道ID)好比是计算机的端口号,在访问网络时每个应用程序会对应不同的端口号。在L2CAP层中也类似,对应过来的是每个协议profile实例对应L2CAP层的一个CID。

    3.2蓝牙配对流程分析
            蓝牙配对是整个蓝牙连接过程中必不可少的环节,配对的目的是通过各种方法创建共享的链路密钥(LinkKey),该密钥用于设备间连接时认证鉴权并加密相互交互的数据,使得蓝牙技术更具有安全性。密钥不但可以用于当前的连接中,还可以用于两个设备后续的重连过程中,但是鉴权过程中如果比较双方的Link Key不一致,则必须重新开始配对流程,从而创建新的链路密钥(Link Key)用于新的连接交互流程。
     
    Bluetooth Core Specification定义了两种标准配对流程:
    (1) PIN码配对:蓝牙核心协议2.0及之前版本使用的配对流程。
    (2) SSP安全简单配对:Secure Simple Pairing ,蓝牙核心协议2.1及之后版本新增的一种配对流程,由于相较于PIN码配对具有更高的安全行、更方便的操作,从而现在市面上的蓝牙设备基本上都使用这种配对流程。

            两种配对流程的触发时机都是在鉴权过程中(Authentication Requested),向协议栈Host请求Link Key,Host回复没有该链路密钥,从而芯片开始配对流程。因为现在大部分产品用的都是SPP安全简单配对流程,所以我们重点分析SSP安全简单配对。
    3.2.1 SSP安全简单配对介绍
            新增SSP安全简单配对的主要目的是简化用户的配对过程,次要目的是维护或提高蓝牙无线技术的安全性。由于在许多技术和产品中,高安全和易于使用的程度往往是相反的,因此从最终用户的角度来看,最终就是为了最大限度地提高安全性,同时尽量减少复杂性。
    安全简单配对流程有如下两个安全目标:
    a.防止被动窃听
    B.防止中间人(MITM)攻击

    SSP流程根据两个配对连接的蓝牙设备双方的I/O能力来确定采用如下哪种模型:
    (1)  Numeric Comparison:数字比较模型,配对连接的两个蓝牙设备都有屏幕显示一个六位数字,并且有提供用户输入“Yes”或者“No”的选项。显示的这6位数字的范围是:000000~999999,用户比较两个设备上显示的数字是否一致。连个设备上都确定Yes,配对才能成功,这样可以有效防止中间人(MITM)攻击。
    使用场景:手机、车机、个人电脑等带有屏幕可以显示六位数字且提供Yes/No选项的设备间进行配对。
     
    (2)  Passkey Entry:密码输入模型,配对连接的设备中其中一个设备具有输入能力,但不具有显示六位数字的能力,另一个设备具有输出显示六位数字的能力。只具有输入能力的设备正确输入另一个设备上显示的六位数字,配对才能成功,有效防止中间人(MITM)攻击。
    使用场景:手机和蓝牙键盘这样的组合,一个设备只具有输入能力,另一个设备具备输出显示能力。
     
    (3)  Just Works:工作模型,配对连接的设备中至少有一台设备没有能够显示六位数字的显示器,也没有能够输入六位数字的键盘。该模型使用类似Numeric Comparison数字比较,但不会向用户显示那六位数字,应用程序可以简单地要求用户接受连接即可。由于看不到配对过程,总是同意配对,所以无法防止中间人(MITM)攻击。
    使用场景:蓝牙耳机和其他设备配对,因为大部分蓝牙耳机没有显示也没有数字输入功能。
     
    (4)  Out Of Band:简称OOB,两个配对设备通过其他途径交换配对信息,比如带有NFC功能的蓝牙音箱等,但此种模型很少使用到,本篇不做过多讲解。
          以上四种模型就是安全简单配对SSP的模型了,每种模型都有自己的使用场景,根据配对连接设备的具体条件来决定采用哪种模型来让用户确定是否配对。详细了解可以参考蓝牙核心协议《Core_v5.2.pdf》的Vol 1 -> Part A ->
    5.2.4Association models部分。
     
    3.2.2 SSP流程总体上的消息序列图如下

     从上图可以明显看出整个过程大概分为11个步骤,有些步骤存在不同的可选项,接下来依次讲解下每个步骤的流程。
     
    Step 1: Optional OOB Information
    Collection
    可选步骤,OOB信息收集。如果蓝牙设备支持OOB信息交换,则协议栈Host应该从蓝牙控制器Controller获取到OOB信息的C和R值,并将此信息传输到OOB系统。
    获取信息的流程图如下:

     如果协议栈Host和Controller都支持安全连接(安全连接见 Step 2分析),则流程图如下:

     Step 2: Enable Simple Pairing and
    SecureConnections
    必选步骤,根据本端Controller的能力来决定是否使能简单配对及安全连接。这两个使能动作应该在使用简单配对或创建连接之前完成。
    使能简单配对流程图如下:

     使能安全连接流程图如下:

     这两条使能指令的HCI信息如下:

     Step 3: Connection Establishment
    可选步骤,简单配对如果使能启用,那在之后的蓝牙交互过程中有两个可能的操作会触发SSP流程。
    a.l2cap链路连接请求触发到需要安全鉴定的服务
    b.OOB信息传输
    由于OOB在现有的蓝牙设备上使用比较少,所以大部分安全简单配对流程都是由安全服务的l2cap连接请求触发的,其流程图如下:

     Step 4: Start Simple Pairing
    必选步骤,协议栈主机Host确定启动简单配对,它就会向控制器Controller发出请求的身份鉴权验证命令。Controller接收到鉴权指令后会请求Host提供链路密钥(Link Key),如果Host有此连接的链接密钥,则不需要配对流程,直接身份验证通过后就可以立即使用该链接密钥;如果Host没有此链路密钥,则开启简单配对流程生成新的链路密钥。
    其流程图如下:

     HCI上的信息交互如图所示:

     Step 5: IO Capability Exchange
    必选步骤,为了确定使用SSP中的哪种身份验证模型,需要配对连接的两个设备相互交换输入/输出能力。流程图为:

     HCI交互信息如下图:

     Step 6: Public Key Exchange
    必选步骤,两个配对的设备间交换公钥,一旦接受到对端发送过来的公钥,本端就可以用此公钥来计算出DHKey,该流程协议栈Host不参与交互,完全是两个设备Controller间的信息交互。

    Step 7: Authentication
    必选步骤,蓝牙设备可以通过使用 Numeric Comparison、Passkey Entry、OOB 这三种鉴权模型之一进行身份验证,鉴权模型的选择取决于两个设备的IO能力的组合。
    Numeric Comparison:当两个设备都具有输出功能时,或者其中一个设备没有输入或输出功能,则将执行数字比较步骤。如果两个设备都有输出功能,则此步骤需要向用户显示确认值这个值应该显示到步骤8结束为止;如果一个或两个设备没有输出功能,则使用相同的协议,但是协议栈Host将跳过要求用户确认的环节,这就是Just Works模型。
    其流程图如下,该图省略了用户确认数字的计算过程:

     HCI信息交互为:

     Passkey Entry:在如下两种情况下使用Passkey Entry模型执行省份验证
    a.当一个设备仅有数字输入,而另一个设备具有显示或数字输入能力
    b.当两个设备都仅具有数字输入能力
    流程图如下:

     Out of Band:只有当两个设备都有一些OOB信息要使用时,OOB身份验证才会完成。此身份验证方式不需要用户参与。由于市面上使用该方法验证省份进行配对的蓝牙设备比较少,这里就不做深入分析了。


    Step 8: DHKey Checks
    必选步骤,一旦设备经过身份验证,并且DHKey计算已经完成(通过步骤6交换的公钥计算得到的),就会检查生成的DHKey值。如果成功,那么这两个设备都将完成向用户显示信息的操作,因此从Controller向Host发送消息通知双方停止显示此信息。
    流程图如下:

     Step 9: Calculate Link Key
    必选步骤,简单的配对完成,链接密钥(Link Key)就可以从DHKey中计算出来,这应该用作标准相互身份验证的输入,并通过HCI事件上报Host保存。
    流程图为:

     HCI上报信息如下图:

     Step 10: Enable Encryption
    必选步骤,鉴权随着Link Key的上报就完成了,那接下来Host就可以使用标准方法对数据交互进行加密。
    流程图为:

     HCI信息交互如下图所示:

     鉴权加密流程成功完成也即是SSP简单配对流程结束,接下来蓝牙l2cap链路就可以安全地建立连接了。

    蓝牙耳机扫描连接流程实例分析
    下面通过一个耳机连接手表的实例进行下梳理分析:
    耳机发起inquiry,发送ID packet,如下:

     详细的inquiry包数据如下:

     然后耳机发送HCI_Create_Connection命令给Controller,然后Controller通过指定的蓝牙地址去page希望配对的手表设备,下图为对应的HCI log和空口数据包:

     

     接下来两个设备的Controller之间exchange feature,获取对方的特性信息,如下图空口包所示

     然后耳机的Controller发送LMP_host_connection_req请求,远程手表设备Controller接受请求,HCI log和空口包如下图示:

     

     紧接着耳机发送LMP_set_AFH 和LMP_channel_classification_req请求,手表Controller回复LMP_channel_classification,空口包如下图示:

     

     然后两端的Controller使能简单配对和安全连接,HCI log如下图示:

     

     然后开始鉴权,耳机Controller将会为这个连接向host请求Link Key。在HCI中能看到Link key request的事件,鉴权过程的HCI log和空口包如下图示:

     

     

     

     耳机Controller向Host请求link key,host回复Negative replay,然后两个设备相互交换输入/输出能力,以确认使用SSP中的哪种身份验证模型,确认使用Just work模型。然后示user confirmation 用户确认。接着就是DHKey check,检验成功后这两个设备都将完成向用户显示信息的操作,因此从Controller向Host发送消息通知双方停止显示此信息。链接密钥(Link Key)就可以从DHKey中计算出来,这应该用作标准相互身份验证的输入,并通过HCI事件上报Host保存。

    Link Key的上报后鉴权就完成了,那接下来Host就可以使用标准方法对数据交互进行加密,HCI log和空口数据如下图示:

     

     鉴权加密流程成功完成也即是SSP简单配对流程结束,耳机与手表已经配对上。

     此时耳机与手表已经在物理通道上建立了链接,但上层应用若需要在设备之间通信,还需要在L2CAP层次建立连接,然后是SDP服务发现协议,之后RFCOMM建立连接,最后应用层建立连接。这些过程在这里经不详细介绍了,详细可以去查看具体的蓝牙协议学习了解。

     

    展开全文
  • 最近公司开发需要用到蓝牙,花了大约一天的时间总结整理了一下。主要是为了以后自己方便看。有需要的朋友可以看下。欢迎一起讨论。后面会带上博客。里面是实现了蓝牙搜索,配对,连接,数据互传。
  • 安卓系统蓝牙配对流程分析 配对流程基本上始于首次连接一个蓝牙设备的过程中,本端的搜索流程结束获取到该设备的BluetoothDevice信息,就可以开启配对流程。 配对,顾名思义就是将两个设备通过相关技术进行绑定,...

    安卓系统蓝牙配对流程分析

    在这里插入图片描述

    配对流程基本上始于首次连接一个蓝牙设备的过程中,本端的搜索流程结束获取到该设备的BluetoothDevice信息,就可以开启配对流程。

    配对,顾名思义就是将两个设备通过相关技术进行绑定,达到能够互相识别彼此的目的。因此通过蓝牙技术进行的配对就是大家理解中的蓝牙配对。随着蓝牙技术在消费市场上越来越普及,生活中很容易就能遇到蓝牙配对这一使用蓝牙功能事必不可缺失的步骤。本期我们就简单聊聊安卓系统蓝牙配对的流程是如何运行的。

    Application通过framework层BluetoothDevice对外提供的接口createBond()开启这一流程。createBondOutOfBand()通过OOB技术进行配对的接口,一般很少使用。所以应用层需要根据自己的实际使用场景选择正确的接口。

    蓝牙服务层在配对流程中基本没有复杂的操作,只是简单地下发指令成功后,通过JNI层的异步回调处理配对状态变化并将该变化对外广播。其时序图如下:
    在这里插入图片描述

    从上图我们唯一需要注意的点就是配对流程开始前需要停掉搜索扫描流程(如果底层正在进行该流程),增加该操作的主要目的是为了确保配对流程能够正常进行。因为搜索扫描和配对流程都会对远端设备进行Discovery操作,防止这两个流程冲突导致配对失败。

    协议栈接收到开始配对指令后,主要按照如下的配对状态机进行切换(Numeric Comparison模式):
    在这里插入图片描述

    • IDLE:初始状态值
    • GET_REM_NAME:获取远端设备的蓝牙名字
    • WAIT_PIN_REQ:等待Controller请求PIN
    • WAIT_LOCAL_IOCAPS:等待本端提供IO能力
    • WAIT_NUM_CONFIRM:等待本端Host确认配对
    • WAIT_AUTH_COMPLETE:等待鉴权(配对)流程完成

    状态切换对应到HCI上的交互见下图:
    在这里插入图片描述

    每一个配对状态的切换触发时机从上面这张图就十分明了,这里我就不做过多分析。感兴趣的同学可以尝试跟着上图中HCI命令跟踪下代码流程。

    协议栈的配对状态随着鉴权完成事件Authentication Complete的上报就切换到初始值了,那这时是不是就代表整个配对流程完成了呢?非也,其实在安卓系统中到这里还不代表配对已经完成,因为上报JNI层的配对状态变化回调并不是通过鉴权完成事件上报完成而回调的。

    配对流程的主要工作完成后,协议栈还会继续对该设备尝试进行SDP服务发现,搜索该设备支持哪些协议服务,SDP服务搜索完成后才会上报JNI层配对状态变成Bonded。

    触发SDP服务搜索的触发时机是处理上报Link Key的回调bta_dm_new_link_key_cback()中通过 event = BTA_DM_AUTH_CMPL_EVT 触发的。

    随后在函数btif_dm_auth_cmpl_evt()中尝试对该设备进行SDP服务发现,如下图:
    在这里插入图片描述

    SDP服务发现完成后通过回调处理函数btif_dm_search_services_evt()中 event = BTA_DM_DISC_RES_EVT上 报JNI 层配对状态变化到Bonded。

    至此安卓系统中蓝牙配对流程才算闭环完成,上述过程我以Numeric Comparison配对模型为依据介绍了整个流程,其他配对模型类似,就不一一做出说明了,但协议栈配对状态机的全部值如下,其他模型的配对流程也是在这几种状态机组合下进行切换运行的:

    enum {
      BTM_PAIR_STATE_IDLE, 					/* Idle */
      	
      BTM_PAIR_STATE_GET_REM_NAME, 			/* Getting the remote name
      												(to check for SM4) */
      BTM_PAIR_STATE_WAIT_PIN_REQ, 			/* Started authentication, waiting for PIN req
                                      				(PIN is pre-fetched) */
      BTM_PAIR_STATE_WAIT_LOCAL_PIN,       	/* Waiting for local PIN code */
      
      BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM, 	/* Waiting user 'yes' to numeric confirmation */
      
      BTM_PAIR_STATE_KEY_ENTRY, 			/* Key entry state (we are a keyboard) */
      
      BTM_PAIR_STATE_WAIT_LOCAL_OOB_RSP, 	/* Waiting for local response to peer OOB
      												data */
      BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS, 	/* Waiting for local IO capabilities and OOB
                                           			data */
      BTM_PAIR_STATE_INCOMING_SSP, 			/* Incoming SSP (got peer IO caps when idle) */
      
      BTM_PAIR_STATE_WAIT_AUTH_COMPLETE, 	/* All done, waiting authentication
                                            		cpmplete */
      BTM_PAIR_STATE_WAIT_DISCONNECT     	/* Waiting to disconnect the ACL */
    };
    

    配对流程实际上并不是孤立而存在的,配合着搜索扫描、连接等流程共同起作用的,彼此依赖,缺一不可。

    本期的分享就到这里,感兴趣的小伙伴欢迎私信留言一起讨论。

    更多互联互通技术,欢迎关注微信公众号:Connectivity
    在这里插入图片描述

    展开全文
  • Android 蓝牙配对、协议栈使能、inquiry、discovery、hci发送数据、等等详细源码流程图,非常详细的从btif-bta-btm-hci 数据流程走向,以及从controller收到数据到btm层,将Android 源码使用流程图的形式画了出来,...
  • Android 蓝牙配对连接源码分析文档大全,非常详细的从btif-bta-btm-hci 数据流程走向,以及从controller收到数据到btm层,将Android 源码每个函数都摘录出来,进行分析,使Android 蓝牙开发者更清楚数据收发走向,...
  • Android 蓝牙设备配对pair流程(协议栈)图,非常详细的从btif-bta-btm-hci 数据流程走向,以及从controller收到数据到btm层,将Android 源码使用流程图的形式画了出来,使Android 蓝牙开发者更清楚数据收发走向,...
  • Android 源码使用流程图的形式画了出来,使Android 蓝牙开发者更清楚数据收发走向,代码流程更加形象生动,能够很快的熟悉Android 蓝牙源码,利于后面问题处理和BUG解决。
  • 从上层的连接开始说起,当点击蓝牙进行配对的时候,调用create_bond开始配对连接,这里我们就介绍了蓝牙连接流程的前六步,也就是配对前的步骤,基本上和我们的连接流程吻合
  • Android蓝牙 上层(java-framework)层打开使能流程图,将Android 源码使用流程图的形式画了出来,使Android 蓝牙开发者更清楚数据收发走向,代码流程更加形象生动,能够很快的熟悉Android 蓝牙源码,利于后面问题...
  • Android 蓝牙设备配对pair流程(协议栈)图,非常详细的从btif-bta-btm-hci 数据流程走向,以及从controller收到数据到btm层,将Android 源码使用流程图的形式画了出来,使Android 蓝牙开发者更清楚数据收发走向,...
  • android-蓝牙A2dp-avrcp-hfp-opp-配对流程-ble-rfcomm源码流程 Android 蓝牙低功耗ble 广播、扫描、连接、数据读写源码流程分析大全 - 点击下载1、什么是SMP SMP (Security Manager Protocol)即安全管理协议。SMP是...
  • 蓝牙设备在连接前,会先检查设备是否已经配对过,如果没有则先配对配对完成后,再开始连接。onPreferenceTreeClick蓝牙连接开始于设备列表 DeviceListPreferenceFragment的onPreferenceTreeClick方法。...
  • android-蓝牙A2dp-avrcp-hfp-opp-配对流程-ble-rfcomm源码流程 Android 蓝牙配对连接源码分析文档大全 - 点击下载 蓝牙配对是整个蓝牙连接过程中必不可少的环节,配对的目的是通过各种方法创建共享的链路密钥(Link ...
  • Android系统下蓝牙自动配对连接方法

    千次阅读 2021-05-26 07:33:10
    Android系统下蓝牙自动配对连接方法【专利摘要】本发明涉及一种Android系统下蓝牙自动配对连接方法,其包括如下步骤:步骤1、在Android设备端内存储上次进行蓝牙连接蓝牙外设的蓝牙地址,并存储已配对蓝牙外设的蓝牙...
  • 先回顾一下上一篇文章《Android蓝牙开发系列文章-蓝牙音箱连接》讲到的蓝牙音箱的完成配对、连接的流程:扫描设备–监听DEVICE_FOUND广播–>直到找到目标设备–>对目标设备发起配对–>监听到设备配对成功–>发起设备...
  • 蓝牙配对HCI交互流程

    千次阅读 2021-10-02 10:00:41
    在HCI层看蓝牙的连接过程_启程-CSDN博客前言从蓝牙设备状态转换可以知道,蓝牙设备连接需经过Inquiry,page过程,本文主要从HCI角度分析连接过程。解析抓包文件用到的工具为Frontline 的Capture File Viewer。HCI简介...
  • 在已经搜索出来周围蓝牙的情况下,对你要点击的设备进行配对的步骤是:1.在点击显示周围蓝牙设备的listview的监听onitemclicklistener中,获取到点击的设备device,判断该设备是否已配对,如果已经配对了,用自己...
  • 前言前面两篇文章【Android】蓝牙开发——经典蓝牙(附完整Demo)和【Android】蓝牙开发——经典蓝牙配对介绍(通过手机系统蓝牙演示),分别介绍了经典蓝牙的开发流程以及通过手机系统蓝牙演示经典蓝牙的四种配对方式,...
  • 所以我必须提示用户选择要连接的设备所以我必须将用户连接到已经配对蓝牙设备.但我的努力都没有奏效.我尝试使用以下命令再次运行配对过程:tmp = device.createRfcommSocketToServiceRecord(MY_UUID);以及以下内容...
  • android-蓝牙A2dp-avrcp-hfp-opp-配对流程-ble-rfcomm源码流程 Android 蓝牙配对连接源码分析文档大全 - 点击下载 当对端获取到我们的io capabilities后,根据蓝牙连接流程spec,对端会发送一个confirmation ...
  • Android蓝牙自动配对Demo,亲测好使!!!

    万次阅读 多人点赞 2016-09-01 16:24:45
    蓝牙自动配对,即搜索到其它蓝牙设备之后直接进行配对,不需要弹出配对确认框或者密钥输入框。 经过最近一段时间得研究,针对网上给出的案例。总结了一个亲测好使的Demo。
  • 本文档将蓝牙A2dp播放流程进行了梳理,包括音频stream从audio侧到蓝牙侧的初始化工作、A2dp的sdp流程、A2dp 编解码协商流程、AVDTP连接流程等等
  • kangear注: ... 原文把图给搞丢了,...在BT2.1及之后版本,蓝牙协议有在传统的密码配对(PIN Code Pairing)之外,新增一种简单配对(Simple Pairing)的方式。这种新的配对方式操作更为简单、安全性也更强。目前市面..
  • Android蓝牙Socket通信流程解析。Android的蓝牙部分暂且分为2.0,4.0,虽然苹果已经出了5.0版本了,咱先不说这个。 简单讲解一下,4.0是兼容2.0的功能的,今天讲的bluetoothSocket通信是在2.0上面就已经有了。Android...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,603
精华内容 641
关键字:

android蓝牙配对流程