Android源码相关解析
1.蓝牙扫描
- 调用 BluetoothAdapter.java 的startLeScan()
// BluetoothAdapter.java
public boolean startLeScan(LeScanCallback callback) {
// 1.开始扫描
return startLeScan(null, callback);
}
public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback) {
...
synchronized (mLeScanClients) {
if (mLeScanClients.containsKey(callback)) {
if (DBG) {
Log.e(TAG, "LE Scan has already started");
}
return false;
}
IBluetoothGatt iGatt = getBluetoothGatt();
if (iGatt == null) {
// BLE is not supported
return false;
}
@SuppressLint("AndroidFrameworkBluetoothPermission")
ScanCallback scanCallback =
new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
if (callbackType != ScanSettings.CALLBACK_TYPE_ALL_MATCHES) {
// Should not happen.
Log.e(TAG, "LE Scan has already started");
return;
}
ScanRecord scanRecord = result.getScanRecord();
if (scanRecord == null) {
return;
}
if (serviceUuids != null) {
List<ParcelUuid> uuids = new ArrayList<ParcelUuid>();
for (UUID uuid : serviceUuids) {
uuids.add(new ParcelUuid(uuid));
}
List<ParcelUuid> scanServiceUuids = scanRecord.getServiceUuids();
if (scanServiceUuids == null
|| !scanServiceUuids.containsAll(uuids)) {
if (DBG) {
Log.d(TAG, "uuids does not match");
}
return;
}
}
callback.onLeScan(
result.getDevice(), result.getRssi(), scanRecord.getBytes());
}
};
ScanSettings settings =
new ScanSettings.Builder()
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build();
List<ScanFilter> filters = new ArrayList<ScanFilter>();
if (serviceUuids != null && serviceUuids.length > 0) {
// Note scan filter does not support matching an UUID array so we put one
// UUID to hardware and match the whole array in callback.
ScanFilter filter =
new ScanFilter.Builder()
.setServiceUuid(new ParcelUuid(serviceUuids[0]))
.build();
filters.add(filter);
}
// 2.调用BluetoothLeScanner的扫描,此处scanner是跟随adapter创建的,加了锁
scanner.startScan(filters, settings, scanCallback);
mLeScanClients.put(callback, scanCallback);
return true;
}
}
- 调用BluetoothLeScanner.java 的startScan()
// BluetoothLeScanner.java
private int startScan(
List<ScanFilter> filters,
ScanSettings settings,
final WorkSource workSource,
final ScanCallback callback,
final PendingIntent callbackIntent) {
BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
if (callback == null && callbackIntent == null) {
throw new IllegalArgumentException("callback is null");
}
if (settings == null) {
throw new IllegalArgumentException("settings is null");
}
synchronized (mLeScanClients) {
if (callback != null && mLeScanClients.containsKey(callback)) {
return postCallbackErrorOrReturn(
callback, ScanCallback.SCAN_FAILED_ALREADY_STARTED);
}
IBluetoothGatt gatt = mBluetoothAdapter.getBluetoothGatt();
if (gatt == null) {
return postCallbackErrorOrReturn(callback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
}
if (!isSettingsConfigAllowedForScan(settings)) {
return postCallbackErrorOrReturn(
callback, ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED);
}
if (!isHardwareResourcesAvailableForScan(settings)) {
return postCallbackErrorOrReturn(
callback, ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES);
}
if (!isSettingsAndFilterComboAllowed(settings, filters)) {
return postCallbackErrorOrReturn(
callback, ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED);
}
if (callback != null) {
BleScanCallbackWrapper wrapper =
new BleScanCallbackWrapper(gatt, filters, settings, workSource, callback);
wrapper.startRegistration();
} else {
try {
final SynchronousResultReceiver recv = SynchronousResultReceiver.get();
// 3.调用IBluetoothGatt的startScanForIntent()
gatt.startScanForIntent(
callbackIntent, settings, filters, mAttributionSource, recv);
recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
} catch (TimeoutException | RemoteException e) {
return ScanCallback.SCAN_FAILED_INTERNAL_ERROR;
}
}
}
return ScanCallback.NO_ERROR;
}
调用IBluetoothGatt的startScanForIntent(),其中IBluetoothGatt是aidl接口
调用GattService的startScanForIntent(),此处会进行TransitionalScanHelper对象的初始化,此处是关键
调用ScanMananger的registerScanner()进行蓝牙扫码注册,其中ScanManger对象在GattService 服务启动时,start()会进行初始化
// IBluetoothGatt.aidl 接口方法的实现类,GattService.java
@Override
public void startScanForIntent(
PendingIntent intent,
ScanSettings settings,
List<ScanFilter> filters,
AttributionSource attributionSource) {
GattService service = getService();
if (service == null) {
return;
}
service.getTransitionalScanHelper()
.registerPiAndStartScan(intent, settings, filters, attributionSource);
}
// TransitionalScanHelper的初始化
public final TransitionalScanHelper mTransitionalScanHelper =
new TransitionalScanHelper(this, this::isTestModeEnabled);
@RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
void registerPiAndStartScan(PendingIntent pendingIntent, ScanSettings settings,
List<ScanFilter> filters, AttributionSource attributionSource) {
if (DBG) {
Log.d(TAG, "start scan with filters, for PendingIntent");
}
if (!Utils.checkScanPermissionForDataDelivery(
this, attributionSource, "Starting GATT scan.")) {
return;
}
enforcePrivilegedPermissionIfNeeded(settings);
settings = enforceReportDelayFloor(settings);
enforcePrivilegedPermissionIfNeeded(filters);
UUID uuid = UUID.randomUUID();
String callingPackage = attributionSource.getPackageName();
int callingUid = attributionSource.getUid();
PendingIntentInfo piInfo = new PendingIntentInfo();
piInfo.intent = pendingIntent;
piInfo.settings = settings;
piInfo.filters = filters;
piInfo.callingPackage = callingPackage;
piInfo.callingUid = callingUid;
if (DBG) {
Log.d(
TAG,
"startScan(PI) -"
+ (" UUID=" + uuid)
+ (" Package=" + callingPackage)
+ (" UID=" + callingUid));
}
// Don't start scan if the Pi scan already in mScannerMap.
if (mTransitionalScanHelper.getScannerMap().getByContextInfo(piInfo) != null) {
Log.d(TAG, "Don't startScan(PI) since the same Pi scan already in mScannerMap.");
return;
}
ContextMap.App app =
mTransitionalScanHelper.getScannerMap().add(uuid, null, null, piInfo, this);
app.mUserHandle = UserHandle.getUserHandleForUid(Binder.getCallingUid());
mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
app.mEligibleForSanitizedExposureNotification =
callingPackage.equals(mExposureNotificationPackage);
app.mHasDisavowedLocation =
Utils.hasDisavowedLocationForScan(this, attributionSource, isTestModeEnabled());
if (!app.mHasDisavowedLocation) {
try {
if (checkCallerTargetSdk(this, callingPackage, Build.VERSION_CODES.Q)) {
app.hasLocationPermission = Utils.checkCallerHasFineLocation(
this, attributionSource, app.mUserHandle);
} else {
app.hasLocationPermission = Utils.checkCallerHasCoarseOrFineLocation(
this, attributionSource, app.mUserHandle);
}
} catch (SecurityException se) {
// No need to throw here. Just mark as not granted.
app.hasLocationPermission = false;
}
}
app.mHasNetworkSettingsPermission =
Utils.checkCallerHasNetworkSettingsPermission(this);
app.mHasNetworkSetupWizardPermission =
Utils.checkCallerHasNetworkSetupWizardPermission(this);
app.mHasScanWithoutLocationPermission =
Utils.checkCallerHasScanWithoutLocationPermission(this);
app.mAssociatedDevices = getAssociatedDevices(callingPackage);
// 5.调用ScanMananger的registerScanner()进行扫描注册
mScanManager.registerScanner(uuid);
// If this fails, we should stop the scan immediately.
if (!pendingIntent.addCancelListener(Runnable::run, mScanIntentCancelListener)) {
Log.d(TAG, "scanning PendingIntent is already cancelled, stopping scan.");
stopScan(pendingIntent, attributionSource);
}
}
@Override
public void start() {
if (DBG) {
Log.d(TAG, "start()");
}
mExposureNotificationPackage = getString(R.string.exposure_notification_package);
Settings.Global.putInt(
getContentResolver(), "bluetooth_sanitized_exposure_notification_supported", 1);
mNativeInterface = GattObjectsFactory.getInstance().getNativeInterface();
mNativeInterface.init(this);
mAdapterService = AdapterService.getAdapterService();
mBluetoothAdapterProxy = BluetoothAdapterProxy.getInstance();
mCompanionManager = getSystemService(CompanionDeviceManager.class);
mAppOps = getSystemService(AppOpsManager.class);
mAdvertiseManager =
new AdvertiseManager(
this,
AdvertiseManagerNativeInterface.getInstance(),
mAdapterService,
mAdvertiserMap);
HandlerThread thread = new HandlerThread("BluetoothScanManager");
thread.start();
// 此处是Manager初始化的地方
mScanManager =
GattObjectsFactory.getInstance()
.createScanManager(
this, mAdapterService, mBluetoothAdapterProxy, thread.getLooper());
mPeriodicScanManager = GattObjectsFactory.getInstance()
.createPeriodicScanManager(mAdapterService);
mDistanceMeasurementManager = GattObjectsFactory.getInstance()
.createDistanceMeasurementManager(mAdapterService);
mActivityManager = getSystemService(ActivityManager.class);
mPackageManager = mAdapterService.getPackageManager();
}
- 调用ScanNative的registerScanner(),其中NativeInterface在构造函数时初始化
// ScanMananger.java
public void registerScanner(UUID uuid) {
mScanNative.registerScanner(uuid.getLeastSignificantBits(), uuid.getMostSignificantBits());
}
// ScanMananger.ScanNative.java
private void registerScanner(long appUuidLsb, long appUuidMsb) {
mNativeInterface.registerScanner(appUuidLsb, appUuidMsb);
}
ScanNative(TransitionalScanHelper scanHelper) {
// 此处调用工厂模式,获取到Navtive接口实例对象
mNativeInterface = ScanObjectsFactory.getInstance().getScanNativeInterface();
mNativeInterface.init(scanHelper);
mFilterIndexStack = new ArrayDeque<Integer>();
mClientFilterIndexMap = new HashMap<Integer, Deque<Integer>>();
mAlarmManager = mContext.getSystemService(AlarmManager.class);
Intent batchIntent = new Intent(ACTION_REFRESH_BATCHED_SCAN, null);
mBatchScanIntervalIntent =
PendingIntent.getBroadcast(
mContext, 0, batchIntent, PendingIntent.FLAG_IMMUTABLE);
IntentFilter filter = new IntentFilter();
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
filter.addAction(ACTION_REFRESH_BATCHED_SCAN);
mBatchAlarmReceiver.set(
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "awakened up at time " + SystemClock.elapsedRealtime());
String action = intent.getAction();
if (action.equals(ACTION_REFRESH_BATCHED_SCAN)) {
if (mBatchClients.isEmpty()) {
return;
}
// Note this actually flushes all pending batch data.
if (mBatchClients.iterator().hasNext()) {
flushBatchScanResults(mBatchClients.iterator().next());
}
}
}
});
mContext.registerReceiver(mBatchAlarmReceiver.get(), filter);
}
- ScanObjectsFactory.getInstance(),本质上是ScanNativeInterface.getInstance(),registerScanner()背后调用的是registerScannerNative()方法,到此Framwork层就无法继续往下分析了
// ScanNativeInterface.java
public ScanNativeInterface getScanNativeInterface() {
return ScanNativeInterface.getInstance();
}
// ScanNativeInterface.java
public void registerScanner(long appUuidLsb, long appUuidMsb) {
registerScannerNative(appUuidLsb, appUuidMsb);
}
// Native方法
private native void registerScannerNative(long appUuidLsb, long appUuidMsb);
- 注册成功以后,会开启蓝牙扫描,回调是通过ScanNativeInterface的Nactive回调,在通知到TransitionalScanHelper的onScannerRegistered(),最终调用TransitionalScanHelper.continuePiStartScan()
// ScanNativeInterface.java
void onScannerRegistered(int status, int scannerId, long uuidLsb, long uuidMsb)
throws RemoteException {
if (mScanHelper == null) {
Log.e(TAG, "Scan helper is null!");
return;
}
mScanHelper.onScannerRegistered(status, scannerId, uuidLsb, uuidMsb);
}
// TransitionalScanHelper.java
public void onScannerRegistered(int status, int scannerId, long uuidLsb, long uuidMsb)
throws RemoteException {
UUID uuid = new UUID(uuidMsb, uuidLsb);
Log.d(
TAG,
"onScannerRegistered() - UUID="
+ uuid
+ ", scannerId="
+ scannerId
+ ", status="
+ status);
// First check the callback map
ScannerMap.ScannerApp cbApp = mScannerMap.getByUuid(uuid);
if (cbApp != null) {
if (status == 0) {
cbApp.mId = scannerId;
// If app is callback based, setup a death recipient. App will initiate the start.
// Otherwise, if PendingIntent based, start the scan directly.
if (cbApp.mCallback != null) {
cbApp.linkToDeath(new ScannerDeathRecipient(scannerId, cbApp.mName));
} else {
// 8.调用扫描方法
continuePiStartScan(scannerId, cbApp);
}
} else {
mScannerMap.remove(scannerId);
}
if (cbApp.mCallback != null) {
cbApp.mCallback.onScannerRegistered(status, scannerId);
}
}
}
//
void onScannerRegistered(int status, int scannerId, long uuidLsb, long uuidMsb)
throws RemoteException {
if (mScanHelper == null) {
Log.e(TAG, "Scan helper is null!");
return;
}
mScanHelper.onScannerRegistered(status, scannerId, uuidLsb, uuidMsb);
}
- 开始扫描,最终调用ScanManager.startScan(),本质是发送MSG_START_BLE_SCAN标识消息,最终由ScanManager.ClientHandler的handleStartScan()方法处理
// TransitionalScanHelper.java
public void continuePiStartScan(int scannerId, ScannerMap.ScannerApp app) {
final PendingIntentInfo piInfo = app.mInfo;
final ScanClient scanClient =
new ScanClient(scannerId, piInfo.settings, piInfo.filters, piInfo.callingUid);
scanClient.hasLocationPermission = app.mHasLocationPermission;
scanClient.userHandle = app.mUserHandle;
scanClient.isQApp = checkCallerTargetSdk(mContext, app.mName, Build.VERSION_CODES.Q);
scanClient.eligibleForSanitizedExposureNotification =
app.mEligibleForSanitizedExposureNotification;
scanClient.hasNetworkSettingsPermission = app.mHasNetworkSettingsPermission;
scanClient.hasNetworkSetupWizardPermission = app.mHasNetworkSetupWizardPermission;
scanClient.hasScanWithoutLocationPermission = app.mHasScanWithoutLocationPermission;
scanClient.associatedDevices = app.mAssociatedDevices;
scanClient.hasDisavowedLocation = app.mHasDisavowedLocation;
AppScanStats scanStats = mScannerMap.getAppScanStatsById(scannerId);
if (scanStats != null) {
scanClient.stats = scanStats;
boolean isFilteredScan = (piInfo.filters != null) && !piInfo.filters.isEmpty();
scanStats.recordScanStart(
piInfo.settings, piInfo.filters, isFilteredScan, false, scannerId);
}
mScanManager.startScan(scanClient);
}
// ScanManager.java
public void startScan(ScanClient client) {
Log.d(TAG, "startScan() " + client);
sendMessage(MSG_START_BLE_SCAN, client);
}
private void sendMessage(int what, ScanClient client) {
final ClientHandler handler = mHandler;
if (handler == null) {
Log.d(TAG, "sendMessage: mHandler is null.");
return;
}
Message message = new Message();
message.what = what;
message.obj = client;
handler.sendMessage(message);
}
// ScanManager.ClientHandler.java
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_START_BLE_SCAN:
//此处通知进行扫描
handleStartScan((ScanClient) msg.obj);
break;
case MSG_STOP_BLE_SCAN:
handleStopScan((ScanClient) msg.obj);
break;
case MSG_FLUSH_BATCH_RESULTS:
handleFlushBatchResults((ScanClient) msg.obj);
break;
case MSG_SCAN_TIMEOUT:
mScanNative.regularScanTimeout((ScanClient) msg.obj);
break;
case MSG_SUSPEND_SCANS:
handleSuspendScans();
break;
case MSG_RESUME_SCANS:
handleResumeScans();
break;
case MSG_SCREEN_OFF:
handleScreenOff();
break;
case MSG_SCREEN_ON:
handleScreenOn();
break;
case MSG_REVERT_SCAN_MODE_UPGRADE:
revertScanModeUpgrade((ScanClient) msg.obj);
break;
case MSG_IMPORTANCE_CHANGE:
handleImportanceChange((UidImportance) msg.obj);
break;
case MSG_START_CONNECTING:
handleConnectingState();
break;
case MSG_STOP_CONNECTING:
handleClearConnectingState();
break;
case MSG_BT_PROFILE_CONN_STATE_CHANGED:
handleProfileConnectionStateChanged(msg);
break;
default:
// Shouldn't happen.
Log.e(TAG, "received an unknown message : " + msg.what);
}
}
- 进入扫描处理后,由于扫描通常设置ScanSettings.CALLBACK_TYPE_ALL_MATCHES 设置蓝牙扫描滤波器硬件匹配的匹配模式,所以只会进入isBatchClient()和isAutoBatchScanClientEnabled(),前者是只要不设置ReportDelayMillis(延迟多少秒,统一接收蓝牙广播)参数,就会执行;后者则是沿用上次扫描设置参数,不继续往下分析。最终会调用ScanNative.startBatchScan()方法
// ScanManager.java
void handleStartScan(ScanClient client) {
Log.d(TAG, "handling starting scan");
fetchAppForegroundState(client);
if (!isScanSupported(client)) {
Log.e(TAG, "Scan settings not supported");
return;
}
if (mRegularScanClients.contains(client) || mBatchClients.contains(client)) {
Log.e(TAG, "Scan already started");
return;
}
if (requiresScreenOn(client) && !mScreenOn) {
Log.w(
TAG,
"Cannot start unfiltered scan in screen-off. This scan will be resumed "
+ "later: "
+ client.scannerId);
mSuspendedScanClients.add(client);
if (client.stats != null) {
client.stats.recordScanSuspend(client.scannerId);
}
return;
}
final boolean locationEnabled = mLocationManager.isLocationEnabled();
if (requiresLocationOn(client) && !locationEnabled) {
Log.i(
TAG,
"Cannot start unfiltered scan in location-off. This scan will be"
+ " resumed when location is on: "
+ client.scannerId);
mSuspendedScanClients.add(client);
if (client.stats != null) {
client.stats.recordScanSuspend(client.scannerId);
}
return;
}
if (!mScanNative.isExemptFromAutoBatchScanUpdate(client)) {
if (mScreenOn) {
clearAutoBatchScanClient(client);
} else {
setAutoBatchScanClient(client);
}
}
// Begin scan operations.
if (isBatchClient(client) || isAutoBatchScanClientEnabled(client)) {
mBatchClients.add(client);
// 10.此处进行扫描
mScanNative.startBatchScan(client);
} else {
updateScanModeBeforeStart(client);
updateScanModeConcurrency(client);
mRegularScanClients.add(client);
mScanNative.startRegularScan(client);
if (!mScanNative.isOpportunisticScanClient(client)) {
mScanNative.configureRegularScanParams();
if (!mScanNative.isExemptFromScanTimeout(client)) {
Message msg = obtainMessage(MSG_SCAN_TIMEOUT);
msg.obj = client;
// Only one timeout message should exist at any time
removeMessages(MSG_SCAN_TIMEOUT, client);
sendMessageDelayed(msg, mAdapterService.getScanTimeoutMillis());
Log.d(
TAG,
"apply scan timeout ("
+ mAdapterService.getScanTimeoutMillis()
+ ")"
+ "to scannerId "
+ client.scannerId);
}
}
}
client.started = true;
}
private boolean isBatchClient(ScanClient client) {
if (client == null || client.settings == null) {
return false;
}
ScanSettings settings = client.settings;
return settings.getCallbackType() == ScanSettings.CALLBACK_TYPE_ALL_MATCHES
&& settings.getReportDelayMillis() != 0;
}
private boolean isAutoBatchScanClientEnabled(ScanClient client) {
return client.stats != null && client.stats.isAutoBatchScan(client.scannerId);
}
进入startBatchScan()方法后,此处关键就是configureScanFilters(),后续的isOpportunisticScanClient()则受Mode配置影响,当为mode不是SCAN_MODE_OPPORTUNISTIC会触发
SCAN_MODE_LOW_POWER
这个是Android默认的扫描模式,耗电量最小。如果扫描不再前台,则强制执行此模式。
在这种模式下, Android会扫喵0.5s,暂停4.5s.
4. SCAN_MODE_BALANCED
平衡模式, 平衡扫描频率和耗电量的关系。
在这种模式下,Android会扫描2s, 暂停3s。 这是一种妥协模式。
7. SCAN_MODE_LOW_LATENCY
连续不断的扫描, 建议应用在前台时使用。但会消耗比较多的电量。 扫描结果也会比较快一些。
9. SCAN_MODE_OPPORTUNISTIC
这种模式下, 只会监听其他APP的扫描结果回调。它无法发现你想发现的设备。
// ScanManager.java
void startBatchScan(ScanClient client) {
if (mFilterIndexStack.isEmpty() && isFilteringSupported()) {
initFilterIndexStack();
}
configureScanFilters(client);
// 此处只有Mode为
if (!isOpportunisticScanClient(client)) {
// Reset batch scan. May need to stop the existing batch scan and update scan
// params.
resetBatchScan(client);
}
}
- 最终会调用ScanNativeInterface.gattClientStartBatchScan()方法,剩下的就是Navtive提供回来的回调了
// ScanManager.java
private void resetBatchScan(ScanClient client) {
int scannerId = client.scannerId;
BatchScanParams batchScanParams = getBatchScanParams();
// Stop batch if batch scan params changed and previous params is not null.
if (mBatchScanParams != null && (!mBatchScanParams.equals(batchScanParams))) {
Log.d(TAG, "stopping BLe Batch");
resetCountDownLatch();
mNativeInterface.gattClientStopBatchScan(scannerId);
waitForCallback();
// Clear pending results as it's illegal to config storage if there are still
// pending results.
flushBatchResults(scannerId);
}
// Start batch if batchScanParams changed and current params is not null.
if (batchScanParams != null && (!batchScanParams.equals(mBatchScanParams))) {
int notifyThreshold = 95;
Log.d(TAG, "Starting BLE batch scan");
int resultType = getResultType(batchScanParams);
int fullScanPercent = getFullScanStoragePercent(resultType);
resetCountDownLatch();
Log.d(TAG, "configuring batch scan storage, appIf " + client.scannerId);
mNativeInterface.gattClientConfigBatchScanStorage(
client.scannerId, fullScanPercent, 100 - fullScanPercent, notifyThreshold);
waitForCallback();
resetCountDownLatch();
int scanInterval =
Utils.millsToUnit(getBatchScanIntervalMillis(batchScanParams.scanMode));
int scanWindow =
Utils.millsToUnit(getBatchScanWindowMillis(batchScanParams.scanMode));
// 12.此处进行native调用
mNativeInterface.gattClientStartBatchScan(
scannerId,
resultType,
scanInterval,
scanWindow,
0,
DISCARD_OLDEST_WHEN_BUFFER_FULL);
waitForCallback();
}
mBatchScanParams = batchScanParams;
setBatchAlarm();
}
2.蓝牙扫描限制
BluetoothLeScanner.java 的startScan()中,会调用BleScanCallbackWrapper.startRegistration() ,而BleScanCallbackWrapper本质上是去调用GattService.registerScanner()
在GattService.registerScanner()方法中,AppScanStats.isScanningTooFrequently()用于判断是否过于频繁扫描
// BluetoothLeScanner.java
private int startScan(
List<ScanFilter> filters,
ScanSettings settings,
final WorkSource workSource,
final ScanCallback callback,
final PendingIntent callbackIntent) {
...
// 1.此处就是注册
BleScanCallbackWrapper wrapper =
new BleScanCallbackWrapper(gatt, filters, settings, workSource, callback);
wrapper.startRegistration();
...
// 此处就是之前分析开始扫描的地方
gatt.startScanForIntent(
callbackIntent, settings, filters, mAttributionSource, recv);
}
// BluetoothLeScanner.BleScanCallbackWrapper.java
public void startRegistration() {
synchronized (this) {
// Scan stopped.
if (mScannerId == -1 || mScannerId == -2) return;
try {
// 此处调用GattService.registerScanner()
mBluetoothGatt.registerScanner(this, mWorkSource);
wait(REGISTRATION_CALLBACK_TIMEOUT_MILLIS);
} catch (InterruptedException | RemoteException e) {
Log.e(TAG, "application registeration exception", e);
postCallbackError(mScanCallback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
}
if (mScannerId > 0) {
mLeScanClients.put(mScanCallback, this);
} else {
// Registration timed out or got exception, reset RscannerId to -1 so no
// subsequent operations can proceed.
if (mScannerId == 0) mScannerId = -1;
// If scanning too frequently, don't report anything to the app.
if (mScannerId == -2) return;
postCallbackError(mScanCallback,
ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED);
}
}
}
// GattService.BluetoothGattBinder.java
@Override
public void registerScanner(IScannerCallback callback, WorkSource workSource)
throws RemoteException {
GattService service = getService();
if (service == null) {
return;
}
service.registerScanner(callback, workSource);
}
// GattService.java
void registerScanner(IScannerCallback callback, WorkSource workSource) throws RemoteException {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
UUID uuid = UUID.randomUUID();
if (DBG) {
Log.d(TAG, "registerScanner() - UUID=" + uuid);
}
if (workSource != null) {
enforceImpersonatationPermission();
}
AppScanStats app = mScannerMap.getAppScanStatsByUid(Binder.getCallingUid());
if (app != null && app.isScanningTooFrequently()
&& checkCallingOrSelfPermission(BLUETOOTH_PRIVILEGED) != PERMISSION_GRANTED) {
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);
}
@Override
public void registerScanner(IScannerCallback callback, WorkSource workSource)
throws RemoteException {
GattService service = getService();
if (service == null) {
return;
}
service.registerScanner(callback, workSource);
}
-
AppScanStats.isScanningTooFrequently() android 14版本 与Andoid10 源码只有标识名称不同,上面为14,下面为10,其中有俩个条件会触发扫描频率过快问题。
AdapterService.getScanQuotaCount()返回最大次数为5,即扫描次数累计小于5
当前时间戳 - 上次扫描结束时间戳(mLastScans最先加入的一组,即第一次扫描结束时间,mLastScans大小为4) < 30s
// AdapterService.DeviceConfigListener.java
private static final int DEFAULT_SCAN_QUOTA_COUNT = 5; //扫描的最大次数
private static final long DEFAULT_SCAN_QUOTA_WINDOW_MILLIS = 30 * SECOND_IN_MILLIS; // 扫描周期
// AppScanStats.java
public synchronized boolean isScanningTooFrequently() {
if (mLastScans.size() < mAdapterService.getScanQuotaCount()) {
return false;
}
return (SystemClock.elapsedRealtime() - mLastScans.get(0).timestamp)
< mAdapterService.getScanQuotaWindowMillis();
}
public synchronized void recordScanStop(int scannerId) {
LastScan scan = getScanFromScannerId(scannerId);
if (scan == null) {
return;
}
this.mScansStopped++;
stopTime = SystemClock.elapsedRealtime();
long scanDuration = stopTime - scan.timestamp;
scan.duration = scanDuration;
if (scan.isSuspended) {
long suspendDuration = stopTime - scan.suspendStartTime;
scan.suspendDuration += suspendDuration;
mTotalSuspendTime += suspendDuration;
}
mOngoingScans.remove(scannerId);
if (mLastScans.size() >= mAdapterService.getScanQuotaCount()) {
mLastScans.remove(0);
}
// 此处在扫描结束时会记录第一次扫描结束时间
mLastScans.add(scan);
...
}
// andorid 10版本,标记名称会不一样
static final int NUM_SCAN_DURATIONS_KEPT = 5;
static final long EXCESSIVE_SCANNING_PERIOD_MS = 30 * 1000;
蓝牙广播
- 广播状态
设备每次进行广播时,通过三个广播通道发送相同数据包。此三个数据包序列称为一个“广播事件”。两个广播事件之间的时间被称为广播间隔,长度从 20 毫秒到 10.24 秒不等。

- 广播时序
2个广播的间隔时间 T_advEvent= 广播间隔 + advDelay(0 - 10ms)
其中广播会在不同的广播通道(37/38/39)中进行广播,每跳一个通道,其广播时长 <= 10.24s

- 扫描时序
设备扫描会在 (37/38/39)广播通道索引高中扫描,扫描窗口时间和扫描间隔时间 < 10.24s,每个通道索引的扫描时间独立。
- 安卓蓝牙连接
进行连接前,需要扫描到蓝牙广播,否则连接时会报GATT_ERROR = 133代码,且安卓蓝牙扫描所返回的列表,是由底层Native控制的,其扫描列表有大小限制,具体参数未能查询到相关参数
