必务先看“connectGatt(1/4):从frameworks到bt-JNI”,看过它才能知道sGattIf指向bt协议栈的btgattInterface等概念。

一、app代码
使用BluetoothLeScanner,指定ScanCallback进行扫描。
BluetoothLeScanner mLEScanner; ScanCallback mScanCallback = {} // 一个app提供的回调结构,包括onScanResult。 public void startScan() { ScanSettings settings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build(); List<ScanFilter> filters = new ArrayList<ScanFilter>(); settings = setScanSettings(); // An app must hold ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission in order to get results. mLEScanner.startScan(mScanFilters, settings, mScanCallback); }
List<ScanFilter> mScanFilters
bt协议栈扫描到一个peripheral,并通知到bt-JNI。bt-JNI用它和peripheral广播出的数据进行匹配,匹配通过的话,就上报给app的onScanResult,否则不上报。行为发生在GattService.onScanResultInternal中的matchesFilters。它是在扫描到peripheral后才进行筛选,不会影响发到蓝牙硬件模块的扫描命令参数。
ScanSettings setting
类型 | SCAN_MODE_LOW_POWER(0) | SCAN_MODE_BALANCED(1) | SCAN_MODE_LOW_LATENCY(2) |
扫描窗口(scan window) | 512毫秒/819 | 1024毫秒/1638 | 4096毫秒/6553 |
扫描间隔(scan interval) | 5120毫秒/8192 | 4096毫秒/6553 | 4096毫秒/6553 |
用于设置扫描参数。当中有较多字段,这里只说三个。
- mScanMode。默认SCAN_MODE_LOW_POWER(0),其它常用值:SCAN_MODE_BALANCED(1)、SCAN_MODE_LOW_LATENCY(2)。该值决定蓝牙硬件模块要使用的扫描间隔,什么是扫描间隔参考“ble原理(4)主机的扫描间隔”。因为设置的扫描间隔、扫描窗口必须是0.625ms的整倍数,“4096毫秒/6553”中65535就是4096除以0.625之后的值,也是送到bt协议栈的值。额外说下,一个扫描窗口能扫到多个peripheral,不是只限一个。librose这么填这个值:如果该app在前台,使用SCAN_MODE_BALANCED,在后台用SCAN_MODE_LOW_POWER。
- mCallbackType。不会影响蓝牙硬件模块扫描参数。默认CALLBACK_TYPE_ALL_MATCHES(1)。位掩码类型标记。如果想收到扫描到的peripheral,必须包含CALLBACK_TYPE_ALL_MATCHES。GattService.onScanResultInternal有在用mCallbackType。
- mLegacy。不会影响蓝牙硬件模块扫描参数。默认true。是否只支持legacy广播。legacy最多支持62字节的广播数据,其中31字节的定时包,31字节的扫描回复包。GattService.onScanResultInternal有在用mLegacy。
二、frameworks:BluetoothLeScanner、BleScanCallbackWrapper
<aosp>/frameworks/base/core/java/android/bluetooth/le/BluetoothLeScanner.java ------ public void startScan(List<ScanFilter> filters, ScanSettings settings, final ScanCallback callback) { startScan(filters, settings, null, callback, /*callbackIntent=*/ null, null); } 三参数版本startScan转到6参数版本。 private int startScan(List<ScanFilter> filters, ScanSettings settings, final WorkSource workSource, final ScanCallback callback, final PendingIntent callbackIntent, List<List<ResultStorageDescriptor>> resultStorages) { // 从3参数版本可看到,此处的workSource、callbackIntent、resultStorages可能是null。 // callback、callbackIntent不能同时是null。 synchronized (mLeScanClients) { if (callback != null && mLeScanClients.containsKey(callback)) { return postCallbackErrorOrReturn(callback, ScanCallback.SCAN_FAILED_ALREADY_STARTED); } IBluetoothGatt gatt; try { gatt = mBluetoothManager.getBluetoothGatt(); } catch (RemoteException e) { gatt = null; } ... if (callback != null) { // 进这个入口 BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters, settings, workSource, callback, resultStorages); wrapper.startRegistration(); } else { try { gatt.startScanForIntent(callbackIntent, settings, filters, mAttributionSource); } catch (RemoteException e) { return ScanCallback.SCAN_FAILED_INTERNAL_ERROR; } } } return ScanCallback.NO_ERROR; }
对gatt,之前已分析过,它是bt-jni中binder本地对象BluetoothGattBinder的代理对象。“callback != null”时,创建BleScanCallbackWrapper,并调用startRegistration方法。
private class BleScanCallbackWrapper extends IScannerCallback.Stub { public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt, List<ScanFilter> filters, ScanSettings settings, WorkSource workSource, ScanCallback scanCallback, List<List<ResultStorageDescriptor>> resultStorages) { mBluetoothGatt = bluetoothGatt; mFilters = filters; mSettings = settings; mWorkSource = workSource; mScanCallback = scanCallback; mScannerId = 0; mResultStorages = resultStorages; } public void startRegistration() { synchronized (this) { // Scan stopped. ... try { // 调用BluetoothGattBinder.registerScanner。 mBluetoothGatt.registerScanner(this, mWorkSource, mAttributionSource); wait(REGISTRATION_CALLBACK_TIMEOUT_MILLIS); } ... } } }
startRegistration的主要任务是调用mBluetoothGatt.registerScanner,即BluetoothGattBinder.registerScanner。至此进入bt-JNI。
二、bt-JNI:BluetoothGattBinder.registerScanner
<aosp>/packages/apps/Bluetooth/src/com/android/bluetooth/gatt/GattService.java ------ public class GattService extends ProfileService { private ScanManager mScanManager; private static class BluetoothGattBinder extends IBluetoothGatt.Stub implements IProfileServiceBinder { public void registerScanner(IScannerCallback callback, WorkSource workSource, AttributionSource attributionSource) throws RemoteException { GattService service = getService(); if (service == null) { return; } service.registerScanner(callback, workSource, attributionSource); } } void registerScanner(IScannerCallback callback, WorkSource workSource, AttributionSource attributionSource) throws RemoteException { if (!Utils.checkScanPermissionForDataDelivery( this, attributionSource, "GattService registerScanner")) { return; } UUID uuid = UUID.randomUUID(); if (DBG) { Log.d(TAG, "registerScanner() - UUID=" + uuid); } enforceImpersonatationPermissionIfNeeded(workSource); AppScanStats app = mScannerMap.getAppScanStatsByUid(Binder.getCallingUid()); if (app != null && app.isScanningTooFrequently() && !Utils.checkCallerHasPrivilegedPermission(this)) { Log.e(TAG, "App '" + app.appName + "' is scanning too frequently"); callback.onScannerRegistered(ScanCallback.SCAN_FAILED_SCANNING_TOO_FREQUENTLY, -1); return; } mScannerMap.add(uuid, workSource, callback, null, this); mScanManager.registerScanner(uuid); } }
BluetoothGattBinder.registerScanner逻辑很简单,调用service.registerScanner,sevice是GattService。GattService.registerScanner主要两个操作。
- 随机生成一个uuid,类似connectGatt,将作为唯一标识,用于在扫描client集合中作为key。
- mScanManager.registerScanner(uuid)。调用ScanManager.registerScanner,继续注册Scanner。
<aosp>/packages/apps/Bluetooth/src/com/android/bluetooth/gatt/ScanManager.java ------ public class ScanManager { private ScanNative mScanNative; void registerScanner(UUID uuid) { mScanNative.registerScannerNative(uuid.getLeastSignificantBits(), uuid.getMostSignificantBits()); } } private class ScanNative { private native void registerScannerNative(long appUuidLsb, long appUuidMsb); }
ScanManager.registerScanner调用mScanNative.registerScannerNative,后者出现了Native字样,直觉会猜这是用c/c++实现。的确如此,此至要转入c/c++代码。
<aosp>/packages/apps/Bluetooth/jni/com_android_bluetooth_gatt.cpp ------ static JNINativeMethod sScanMethods[] = { {"registerScannerNative", "(JJ)V", (void*)registerScannerNative}, {"unregisterScannerNative", "(I)V", (void*)unregisterScannerNative}, {"gattClientScanNative", "(Z)V", (void*)gattClientScanNative}, ...... }; static void registerScannerNative(JNIEnv* env, jobject object, jlong app_uuid_lsb, jlong app_uuid_msb) { if (!sGattIf) return; Uuid uuid = from_java_uuid(app_uuid_msb, app_uuid_lsb); sGattIf->scanner->RegisterScanner( uuid, base::Bind(&btgattc_register_scanner_cb, uuid)); }
sGattIf指向bt协议栈的btgattInterface。sGattIf->scanner指向bt协议栈的get_ble_scanner_instance()。
三、bt协议栈:btLeScannerInstance->RegisterScanner
<aosp>/system/bt/btif/src/btif_ble_scanner.cc ------ BleScannerInterface* get_ble_scanner_instance() { if (bluetooth::shim::is_gd_scanning_enabled()) { LOG_INFO("Use gd le scanner"); return bluetooth::shim::get_ble_scanner_instance(); } else if (btLeScannerInstance == nullptr) { btLeScannerInstance = new BleScannerInterfaceImpl(); } return btLeScannerInstance; }
btLeScannerInstance是个指向BleScannerInterface指针,不同主板可能使用不同实现,ROC-RK3588S-PC用的是BleScannerInterfaceImpl。BleScannerInterfaceImpl是一个class,不是c struct,scanner->RegisterScanner(...)是调用BleScannerInterfaceImpl成员函数RegisterScanner。
<aosp>/system/bt/btif/src/btif_ble_scanner.cc ------ class BleScannerInterfaceImpl : public BleScannerInterface { ~BleScannerInterfaceImpl() override{}; void RegisterScanner(const bluetooth::Uuid& app_uuid, RegisterCallback cb) override { do_in_main_thread(FROM_HERE, Bind( [](RegisterCallback cb) { BTA_GATTC_AppRegister( bta_cback, jni_thread_wrapper(FROM_HERE, std::move(cb)), false); }, std::move(cb))); } };
在bt_main_thread线程执行BTA_GATTC_AppRegister。对它,“connectGatt(2/4):btif_gattc_register_app”(https://www.cswamp.com/post/55)有详细介绍。它依次执行两个任务。1)如果没有注册过BTA_ID_GATTC事件类处理函数,调用bta_sys_register执行注册。2)生成一个随机uuid,在bt_main_thread执行bta_gattc_register。以下是第二步bta_gattc_register执行的四个操作。
- 遍历cl_rcb列表,找到一个未使用(in_use==false)插槽,然后调用GATT_Register算出client_if,告知我这个app使用这个插槽了。注意,client_if值是占用单元在一个数组内从1开始的索引,但这个数组不是插槽所在的cl_rcb,而是全局数组gatt_cb.cl_rcb[GATT_MAX_APPS]。因为GATT_MAX_APPS值是32,所以client_if范围是[1, 32]。
- 给bta_gattc_cb.cl_rcb[i]各字段赋值。client_if、in_use、p_cback、app_uuid。额外说下p_cback,它的值来自传给BTA_GATTC_AppRegister的第一个参数,即bta_gattc_cback。后面多个地方会发现,bt协议栈向回调bt-JNI中函数靠的就是它。
- 在bt_main_thread执行bta_gattc_start_if。
- cb.Run(client_if, status)。把此次注册结果返回给bt-JNI,返回说来就是回调bt-JNI注册的回调函数。
可以看到,扫描和connectGatt一样,也会占用一个client_if。
第2个操作赋值的p_cback,值来自传给BTA_GATTC_AppRegister的第一个参数,在这里是bta_cback。
<aosp>/system/bt/btif/src/btif_ble_scanner.cc ------ void bta_cback(tBTA_GATTC_EVT, tBTA_GATTC*) {}
这就是个空操作。所以类似connectGatt的“(*p_clreg->p_cback)(BTA_GATTC_OPEN_EVT, &cb_data)”就没有意义了。p_clreg->p_cback功能是回调bt-JNI中函数,没了这方法,扫描时需要回调bt-JNI,是怎么实现的?
- register_scanner成功后,通过这里的cb.Run回调。
- 扫描到一个peripheral,通过bta_scan_results_cb回调。
当然,还会有其它回调,总之,因为回调类型少,扫描过程时不需要p_clreg->p_cback这种“统一”回调途径。
继续说第四个操作,cb.Run(client_if, status)。溯源cb,它是bt-JNI中的btgattc_register_scanner_cb,调用它,意味着在回调bt-JNI。
四、bt-JNI:btgattc_register_scanner_cb
此时bt协议栈已为此次扫描注册了一个client_if,client_if传给这里的参数scannerId。
<aosp>/packages/apps/Bluetooth/jni/com_android_bluetooth_gatt.cpp ------ void btgattc_register_scanner_cb(const Uuid& app_uuid, uint8_t scannerId, uint8_t status) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onScannerRegistered, status, scannerId, UUID_PARAMS(app_uuid)); } static void classInitNative(JNIEnv* env, jclass clazz) { // Client callbacks ... method_onScannerRegistered = env->GetMethodID(clazz, "onScannerRegistered", "(IIJJ)V"); method_onScanResult = env->GetMethodID(clazz, "onScanResult", "(IILjava/lang/String;IIIIII[BLjava/lang/String;)V"); ... };
clazz的是GattService,method_onScannerRegistered就是GattService.onScannerRegistered。
<aosp>/packages/apps/Bluetooth/src/com/android/bluetooth/gatt/GattService.java ------ public class GattService extends ProfileService { void onScannerRegistered(int status, int scannerId, long uuidLsb, long uuidMsb) throws RemoteException { UUID uuid = new UUID(uuidMsb, uuidLsb); if (DBG) { Log.d(TAG, "onScannerRegistered() - UUID=" + uuid + ", scannerId=" + scannerId + ", status=" + status); } // First check the callback map ScannerMap.App cbApp = mScannerMap.getByUuid(uuid); if (cbApp != null) { ... if (cbApp.callback != null) { cbApp.callback.onScannerRegistered(status, scannerId); } } } }
onScannerRegistered主要操作是调用cbApp.callback.onScannerRegistered。cbApp.callback是什么?——framewaorks中的BleScanCallbackWrapper,继续向上到frameworks代码。
private class BleScanCallbackWrapper extends IScannerCallback.Stub { public void onScannerRegistered(int status, int scannerId) { Log.d(TAG, "onScannerRegistered() - status=" + status + " scannerId=" + scannerId + " mScannerId=" + mScannerId); synchronized (this) { if (status == BluetoothGatt.GATT_SUCCESS) { try { if (mScannerId == -1) { // Registration succeeds after timeout, unregister scanner. mBluetoothGatt.unregisterScanner(scannerId, mAttributionSource); } else { mScannerId = scannerId; mBluetoothGatt.startScan(mScannerId, mSettings, mFilters, mResultStorages, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "fail to start le scan: " + e); mScannerId = -1; } } ... } } }
此时mScannerId是个[1, 32]范围内的client_if,将执行mBluetoothGatt.startScan。mBluetoothGatt是bt-JNI的BluetoothGattBinder,这是第二次执行BluetoothGattBinder中方法了,第一次是registerScanner,这一次是startScan。
<aosp>/packages/apps/Bluetooth/src/com/android/bluetooth/gatt/GattService.java ------ public class GattService extends ProfileService { private ScanManager mScanManager; private static class BluetoothGattBinder extends IBluetoothGatt.Stub implements IProfileServiceBinder { @Override public void startScan(int scannerId, ScanSettings settings, List<ScanFilter> filters, List storages, AttributionSource attributionSource) { GattService service = getService(); if (service == null) { return; } service.startScan(scannerId, settings, filters, storages, attributionSource); } } void startScan(int scannerId, ScanSettings settings, List<ScanFilter> filters, List<List<ResultStorageDescriptor>> storages, AttributionSource attributionSource) { if (DBG) { Log.d(TAG, "start scan with filters"); } if (!Utils.checkScanPermissionForDataDelivery( this, attributionSource, "Starting GATT scan.")) { return; } enforcePrivilegedPermissionIfNeeded(settings); String callingPackage = attributionSource.getPackageName(); settings = enforceReportDelayFloor(settings); enforcePrivilegedPermissionIfNeeded(filters); final ScanClient scanClient = new ScanClient(scannerId, settings, filters, storages); scanClient.userHandle = UserHandle.of(UserHandle.getCallingUserId()); mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); scanClient.eligibleForSanitizedExposureNotification = callingPackage.equals(mExposureNotificationPackage); ... scanClient.isQApp = Utils.isQApp(this, callingPackage); // isQApp指的是targetSdkVersion>= Build.VERSION_CODES.Q的app, // Q对应android-10,那要没意外,这值是true。 // 一次运时的变量值 // callingPackage: com.kos.launcher // scanClient.isQApp: true // scanClient.hasisavowedLocation: false if (!scanClient.hasisavowedLocation) { if (scanClient.isQApp) { // 一个普通用户级app,进入后台, // 数分钟后,像4分钟,checkCallerHasFineLocation会返回false。 scanClient.hasLocationPermission = Utils.checkCallerHasFineLocation( this, attributionSource, scanClient.userHandle); } else { scanClient.hasLocationPermission = Utils.checkCallerHasCoarseOrFineLocation( this, attributionSource, scanClient.userHandle); } } // 一个普通用户级app,对应scanClient要获取扫描到的peripheral,scanClient.hasLocationPermission须是true。 scanClient.hasNetworkSettingsPermission = Utils.checkCallerHasNetworkSettingsPermission(this); scanClient.hasNetworkSetupWizardPermission = Utils.checkCallerHasNetworkSetupWizardPermission(this); scanClient.hasScanWithoutLocationPermission = Utils.checkCallerHasScanWithoutLocationPermission(this); scanClient.associatedDevices = getAssociatedDevices(callingPackage, scanClient.userHandle); AppScanStats app = mScannerMap.getAppScanStatsById(scannerId); ScannerMap.App cbApp = mScannerMap.getById(scannerId); if (app != null) { scanClient.stats = app; boolean isFilteredScan = (filters != null) && !filters.isEmpty(); boolean isCallbackScan = false; if (cbApp != null) { isCallbackScan = cbApp.callback != null; } app.recordScanStart(settings, filters, isFilteredScan, isCallbackScan, scannerId); } mScanManager.startScan(scanClient); } }
BluetoothGattBinder.startScan逻辑很简单,调用service.startScan,即GattService.startScan,它主要两个操作。
- 创建一个scanClient,并填充它。这里要注意,一个普通用户级app,对应scanClient要获取扫描到的peripheral,scanClient.hasLocationPermission须是true。但是,如果这个app切到后台,数分钟后,像4分钟,hasLocationPermission是会得到false。所以,要是按常规设定,切到后台数分钟后,普通用户级app是无法扫描到peripheral。
- mScanManager.startScan(scanClient)。调用ScanManager.startScan,继续启动扫描。
<aosp>/packages/apps/Bluetooth/src/com/android/bluetooth/gatt/ScanManager.java ------ public class ScanManager { void startScan(ScanClient client) { sendMessage(MSG_START_BLE_SCAN, client); } private class ClientHandler extends Handler { ClientHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_START_BLE_SCAN: handleStartScan((ScanClient) msg.obj); break; ...... } } void handleStartScan(ScanClient client) { ... // Begin scan operations. if (isBatchClient(client)) { mBatchClients.add(client); mScanNative.startBatchScan(client); } else { mRegularScanClients.add(client); // 主要操作:mScanNative.startRegularScan(client) mScanNative.startRegularScan(client); if (!mScanNative.isOpportunisticScanClient(client)) { mScanNative.configureRegularScanParams(); if (!mScanNative.isExemptFromScanDowngrade(client)) { Message msg = obtainMessage(MSG_SCAN_TIMEOUT); msg.obj = client; // Only one timeout message should exist at any time sendMessageDelayed(msg, AppScanStats.getScanTimeoutMillis()); } } } } } }
在ClientHandler线程执行handleStartScan,后者调用mScanNative.startRegularScan。
<aosp>/packages/apps/Bluetooth/src/com/android/bluetooth/gatt/ScanManager.java ------ private class ScanNative { void startRegularScan(ScanClient client) { ... // Start scan native only for the first client. if (numRegularScanClients() == 1) { gattClientScanNative(true); } } private native void gattClientScanNative(boolean start); }
startRegularScan调用gattClientScanNative,后者是个c/c++实现的原生函数,至此再次进入c/c++代码。
<aosp>/packages/apps/Bluetooth/jni/com_android_bluetooth_gatt.cpp ------ static void gattClientScanNative(JNIEnv* env, jobject object, jboolean start) { if (!sGattIf) return; sGattIf->scanner->Scan(start); }
sGattIf指向bt协议栈的btgattInterface。sGattIf->scanner指向bt协议栈的BleScannerInterfaceImpl。BleScannerInterfaceImpl是一个class,scanner->Scan调用它的Scan方法。
五、bt-协议栈:btLeScannerInstance->Scan
<aosp>/system/bt/btif/src/btif_ble_scanner.cc ------ void Scan(bool start) override { do_in_jni_thread(Bind( [](bool start) { if (!start) { do_in_main_thread(FROM_HERE, Bind(&BTA_DmBleObserve, false, 0, nullptr)); return; } btif_address_cache_init(); do_in_main_thread( FROM_HERE, Bind(&BTA_DmBleObserve, true, 0, bta_scan_results_cb)); }, start)); }
在bt_jni_thread线程执行一个lambda函数。对启动扫描,即start==true,lambda函数是两个操作。
- btif_address_cache_init。
- 在bt_main_thread线程执行BTA_DmBleObserve。
<aosp>/system/bt/bta/dm/bta_dm_api.cc ------ extern void BTA_DmBleObserve(bool start, uint8_t duration, tBTA_DM_SEARCH_CBACK* p_results_cb) { APPL_TRACE_API("%s:start = %d ", __func__, start); do_in_main_thread( FROM_HERE, base::Bind(bta_dm_ble_observe, start, duration, p_results_cb)); } <aosp>/system/bt/bta/dm/bta_dm_act.cc ------ void bta_dm_ble_observe(bool start, uint8_t duration, tBTA_DM_SEARCH_CBACK* p_cback) { if (!start) { bta_dm_search_cb.p_scan_cback = NULL; BTM_BleObserve(false, 0, NULL, NULL); return; } /*Save the callback to be called when a scan results are available */ bta_dm_search_cb.p_scan_cback = p_cback; tBTM_STATUS status = BTM_BleObserve(true, duration, bta_dm_observe_results_cb, bta_dm_observe_cmpl_cb); if (status != BTM_CMD_STARTED) { ... } }
BTA_DmBleObserve操作都在bta_dm_ble_observe,它依次执行两个操作。
- bta_dm_search_cb.p_scan_cback = p_cback。设置函数指针p_scan_cback,它会在bta_dm_observe_results_cb内被回调。
- BTM_BleObserve。通过hci,告知底层蓝牙硬件模块,开始扫描。
这里不深入BTM_BleObserve,只要知道,底层蓝牙硬件模块扫描到一个peripheral,它会回调传给BTM_BleObserve的第二个参数:bta_dm_observe_results_cb。
<aosp>/system/bt/bta/dm/bta_dm_act.cc ------ static void bta_dm_observe_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; APPL_TRACE_DEBUG("bta_dm_observe_results_cb"); result.inq_res.bd_addr = p_inq->remote_bd_addr; result.inq_res.original_bda = p_inq->original_bda; 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; result.inq_res.ble_evt_type = p_inq->ble_evt_type; result.inq_res.ble_primary_phy = p_inq->ble_primary_phy; result.inq_res.ble_secondary_phy = p_inq->ble_secondary_phy; result.inq_res.ble_advertising_sid = p_inq->ble_advertising_sid; result.inq_res.ble_tx_power = p_inq->ble_tx_power; result.inq_res.ble_periodic_adv_int = p_inq->ble_periodic_adv_int; /* 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_scan_cback) { // 回调传给bta_dm_ble_observe第三个参数:p_cback bta_dm_search_cb.p_scan_cback(BTA_DM_INQ_RES_EVT, &result); } ... }
扫描到一个peripheral后,就会调用一次bta_dm_observe_results_cb,然后它会调用bta_dm_search_cb.p_scan_cback(BTA_DM_INQ_RES_EVT, &result),那bta_dm_search_cb.p_scan_cback是什么?——bta_dm_search_cb.p_scan_cback --> p_cback --> p_results_cb --> bta_scan_results_cb
<aosp>/system/bt/btif/src/btif_ble_scanner.cc ------ void bta_scan_results_cb(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH* p_data) { uint8_t len; ... tBTA_DM_INQ_RES* r = &p_data->inq_res; do_in_jni_thread(Bind(bta_scan_results_cb_impl, r->bd_addr, r->device_type, r->rssi, r->ble_addr_type, r->ble_evt_type, r->ble_primary_phy, r->ble_secondary_phy, r->ble_advertising_sid, r->ble_tx_power, r->ble_periodic_adv_int, std::move(value), r->original_bda)); }
此时传给bta_scan_results_cb的两个参数值。
- event。BTA_DM_INQ_RES_EVT。
- p_data。bta_dm_observe_results_cb生成的一个tBTA_DM_SEARC类型的结构。
主要操作是在bt_jni_thread执行bta_scan_results_cb_impl。
<aosp>/system/bt/btif/src/btif_ble_scanner.cc ------ void bta_scan_results_cb_impl(RawAddress bd_addr, tBT_DEVICE_TYPE device_type, int8_t rssi, tBLE_ADDR_TYPE addr_type, uint16_t ble_evt_type, uint8_t ble_primary_phy, uint8_t ble_secondary_phy, uint8_t ble_advertising_sid, int8_t ble_tx_power, uint16_t ble_periodic_adv_int, vector<uint8_t> value, RawAddress original_bda) { uint8_t remote_name_len; bt_device_type_t dev_type; bt_property_t properties; const uint8_t* p_eir_remote_name = AdvertiseDataParser::GetFieldByType( value, BTM_EIR_COMPLETE_LOCAL_NAME_TYPE, &remote_name_len); ... dev_type = (bt_device_type_t)device_type; BTIF_STORAGE_FILL_PROPERTY(&properties, BT_PROPERTY_TYPE_OF_DEVICE, sizeof(dev_type), &dev_type); btif_storage_set_remote_device_property(&(bd_addr), &properties); btif_storage_set_remote_addr_type(&bd_addr, addr_type); HAL_CBACK(bt_gatt_callbacks, scanner->scan_result_cb, ble_evt_type, addr_type, &bd_addr, ble_primary_phy, ble_secondary_phy, ble_advertising_sid, ble_tx_power, rssi, ble_periodic_adv_int, std::move(value), &original_bda); }
以上HAL_CBACK语句会在bt_jni_thread线程调用位在bt-JNI内btgattc_scan_result_cb。
六、bt-JNI:btgattc_scan_result_cb
<aosp>/packages/apps/Bluetooth/jni/com_android_bluetooth_gatt.cpp ------ void btgattc_scan_result_cb(uint16_t event_type, uint8_t addr_type, RawAddress* bda, uint8_t primary_phy, uint8_t secondary_phy, uint8_t advertising_sid, int8_t tx_power, int8_t rssi, uint16_t periodic_adv_int, std::vector<uint8_t> adv_data, RawAddress* original_bda) { CallbackEnv sCallbackEnv(__func__); ... sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onScanResult, event_type, addr_type, address.get(), primary_phy, secondary_phy, advertising_sid, tx_power, rssi, periodic_adv_int, jb.get(), original_address.get()); }
clazz的是GattService,method_onScanResult就是GattService.onScanResult。
<aosp>/packages/apps/Bluetooth/src/com/android/bluetooth/gatt/GattService.java ------ public class GattService extends ProfileService { void onScanResult(int eventType, int addressType, String address, int primaryPhy, int secondaryPhy, int advertisingSid, int txPower, int rssi, int periodicAdvInt, byte[] advData, String originalAddress) { // When in testing mode, ignore all real-world events if (isTestModeEnabled()) return; onScanResultInternal(eventType, addressType, address, primaryPhy, secondaryPhy, advertisingSid, txPower, rssi, periodicAdvInt, advData, originalAddress); } void onScanResultInternal(int eventType, int addressType, String address, int primaryPhy, int secondaryPhy, int advertisingSid, int txPower, int rssi, int periodicAdvInt, byte[] advData, String originalAddress) { // 一次运行时变量值 // mScanManager.getRegularScanQueue().size: 1; byte[] legacyAdvData = Arrays.copyOfRange(advData, 0, 62); for (ScanClient client : mScanManager.getRegularScanQueue()) { ScannerMap.App app = mScannerMap.getById(client.scannerId); if (app == null) { if (VDBG || mAdapterService.getIsVerboseLoggingEnabledForAll()) { Log.d(TAG, "App is null for scanner ID " + client.scannerId); } continue; } BluetoothDevice device = getAnonymousDevice(address); ScanSettings settings = client.settings; byte[] scanRecordData; // This is for compability with applications that assume fixed size scan data. if (settings.getLegacy()) { if ((eventType & ET_LEGACY_MASK) == 0) { // If this is legacy scan, but nonlegacy result - skip. Log.i(TAG, "Non legacy result in legacy scan, skipping scanner id " + client.scannerId + ", eventType=" + eventType); continue; } else { // Some apps are used to fixed-size advertise data. scanRecordData = legacyAdvData; } } else { scanRecordData = advData; } ScanRecord scanRecord = ScanRecord.parseFromBytes(scanRecordData); ScanResult result = new ScanResult(device, eventType, primaryPhy, secondaryPhy, advertisingSid, txPower, rssi, periodicAdvInt, scanRecord, SystemClock.elapsedRealtimeNanos()); ... boolean hasPermission = hasScanResultPermission(client); // 普通用户级app,切到后台,持续几分钟后,hasScanResultPermission将返回false。 // 使得hasPermission是false,false将导致没法收到扫描到的peripheral。但有两例外情况。 if (!hasPermission) { // 例外(1/2):该peripheral属于associatedDevices for (String associatedDevice : client.associatedDevices) { if (associatedDevice.equalsIgnoreCase(address)) { hasPermission = true; break; } } } if (!hasPermission && client.eligibleForSanitizedExposureNotification) { // 例外(2/2):该peripheral发出的广播属于SanitizedExposureNotification ScanResult sanitized = getSanitizedExposureNotification(result); if (sanitized != null) { hasPermission = true; result = sanitized; } } if (!hasPermission) { if (VDBG || mAdapterService.getIsVerboseLoggingEnabledForAll()) { Log.d(TAG, "scanner id " + client.scannerId + " has no result permission"); } continue; } MatchResult matchResult = matchesFilters(client, result, originalAddress); Log.w(TAG, "{leagor}{bt}pre matchesFilters: " + matchResult); if (!matchResult.getMatches()) { if (VDBG || mAdapterService.getIsVerboseLoggingEnabledForAll()) { Log.d(TAG, "result did not match filter for scanner id " + client.scannerId); } continue; } if (matchResult.getMatchOrigin() == MatchOrigin.ORIGINAL_ADDRESS) { result = new ScanResult(getAnonymousDevice(originalAddress), eventType, primaryPhy, secondaryPhy, advertisingSid, txPower, rssi, periodicAdvInt, scanRecord, SystemClock.elapsedRealtimeNanos()); } if ((settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_ALL_MATCHES) == 0) { if (VDBG || mAdapterService.getIsVerboseLoggingEnabledForAll()) { Log.d(TAG, "callback type " + settings.getCallbackType() + " is not ALL_MATCHES for scanner id " + client.scannerId); } continue; } try { app.appScanStats.addResult(client.scannerId); if (app.callback != null) { // 调用BleScanCallbackWrapper.onScanResult app.callback.onScanResult(result); } else { // Send the PendingIntent ArrayList<ScanResult> results = new ArrayList<>(); results.add(result); sendResultsByPendingIntent(app.info, results, ScanSettings.CALLBACK_TYPE_ALL_MATCHES); } } catch (RemoteException | PendingIntent.CanceledException e) { Log.e(TAG, "Stop scan for scanner id " + client.scannerId + " due to : " + e); mScannerMap.remove(client.scannerId); mScanManager.stopScan(client.scannerId); } } } }
mScanManager.getRegularScanQueue()存储着等待蓝牙扫描结果的app。一般来说,就一个。
首先检查扫描设置中的mLeagacy(settings.getLegacy()),目前一般是true。如果是,那意味着只要legacy广播,“copyOfRange(advData, 0, 62)”,只取前面62字节。因为legacy最多支持62字节的广播数据,其中31字节的定时包,31字节的扫描回复包。
对每个app,能否收到要经过三种滤除。一是得有接收到的权限,二是自设的筛选器没把它滤掉,三是扫描设置的CALLBACK_TYPE_ALL_MATCHES。
滤除1:需要有接收权限。
boolean hasPermission = hasScanResultPermission(client);
这里需要注意,一旦app切到后台,数分钟后,此时client.hasLocationPermission是false,将导致hasScanResultPermission是false。但在这种情况,有两个例外会让它重新有权限。换句话说,这两种情况可让app在后台时,仍旧能扫描到peripheral。
- 例外1:该peripheral属于associatedDevices。associatedDevices是啥?——不确定是否对,“Android 12引入了CompanionDeviceService API,旨在改善管理智能手表和健身追踪器等设备的应用。当一个可穿戴设备与你的手机亲密接触时,伴侣应用将被唤醒。”
- 例外2:该peripheral发出的广播属于SanitizedExposureNotification。SanitizedExposureNotification是啥?——不确定是否对,“手机的接触通知功能(exposure notification),用于提醒可能接触过COVID-19(新冠病毒)的用户。可以在安卓手机上启用MassNotify,以便阻止COVID-19扩散”。
或许有些人认为,在ScanFilter设置ServiceUuid,可以让自个app切到后台后,仍旧可以收到这ServiceUuid的peripheral,这是不存在的。
滤除2:自设的筛选器(ScanFilter)没把它滤掉
自设的筛选器指调用mLEScanner.startScan时,设置的第一个参数:List<ScanFilter> filters。举个例子,在ScanFilter,可以设置ServiceUuid,那这client只接收是这ServiceUuid的peripherial。
滤除3:扫描设置的CALLBACK_TYPE_ALL_MATCHES
“(settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_ALL_MATCHES) == 0”,要上报到app,app需要在扫描设置中让mCallbackType含有ScanSettings.CALLBACK_TYPE_ALL_MATCHES标记。
判断出这peripherl不滤除后,调用app.callback.onScanResult,app.callback是frameworks的BleScanCallbackWrapper。
<aosp>/frameworks/base/core/java/android/bluetooth/le/BluetoothLeScanner.java ------ private class BleScanCallbackWrapper extends IScannerCallback.Stub { public void onScanResult(final ScanResult scanResult) { Attributable.setAttributionSource(scanResult, mAttributionSource); if (VDBG) Log.d(TAG, "onScanResult() - " + scanResult.toString()); // Check null in case the scan has been stopped synchronized (this) { if (mScannerId <= 0) return; } Handler handler = new Handler(Looper.getMainLooper()); handler.post(new Runnable() { @Override public void run() { // 调用app提供的onScanResult。 mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult); } }); }
BleScanCallbackWrapper.onScanResult继结向上回调mScanCallback.onScanResult,这个就是app提供的onScanResult,至此app知道了扫描到一个peripheral。
七、app进入后台后,允许正常蓝牙扫描
app进入后台,只能扫到associatedDevices、SanitizedExposureNotification这两类peripheral,但在一些应用,这是不够的。
<aosp>/packages/apps/Bluetooth/src/com/android/bluetooth/gatt/GattService.java ------ void startScan(int scannerId, ScanSettings settings, List<ScanFilter> filters, List<List<ResultStorageDescriptor>> storages, AttributionSource attributionSource) { ... scanClient.isQApp = Utils.isQApp(this, callingPackage); // isQApp指的是targetSdkVersion>= Build.VERSION_CODES.Q的app, // Q对应android-10,要没意外,这值总是true。 if (!scanClient.hasEisavowedLocation) { if (scanClient.isQApp) { // 检查是否有ACCESS_FINE_LOCATION权限 scanClient.hasLocationPermission = Utils.checkCallerHasFineLocation( this, attributionSource, scanClient.userHandle); } else { scanClient.hasLocationPermission = Utils.checkCallerHasCoarseOrFineLocation( this, attributionSource, scanClient.userHandle); } } <aosp>/packages/apps/Bluetooth/src/com/android/bluetooth/Utils.java ------ public static boolean checkCallerHasFineLocation( Context context, AttributionSource attributionSource, UserHandle userHandle) { if (blockedByLocationOff(context, userHandle)) { Log.e(TAG, "Permission denial: Location is off."); return false; } // STOPSHIP(b/188391719): enable this security enforcement // attributionSource.enforceCallingUid(); // app进入后台,数分钟后,checkPermissionForDataDeliveryFromDataSource将返回false if (PermissionChecker.checkPermissionForDataDeliveryFromDataSource( context, ACCESS_FINE_LOCATION, PID_UNKNOWN, new AttributionSource(context.getAttributionSource(), attributionSource), "Bluetooth location check") == PERMISSION_GRANTED) { return true; } Log.e(TAG, "Permission denial: Need ACCESS_FINE_LOCATION " + "permission to get scan results"); return false; }
app进入后台,数分钟后,前台时返回true的PermissionChecker.checkPermissionForDataDeliveryFromDataSource,此时会改为false,导致checkCallerHasFineLocation返回false,让认为app没有ACCESS_FINE_LOCATION权限。因为scanClient.isQApp是true,checkCallerHasFineLocation返回false导致scanClient.hasLocationPermission值是false。
<aosp>/packages/apps/Bluetooth/src/com/android/bluetooth/gatt/GattService.java ------ private boolean hasScanResultPermission(final ScanClient client) { if (client.hasNetworkSettingsPermission || client.hasNetworkSetupWizardPermission || client.hasScanWithoutLocationPermission) { return true; } if (client.hasEisavowedLocation) { return true; } Log.d(TAG, "{leagor}{bt}client.hasLocationPermission=" + client.hasLocationPermission + ", !blockedByLocationOff=" + (!Utils.blockedByLocationOff(this, client.userHandle))); return client.hasLocationPermission && !Utils.blockedByLocationOff(this, client.userHandle); }
scanClient.hasLocationPermission是flase导致hasScanResultPermission返回false。在GattService.onScanResultInternal已说过,hasScanResultPermission是false,将导致除那两种例外的periphernal都不会上报到app。
源头是PermissionChecker.checkPermissionForDataDeliveryFromDataSource返回false,当然,有更深原因导致checkPermissionForDataDeliveryFromDataSource返回false。这我没深入,猜原因类似“(android-12)app进入后台,关掉杀死serivce、停止摄像头”。
<aosp>/packages/apps/Bluetooth/src/com/android/bluetooth/Utils.java ------ public static boolean checkCallerHasFineLocation( Context context, AttributionSource attributionSource, UserHandle userHandle) { if (blockedByLocationOff(context, userHandle)) { Log.e(TAG, "Permission denial: Location is off."); return false; } // STOPSHIP(b/188391719): enable this security enforcement // attributionSource.enforceCallingUid(); if (PermissionChecker.checkPermissionForDataDeliveryFromDataSource( context, ACCESS_FINE_LOCATION, PID_UNKNOWN, new AttributionSource(context.getAttributionSource(), attributionSource), "Bluetooth location check") == PERMISSION_GRANTED) { return true; } else { // 修改:checkCallerHasFineLocation==false时,函数依旧返回true。 Log.e(TAG, "{leagor}checkPermissionForDataDeliveryFromDataSource's false to true"); return true; } /* Log.e(TAG, "Permission denial: Need ACCESS_FINE_LOCATION " + "permission to get scan results"); return false; */ }
修改方法:checkPermissionForDataDeliveryFromDataSource返回false时,checkCallerHasFineLocation依旧返回true。由于“if”两个分支都已有return,得注释掉后面Log.e、return false,免得编译出错。