diff options
5 files changed, 349 insertions, 86 deletions
diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterService.java b/android/app/src/com/android/bluetooth/btservice/AdapterService.java index 0780ee9297..a206b7a638 100644 --- a/android/app/src/com/android/bluetooth/btservice/AdapterService.java +++ b/android/app/src/com/android/bluetooth/btservice/AdapterService.java @@ -4012,6 +4012,9 @@ public class AdapterService extends Service { private long mScanQuotaWindowMillis = DeviceConfigListener.DEFAULT_SCAN_QUOTA_WINDOW_MILLIS; @GuardedBy("mDeviceConfigLock") private long mScanTimeoutMillis = DeviceConfigListener.DEFAULT_SCAN_TIMEOUT_MILLIS; + @GuardedBy("mDeviceConfigLock") + private int mScanUpgradeDurationMillis = + DeviceConfigListener.DEFAULT_SCAN_UPGRADE_DURATION_MILLIS; public @NonNull Predicate<String> getLocationDenylistName() { synchronized (mDeviceConfigLock) { @@ -4049,6 +4052,15 @@ public class AdapterService extends Service { } } + /** + * Returns scan upgrade duration in millis. + */ + public long getScanUpgradeDurationMillis() { + synchronized (mDeviceConfigLock) { + return mScanUpgradeDurationMillis; + } + } + private final DeviceConfigListener mDeviceConfigListener = new DeviceConfigListener(); private class DeviceConfigListener implements DeviceConfig.OnPropertiesChangedListener { @@ -4064,6 +4076,8 @@ public class AdapterService extends Service { "scan_quota_window_millis"; private static final String SCAN_TIMEOUT_MILLIS = "scan_timeout_millis"; + private static final String SCAN_UPGRADE_DURATION_MILLIS = + "scan_upgrade_duration_millis"; /** * Default denylist which matches Eddystone and iBeacon payloads. @@ -4074,6 +4088,7 @@ public class AdapterService extends Service { private static final int DEFAULT_SCAN_QUOTA_COUNT = 5; private static final long DEFAULT_SCAN_QUOTA_WINDOW_MILLIS = 30 * SECOND_IN_MILLIS; private static final long DEFAULT_SCAN_TIMEOUT_MILLIS = 30 * MINUTE_IN_MILLIS; + private static final int DEFAULT_SCAN_UPGRADE_DURATION_MILLIS = (int) SECOND_IN_MILLIS * 6; public void start() { DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_BLUETOOTH, @@ -4099,6 +4114,8 @@ public class AdapterService extends Service { DEFAULT_SCAN_QUOTA_WINDOW_MILLIS); mScanTimeoutMillis = properties.getLong(SCAN_TIMEOUT_MILLIS, DEFAULT_SCAN_TIMEOUT_MILLIS); + mScanUpgradeDurationMillis = properties.getInt(SCAN_UPGRADE_DURATION_MILLIS, + DEFAULT_SCAN_UPGRADE_DURATION_MILLIS); } } } diff --git a/android/app/src/com/android/bluetooth/gatt/AppScanStats.java b/android/app/src/com/android/bluetooth/gatt/AppScanStats.java index 6a42f764a9..e2662584a3 100644 --- a/android/app/src/com/android/bluetooth/gatt/AppScanStats.java +++ b/android/app/src/com/android/bluetooth/gatt/AppScanStats.java @@ -54,6 +54,8 @@ import java.util.Objects; static final int BALANCED_WEIGHT = 25; static final int LOW_LATENCY_WEIGHT = 100; + static final int LARGE_SCAN_TIME_GAP_MS = 24000; + // ContextMap here is needed to grab Apps and Connections ContextMap mContextMap; @@ -119,6 +121,11 @@ import java.util.Objects; return AdapterService.getAdapterService().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 @@ -363,6 +370,15 @@ import java.util.Objects; return (SystemClock.elapsedRealtime() - mScanStartTime) > getScanTimeoutMillis(); } + synchronized boolean hasRecentScan() { + if (!isScanning() || mLastScans.isEmpty()) { + return false; + } + LastScan lastScan = mLastScans.get(mLastScans.size() - 1); + return ((SystemClock.elapsedRealtime() - lastScan.duration - lastScan.timestamp) + < LARGE_SCAN_TIME_GAP_MS); + } + // This function truncates the app name for privacy reasons. Apps with // four part package names or more get truncated to three parts, and apps // with three part package names names get truncated to two. Apps with two diff --git a/android/app/src/com/android/bluetooth/gatt/ScanClient.java b/android/app/src/com/android/bluetooth/gatt/ScanClient.java index 4f39584a5f..c62872e058 100644 --- a/android/app/src/com/android/bluetooth/gatt/ScanClient.java +++ b/android/app/src/com/android/bluetooth/gatt/ScanClient.java @@ -32,7 +32,8 @@ import java.util.Objects; /* package */class ScanClient { public int scannerId; public ScanSettings settings; - public ScanSettings passiveSettings; + public int scanModeApp; + public boolean started = false; public int appUid; public List<ScanFilter> filters; // App associated with the scan client died. @@ -59,7 +60,7 @@ import java.util.Objects; ScanClient(int scannerId, ScanSettings settings, List<ScanFilter> filters) { this.scannerId = scannerId; this.settings = settings; - this.passiveSettings = null; + this.scanModeApp = settings.getScanMode(); this.filters = filters; this.appUid = Binder.getCallingUid(); } @@ -80,4 +81,37 @@ import java.util.Objects; public int hashCode() { return Objects.hash(scannerId); } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(" [ScanClient") + .append(" scanModeApp ").append(scanModeApp) + .append(" scanModeUsed ").append(settings.getScanMode()); + if (stats != null && stats.appName != null) { + sb.append(" [appScanStats ").append(stats.appName).append("]"); + } + sb.append("]"); + return sb.toString(); + } + + /** + * Update scan settings with the new scan mode. + * @param newScanMode + * @return true if scan settings are updated, false otherwise. + */ + public boolean updateScanMode(int newScanMode) { + if (settings.getScanMode() == newScanMode) { + return false; + } + + ScanSettings.Builder builder = new ScanSettings.Builder(); + settings = builder.setScanMode(newScanMode) + .setCallbackType(settings.getCallbackType()) + .setScanResultType(settings.getScanResultType()) + .setReportDelay(settings.getReportDelayMillis()) + .setNumOfMatches(settings.getNumOfMatches()) + .build(); + return true; + } } diff --git a/android/app/src/com/android/bluetooth/gatt/ScanManager.java b/android/app/src/com/android/bluetooth/gatt/ScanManager.java index 20b37b6d3b..2aee8637bf 100644 --- a/android/app/src/com/android/bluetooth/gatt/ScanManager.java +++ b/android/app/src/com/android/bluetooth/gatt/ScanManager.java @@ -39,6 +39,8 @@ import android.os.RemoteException; import android.os.SystemClock; import android.provider.Settings; import android.util.Log; +import android.util.SparseBooleanArray; +import android.util.SparseIntArray; import android.view.Display; import com.android.bluetooth.Utils; @@ -78,11 +80,15 @@ public class ScanManager { 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; private static final String ACTION_REFRESH_BATCHED_SCAN = "com.android.bluetooth.gatt.REFRESH_BATCHED_SCAN"; // Timeout for each controller operation. private static final int OPERATION_TIME_OUT_MILLIS = 500; + private static final int MAX_IS_UID_FOREGROUND_MAP_SIZE = 500; private int mLastConfiguredScanSetting = Integer.MIN_VALUE; // Scan parameters for batch scan. @@ -98,7 +104,7 @@ public class ScanManager { private Set<ScanClient> mRegularScanClients; private Set<ScanClient> mBatchClients; private Set<ScanClient> mSuspendedScanClients; - private HashMap<Integer, Integer> mPriorityMap = new HashMap<Integer, Integer>(); + private SparseIntArray mPriorityMap = new SparseIntArray(); private CountDownLatch mLatch; @@ -108,6 +114,10 @@ public class ScanManager { private LocationManager mLocationManager; private static final int FOREGROUND_IMPORTANCE_CUTOFF = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE; + private static final boolean DEFAULT_UID_IS_FOREGROUND = true; + private static final int SCAN_MODE_APP_IN_BACKGROUND = ScanSettings.SCAN_MODE_LOW_POWER; + private final SparseBooleanArray mIsUidForegroundMap = new SparseBooleanArray(); + private boolean mScreenOn = false; private class UidImportance { public int uid; @@ -133,10 +143,13 @@ public class ScanManager { mLocationManager = mService.getSystemService(LocationManager.class); mPriorityMap.put(ScanSettings.SCAN_MODE_OPPORTUNISTIC, 0); - mPriorityMap.put(ScanSettings.SCAN_MODE_LOW_POWER, 1); - mPriorityMap.put(ScanSettings.SCAN_MODE_BALANCED, 2); - mPriorityMap.put(ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY, 3); - mPriorityMap.put(ScanSettings.SCAN_MODE_LOW_LATENCY, 4); + mPriorityMap.put(ScanSettings.SCAN_MODE_SCREEN_OFF, 1); + mPriorityMap.put(ScanSettings.SCAN_MODE_LOW_POWER, 2); + mPriorityMap.put(ScanSettings.SCAN_MODE_SCREEN_OFF_BALANCED, 3); + // BALANCED and AMBIENT_DISCOVERY now have the same settings and priority. + mPriorityMap.put(ScanSettings.SCAN_MODE_BALANCED, 4); + mPriorityMap.put(ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY, 4); + mPriorityMap.put(ScanSettings.SCAN_MODE_LOW_LATENCY, 5); } void start() { @@ -228,6 +241,9 @@ public class ScanManager { } void startScan(ScanClient client) { + if (DBG) { + Log.d(TAG, "startScan() " + client); + } sendMessage(MSG_START_BLE_SCAN, client); } @@ -298,6 +314,15 @@ public class ScanManager { 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; @@ -348,6 +373,7 @@ public class ScanManager { mBatchClients.add(client); mScanNative.startBatchScan(client); } else { + updateScanModeBeforeStart(client); mRegularScanClients.add(client); mScanNative.startRegularScan(client); if (!mScanNative.isOpportunisticScanClient(client)) { @@ -361,6 +387,7 @@ public class ScanManager { } } } + client.started = true; } private boolean requiresScreenOn(ScanClient client) { @@ -378,11 +405,14 @@ public class ScanManager { if (client == null) { return; } + if (DBG) { + Log.d(TAG, "handling stopping scan " + client); + } if (mSuspendedScanClients.contains(client)) { mSuspendedScanClients.remove(client); } - + removeMessages(MSG_REVERT_SCAN_MODE_UPGRADE, client); if (mRegularScanClients.contains(client)) { mScanNative.stopRegularScan(client); @@ -432,6 +462,18 @@ public class ScanManager { && settings.getReportDelayMillis() == 0; } + void handleScreenOff() { + if (!mScreenOn) { + return; + } + mScreenOn = false; + if (DBG) { + Log.d(TAG, "handleScreenOff()"); + } + handleSuspendScans(); + updateRegularScanClientsScreenOff(); + } + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) void handleSuspendScans() { for (ScanClient client : mRegularScanClients) { @@ -441,12 +483,138 @@ public class ScanManager { if (client.stats != null) { client.stats.recordScanSuspend(client.scannerId); } + if (DBG) { + Log.d(TAG, "suspend scan " + client); + } handleStopScan(client); mSuspendedScanClients.add(client); } } } + private void updateRegularScanClientsScreenOff() { + boolean updatedScanParams = false; + for (ScanClient client : mRegularScanClients) { + if (updateScanModeScreenOff(client)) { + updatedScanParams = true; + if (DBG) { + Log.d(TAG, "Scan mode update during screen off" + client); + } + } + } + if (updatedScanParams) { + mScanNative.configureRegularScanParams(); + } + } + + private boolean updateScanModeScreenOff(ScanClient client) { + if (!isAppForeground(client) && !mScanNative.isOpportunisticScanClient(client)) { + return client.updateScanMode(ScanSettings.SCAN_MODE_SCREEN_OFF); + } + + // The following codes are effectively only for services + // Apps are either already or will be soon handled by handleImportanceChange(). + switch (client.scanModeApp) { + case ScanSettings.SCAN_MODE_LOW_POWER: + return client.updateScanMode(ScanSettings.SCAN_MODE_SCREEN_OFF); + case ScanSettings.SCAN_MODE_BALANCED: + case ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY: + return client.updateScanMode(ScanSettings.SCAN_MODE_SCREEN_OFF_BALANCED); + case ScanSettings.SCAN_MODE_OPPORTUNISTIC: + case ScanSettings.SCAN_MODE_LOW_LATENCY: + default: + return false; + } + } + + /** + * Services and Apps are assumed to be in the foreground by default unless it changes to the + * background triggering onUidImportance(). + */ + private boolean isAppForeground(ScanClient client) { + return mIsUidForegroundMap.get(client.appUid, DEFAULT_UID_IS_FOREGROUND); + } + + private boolean updateScanModeBeforeStart(ScanClient client) { + if (upgradeScanModeBeforeStart(client)) { + return true; + } + if (isScreenOn()) { + return updateScanModeScreenOn(client); + } else { + return updateScanModeScreenOff(client); + } + } + + private boolean upgradeScanModeBeforeStart(ScanClient client) { + if (client.started || AppScanStats.getScanUpgradeDurationMillis() == 0) { + return false; + } + if (client.stats == null || client.stats.hasRecentScan()) { + return false; + } + if (!isAppForeground(client) || isBatchClient(client)) { + return false; + } + + if (upgradeScanModeByOneLevel(client)) { + Message msg = obtainMessage(MSG_REVERT_SCAN_MODE_UPGRADE); + msg.obj = client; + if (DBG) { + Log.d(TAG, "scanMode is upgraded for " + client); + } + sendMessageDelayed(msg, AppScanStats.getScanUpgradeDurationMillis()); + return true; + } + return false; + } + + private boolean upgradeScanModeByOneLevel(ScanClient client) { + switch (client.scanModeApp) { + case ScanSettings.SCAN_MODE_LOW_POWER: + return client.updateScanMode(ScanSettings.SCAN_MODE_BALANCED); + case ScanSettings.SCAN_MODE_BALANCED: + case ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY: + return client.updateScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY); + case ScanSettings.SCAN_MODE_OPPORTUNISTIC: + case ScanSettings.SCAN_MODE_LOW_LATENCY: + default: + return false; + } + } + + void revertScanModeUpgrade(ScanClient client) { + if (mPriorityMap.get(client.settings.getScanMode()) + <= mPriorityMap.get(client.scanModeApp)) { + return; + } + if (client.updateScanMode(client.scanModeApp)) { + if (DBG) { + Log.d(TAG, "scanMode upgrade is reverted for " + client); + } + mScanNative.configureRegularScanParams(); + } + } + + private boolean updateScanModeScreenOn(ScanClient client) { + int newScanMode = (isAppForeground(client) + || mScanNative.isOpportunisticScanClient(client)) + ? client.scanModeApp : SCAN_MODE_APP_IN_BACKGROUND; + return client.updateScanMode(newScanMode); + } + + void handleScreenOn() { + if (mScreenOn) { + return; + } + mScreenOn = true; + if (DBG) { + Log.d(TAG, "handleScreenOn()"); + } + handleResumeScans(); + updateRegularScanClientsScreenOn(); + } + void handleResumeScans() { for (ScanClient client : mSuspendedScanClients) { if ((!requiresScreenOn(client) || isScreenOn()) @@ -454,11 +622,26 @@ public class ScanManager { if (client.stats != null) { client.stats.recordScanResume(client.scannerId); } + if (DBG) { + Log.d(TAG, "resume scan " + client); + } handleStartScan(client); } } mSuspendedScanClients.clear(); } + + private void updateRegularScanClientsScreenOn() { + boolean updatedScanParams = false; + for (ScanClient client : mRegularScanClients) { + if (updateScanModeScreenOn(client)) { + updatedScanParams = true; + } + } + if (updatedScanParams) { + mScanNative.configureRegularScanParams(); + } + } } /** @@ -513,14 +696,16 @@ public class ScanManager { /** * Scan params corresponding to regular scan setting */ - private static final int SCAN_MODE_LOW_POWER_WINDOW_MS = 512; - private static final int SCAN_MODE_LOW_POWER_INTERVAL_MS = 5120; - private static final int SCAN_MODE_BALANCED_WINDOW_MS = 1024; - private static final int SCAN_MODE_BALANCED_INTERVAL_MS = 4096; + private static final int SCAN_MODE_LOW_POWER_WINDOW_MS = 35; + private static final int SCAN_MODE_LOW_POWER_INTERVAL_MS = 350; + private static final int SCAN_MODE_BALANCED_WINDOW_MS = 35; + private static final int SCAN_MODE_BALANCED_INTERVAL_MS = 175; private static final int SCAN_MODE_LOW_LATENCY_WINDOW_MS = 4096; private static final int SCAN_MODE_LOW_LATENCY_INTERVAL_MS = 4096; - private static final int SCAN_MODE_AMBIENT_DISCOVERY_WINDOW_MS = 128; - private static final int SCAN_MODE_AMBIENT_DISCOVERY_INTERVAL_MS = 640; + private static final int SCAN_MODE_SCREEN_OFF_WINDOW_MS = 512; + private static final int SCAN_MODE_SCREEN_OFF_INTERVAL_MS = 10240; + private static final int SCAN_MODE_SCREEN_OFF_BALANCED_WINDOW_MS = 128; + private static final int SCAN_MODE_SCREEN_OFF_BALANCED_INTERVAL_MS = 640; /** * Onfound/onlost for scan settings @@ -530,18 +715,6 @@ public class ScanManager { private static final int ONLOST_FACTOR = 2; private static final int ONLOST_ONFOUND_BASE_TIMEOUT_MS = 500; - /** - * Scan params corresponding to batch scan setting - */ - private static final int SCAN_MODE_BATCH_LOW_POWER_WINDOW_MS = 1500; - private static final int SCAN_MODE_BATCH_LOW_POWER_INTERVAL_MS = 150000; - private static final int SCAN_MODE_BATCH_BALANCED_WINDOW_MS = 1500; - private static final int SCAN_MODE_BATCH_BALANCED_INTERVAL_MS = 15000; - private static final int SCAN_MODE_BATCH_LOW_LATENCY_WINDOW_MS = 1500; - private static final int SCAN_MODE_BATCH_LOW_LATENCY_INTERVAL_MS = 5000; - private static final int SCAN_MODE_BATCH_AMBIENT_DISCOVERY_WINDOW_MS = 1500; - private static final int SCAN_MODE_BATCH_AMBIENT_DISCOVERY_INTERVAL_MS = 15000; - // The logic is AND for each filter field. private static final int LIST_LOGIC_TYPE = 0x1111111; private static final int FILTER_LOGIC_TYPE = 1; @@ -610,23 +783,23 @@ public class ScanManager { curScanSetting = client.settings.getScanMode(); } - if (DBG) { - Log.d(TAG, "configureRegularScanParams() - ScanSetting Scan mode=" + curScanSetting - + " mLastConfiguredScanSetting=" + mLastConfiguredScanSetting); - } - if (curScanSetting != Integer.MIN_VALUE && curScanSetting != ScanSettings.SCAN_MODE_OPPORTUNISTIC) { if (curScanSetting != mLastConfiguredScanSetting) { - int scanWindow = getScanWindowMillis(client.settings); - int scanInterval = getScanIntervalMillis(client.settings); + int scanWindowMs = getScanWindowMillis(client.settings); + int scanIntervalMs = getScanIntervalMillis(client.settings); + // convert scanWindow and scanInterval from ms to LE scan units(0.625ms) - scanWindow = Utils.millsToUnit(scanWindow); - scanInterval = Utils.millsToUnit(scanInterval); + int scanWindow = Utils.millsToUnit(scanWindowMs); + int scanInterval = Utils.millsToUnit(scanIntervalMs); gattClientScanNative(false); if (DBG) { - Log.d(TAG, "configureRegularScanParams - scanInterval = " + scanInterval - + "configureRegularScanParams - scanWindow = " + scanWindow); + Log.d(TAG, "Start gattClientScanNative with" + + " old scanMode " + mLastConfiguredScanSetting + + " new scanMode " + curScanSetting + + " ( in MS: " + scanIntervalMs + " / " + scanWindowMs + + ", in scan unit: " + scanInterval + " / " + scanWindow + " )" + + client); } gattSetScanParametersNative(client.scannerId, scanInterval, scanWindow); gattClientScanNative(true); @@ -635,7 +808,7 @@ public class ScanManager { } else { mLastConfiguredScanSetting = curScanSetting; if (DBG) { - Log.d(TAG, "configureRegularScanParams() - queue emtpy, scan stopped"); + Log.d(TAG, "configureRegularScanParams() - queue empty, scan stopped"); } } } @@ -665,6 +838,9 @@ public class ScanManager { if (numRegularScanClients() == 1 && client.settings != null && client.settings.getScanMode() != ScanSettings.SCAN_MODE_OPPORTUNISTIC) { + if (DBG) { + Log.d(TAG, "start gattClientScanNative from startRegularScan()"); + } gattClientScanNative(true); } } @@ -766,10 +942,13 @@ public class ScanManager { return null; } BatchScanParams params = new BatchScanParams(); + ScanClient winner = getAggressiveClient(mBatchClients); + if (winner != null) { + params.scanMode = winner.settings.getScanMode(); + } // TODO: split full batch scan results and truncated batch scan results to different // collections. for (ScanClient client : mBatchClients) { - params.scanMode = Math.max(params.scanMode, client.settings.getScanMode()); if (client.settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_FULL) { params.fullScanscannerId = client.scannerId; } else { @@ -779,33 +958,24 @@ public class ScanManager { return params; } + // Batched scan doesn't require high duty cycle scan because scan result is reported + // infrequently anyway. To avoid redefining paramete sets, map to the low duty cycle + // parameter set as follows. private int getBatchScanWindowMillis(int scanMode) { switch (scanMode) { case ScanSettings.SCAN_MODE_LOW_LATENCY: - return SCAN_MODE_BATCH_LOW_LATENCY_WINDOW_MS; - case ScanSettings.SCAN_MODE_BALANCED: - return SCAN_MODE_BATCH_BALANCED_WINDOW_MS; - case ScanSettings.SCAN_MODE_LOW_POWER: - return SCAN_MODE_BATCH_LOW_POWER_WINDOW_MS; - case ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY: - return SCAN_MODE_BATCH_AMBIENT_DISCOVERY_WINDOW_MS; + return SCAN_MODE_BALANCED_WINDOW_MS; default: - return SCAN_MODE_BATCH_LOW_POWER_WINDOW_MS; + return SCAN_MODE_LOW_POWER_WINDOW_MS; } } private int getBatchScanIntervalMillis(int scanMode) { switch (scanMode) { case ScanSettings.SCAN_MODE_LOW_LATENCY: - return SCAN_MODE_BATCH_LOW_LATENCY_INTERVAL_MS; - case ScanSettings.SCAN_MODE_BALANCED: - return SCAN_MODE_BATCH_BALANCED_INTERVAL_MS; - case ScanSettings.SCAN_MODE_LOW_POWER: - return SCAN_MODE_BATCH_LOW_POWER_INTERVAL_MS; - case ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY: - return SCAN_MODE_BATCH_AMBIENT_DISCOVERY_INTERVAL_MS; + return SCAN_MODE_BALANCED_INTERVAL_MS; default: - return SCAN_MODE_BATCH_LOW_POWER_INTERVAL_MS; + return SCAN_MODE_LOW_POWER_INTERVAL_MS; } } @@ -850,7 +1020,7 @@ public class ScanManager { mRegularScanClients.remove(client); if (numRegularScanClients() == 0) { if (DBG) { - Log.d(TAG, "stop scan"); + Log.d(TAG, "stop gattClientScanNative"); } gattClientScanNative(false); } @@ -870,7 +1040,7 @@ public class ScanManager { configureRegularScanParams(); if (numRegularScanClients() == 0) { if (DBG) { - Log.d(TAG, "stop scan"); + Log.d(TAG, "stop gattClientScanNative"); } gattClientScanNative(false); } @@ -1160,6 +1330,7 @@ public class ScanManager { Settings.Global.BLE_SCAN_LOW_LATENCY_WINDOW_MS, SCAN_MODE_LOW_LATENCY_WINDOW_MS); case ScanSettings.SCAN_MODE_BALANCED: + case ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY: return Settings.Global.getInt( resolver, Settings.Global.BLE_SCAN_BALANCED_WINDOW_MS, @@ -1169,8 +1340,10 @@ public class ScanManager { resolver, Settings.Global.BLE_SCAN_LOW_POWER_WINDOW_MS, SCAN_MODE_LOW_POWER_WINDOW_MS); - case ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY: - return SCAN_MODE_AMBIENT_DISCOVERY_WINDOW_MS; + case ScanSettings.SCAN_MODE_SCREEN_OFF: + return SCAN_MODE_SCREEN_OFF_WINDOW_MS; + case ScanSettings.SCAN_MODE_SCREEN_OFF_BALANCED: + return SCAN_MODE_SCREEN_OFF_BALANCED_WINDOW_MS; default: return Settings.Global.getInt( resolver, @@ -1194,6 +1367,7 @@ public class ScanManager { Settings.Global.BLE_SCAN_LOW_LATENCY_INTERVAL_MS, SCAN_MODE_LOW_LATENCY_INTERVAL_MS); case ScanSettings.SCAN_MODE_BALANCED: + case ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY: return Settings.Global.getInt( resolver, Settings.Global.BLE_SCAN_BALANCED_INTERVAL_MS, @@ -1203,8 +1377,10 @@ public class ScanManager { resolver, Settings.Global.BLE_SCAN_LOW_POWER_INTERVAL_MS, SCAN_MODE_LOW_POWER_INTERVAL_MS); - case ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY: - return SCAN_MODE_AMBIENT_DISCOVERY_INTERVAL_MS; + case ScanSettings.SCAN_MODE_SCREEN_OFF: + return SCAN_MODE_SCREEN_OFF_INTERVAL_MS; + case ScanSettings.SCAN_MODE_SCREEN_OFF_BALANCED: + return SCAN_MODE_SCREEN_OFF_BALANCED_INTERVAL_MS; default: return Settings.Global.getInt( resolver, @@ -1361,9 +1537,9 @@ public class ScanManager { @Override public void onDisplayChanged(int displayId) { if (isScreenOn()) { - sendMessage(MSG_RESUME_SCANS, null); + sendMessage(MSG_SCREEN_ON, null); } else { - sendMessage(MSG_SUSPEND_SCANS, null); + sendMessage(MSG_SCREEN_OFF, null); } } }; @@ -1404,35 +1580,36 @@ public class ScanManager { int uid = imp.uid; int importance = imp.importance; boolean updatedScanParams = false; - if (importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE) { - for (ScanClient client : mRegularScanClients) { - if (client.appUid == uid && client.passiveSettings != null) { - client.settings = client.passiveSettings; - client.passiveSettings = null; + boolean isForeground = importance <= ActivityManager.RunningAppProcessInfo + .IMPORTANCE_FOREGROUND_SERVICE; + + if (mIsUidForegroundMap.size() < MAX_IS_UID_FOREGROUND_MAP_SIZE) { + mIsUidForegroundMap.put(uid, isForeground); + } + + for (ScanClient client : mRegularScanClients) { + if (client.appUid != uid) { + continue; + } + if (isForeground) { + if (client.updateScanMode(client.scanModeApp)) { updatedScanParams = true; } - } - } else { - ContentResolver resolver = mService.getContentResolver(); - int backgroundScanMode = Settings.Global.getInt( - resolver, - Settings.Global.BLE_SCAN_BACKGROUND_MODE, - ScanSettings.SCAN_MODE_LOW_POWER); - for (ScanClient client : mRegularScanClients) { - if (client.appUid == uid && !mScanNative.isOpportunisticScanClient(client)) { - client.passiveSettings = client.settings; - ScanSettings.Builder builder = new ScanSettings.Builder(); - ScanSettings settings = client.settings; - builder.setScanMode(backgroundScanMode); - builder.setCallbackType(settings.getCallbackType()); - builder.setScanResultType(settings.getScanResultType()); - builder.setReportDelay(settings.getReportDelayMillis()); - builder.setNumOfMatches(settings.getNumOfMatches()); - client.settings = builder.build(); + } else { + // Skip scan mode update in any of following cases + // 1. screen is already off which triggers handleScreenOff() + // 2. opportunistics scan + if (mScreenOn && !mScanNative.isOpportunisticScanClient(client) + && client.updateScanMode(SCAN_MODE_APP_IN_BACKGROUND)) { updatedScanParams = true; } } + if (DBG) { + Log.d(TAG, "uid " + uid + " isForeground " + isForeground + + " scanMode " + client.settings.getScanMode()); + } } + if (updatedScanParams) { mScanNative.configureRegularScanParams(); } diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index 1aa7cb5111..1f9d4caec5 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -62,6 +62,23 @@ public final class ScanSettings implements Parcelable { public static final int SCAN_MODE_AMBIENT_DISCOVERY = 3; /** + * Default Bluetooth LE scan mode when the screen is off. + * This mode has the low duty cycle and long scan interval which results in the lowest + * power consumption among all modes. It is for the framework internal use only. + * + * @hide + */ + public static final int SCAN_MODE_SCREEN_OFF = 4; + + /** + * Balanced Bluetooth LE scan mode for foreground service when the screen is off. + * It is for the framework internal use only. + * + * @hide + */ + public static final int SCAN_MODE_SCREEN_OFF_BALANCED = 5; + + /** * Trigger a callback for every Bluetooth advertisement found that matches the filter criteria. * If no filter is active, all advertisement packets are reported. */ @@ -292,6 +309,8 @@ public final class ScanSettings implements Parcelable { case SCAN_MODE_BALANCED: case SCAN_MODE_LOW_LATENCY: case SCAN_MODE_AMBIENT_DISCOVERY: + case SCAN_MODE_SCREEN_OFF: + case SCAN_MODE_SCREEN_OFF_BALANCED: mScanMode = scanMode; break; default: |