Bluetooth源碼分析(三)藍(lán)牙配對流程

先附上總結(jié)的時(shí)序圖:

藍(lán)牙配對時(shí)序圖.png

1 UI

藍(lán)牙配對開始于settings設(shè)備列表 /packages/apps/Settings/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java中。
DeviceListPreferenceFragment是藍(lán)牙掃描到的設(shè)備列表,點(diǎn)擊其中一個(gè)藍(lán)牙設(shè)備,調(diào)用onPreferenceTreeClick方法開始藍(lán)牙的配對過程。

    @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方法中調(diào)用/packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothDevicePreference.java的onClicked方法:

      void onClicked() {
          Context context = getContext();
          int bondState = mCachedDevice.getBondState();// 獲取設(shè)備的綁定狀態(tài)

          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);// 已綁定,則進(jìn)行連接
          } 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();// 如果未綁定,則進(jìn)行配對
          }
      }

這里先獲取mCachedDevice的綁定狀態(tài),如果已經(jīng)連接,則詢問是否斷開;如果已經(jīng)綁定未連接,則開始連接;如果未連接也未綁定,則開始配對。這里我們先看配對。配對調(diào)用的是本地的pair方法:

      private void pair() {
          if (!mCachedDevice.startPairing()) {
              Utils.showError(getContext(), mCachedDevice.getName(),
                      R.string.bluetooth_pairing_error_message);
          }
      }

pair方法會調(diào)用/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
        // 配對時(shí),如果正在掃描,則取消掃描
        if (mLocalAdapter.isDiscovering()) {
            mLocalAdapter.cancelDiscovery();
        }
        // 開始配對
        if (!mDevice.createBond()) {
            return false;
        }
        // 標(biāo)識位,配對完成后,自動連接
        mConnectAfterPairing = true;  // auto-connect after pairing
        return true;
    }

createBond調(diào)用/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接著調(diào)用IBluetooth的createBond方法,通過aidl方式調(diào)用藍(lán)牙遠(yuǎn)程服務(wù)。


3 Bluetooth app

和藍(lán)牙掃描一樣,實(shí)現(xiàn)IBluetooth接口的類是AdapterServiceBinder,AdapterServiceBinder實(shí)現(xiàn)IBluetooth.Stub接口,是/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService的私有內(nèi)部類,AdapterServiceBinder收到的操作,都會轉(zhuǎn)交AdapterService處理,所以會調(diào)用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();// 配對過程,取消掃描
        // 給配對的狀態(tài)機(jī)發(fā)消息,創(chuàng)建了BondStateMachine.CREATE_BOND
        Message msg = mBondStateMachine.obtainMessage(BondStateMachine.CREATE_BOND);
        msg.obj = device;
        msg.arg1 = transport;
        mBondStateMachine.sendMessage(msg);
        return true;
    }

createBond 方法會檢查一下遠(yuǎn)程設(shè)備屬性信息,取消藍(lán)牙掃描任務(wù),將配對任務(wù)轉(zhuǎn)交mBondStateMachine,由狀態(tài)機(jī)處理該信息。

@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處理服務(wù)發(fā)送過來的BondStateMachine.CREATE_BOND消息 ,在processMessage 中調(diào)用 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) {// 判斷是否借助其他硬件進(jìn)行無綁定配對
                  result = mAdapterService.createBondOutOfBandNative(addr, transport, oobData);
              } else {
                  result = mAdapterService.createBondNative(addr, transport);// 調(diào)用到JNI層,進(jìn)行配對
              }

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

createBondNative方法實(shí)現(xiàn)在/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;
    }
    // 調(diào)用到hal層的配對函數(shù)
    int ret = sBluetoothInterface->create_bond((RawAddress*)addr, transport);
    env->ReleaseByteArrayElements(address, addr, 0);
    return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
  }

這里通過create_bond這個(gè)方法調(diào)用到了藍(lán)牙協(xié)議棧里面。


