Android 9.0 Bluetooth原始碼分析(三)藍芽配對流程
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);// 第一次呼叫會走這裡 } /*Trackoriginator 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_TYPEdev_type; tBLE_ADDR_TYPEaddr_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進行配對狀態的變更。
到此,配對流程就分析結束了。