必务先看“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,免得编译出错。