Android 9.0 Bluetooth原始碼分析(二)藍芽掃描流程
1 UI
藍芽開始掃描位於setting的 /packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothPairingDetail.java 中。
void enableScanning() { // Clear all device states before first scan if (!mInitialScanStarted) { if (mAvailableDevicesCategory != null) { removeAllDevices(); } mLocalManager.getCachedDeviceManager().clearNonBondedDevices(); mInitialScanStarted = true; } super.enableScanning(); }
在這裡如果沒有開始掃描就清除快取中所有的裝置,然後將快取的裝置中沒有配對的裝置清除,最後呼叫了父類 DeviceListPreferenceFragment 的enableScanning()方法:
@VisibleForTesting void enableScanning() { // LocalBluetoothAdapter already handles repeated scan requests mLocalAdapter.startScanning(true); mScanEnabled = true; }
這裡其實最終呼叫了/frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter的startScanning方法開始掃描。
2 framework
LocalBluetoothAdapter的startScanning方法:
public void startScanning(boolean force) { // Only start if we're not already scanning // 只會在沒有進行掃描的時候開始掃描 if (!mAdapter.isDiscovering()) { // 如果不是強制掃描,在SCAN_EXPIRATION_MS間隔內,只掃描一次 if (!force) { // Don't scan more than frequently than SCAN_EXPIRATION_MS, // unless forced if (mLastScan + SCAN_EXPIRATION_MS > System.currentTimeMillis()) { return; } // If we are playing music, don't scan unless forced. // 播放藍芽音樂時,不進行掃描 A2dpProfile a2dp = mProfileManager.getA2dpProfile(); if (a2dp != null && a2dp.isA2dpPlaying()) { return; } } // 開始掃描 if (mAdapter.startDiscovery()) { mLastScan = System.currentTimeMillis(); } } }
這裡最後呼叫frameworks/base/core/java/android/bluetooth/BluetoothAdapter.java的startDiscovery方法,啟動掃描任務
public boolean startDiscovery() { if (!Utils.checkCaller()) { Log.w(TAG, "startDiscovery() - Not allowed for non-active user"); return false; } AdapterService service = getService(); if (service == null) return false; return service.startDiscovery(); }
3 Bluetooth App
這裡的 service.startDiscovery 屬於 aidl 跨程序通訊,通過 IBluetooth.aidl 呼叫遠端服務 /packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService 中的 startDiscovery 方法:
boolean startDiscovery() { enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); //do not allow new connections with active multicast A2dpService a2dpService = A2dpService.getA2dpService(); if (a2dpService != null && a2dpService.isMulticastFeatureEnabled() && a2dpService.isMulticastOngoing(null)) { Log.i(TAG,"A2dp Multicast is Ongoing, ignore discovery"); return false; } // 掃描中則終止操作 if (mAdapterProperties.isDiscovering()) { Log.i(TAG,"discovery already active, ignore startDiscovery"); return false; } return startDiscoveryNative(); }
這裡最終呼叫tartDiscoveryNative()方法,從 Java 層調到 JNI 層的com_android_bluetooth_btservice_AdapterService.cpp檔案中的 startDiscoveryNative 方法:
static jboolean startDiscoveryNative(JNIEnv* env, jobject obj) { ALOGV("%s:",__FUNCTION__); jboolean result = JNI_FALSE; if (!sBluetoothInterface) return result; int ret = sBluetoothInterface->start_discovery();//該介面呼叫到hal層的配對函式 result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; return result; }
int ret = sBluetoothInterface->start_discovery() 這行程式碼調進了hal層的藍芽協議棧中。
4 藍芽協議棧
上面呼叫的start_discovery 方法位於 /system/bt/btif/src/bluetooth.cc:
static int start_discovery(void) { /* sanity check */ //完整性檢查 if (interface_ready() == FALSE) return BT_STATUS_NOT_READY; return btif_dm_start_discovery(); }
start_discovery方法呼叫/system/bt/btif/src/btif_dm.cc的btif_dm_start_discovery方法,btif_dm.cc 用於裝置管理相關的功能。
bt_status_t btif_dm_start_discovery(void) { tBTA_DM_INQ inq_params; tBTA_SERVICE_MASK services = 0; BTIF_TRACE_EVENT("%s : pairing_cb.state: 0x%x", __FUNCTION__, pairing_cb.state); /* We should not go for inquiry in BONDING STATE. */ // 處於繫結狀態,則不進行掃描 if (pairing_cb.state == BT_BOND_STATE_BONDING) return BT_STATUS_BUSY; /* Cleanup anything remaining on index 0 */ do_in_bta_thread( FROM_HERE, base::Bind(&BTM_BleAdvFilterParamSetup, BTM_BLE_SCAN_COND_DELETE, 0, nullptr, base::Bind(&bte_scan_filt_param_cfg_evt, 0))); auto adv_filt_param = std::make_unique<btgatt_filt_param_setup_t>(); /* Add an allow-all filter on index 0*/ adv_filt_param->dely_mode = IMMEDIATE_DELY_MODE; adv_filt_param->feat_seln = ALLOW_ALL_FILTER; adv_filt_param->filt_logic_type = BTA_DM_BLE_PF_FILT_LOGIC_OR; adv_filt_param->list_logic_type = BTA_DM_BLE_PF_LIST_LOGIC_OR; adv_filt_param->rssi_low_thres = LOWEST_RSSI_VALUE; adv_filt_param->rssi_high_thres = LOWEST_RSSI_VALUE; do_in_bta_thread( FROM_HERE, base::Bind(&BTM_BleAdvFilterParamSetup, BTM_BLE_SCAN_COND_ADD, 0, base::Passed(&adv_filt_param), base::Bind(&bte_scan_filt_param_cfg_evt, 0))); /* TODO: Do we need to handle multiple inquiries at the same time? */ /* Set inquiry params and call API */ inq_params.mode = BTA_DM_GENERAL_INQUIRY | BTA_BLE_GENERAL_INQUIRY; #if (BTA_HOST_INTERLEAVE_SEARCH == TRUE) inq_params.intl_duration[0] = BTIF_DM_INTERLEAVE_DURATION_BR_ONE; inq_params.intl_duration[1] = BTIF_DM_INTERLEAVE_DURATION_LE_ONE; inq_params.intl_duration[2] = BTIF_DM_INTERLEAVE_DURATION_BR_TWO; inq_params.intl_duration[3] = BTIF_DM_INTERLEAVE_DURATION_LE_TWO; #endif inq_params.duration = BTIF_DM_DEFAULT_INQ_MAX_DURATION; inq_params.max_resps = BTIF_DM_DEFAULT_INQ_MAX_RESULTS; inq_params.report_dup = true; inq_params.filter_type = BTA_DM_INQ_CLR; /* TODO: Filter device by BDA needs to be implemented here */ /* Will be enabled to true once inquiry busy level has been received */ btif_dm_inquiry_in_progress = false; /* find nearby devices */ BTA_DmSearch(&inq_params, services, bte_search_devices_evt); return BT_STATUS_SUCCESS; }
btif_dm_start_discovery方法中最終呼叫 /system/bt/bta/dm/bta_dm_api.cc中的 BTA_DmSearch(&inq_params, services, bte_search_devices_evt);
void BTA_DmSearch(tBTA_DM_INQ *p_dm_inq, tBTA_SERVICE_MASK services, tBTA_DM_SEARCH_CBACK *p_cback) { tBTA_DM_API_SEARCH*p_msg; if ((p_msg = (tBTA_DM_API_SEARCH *) GKI_getbuf(sizeof(tBTA_DM_API_SEARCH))) != NULL) { memset(p_msg, 0, sizeof(tBTA_DM_API_SEARCH)); p_msg->hdr.event = BTA_DM_API_SEARCH_EVT; memcpy(&p_msg->inq_params, p_dm_inq, sizeof(tBTA_DM_INQ)); p_msg->services = services; p_msg->p_cback = p_cback; p_msg->rs_res= BTA_DM_RS_NONE; bta_sys_sendmsg(p_msg);//傳送到另一個執行緒 } }
呼叫bta_sys_sendmsg向BTA傳送掃描任務訊息。bta_sys_sendmsg這是藍芽協議棧的程序間收發訊息的機制,不是三言兩語能說清楚的,若想了解,可以參考《android bluedroid協議棧裡面的各個元件之間的訊息處理機制》 這篇文章。
通過搜尋event標記BTA_DM_API_SEARCH_EVT可以找到/system/bt/bta/dm/bta_dm_act.cc裡面:
static void bta_dm_rs_cback(UNUSED_ATTR void* p1) { APPL_TRACE_WARNING("bta_dm_rs_cback:%d", bta_dm_cb.rs_event); if (bta_dm_cb.rs_event == BTA_DM_API_SEARCH_EVT) { bta_dm_cb.search_msg.rs_res = BTA_DM_RS_OK; /* do not care about the result for now */ bta_dm_cb.rs_event = 0; bta_dm_search_start((tBTA_DM_MSG*)&bta_dm_cb.search_msg);// 呼叫內部的開啟掃描方法 } }
這邊判斷了如果event是BTA_DM_API_SEARCH_EVT就呼叫bta本身的bta_dm_search_start方法:
void bta_dm_search_start(tBTA_DM_MSG* p_data) { tBTM_INQUIRY_CMPL result = {}; size_t len = sizeof(Uuid) * p_data->search.num_uuid; bta_dm_gattc_register(); APPL_TRACE_DEBUG("%s avoid_scatter=%d", __func__, p_bta_dm_cfg->avoid_scatter); if (p_bta_dm_cfg->avoid_scatter && (p_data->search.rs_res == BTA_DM_RS_NONE) && bta_dm_check_av(BTA_DM_API_SEARCH_EVT)) { LOG(INFO) << __func__ << ": delay search to avoid scatter"; memcpy(&bta_dm_cb.search_msg, &p_data->search, sizeof(tBTA_DM_API_SEARCH)); return; } BTM_ClearInqDb(nullptr);// 清除BTM中之前儲存的搜尋資料 /* save search params */ bta_dm_search_cb.p_search_cback = p_data->search.p_cback; bta_dm_search_cb.services = p_data->search.services; osi_free_and_reset((void**)&bta_dm_search_cb.p_srvc_uuid); if ((bta_dm_search_cb.num_uuid = p_data->search.num_uuid) != 0 && p_data->search.p_uuid != nullptr) { bta_dm_search_cb.p_srvc_uuid = (Uuid*)osi_malloc(len); *bta_dm_search_cb.p_srvc_uuid = *p_data->search.p_uuid; } result.status = BTM_StartInquiry((tBTM_INQ_PARMS*)&p_data->search.inq_params, bta_dm_inq_results_cb, bta_dm_inq_cmpl_cb);// 呼叫btm中的啟動掃描 APPL_TRACE_EVENT("%s status=%d", __func__, result.status); if (result.status != BTM_CMD_STARTED) { LOG(ERROR) << __func__ << ": BTM_StartInquiry returned " << std::to_string(result.status); result.num_resp = 0; bta_dm_inq_cmpl_cb((void*)&result); } }
這裡程式碼很多,大概就是先清除BTM中之前的搜尋資料,然後儲存搜尋的引數,呼叫btm中的BTM_StartInquiry方法進行掃描。
btm的掃描操作在/system/bt/stack/btm/btm_inq.cc裡面:
tBTM_STATUS BTM_StartInquiry(tBTM_INQ_PARMS* p_inqparms, tBTM_INQ_RESULTS_CB* p_results_cb, tBTM_CMPL_CB* p_cmpl_cb) { tBTM_INQUIRY_VAR_ST* p_inq = &btm_cb.btm_inq_vars; BTM_TRACE_API("BTM_StartInquiry: mode: %d, dur: %d, rsps: %d, flt: %d", p_inqparms->mode, p_inqparms->duration, p_inqparms->max_resps, p_inqparms->filter_cond_type); /* Only one active inquiry is allowed in this implementation. Also do not allow an inquiry if the inquiry filter is being updated */ if (p_inq->inq_active || p_inq->inqfilt_active) { /*check if LE observe is already running*/ if (p_inq->scan_type == INQ_LE_OBSERVE && p_inq->p_inq_ble_results_cb != nullptr) { BTM_TRACE_API("BTM_StartInquiry: LE observe in progress"); p_inq->scan_type = INQ_GENERAL; p_inq->inq_active = BTM_INQUIRY_INACTIVE; btm_cb.ble_ctr_cb.inq_var.scan_type = BTM_BLE_SCAN_MODE_NONE; // 向hci傳送開啟掃描訊息 btm_send_hci_scan_enable(BTM_BLE_SCAN_DISABLE, BTM_BLE_DUPLICATE_ENABLE); } else { LOG(ERROR) << __func__ << ": BTM_BUSY"; return (BTM_BUSY); } } else { p_inq->scan_type = INQ_GENERAL; } .............省略................. }
這邊就呼叫/system/bt/stack/btm/btm_ble_gap.cc裡面的btm_send_hci_scan_enable方法向hci傳送掃描的通知:
void btm_send_hci_scan_enable(uint8_t enable, uint8_t filter_duplicates) { if (controller_get_interface()->supports_ble_extended_advertising()) { btsnd_hcic_ble_set_extended_scan_enable(enable, filter_duplicates, 0x0000, 0x0000); } else { btsnd_hcic_ble_set_scan_enable(enable, filter_duplicates);//真正與hci打交道 } }
在這裡會判斷是否支援ble拓展,如果支援則呼叫另外一個掃描方法,這裡不具體分析,這裡只分析傳統的掃描流程,其實內部邏輯是差不多的。
如果不支援ble拓展,則呼叫/system/bt/stack/hcic/hciblecmds.cc的btsnd_hcic_ble_set_scan_enable方法:
184void btsnd_hcic_ble_set_scan_enable(uint8_t scan_enable, uint8_t duplicate) { 185BT_HDR* p = (BT_HDR*)osi_malloc(HCI_CMD_BUF_SIZE); 186uint8_t* pp = (uint8_t*)(p + 1); 187 188p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_WRITE_SCAN_ENABLE; 189p->offset = 0; 190 191UINT16_TO_STREAM(pp, HCI_BLE_WRITE_SCAN_ENABLE); 192UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_BLE_WRITE_SCAN_ENABLE); 193 194UINT8_TO_STREAM(pp, scan_enable); 195UINT8_TO_STREAM(pp, duplicate); 196 197btu_hcif_send_cmd(LOCAL_BR_EDR_CONTROLLER_ID, p);//這裡是向hci層發命令 198}
可以看出來,這裡是通過和hci層的通訊,host告訴controlor藍芽地址、資料、命令等,從而控制其底層硬體發起配對操作。具體btu如何與hci通訊,過程也是很繁瑣,可以參考《Android BT STACK BTU 和 HCI之間的訊息傳遞》 這篇文章。
到此開啟掃描的流程就結束了。有一個遺留問題就是開啟掃描之後,掃描到的藍芽裝置是怎麼返給上層的呢?
5 掃描結果返回
我們回頭看,之前呼叫了在/system/bt/bta/dm/bta_dm_act.cc裡面的bta_dm_search_start方法時,關鍵程式碼是:
result.status = BTM_StartInquiry((tBTM_INQ_PARMS*)&p_data->search.inq_params, bta_dm_inq_results_cb, bta_dm_inq_cmpl_cb);
這裡可以看到在呼叫btm的開始掃描方法的時候會傳入三個引數,第一個就是啟動掃描所需要傳下去的引數,第二個bta_dm_inq_results_cb就是掃描結果的回撥,第三個bta_dm_inq_cmpl_cb就是掃描完成的回撥。也就是說btm啟動掃描之後,掃描到藍芽裝置之後會通過bta_dm_inq_results_cb這個回撥返給bta,而掃描結束之後會通過bta_dm_inq_cmpl_cb這個回撥返給bta。
這裡我們拿bta_dm_inq_results_cb這個回撥來分析:
static void bta_dm_inq_results_cb(tBTM_INQ_RESULTS* p_inq, uint8_t* p_eir, uint16_t eir_len) { tBTA_DM_SEARCH result; tBTM_INQ_INFO* p_inq_info; uint16_t service_class; result.inq_res.bd_addr = p_inq->remote_bd_addr; memcpy(result.inq_res.dev_class, p_inq->dev_class, DEV_CLASS_LEN); BTM_COD_SERVICE_CLASS(service_class, p_inq->dev_class); result.inq_res.is_limited = (service_class & BTM_COD_SERVICE_LMTD_DISCOVER) ? true : false; result.inq_res.rssi = p_inq->rssi; result.inq_res.ble_addr_type = p_inq->ble_addr_type; result.inq_res.inq_result_type = p_inq->inq_result_type; result.inq_res.device_type = p_inq->device_type; result.inq_res.flag = p_inq->flag; /* application will parse EIR to find out remote device name */ result.inq_res.p_eir = p_eir; result.inq_res.eir_len = eir_len; p_inq_info = BTM_InqDbRead(p_inq->remote_bd_addr); if (p_inq_info != NULL) { /* initialize remt_name_not_required to false so that we get the name by * default */ result.inq_res.remt_name_not_required = false; } if (bta_dm_search_cb.p_search_cback) bta_dm_search_cb.p_search_cback(BTA_DM_INQ_RES_EVT, &result); if (p_inq_info) { /* application indicates if it knows the remote name, inside the callback copy that to the inquiry data base*/ if (result.inq_res.remt_name_not_required) p_inq_info->appl_knows_rem_name = true; } }
這裡面把掃描到的device資料放入result裡面通過bta_dm_search_cb.p_search_cback(BTA_DM_INQ_RES_EVT, &result)回傳。event是BTA_DM_INQ_RES_EVT,通過搜尋這個BTA_DM_INQ_RES_EVT發現在/system/bt/btif/src/btif_dm.cc裡面有一個bte_search_devices_evt方法:
static void bte_search_devices_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data) { UINT16 param_len = 0; if (p_data) param_len += sizeof(tBTA_DM_SEARCH); /* Allocate buffer to hold the pointers (deep copy). The pointers will point to the end of the tBTA_DM_SEARCH */ switch (event) { case BTA_DM_INQ_RES_EVT: { if (p_data->inq_res.p_eir) param_len += HCI_EXT_INQ_RESPONSE_LEN; } break; case BTA_DM_DISC_RES_EVT: { if (p_data->disc_res.raw_data_size && p_data->disc_res.p_raw_data) param_len += p_data->disc_res.raw_data_size; } break; } BTIF_TRACE_DEBUG("%s event=%s param_len=%d", __FUNCTION__, dump_dm_search_event(event), param_len); /* if remote name is available in EIR, set teh flag so that stack doesnt trigger RNR */ if (event == BTA_DM_INQ_RES_EVT) p_data->inq_res.remt_name_not_required = check_eir_remote_name(p_data, NULL, NULL); btif_transfer_context (btif_dm_search_devices_evt , (UINT16) event, (void *)p_data, param_len, (param_len > sizeof(tBTA_DM_SEARCH)) ? search_devices_copy_cb : NULL); }
這個bte_search_devices_evt方法就是掃描任務的回撥函式,當掃描到裝置時,回撥這個方法,將上下文從BTE切換到BTIF
再呼叫btif_dm_search_devices_evt,將掃描到的裝置通過HAL_CBACK方式返回。
static void btif_dm_search_devices_evt (UINT16 event, char *p_param) { tBTA_DM_SEARCH *p_search_data; BTIF_TRACE_EVENT("%s event=%s", __FUNCTION__, dump_dm_search_event(event)); switch (event) { case BTA_DM_DISC_RES_EVT: { p_search_data = (tBTA_DM_SEARCH *)p_param; /* Remote name update */ if (strlen((const char *) p_search_data->disc_res.bd_name)) { bt_property_t properties[1]; bt_bdaddr_t bdaddr; bt_status_t status; properties[0].type = BT_PROPERTY_BDNAME; properties[0].val = p_search_data->disc_res.bd_name; properties[0].len = strlen((char *)p_search_data->disc_res.bd_name); bdcpy(bdaddr.address, p_search_data->disc_res.bd_addr); status = btif_storage_set_remote_device_property(&bdaddr, &properties[0]); ASSERTC(status == BT_STATUS_SUCCESS, "failed to save remote device property", status); HAL_CBACK(bt_hal_cbacks, remote_device_properties_cb, status, &bdaddr, 1, properties); } /* TODO: Services? */ } ..........................省略............................ } }
掃描到裝置時,呼叫的是HAL_CBACK(bt_hal_cbacks, device_found_cb,num_properties, properties);
device_found_cb即是device_found_callback方法,在com_android_bluetooth_btservice_AdapterService.cpp中實現
static void device_found_callback(int num_properties, bt_property_t *properties) { jbyteArray addr = NULL; int addr_index; for (int i = 0; i < num_properties; i++) { if (properties[i].type == BT_PROPERTY_BDADDR) { addr = callbackEnv->NewByteArray(properties[i].len); if (addr) { callbackEnv->SetByteArrayRegion(addr, 0, properties[i].len, (jbyte*)properties[i].val); addr_index = i; } else { ALOGE("Address is NULL (unable to allocate) in %s", __FUNCTION__); return; } } } if (addr == NULL) { ALOGE("Address is NULL in %s", __FUNCTION__); return; } ALOGV("%s: Properties: %d, Address: %s", __FUNCTION__, num_properties, (const char *)properties[addr_index].val); remote_device_properties_callback(BT_STATUS_SUCCESS, (bt_bdaddr_t *)properties[addr_index].val, num_properties, properties); callbackEnv->CallVoidMethod(sJniCallbacksObj, method_deviceFoundCallback, addr); checkAndClearExceptionFromCallback(callbackEnv, __FUNCTION__); callbackEnv->DeleteLocalRef(addr); }
該方法對應JNI方法為method_deviceFoundCallback,即
jclass jniCallbackClass = env->FindClass("com/android/bluetooth/btservice/JniCallbacks"); .................. method_deviceFoundCallback = env->GetMethodID(jniCallbackClass, "deviceFoundCallback", "([B)V");
對應JniCallbacks.java的deviceFoundCallback方法。
void deviceFoundCallback(byte[] address) { mRemoteDevices.deviceFoundCallback(address); }
deviceFoundCallback將掃描到的裝置通過廣播發送出去
void deviceFoundCallback(byte[] address) { // The device properties are already registered - we can send the intent // now BluetoothDevice device = getDevice(address); debugLog("deviceFoundCallback: Remote Address is:" + device); DeviceProperties deviceProp = getDeviceProperties(device); if (deviceProp == null) { errorLog("Device Properties is null for Device:" + device); return; } Intent intent = new Intent(BluetoothDevice.ACTION_FOUND); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothDevice.EXTRA_CLASS, new BluetoothClass(deviceProp.mBluetoothClass)); intent.putExtra(BluetoothDevice.EXTRA_RSSI, deviceProp.mRssi); intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProp.mName); mAdapterService.sendBroadcastMultiplePermissions(intent, new String[] {AdapterService.BLUETOOTH_PERM, android.Manifest.permission.ACCESS_COARSE_LOCATION}); }
只要是在上層app中註冊android.bluetooth.device.action.FOUND就都可以收到該廣播。