4 藍(lán)牙協(xié)議棧

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方法調(diào)用/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);
    // 如果如果不是未配對狀態(tài),則取消配對
    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在上面已經(jīng)傳入了要綁定的藍(lán)牙地址,
    // 會分別發(fā)送給底層兩部分,最后會調(diào)用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方法最終調(diào)用了本地的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: {// 根據(jù)傳入的事件,走這里進(jìn)行配對
        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;
      ...........................省略......................................
    }
 }

這里又調(diào)用本地的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);
    // 這里開始回調(diào),將綁定狀態(tài)變成綁定中
    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);// 第一次調(diào)用會走這里
    }
    /*  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)
{
    // 調(diào)用bta的bta_dm_bond方法
    do_in_bta_thread(FROM_HERE, base::Bind(bta_dm_bond, bd_addr, transport));
}

這里通過do_in_bta_thread調(diào)用/system/bt/bta/dm/bta_dm_act.cc里面的bta_dm_bond方法,進(jìn)入bta進(jìn)程:

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);// 配對事件回調(diào)
    }
}

然后來到\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);
}

調(diào)用本地btm_sec_bond_by_transport方法,這個(gè)方法內(nèi)容很多,著重看這段代碼:

 if (!controller_get_interface()->supports_simple_pairing())//這里做一個(gè)判斷,看是否支持簡單配對方式
    {
        /* 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層打交道
        }
    }

這里調(diào)用system/bt/stack/hcic/hcicmds.cc的btsnd_hcic_write_pin_type方法通過HCI向底層發(fā)送命令進(jìn)行控制

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層發(fā)命令,
}

可以看出,這里是通過和hci層的通信,host告訴controlor藍(lán)牙地址、數(shù)據(jù)、命令等,從而控制其底層硬件發(fā)起配對操作。具體btu如何與hci通信,過程也是很繁瑣,可以參考《Android BT STACK BTU 和 HCI之間的消息傳遞》這篇文章。
到此綁定的流程就結(jié)束了。有一個(gè)遺留問題就是綁定狀態(tài)是如何返回給上層的呢?


5 配對狀態(tài)改變的回傳

上文我們在bta里面調(diào)用/system/bt/bta/dm/bta_dm_act.cc里面的bta_dm_bond方法,進(jìn)行配對,這個(gè)方法里面有這樣一段代碼:

bta_dm_cb.p_sec_cback(BTA_DM_AUTH_CMPL_EVT, &sec_event);

這個(gè)就是bta的回調(diào)函數(shù),回調(diào)事件是BTA_DM_AUTH_CMPL_EVT,根據(jù)這個(gè)事件標(biāo)志,我們找到了 /system/bt/btif/src/btif_dm.cc里面的btif_dm_upstreams_evt方法,這個(gè)方法就是用于向上層回調(diào)消息的,相關(guān)代碼是:

     case BTA_DM_AUTH_CMPL_EVT:
        btif_dm_auth_cmpl_evt(&p_data->auth_cmpl);
        break;

可以看到是調(diào)用這個(gè)函數(shù),返回配對完成的事件,這個(gè)函數(shù)代碼很多這里就不引用了,無論配對成功還是失敗,這里都會用 bond_state_changed這個(gè)方法進(jìn)行處理:

  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__);
    }
  }

可以發(fā)現(xiàn)也是通過HAL_CBACK(bt_hal_cbacks, bond_state_changed_cb, status, &tmp, state);這樣的方法進(jìn)行回調(diào)的,bond_state_changed_cb這個(gè)函數(shù)在bluetooth.h被定義對應(yīng)的是com_android_bluetooth_btservice_AdapterService.cpp里的bond_state_changed_callback,關(guān)鍵代碼如下:

  sCallbackEnv->CallVoidMethod(sJniCallbacksObj, method_bondStateChangeCallback,
                                 (jint)status, addr.get(), (jint)state);

這里將bondStateChangeCallback方法對應(yīng)到j(luò)ni的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);
      }

接下來便進(jìn)入了/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/BondStateMachine.java狀態(tài)機(jī)里面:

      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);
      }

狀態(tài)機(jī)里面通過sendMessage進(jìn)行配對狀態(tài)的變更。
到此,配對流程就分析結(jié)束了。

作者:貓疏
鏈接:http://www.itdecent.cn/p/0b748b11fa62

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容