Fixed batch scan returning status 12 when restarted.
Also fixed no batch scan results when both truncated and
full batch are enabled.
Added a BatchScanParams to hold parameters for batch scan.
Bug:16850169
Bug:17213118
Change-Id: Ib3bb7442aa2657912ef4377f825388ac8e1010bb
diff --git a/android/app/src/com/android/bluetooth/Utils.java b/android/app/src/com/android/bluetooth/Utils.java
index 0c85a7a..56d5a64 100644
--- a/android/app/src/com/android/bluetooth/Utils.java
+++ b/android/app/src/com/android/bluetooth/Utils.java
@@ -31,6 +31,7 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.UUID;
+import java.util.concurrent.TimeUnit;
/**
* @hide
@@ -38,6 +39,8 @@
final public class Utils {
private static final String TAG = "BluetoothUtils";
+ private static final int MICROS_PER_UNIT = 625;
+
static final int BD_ADDR_LEN = 6; // bytes
static final int BD_UUID_LEN = 16; // bytes
@@ -217,4 +220,10 @@
"Need BLUETOOTH_ADMIN permission");
}
+ /**
+ * Converts {@code millisecond} to unit. Each unit is 0.625 millisecond.
+ */
+ public static int millsToUnit(int milliseconds) {
+ return (int) (TimeUnit.MILLISECONDS.toMicros(milliseconds) / MICROS_PER_UNIT);
+ }
}
diff --git a/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java b/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java
index 20b4759..a2c8593 100644
--- a/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java
+++ b/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java
@@ -238,7 +238,6 @@
// Add some randomness to the advertising min/max interval so the controller can do some
// optimization.
private static final int ADVERTISING_INTERVAL_DELTA_UNIT = 10;
- private static final int ADVERTISING_INTERVAL_MICROS_PER_UNIT = 625;
// The following constants should be kept the same as those defined in bt stack.
private static final int ADVERTISING_CHANNEL_37 = 1 << 0;
@@ -417,22 +416,17 @@
private long getAdvertisingIntervalUnit(AdvertiseSettings settings) {
switch (settings.getMode()) {
case AdvertiseSettings.ADVERTISE_MODE_LOW_POWER:
- return millsToUnit(ADVERTISING_INTERVAL_HIGH_MILLS);
+ return Utils.millsToUnit(ADVERTISING_INTERVAL_HIGH_MILLS);
case AdvertiseSettings.ADVERTISE_MODE_BALANCED:
- return millsToUnit(ADVERTISING_INTERVAL_MEDIUM_MILLS);
+ return Utils.millsToUnit(ADVERTISING_INTERVAL_MEDIUM_MILLS);
case AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY:
- return millsToUnit(ADVERTISING_INTERVAL_LOW_MILLS);
+ return Utils.millsToUnit(ADVERTISING_INTERVAL_LOW_MILLS);
default:
// Shouldn't happen, just in case.
- return millsToUnit(ADVERTISING_INTERVAL_HIGH_MILLS);
+ return Utils.millsToUnit(ADVERTISING_INTERVAL_HIGH_MILLS);
}
}
- private long millsToUnit(int millisecond) {
- return TimeUnit.MILLISECONDS.toMicros(millisecond)
- / ADVERTISING_INTERVAL_MICROS_PER_UNIT;
- }
-
// Native functions
private native void gattClientDisableAdvNative(int client_if);
diff --git a/android/app/src/com/android/bluetooth/gatt/GattService.java b/android/app/src/com/android/bluetooth/gatt/GattService.java
index a6e283c..6d5c513 100644
--- a/android/app/src/com/android/bluetooth/gatt/GattService.java
+++ b/android/app/src/com/android/bluetooth/gatt/GattService.java
@@ -980,7 +980,7 @@
if (app == null) return;
app.callback.onBatchScanResults(new ArrayList<ScanResult>(results));
} else {
- for (ScanClient client : mScanManager.getBatchScanQueue()) {
+ for (ScanClient client : mScanManager.getFullBatchScanQueue()) {
// Deliver results for each client.
deliverBatchScan(client, results);
}
diff --git a/android/app/src/com/android/bluetooth/gatt/ScanManager.java b/android/app/src/com/android/bluetooth/gatt/ScanManager.java
index d22e8de..1281fe8 100644
--- a/android/app/src/com/android/bluetooth/gatt/ScanManager.java
+++ b/android/app/src/com/android/bluetooth/gatt/ScanManager.java
@@ -36,11 +36,9 @@
import com.android.bluetooth.btservice.AdapterService;
import java.util.ArrayDeque;
-import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
@@ -58,6 +56,7 @@
// Result type defined in bt stack. Need to be accessed by GattService.
static final int SCAN_RESULT_TYPE_TRUNCATED = 1;
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;
@@ -71,10 +70,8 @@
private static final int OPERATION_TIME_OUT_MILLIS = 500;
private int mLastConfiguredScanSetting = Integer.MIN_VALUE;
- private int mLastConfiguredBatchFullScanSetting = Integer.MIN_VALUE;
- private int mLastConfiguredBatchFullClientIf = Integer.MIN_VALUE;
- private int mLastConfiguredBatchTruncScanSetting = Integer.MIN_VALUE;
- private int mLastConfiguredBatchTruncClientIf = Integer.MIN_VALUE;
+ // Scan parameters for batch scan.
+ private BatchScanParams mBatchScanParms;
private GattService mService;
private BroadcastReceiver mBatchAlarmReceiver;
@@ -120,6 +117,21 @@
return mBatchClients;
}
+ /**
+ * Returns a set of full batch scan clients.
+ */
+ Set<ScanClient> getFullBatchScanQueue() {
+ // TODO: split full batch scan clients and truncated batch clients so we don't need to
+ // construct this every time.
+ Set<ScanClient> fullBatchClients = new HashSet<ScanClient>();
+ for (ScanClient client : mBatchClients) {
+ if (client.settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_FULL) {
+ fullBatchClients.add(client);
+ }
+ }
+ return fullBatchClients;
+ }
+
void startScan(ScanClient client) {
sendMessage(MSG_START_BLE_SCAN, client);
}
@@ -195,8 +207,6 @@
if (isBatchClient(client)) {
mBatchClients.add(client);
mScanNative.startBatchScan(client);
- mScanNative.configureBatchScanParams(SCAN_RESULT_TYPE_FULL);
- mScanNative.configureBatchScanParams(SCAN_RESULT_TYPE_TRUNCATED);
} else {
mRegularScanClients.add(client);
mScanNative.startRegularScan(client);
@@ -249,6 +259,35 @@
}
}
+ /**
+ * Parameters for batch scans.
+ */
+ class BatchScanParams {
+ int scanMode;
+ int fullScanClientIf;
+ int truncatedScanClientIf;
+
+ BatchScanParams() {
+ scanMode = -1;
+ fullScanClientIf = -1;
+ truncatedScanClientIf = -1;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ BatchScanParams other = (BatchScanParams) obj;
+ return scanMode == other.scanMode && fullScanClientIf == other.fullScanClientIf
+ && truncatedScanClientIf == other.truncatedScanClientIf;
+
+ }
+ }
+
private class ScanNative {
// Delivery mode defined in bt stack.
@@ -335,68 +374,23 @@
}
}
- void updateAndConfigureScanParam(int clientIf, int curScanSetting, int resultType,
- int scanWindow, int scanInterval) {
-
- int discardRule = DISCARD_OLDEST_WHEN_BUFFER_FULL;
- int addressType = 0;
-
- if (resultType == SCAN_RESULT_TYPE_FULL) {
- if (mLastConfiguredBatchFullClientIf != Integer.MIN_VALUE) {
- gattClientStopBatchScanNative(mLastConfiguredBatchFullClientIf);
- }
- gattClientStartBatchScanNative(clientIf, resultType, scanInterval,
- scanWindow, addressType, discardRule);
- mLastConfiguredBatchFullScanSetting = curScanSetting;
- mLastConfiguredBatchFullClientIf = clientIf;
- } else if (resultType == SCAN_RESULT_TYPE_TRUNCATED) {
- if (mLastConfiguredBatchTruncClientIf != Integer.MIN_VALUE) {
- gattClientStopBatchScanNative(mLastConfiguredBatchTruncClientIf);
- }
- gattClientStartBatchScanNative(clientIf, resultType, scanInterval,
- scanWindow, addressType, discardRule);
- mLastConfiguredBatchTruncScanSetting = curScanSetting;
- mLastConfiguredBatchTruncClientIf = clientIf;
- }
- }
-
- int getLastConfiguredBatchScanSetting(int resultType) {
- if (resultType == SCAN_RESULT_TYPE_FULL) {
- return mLastConfiguredBatchFullScanSetting;
- } else if (resultType == SCAN_RESULT_TYPE_TRUNCATED) {
- return mLastConfiguredBatchTruncScanSetting;
- }
- return Integer.MIN_VALUE;
- }
-
- void resetBatchScanParam(int resultType) {
- if (resultType == SCAN_RESULT_TYPE_FULL) {
- mLastConfiguredBatchFullScanSetting = Integer.MIN_VALUE;
- mLastConfiguredBatchFullClientIf = Integer.MIN_VALUE;
- } else if (resultType == SCAN_RESULT_TYPE_TRUNCATED) {
- mLastConfiguredBatchTruncScanSetting = Integer.MIN_VALUE;
- mLastConfiguredBatchTruncClientIf = Integer.MIN_VALUE;
- }
- }
-
-
void configureRegularScanParams() {
- if (DBG) Log.d(TAG, "configureRegularScanParams() - queue=" + mRegularScanClients.size());
+ logd("configureRegularScanParams() - queue=" + mRegularScanClients.size());
int curScanSetting = Integer.MIN_VALUE;
ScanClient client = null;
- client = getAggressiveClient(mRegularScanClients, false, SCAN_RESULT_TYPE_FULL);
+ client = getAggressiveClient(mRegularScanClients);
if (client != null) {
curScanSetting = client.settings.getScanMode();
}
- if (DBG) Log.d(TAG, "configureRegularScanParams() - ScanSetting Scan mode=" + curScanSetting +
+ logd("configureRegularScanParams() - ScanSetting Scan mode=" + curScanSetting +
" mLastConfiguredScanSetting=" + mLastConfiguredScanSetting);
if (curScanSetting != Integer.MIN_VALUE) {
if (curScanSetting != mLastConfiguredScanSetting) {
int scanWindow, scanInterval;
- switch (curScanSetting){
+ switch (curScanSetting) {
case ScanSettings.SCAN_MODE_LOW_POWER:
scanWindow = SCAN_MODE_LOW_POWER_WINDOW_MS;
scanInterval = SCAN_MODE_LOW_POWER_INTERVAL_MS;
@@ -416,8 +410,8 @@
break;
}
// convert scanWindow and scanInterval from ms to LE scan units(0.625ms)
- scanWindow = (scanWindow * 1000)/625;
- scanInterval = (scanInterval * 1000)/625;
+ scanWindow = Utils.millsToUnit(scanWindow);
+ scanInterval = Utils.millsToUnit(scanInterval);
gattClientScanNative(false);
gattSetScanParametersNative(scanInterval, scanWindow);
gattClientScanNative(true);
@@ -425,80 +419,23 @@
}
} else {
mLastConfiguredScanSetting = curScanSetting;
- if (DBG) Log.d(TAG, "configureRegularScanParams() - queue emtpy, scan stopped");
+ logd("configureRegularScanParams() - queue emtpy, scan stopped");
}
}
- ScanClient getAggressiveClient(Set<ScanClient> cList, boolean isBatchClientList, int resultType) {
+ ScanClient getAggressiveClient(Set<ScanClient> cList) {
ScanClient result = null;
int curScanSetting = Integer.MIN_VALUE;
- for(ScanClient client : cList) {
- // ScanClient scan settings are assumed to be monotonically increasing in value for more
- // power hungry(higher duty cycle) operation
- // For batch clients, there are 2 possible scan modes Truncated or Full
- // Match resultType based on client list
- if (!isBatchClientList ||
- (isBatchClientList && resultType == getResultType(client.settings))) {
- if (client.settings.getScanMode() > curScanSetting) {
- result = client;
- }
+ for (ScanClient client : cList) {
+ // ScanClient scan settings are assumed to be monotonically increasing in value for
+ // more power hungry(higher duty cycle) operation.
+ if (client.settings.getScanMode() > curScanSetting) {
+ result = client;
}
}
return result;
}
- void configureBatchScanParams(int resultType) {
- if (DBG) Log.d(TAG, "configureBathScanParams() - queue=" + mBatchClients.size());
- int curScanSetting = Integer.MIN_VALUE;
- int clientIf = Integer.MIN_VALUE;
- ScanClient client = null;
- int lastConfiguredBatchScanSetting = getLastConfiguredBatchScanSetting(resultType);
-
- client = getAggressiveClient(mBatchClients, true, resultType);
- if (client != null) {
- curScanSetting = client.settings.getScanMode();
- clientIf = client.clientIf;
- }
-
- if (DBG) Log.d(TAG, "configureBatchScanParams() - ScanSetting Scan mode=" + curScanSetting +
- " lastConfiguredBatchScanSetting=" + lastConfiguredBatchScanSetting +
- " scanType=" + resultType);
-
- if (curScanSetting != Integer.MIN_VALUE) {
- if (curScanSetting != lastConfiguredBatchScanSetting) {
- int scanWindow, scanInterval;
- switch (curScanSetting){
- case ScanSettings.SCAN_MODE_LOW_POWER:
- scanWindow = SCAN_MODE_BATCH_LOW_POWER_WINDOW_MS;
- scanInterval = SCAN_MODE_BATCH_LOW_POWER_INTERVAL_MS;
- break;
- case ScanSettings.SCAN_MODE_BALANCED:
- scanWindow = SCAN_MODE_BATCH_BALANCED_WINDOW_MS;
- scanInterval = SCAN_MODE_BATCH_BALANCED_INTERVAL_MS;
- break;
- case ScanSettings.SCAN_MODE_LOW_LATENCY:
- scanWindow = SCAN_MODE_BATCH_LOW_LATENCY_WINDOW_MS;
- scanInterval = SCAN_MODE_BATCH_LOW_LATENCY_INTERVAL_MS;
- break;
- default:
- Log.e(TAG, "Invalid value for curScanSetting " + curScanSetting);
- scanWindow = SCAN_MODE_BATCH_LOW_POWER_WINDOW_MS;
- scanInterval = SCAN_MODE_BATCH_LOW_POWER_INTERVAL_MS;
- break;
- }
- // convert scanWindow and scanInterval from ms to LE scan units(0.625ms)
- scanWindow = (scanWindow * 1000)/625;
- scanInterval = (scanInterval * 1000)/625;
- updateAndConfigureScanParam(clientIf, curScanSetting, resultType,
- scanWindow, scanInterval);
- }
- } else {
- resetBatchScanParam(resultType);
- if (DBG) Log.d(TAG, "configureBatchScanParams() - queue emtpy," +
- "resetting batch scan config params");
- }
- }
-
void startRegularScan(ScanClient client) {
if (mFilterIndexStack.isEmpty() && isFilteringSupported()) {
initFilterIndexStack();
@@ -517,22 +454,112 @@
initFilterIndexStack();
}
configureScanFilters(client);
- int fullScanPercent = 50;
- int notifyThreshold = 95;
- resetCountDownLatch();
- logd("configuring batch scan storage, appIf " + client.clientIf);
- gattClientConfigBatchScanStorageNative(client.clientIf, fullScanPercent,
- 100 - fullScanPercent, notifyThreshold);
- waitForCallback();
- logd("Starting BLE batch scan");
+ // Reset batch scan. May need to stop the existing batch scan and update scan params.
+ resetBatchScan(client);
+ }
+
+ private void resetBatchScan(ScanClient client) {
+ int clientIf = client.clientIf;
+ BatchScanParams batchScanParams = getBatchScanParams();
+ // Stop batch if batch scan params changed and previous params is not null.
+ if (mBatchScanParms != null && (!mBatchScanParms.equals(batchScanParams))) {
+ logd("stopping BLe Batch");
+ resetCountDownLatch();
+ gattClientStopBatchScanNative(clientIf);
+ waitForCallback();
+ // Clear pending results as it's illegal to config storage if there are still
+ // pending results.
+ flushBatchResults(clientIf);
+ }
+ // Start batch if batchScanParams changed and current params is not null.
+ if (batchScanParams != null && (!batchScanParams.equals(mBatchScanParms))) {
+ int notifyThreshold = 95;
+ logd("Starting BLE batch scan");
+ int resultType = getResultType(batchScanParams);
+ int fullScanPercent = getFullScanStoragePercent(resultType);
+ resetCountDownLatch();
+ logd("configuring batch scan storage, appIf " + client.clientIf);
+ gattClientConfigBatchScanStorageNative(client.clientIf, fullScanPercent,
+ 100 - fullScanPercent, notifyThreshold);
+ waitForCallback();
+ resetCountDownLatch();
+ int scanInterval =
+ Utils.millsToUnit(getBatchScanIntervalMillis(batchScanParams.scanMode));
+ int scanWindow =
+ Utils.millsToUnit(getBatchScanWindowMillis(batchScanParams.scanMode));
+ gattClientStartBatchScanNative(clientIf, resultType, scanInterval,
+ scanWindow, 0, DISCARD_OLDEST_WHEN_BUFFER_FULL);
+ waitForCallback();
+ }
+ mBatchScanParms = batchScanParams;
setBatchAlarm();
}
+ private int getFullScanStoragePercent(int resultType) {
+ switch (resultType) {
+ case SCAN_RESULT_TYPE_FULL:
+ return 100;
+ case SCAN_RESULT_TYPE_TRUNCATED:
+ return 0;
+ case SCAN_RESULT_TYPE_BOTH:
+ return 50;
+ default:
+ return 50;
+ }
+ }
+
+ private BatchScanParams getBatchScanParams() {
+ if (mBatchClients.isEmpty()) {
+ return null;
+ }
+ BatchScanParams params = new BatchScanParams();
+ // 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.fullScanClientIf = client.clientIf;
+ } else {
+ params.truncatedScanClientIf = client.clientIf;
+ }
+ }
+ return params;
+ }
+
+ 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;
+ default:
+ return SCAN_MODE_BATCH_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;
+ default:
+ return SCAN_MODE_BATCH_LOW_POWER_INTERVAL_MS;
+ }
+ }
+
// Set the batch alarm to be triggered within a short window after batch interval. This
// allows system to optimize wake up time while still allows a degree of precise control.
private void setBatchAlarm() {
// Cancel any pending alarm just in case.
mAlarmManager.cancel(mBatchScanIntervalIntent);
+ if (mBatchClients.isEmpty()) {
+ return;
+ }
long batchTriggerIntervalMillis = getBatchTriggerIntervalMillis();
// Allows the alarm to be triggered within
// [batchTriggerIntervalMillis, 1.1 * batchTriggerIntervalMillis]
@@ -554,25 +581,25 @@
}
void stopBatchScan(ScanClient client) {
- flushBatchResults(client.clientIf);
- removeScanFilters(client.clientIf);
mBatchClients.remove(client);
- configureBatchScanParams(SCAN_RESULT_TYPE_FULL);
- configureBatchScanParams(SCAN_RESULT_TYPE_TRUNCATED);
- setBatchAlarm();
+ removeScanFilters(client.clientIf);
+ resetBatchScan(client);
}
void flushBatchResults(int clientIf) {
logd("flushPendingBatchResults - clientIf = " + clientIf);
- ScanClient client = getBatchScanClient(clientIf);
- if (client == null) {
- logd("unknown client : " + clientIf);
- return;
+ if (mBatchScanParms.fullScanClientIf != -1) {
+ resetCountDownLatch();
+ gattClientReadScanReportsNative(mBatchScanParms.fullScanClientIf,
+ SCAN_RESULT_TYPE_FULL);
+ waitForCallback();
}
- int resultType = getResultType(client.settings);
- resetCountDownLatch();
- gattClientReadScanReportsNative(client.clientIf, resultType);
- waitForCallback();
+ if (mBatchScanParms.truncatedScanClientIf != -1) {
+ resetCountDownLatch();
+ gattClientReadScanReportsNative(mBatchScanParms.truncatedScanClientIf,
+ SCAN_RESULT_TYPE_TRUNCATED);
+ waitForCallback();
+ }
setBatchAlarm();
}
@@ -659,9 +686,17 @@
/**
* Return batch scan result type value defined in bt stack.
*/
- private int getResultType(ScanSettings settings) {
- return settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_FULL ?
- SCAN_RESULT_TYPE_FULL : SCAN_RESULT_TYPE_TRUNCATED;
+ private int getResultType(BatchScanParams params) {
+ if (params.fullScanClientIf != -1 && params.truncatedScanClientIf != -1) {
+ return SCAN_RESULT_TYPE_BOTH;
+ }
+ if (params.truncatedScanClientIf != -1) {
+ return SCAN_RESULT_TYPE_TRUNCATED;
+ }
+ if (params.fullScanClientIf != -1) {
+ return SCAN_RESULT_TYPE_FULL;
+ }
+ return -1;
}
// Check if ALLOW_FILTER should be used for the client.
@@ -823,6 +858,6 @@
}
private void logd(String s) {
- Log.d(TAG, s);
+ if (DBG) Log.d(TAG, s);
}
}