android 蓝牙扫描流程,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();

/* 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方法:

184 void btsnd_hcic_ble_set_scan_enable(uint8_t scan_enable, uint8_t duplicate) {

185 BT_HDR* p = (BT_HDR*)osi_malloc(HCI_CMD_BUF_SIZE);

186 uint8_t* pp = (uint8_t*)(p + 1);

187

188 p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_WRITE_SCAN_ENABLE;

189 p->offset = 0;

190

191 UINT16_TO_STREAM(pp, HCI_BLE_WRITE_SCAN_ENABLE);

192 UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_BLE_WRITE_SCAN_ENABLE);

193

194 UINT8_TO_STREAM(pp, scan_enable);

195 UINT8_TO_STREAM(pp, duplicate);

196

197 btu_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就都可以收到该广播。

参考文档