Loading android/app/src/com/android/bluetooth/btservice/AdapterService.java +18 −21 Original line number Diff line number Diff line Loading @@ -5226,33 +5226,30 @@ public class AdapterService extends Service { } } public static int getScanQuotaCount() { if (sAdapterService == null) { return DeviceConfigListener.DEFAULT_SCAN_QUOTA_COUNT; } synchronized (sAdapterService.mDeviceConfigLock) { return sAdapterService.mScanQuotaCount; } /** * Returns scan quota count. */ public int getScanQuotaCount() { synchronized (mDeviceConfigLock) { return mScanQuotaCount; } public static long getScanQuotaWindowMillis() { if (sAdapterService == null) { return DeviceConfigListener.DEFAULT_SCAN_QUOTA_WINDOW_MILLIS; } synchronized (sAdapterService.mDeviceConfigLock) { return sAdapterService.mScanQuotaWindowMillis; } /** * Returns scan quota window in millis. */ public long getScanQuotaWindowMillis() { synchronized (mDeviceConfigLock) { return mScanQuotaWindowMillis; } public static long getScanTimeoutMillis() { if (sAdapterService == null) { return DeviceConfigListener.DEFAULT_SCAN_TIMEOUT_MILLIS; } synchronized (sAdapterService.mDeviceConfigLock) { return sAdapterService.mScanTimeoutMillis; /** * Returns scan timeout in millis. */ public long getScanTimeoutMillis() { synchronized (mDeviceConfigLock) { return mScanTimeoutMillis; } } Loading android/app/src/com/android/bluetooth/btservice/BluetoothAdapterProxy.java 0 → 100644 +75 −0 Original line number Diff line number Diff line /* * Copyright 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.bluetooth.btservice; import android.bluetooth.BluetoothAdapter; import android.util.Log; import com.android.bluetooth.Utils; import com.android.internal.annotations.VisibleForTesting; /** * A proxy class that facilitates testing of the ScanManager. * * This is necessary due to the "final" attribute of the BluetoothAdapter class. In order to * test the correct functioning of the ScanManager class, the final class must be put * into a container that can be mocked correctly. */ public class BluetoothAdapterProxy { private static final String TAG = BluetoothAdapterProxy.class.getSimpleName(); private static BluetoothAdapterProxy sInstance; private static final Object INSTANCE_LOCK = new Object(); private BluetoothAdapterProxy() {} /** * Get the singleton instance of proxy. * * @return the singleton instance, guaranteed not null */ public static BluetoothAdapterProxy getInstance() { synchronized (INSTANCE_LOCK) { if (sInstance == null) { sInstance = new BluetoothAdapterProxy(); } return sInstance; } } /** * Proxy function that calls {@link BluetoothAdapter#isOffloadedFilteringSupported()}. * * @return whether the offloaded scan filtering is supported */ public boolean isOffloadedScanFilteringSupported() { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); return adapter.isOffloadedFilteringSupported(); } /** * Allow unit tests to substitute BluetoothAdapterProxy with a test instance * * @param proxy a test instance of the BluetoothAdapterProxy */ @VisibleForTesting public static void setInstanceForTesting(BluetoothAdapterProxy proxy) { Utils.enforceInstrumentationTestMode(); synchronized (INSTANCE_LOCK) { Log.d(TAG, "setInstanceForTesting(), set to " + proxy); sInstance = proxy; } } } android/app/src/com/android/bluetooth/gatt/AppScanStats.java +0 −23 Original line number Diff line number Diff line Loading @@ -105,29 +105,6 @@ import java.util.Objects; this.filterString = ""; } } static int getNumScanDurationsKept() { return AdapterService.getScanQuotaCount(); } // This constant defines the time window an app can scan multiple times. // Any single app can scan up to |NUM_SCAN_DURATIONS_KEPT| times during // this window. Once they reach this limit, they must wait until their // earliest recorded scan exits this window. static long getExcessiveScanningPeriodMillis() { return AdapterService.getScanQuotaWindowMillis(); } // Maximum msec before scan gets downgraded to opportunistic static long getScanTimeoutMillis() { return AdapterService.getScanTimeoutMillis(); } // Scan mode upgrade duration after scanStart() static long getScanUpgradeDurationMillis() { return AdapterService.getAdapterService().getScanUpgradeDurationMillis(); } public String appName; public WorkSource mWorkSource; // Used for BatteryStatsManager public final WorkSourceUtil mWorkSourceUtil; // Used for BluetoothStatsLog Loading android/app/src/com/android/bluetooth/gatt/GattService.java +13 −1 Original line number Diff line number Diff line Loading @@ -75,6 +75,7 @@ import com.android.bluetooth.R; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.AbstractionLayer; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.BluetoothAdapterProxy; import com.android.bluetooth.btservice.ProfileService; import com.android.bluetooth.util.NumberUtils; import com.android.internal.annotations.VisibleForTesting; Loading Loading @@ -262,6 +263,7 @@ public class GattService extends ProfileService { private final HashMap<String, AtomicBoolean> mPermits = new HashMap<>(); private AdapterService mAdapterService; private BluetoothAdapterProxy mBluetoothAdapterProxy; private AdvertiseManager mAdvertiseManager; private PeriodicScanManager mPeriodicScanManager; private ScanManager mScanManager; Loading Loading @@ -319,12 +321,13 @@ public class GattService extends ProfileService { initializeNative(); mAdapterService = AdapterService.getAdapterService(); mBluetoothAdapterProxy = BluetoothAdapterProxy.getInstance(); mCompanionManager = getSystemService(CompanionDeviceManager.class); mAppOps = getSystemService(AppOpsManager.class); mAdvertiseManager = new AdvertiseManager(this, mAdapterService); mAdvertiseManager.start(); mScanManager = new ScanManager(this, mAdapterService); mScanManager = new ScanManager(this, mAdapterService, mBluetoothAdapterProxy); mScanManager.start(); mPeriodicScanManager = new PeriodicScanManager(mAdapterService); Loading Loading @@ -426,6 +429,15 @@ public class GattService extends ProfileService { return sGattService; } @VisibleForTesting ScanManager getScanManager() { if (mScanManager == null) { Log.w(TAG, "getScanManager(): scan manager is null"); return null; } return mScanManager; } private static synchronized void setGattService(GattService instance) { if (DBG) { Log.d(TAG, "setGattService(): set to: " + instance); Loading android/app/src/com/android/bluetooth/gatt/ScanManager.java +80 −32 Original line number Diff line number Diff line Loading @@ -20,7 +20,6 @@ import android.annotation.RequiresPermission; import android.app.ActivityManager; import android.app.AlarmManager; import android.app.PendingIntent; import android.bluetooth.BluetoothAdapter; import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanSettings; Loading @@ -45,6 +44,8 @@ import android.view.Display; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.BluetoothAdapterProxy; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayDeque; import java.util.Collections; Loading Loading @@ -86,17 +87,18 @@ public class ScanManager { static final int SCAN_RESULT_TYPE_FULL = 2; static final int SCAN_RESULT_TYPE_BOTH = 3; // Internal messages for handling BLE scan operations. private static final int MSG_START_BLE_SCAN = 0; private static final int MSG_STOP_BLE_SCAN = 1; private static final int MSG_FLUSH_BATCH_RESULTS = 2; private static final int MSG_SCAN_TIMEOUT = 3; private static final int MSG_SUSPEND_SCANS = 4; private static final int MSG_RESUME_SCANS = 5; private static final int MSG_IMPORTANCE_CHANGE = 6; private static final int MSG_SCREEN_ON = 7; private static final int MSG_SCREEN_OFF = 8; private static final int MSG_REVERT_SCAN_MODE_UPGRADE = 9; // Messages for handling BLE scan operations. @VisibleForTesting static final int MSG_START_BLE_SCAN = 0; static final int MSG_STOP_BLE_SCAN = 1; static final int MSG_FLUSH_BATCH_RESULTS = 2; static final int MSG_SCAN_TIMEOUT = 3; static final int MSG_SUSPEND_SCANS = 4; static final int MSG_RESUME_SCANS = 5; static final int MSG_IMPORTANCE_CHANGE = 6; static final int MSG_SCREEN_ON = 7; static final int MSG_SCREEN_OFF = 8; static final int MSG_REVERT_SCAN_MODE_UPGRADE = 9; private static final String ACTION_REFRESH_BATCHED_SCAN = "com.android.bluetooth.gatt.REFRESH_BATCHED_SCAN"; Loading @@ -115,6 +117,7 @@ public class ScanManager { private boolean mBatchAlarmReceiverRegistered; private ScanNative mScanNative; private volatile ClientHandler mHandler; private BluetoothAdapterProxy mBluetoothAdapterProxy; private Set<ScanClient> mRegularScanClients; private Set<ScanClient> mBatchClients; Loading @@ -134,7 +137,8 @@ public class ScanManager { private final SparseBooleanArray mIsUidForegroundMap = new SparseBooleanArray(); private boolean mScreenOn = false; private class UidImportance { @VisibleForTesting static class UidImportance { public int uid; public int importance; Loading @@ -144,7 +148,8 @@ public class ScanManager { } } ScanManager(GattService service, AdapterService adapterService) { ScanManager(GattService service, AdapterService adapterService, BluetoothAdapterProxy bluetoothAdapterProxy) { mRegularScanClients = Collections.newSetFromMap(new ConcurrentHashMap<ScanClient, Boolean>()); mBatchClients = Collections.newSetFromMap(new ConcurrentHashMap<ScanClient, Boolean>()); Loading @@ -157,6 +162,7 @@ public class ScanManager { mActivityManager = mService.getSystemService(ActivityManager.class); mLocationManager = mService.getSystemService(LocationManager.class); mAdapterService = adapterService; mBluetoothAdapterProxy = bluetoothAdapterProxy; mPriorityMap.put(ScanSettings.SCAN_MODE_OPPORTUNISTIC, 0); mPriorityMap.put(ScanSettings.SCAN_MODE_SCREEN_OFF, 1); Loading @@ -175,6 +181,7 @@ public class ScanManager { if (mDm != null) { mDm.registerDisplayListener(mDisplayListener, null); } mScreenOn = isScreenOn(); if (mActivityManager != null) { mActivityManager.addOnUidImportanceListener(mUidImportanceListener, FOREGROUND_IMPORTANCE_CUTOFF); Loading Loading @@ -234,6 +241,13 @@ public class ScanManager { return mRegularScanClients; } /** * Returns the suspended scan queue. */ Set<ScanClient> getSuspendedScanQueue() { return mSuspendedScanClients; } /** * Returns batch scan queue. */ Loading Loading @@ -298,8 +312,11 @@ public class ScanManager { } private boolean isFilteringSupported() { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); return adapter.isOffloadedFilteringSupported(); if (mBluetoothAdapterProxy == null) { Log.e(TAG, "mBluetoothAdapterProxy is null"); return false; } return mBluetoothAdapterProxy.isOffloadedScanFilteringSupported(); } // Handler class that handles BLE scan operations. Loading Loading @@ -363,7 +380,7 @@ public class ScanManager { return; } if (requiresScreenOn(client) && !isScreenOn()) { 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); Loading Loading @@ -400,6 +417,11 @@ public class ScanManager { msg.obj = client; // Only one timeout message should exist at any time sendMessageDelayed(msg, mAdapterService.getScanTimeoutMillis()); if (DBG) { Log.d(TAG, "apply scan timeout (" + mAdapterService.getScanTimeoutMillis() + ")" + "to scannerId " + client.scannerId); } } } } Loading Loading @@ -429,13 +451,10 @@ public class ScanManager { mSuspendedScanClients.remove(client); } removeMessages(MSG_REVERT_SCAN_MODE_UPGRADE, client); removeMessages(MSG_SCAN_TIMEOUT, client); if (mRegularScanClients.contains(client)) { mScanNative.stopRegularScan(client); if (mScanNative.numRegularScanClients() == 0) { removeMessages(MSG_SCAN_TIMEOUT); } if (!mScanNative.isOpportunisticScanClient(client)) { mScanNative.configureRegularScanParams(); } Loading Loading @@ -493,7 +512,7 @@ public class ScanManager { @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) void handleSuspendScans() { for (ScanClient client : mRegularScanClients) { if ((requiresScreenOn(client) && !isScreenOn()) if ((requiresScreenOn(client) && !mScreenOn) || (requiresLocationOn(client) && !mLocationManager.isLocationEnabled())) { /*Suspend unfiltered scans*/ if (client.stats != null) { Loading Loading @@ -524,6 +543,9 @@ public class ScanManager { } private boolean updateScanModeScreenOff(ScanClient client) { if (mScanNative.isTimeoutScanClient(client)) { return false; } if (!isAppForeground(client) && !mScanNative.isOpportunisticScanClient(client)) { return client.updateScanMode(ScanSettings.SCAN_MODE_SCREEN_OFF); } Loading Loading @@ -555,7 +577,7 @@ public class ScanManager { if (upgradeScanModeBeforeStart(client)) { return true; } if (isScreenOn()) { if (mScreenOn) { return updateScanModeScreenOn(client); } else { return updateScanModeScreenOff(client); Loading Loading @@ -613,6 +635,10 @@ public class ScanManager { } private boolean updateScanModeScreenOn(ScanClient client) { if (mScanNative.isTimeoutScanClient(client)) { return false; } int newScanMode = (isAppForeground(client) || mScanNative.isOpportunisticScanClient(client)) ? client.scanModeApp : SCAN_MODE_APP_IN_BACKGROUND; Loading @@ -633,7 +659,7 @@ public class ScanManager { void handleResumeScans() { for (ScanClient client : mSuspendedScanClients) { if ((!requiresScreenOn(client) || isScreenOn()) if ((!requiresScreenOn(client) || mScreenOn) && (!requiresLocationOn(client) || mLocationManager.isLocationEnabled())) { if (client.stats != null) { client.stats.recordScanResume(client.scannerId); Loading Loading @@ -871,14 +897,19 @@ public class ScanManager { } private boolean isExemptFromScanDowngrade(ScanClient client) { return isOpportunisticScanClient(client) || isFirstMatchScanClient(client) || !shouldUseAllPassFilter(client); return isOpportunisticScanClient(client) || isFirstMatchScanClient(client); } private boolean isOpportunisticScanClient(ScanClient client) { return client.settings.getScanMode() == ScanSettings.SCAN_MODE_OPPORTUNISTIC; } private boolean isTimeoutScanClient(ScanClient client) { return (client.stats != null) && (client.stats.getScanFromScannerId(client.scannerId) != null) && (client.stats.getScanFromScannerId(client.scannerId).isTimeout); } private boolean isFirstMatchScanClient(ScanClient client) { return (client.settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0; Loading Loading @@ -1046,11 +1077,23 @@ public class ScanManager { void regularScanTimeout(ScanClient client) { if (!isExemptFromScanDowngrade(client) && client.stats.isScanningTooLong()) { if (DBG) { Log.d(TAG, "regularScanTimeout - client scan time was too long"); } if (client.filters == null || client.filters.isEmpty()) { Log.w(TAG, "Moving scan client to opportunistic (scannerId " + client.scannerId + ")"); "Moving unfiltered scan client to opportunistic scan (scannerId " + client.scannerId + ")"); setOpportunisticScanClient(client); removeScanFilters(client.scannerId); client.stats.setScanTimeout(client.scannerId); } else { Log.w(TAG, "Moving filtered scan client to downgraded scan (scannerId " + client.scannerId + ")"); client.updateScanMode(ScanSettings.SCAN_MODE_LOW_POWER); client.stats.setScanTimeout(client.scannerId); } } // The scan should continue for background scans Loading Loading @@ -1527,6 +1570,11 @@ public class ScanManager { private native void gattClientReadScanReportsNative(int clientIf, int scanType); } @VisibleForTesting ClientHandler getClientHandler() { return mHandler; } private boolean isScreenOn() { Display[] displays = mDm.getDisplays(); Loading Loading @@ -1605,7 +1653,7 @@ public class ScanManager { } for (ScanClient client : mRegularScanClients) { if (client.appUid != uid) { if (client.appUid != uid || mScanNative.isTimeoutScanClient(client)) { continue; } if (isForeground) { Loading Loading
android/app/src/com/android/bluetooth/btservice/AdapterService.java +18 −21 Original line number Diff line number Diff line Loading @@ -5226,33 +5226,30 @@ public class AdapterService extends Service { } } public static int getScanQuotaCount() { if (sAdapterService == null) { return DeviceConfigListener.DEFAULT_SCAN_QUOTA_COUNT; } synchronized (sAdapterService.mDeviceConfigLock) { return sAdapterService.mScanQuotaCount; } /** * Returns scan quota count. */ public int getScanQuotaCount() { synchronized (mDeviceConfigLock) { return mScanQuotaCount; } public static long getScanQuotaWindowMillis() { if (sAdapterService == null) { return DeviceConfigListener.DEFAULT_SCAN_QUOTA_WINDOW_MILLIS; } synchronized (sAdapterService.mDeviceConfigLock) { return sAdapterService.mScanQuotaWindowMillis; } /** * Returns scan quota window in millis. */ public long getScanQuotaWindowMillis() { synchronized (mDeviceConfigLock) { return mScanQuotaWindowMillis; } public static long getScanTimeoutMillis() { if (sAdapterService == null) { return DeviceConfigListener.DEFAULT_SCAN_TIMEOUT_MILLIS; } synchronized (sAdapterService.mDeviceConfigLock) { return sAdapterService.mScanTimeoutMillis; /** * Returns scan timeout in millis. */ public long getScanTimeoutMillis() { synchronized (mDeviceConfigLock) { return mScanTimeoutMillis; } } Loading
android/app/src/com/android/bluetooth/btservice/BluetoothAdapterProxy.java 0 → 100644 +75 −0 Original line number Diff line number Diff line /* * Copyright 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.bluetooth.btservice; import android.bluetooth.BluetoothAdapter; import android.util.Log; import com.android.bluetooth.Utils; import com.android.internal.annotations.VisibleForTesting; /** * A proxy class that facilitates testing of the ScanManager. * * This is necessary due to the "final" attribute of the BluetoothAdapter class. In order to * test the correct functioning of the ScanManager class, the final class must be put * into a container that can be mocked correctly. */ public class BluetoothAdapterProxy { private static final String TAG = BluetoothAdapterProxy.class.getSimpleName(); private static BluetoothAdapterProxy sInstance; private static final Object INSTANCE_LOCK = new Object(); private BluetoothAdapterProxy() {} /** * Get the singleton instance of proxy. * * @return the singleton instance, guaranteed not null */ public static BluetoothAdapterProxy getInstance() { synchronized (INSTANCE_LOCK) { if (sInstance == null) { sInstance = new BluetoothAdapterProxy(); } return sInstance; } } /** * Proxy function that calls {@link BluetoothAdapter#isOffloadedFilteringSupported()}. * * @return whether the offloaded scan filtering is supported */ public boolean isOffloadedScanFilteringSupported() { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); return adapter.isOffloadedFilteringSupported(); } /** * Allow unit tests to substitute BluetoothAdapterProxy with a test instance * * @param proxy a test instance of the BluetoothAdapterProxy */ @VisibleForTesting public static void setInstanceForTesting(BluetoothAdapterProxy proxy) { Utils.enforceInstrumentationTestMode(); synchronized (INSTANCE_LOCK) { Log.d(TAG, "setInstanceForTesting(), set to " + proxy); sInstance = proxy; } } }
android/app/src/com/android/bluetooth/gatt/AppScanStats.java +0 −23 Original line number Diff line number Diff line Loading @@ -105,29 +105,6 @@ import java.util.Objects; this.filterString = ""; } } static int getNumScanDurationsKept() { return AdapterService.getScanQuotaCount(); } // This constant defines the time window an app can scan multiple times. // Any single app can scan up to |NUM_SCAN_DURATIONS_KEPT| times during // this window. Once they reach this limit, they must wait until their // earliest recorded scan exits this window. static long getExcessiveScanningPeriodMillis() { return AdapterService.getScanQuotaWindowMillis(); } // Maximum msec before scan gets downgraded to opportunistic static long getScanTimeoutMillis() { return AdapterService.getScanTimeoutMillis(); } // Scan mode upgrade duration after scanStart() static long getScanUpgradeDurationMillis() { return AdapterService.getAdapterService().getScanUpgradeDurationMillis(); } public String appName; public WorkSource mWorkSource; // Used for BatteryStatsManager public final WorkSourceUtil mWorkSourceUtil; // Used for BluetoothStatsLog Loading
android/app/src/com/android/bluetooth/gatt/GattService.java +13 −1 Original line number Diff line number Diff line Loading @@ -75,6 +75,7 @@ import com.android.bluetooth.R; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.AbstractionLayer; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.BluetoothAdapterProxy; import com.android.bluetooth.btservice.ProfileService; import com.android.bluetooth.util.NumberUtils; import com.android.internal.annotations.VisibleForTesting; Loading Loading @@ -262,6 +263,7 @@ public class GattService extends ProfileService { private final HashMap<String, AtomicBoolean> mPermits = new HashMap<>(); private AdapterService mAdapterService; private BluetoothAdapterProxy mBluetoothAdapterProxy; private AdvertiseManager mAdvertiseManager; private PeriodicScanManager mPeriodicScanManager; private ScanManager mScanManager; Loading Loading @@ -319,12 +321,13 @@ public class GattService extends ProfileService { initializeNative(); mAdapterService = AdapterService.getAdapterService(); mBluetoothAdapterProxy = BluetoothAdapterProxy.getInstance(); mCompanionManager = getSystemService(CompanionDeviceManager.class); mAppOps = getSystemService(AppOpsManager.class); mAdvertiseManager = new AdvertiseManager(this, mAdapterService); mAdvertiseManager.start(); mScanManager = new ScanManager(this, mAdapterService); mScanManager = new ScanManager(this, mAdapterService, mBluetoothAdapterProxy); mScanManager.start(); mPeriodicScanManager = new PeriodicScanManager(mAdapterService); Loading Loading @@ -426,6 +429,15 @@ public class GattService extends ProfileService { return sGattService; } @VisibleForTesting ScanManager getScanManager() { if (mScanManager == null) { Log.w(TAG, "getScanManager(): scan manager is null"); return null; } return mScanManager; } private static synchronized void setGattService(GattService instance) { if (DBG) { Log.d(TAG, "setGattService(): set to: " + instance); Loading
android/app/src/com/android/bluetooth/gatt/ScanManager.java +80 −32 Original line number Diff line number Diff line Loading @@ -20,7 +20,6 @@ import android.annotation.RequiresPermission; import android.app.ActivityManager; import android.app.AlarmManager; import android.app.PendingIntent; import android.bluetooth.BluetoothAdapter; import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanSettings; Loading @@ -45,6 +44,8 @@ import android.view.Display; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.BluetoothAdapterProxy; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayDeque; import java.util.Collections; Loading Loading @@ -86,17 +87,18 @@ public class ScanManager { static final int SCAN_RESULT_TYPE_FULL = 2; static final int SCAN_RESULT_TYPE_BOTH = 3; // Internal messages for handling BLE scan operations. private static final int MSG_START_BLE_SCAN = 0; private static final int MSG_STOP_BLE_SCAN = 1; private static final int MSG_FLUSH_BATCH_RESULTS = 2; private static final int MSG_SCAN_TIMEOUT = 3; private static final int MSG_SUSPEND_SCANS = 4; private static final int MSG_RESUME_SCANS = 5; private static final int MSG_IMPORTANCE_CHANGE = 6; private static final int MSG_SCREEN_ON = 7; private static final int MSG_SCREEN_OFF = 8; private static final int MSG_REVERT_SCAN_MODE_UPGRADE = 9; // Messages for handling BLE scan operations. @VisibleForTesting static final int MSG_START_BLE_SCAN = 0; static final int MSG_STOP_BLE_SCAN = 1; static final int MSG_FLUSH_BATCH_RESULTS = 2; static final int MSG_SCAN_TIMEOUT = 3; static final int MSG_SUSPEND_SCANS = 4; static final int MSG_RESUME_SCANS = 5; static final int MSG_IMPORTANCE_CHANGE = 6; static final int MSG_SCREEN_ON = 7; static final int MSG_SCREEN_OFF = 8; static final int MSG_REVERT_SCAN_MODE_UPGRADE = 9; private static final String ACTION_REFRESH_BATCHED_SCAN = "com.android.bluetooth.gatt.REFRESH_BATCHED_SCAN"; Loading @@ -115,6 +117,7 @@ public class ScanManager { private boolean mBatchAlarmReceiverRegistered; private ScanNative mScanNative; private volatile ClientHandler mHandler; private BluetoothAdapterProxy mBluetoothAdapterProxy; private Set<ScanClient> mRegularScanClients; private Set<ScanClient> mBatchClients; Loading @@ -134,7 +137,8 @@ public class ScanManager { private final SparseBooleanArray mIsUidForegroundMap = new SparseBooleanArray(); private boolean mScreenOn = false; private class UidImportance { @VisibleForTesting static class UidImportance { public int uid; public int importance; Loading @@ -144,7 +148,8 @@ public class ScanManager { } } ScanManager(GattService service, AdapterService adapterService) { ScanManager(GattService service, AdapterService adapterService, BluetoothAdapterProxy bluetoothAdapterProxy) { mRegularScanClients = Collections.newSetFromMap(new ConcurrentHashMap<ScanClient, Boolean>()); mBatchClients = Collections.newSetFromMap(new ConcurrentHashMap<ScanClient, Boolean>()); Loading @@ -157,6 +162,7 @@ public class ScanManager { mActivityManager = mService.getSystemService(ActivityManager.class); mLocationManager = mService.getSystemService(LocationManager.class); mAdapterService = adapterService; mBluetoothAdapterProxy = bluetoothAdapterProxy; mPriorityMap.put(ScanSettings.SCAN_MODE_OPPORTUNISTIC, 0); mPriorityMap.put(ScanSettings.SCAN_MODE_SCREEN_OFF, 1); Loading @@ -175,6 +181,7 @@ public class ScanManager { if (mDm != null) { mDm.registerDisplayListener(mDisplayListener, null); } mScreenOn = isScreenOn(); if (mActivityManager != null) { mActivityManager.addOnUidImportanceListener(mUidImportanceListener, FOREGROUND_IMPORTANCE_CUTOFF); Loading Loading @@ -234,6 +241,13 @@ public class ScanManager { return mRegularScanClients; } /** * Returns the suspended scan queue. */ Set<ScanClient> getSuspendedScanQueue() { return mSuspendedScanClients; } /** * Returns batch scan queue. */ Loading Loading @@ -298,8 +312,11 @@ public class ScanManager { } private boolean isFilteringSupported() { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); return adapter.isOffloadedFilteringSupported(); if (mBluetoothAdapterProxy == null) { Log.e(TAG, "mBluetoothAdapterProxy is null"); return false; } return mBluetoothAdapterProxy.isOffloadedScanFilteringSupported(); } // Handler class that handles BLE scan operations. Loading Loading @@ -363,7 +380,7 @@ public class ScanManager { return; } if (requiresScreenOn(client) && !isScreenOn()) { 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); Loading Loading @@ -400,6 +417,11 @@ public class ScanManager { msg.obj = client; // Only one timeout message should exist at any time sendMessageDelayed(msg, mAdapterService.getScanTimeoutMillis()); if (DBG) { Log.d(TAG, "apply scan timeout (" + mAdapterService.getScanTimeoutMillis() + ")" + "to scannerId " + client.scannerId); } } } } Loading Loading @@ -429,13 +451,10 @@ public class ScanManager { mSuspendedScanClients.remove(client); } removeMessages(MSG_REVERT_SCAN_MODE_UPGRADE, client); removeMessages(MSG_SCAN_TIMEOUT, client); if (mRegularScanClients.contains(client)) { mScanNative.stopRegularScan(client); if (mScanNative.numRegularScanClients() == 0) { removeMessages(MSG_SCAN_TIMEOUT); } if (!mScanNative.isOpportunisticScanClient(client)) { mScanNative.configureRegularScanParams(); } Loading Loading @@ -493,7 +512,7 @@ public class ScanManager { @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) void handleSuspendScans() { for (ScanClient client : mRegularScanClients) { if ((requiresScreenOn(client) && !isScreenOn()) if ((requiresScreenOn(client) && !mScreenOn) || (requiresLocationOn(client) && !mLocationManager.isLocationEnabled())) { /*Suspend unfiltered scans*/ if (client.stats != null) { Loading Loading @@ -524,6 +543,9 @@ public class ScanManager { } private boolean updateScanModeScreenOff(ScanClient client) { if (mScanNative.isTimeoutScanClient(client)) { return false; } if (!isAppForeground(client) && !mScanNative.isOpportunisticScanClient(client)) { return client.updateScanMode(ScanSettings.SCAN_MODE_SCREEN_OFF); } Loading Loading @@ -555,7 +577,7 @@ public class ScanManager { if (upgradeScanModeBeforeStart(client)) { return true; } if (isScreenOn()) { if (mScreenOn) { return updateScanModeScreenOn(client); } else { return updateScanModeScreenOff(client); Loading Loading @@ -613,6 +635,10 @@ public class ScanManager { } private boolean updateScanModeScreenOn(ScanClient client) { if (mScanNative.isTimeoutScanClient(client)) { return false; } int newScanMode = (isAppForeground(client) || mScanNative.isOpportunisticScanClient(client)) ? client.scanModeApp : SCAN_MODE_APP_IN_BACKGROUND; Loading @@ -633,7 +659,7 @@ public class ScanManager { void handleResumeScans() { for (ScanClient client : mSuspendedScanClients) { if ((!requiresScreenOn(client) || isScreenOn()) if ((!requiresScreenOn(client) || mScreenOn) && (!requiresLocationOn(client) || mLocationManager.isLocationEnabled())) { if (client.stats != null) { client.stats.recordScanResume(client.scannerId); Loading Loading @@ -871,14 +897,19 @@ public class ScanManager { } private boolean isExemptFromScanDowngrade(ScanClient client) { return isOpportunisticScanClient(client) || isFirstMatchScanClient(client) || !shouldUseAllPassFilter(client); return isOpportunisticScanClient(client) || isFirstMatchScanClient(client); } private boolean isOpportunisticScanClient(ScanClient client) { return client.settings.getScanMode() == ScanSettings.SCAN_MODE_OPPORTUNISTIC; } private boolean isTimeoutScanClient(ScanClient client) { return (client.stats != null) && (client.stats.getScanFromScannerId(client.scannerId) != null) && (client.stats.getScanFromScannerId(client.scannerId).isTimeout); } private boolean isFirstMatchScanClient(ScanClient client) { return (client.settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0; Loading Loading @@ -1046,11 +1077,23 @@ public class ScanManager { void regularScanTimeout(ScanClient client) { if (!isExemptFromScanDowngrade(client) && client.stats.isScanningTooLong()) { if (DBG) { Log.d(TAG, "regularScanTimeout - client scan time was too long"); } if (client.filters == null || client.filters.isEmpty()) { Log.w(TAG, "Moving scan client to opportunistic (scannerId " + client.scannerId + ")"); "Moving unfiltered scan client to opportunistic scan (scannerId " + client.scannerId + ")"); setOpportunisticScanClient(client); removeScanFilters(client.scannerId); client.stats.setScanTimeout(client.scannerId); } else { Log.w(TAG, "Moving filtered scan client to downgraded scan (scannerId " + client.scannerId + ")"); client.updateScanMode(ScanSettings.SCAN_MODE_LOW_POWER); client.stats.setScanTimeout(client.scannerId); } } // The scan should continue for background scans Loading Loading @@ -1527,6 +1570,11 @@ public class ScanManager { private native void gattClientReadScanReportsNative(int clientIf, int scanType); } @VisibleForTesting ClientHandler getClientHandler() { return mHandler; } private boolean isScreenOn() { Display[] displays = mDm.getDisplays(); Loading Loading @@ -1605,7 +1653,7 @@ public class ScanManager { } for (ScanClient client : mRegularScanClients) { if (client.appUid != uid) { if (client.appUid != uid || mScanNative.isTimeoutScanClient(client)) { continue; } if (isForeground) { Loading