summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--android/app/src/com/android/bluetooth/btservice/AdapterService.java17
-rw-r--r--android/app/src/com/android/bluetooth/gatt/AppScanStats.java16
-rw-r--r--android/app/src/com/android/bluetooth/gatt/ScanClient.java38
-rw-r--r--android/app/src/com/android/bluetooth/gatt/ScanManager.java345
-rw-r--r--framework/java/android/bluetooth/le/ScanSettings.java19
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: