Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7beb048e authored by Jayden Kim's avatar Jayden Kim Committed by Gerrit Code Review
Browse files

Merge "Add Auto Batch Scan Feature"

parents 151465a9 fab85a8f
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
@@ -80,6 +80,7 @@ import java.util.Objects;
        public boolean isFilterScan;
        public boolean isCallbackScan;
        public boolean isBatchScan;
        public boolean isAutoBatchScan;
        public int results;
        public int scannerId;
        public int scanMode;
@@ -97,6 +98,7 @@ import java.util.Objects;
            this.isFilterScan = isFilterScan;
            this.isCallbackScan = isCallbackScan;
            this.isBatchScan = false;
            this.isAutoBatchScan = false;
            this.scanMode = scanMode;
            this.scanCallbackType = scanCallbackType;
            this.results = 0;
@@ -189,6 +191,14 @@ import java.util.Objects;
        return scan.isDowngraded;
    }

    synchronized boolean isAutoBatchScan(int scannerId) {
        LastScan scan = getScanFromScannerId(scannerId);
        if (scan == null) {
            return false;
        }
        return scan.isAutoBatchScan;
    }

    synchronized void recordScanStart(ScanSettings settings, List<ScanFilter> filters,
            boolean isFilterScan, boolean isCallbackScan, int scannerId) {
        LastScan existingScan = getScanFromScannerId(scannerId);
@@ -363,6 +373,13 @@ import java.util.Objects;
        }
    }

    synchronized void setAutoBatchScan(int scannerId, boolean isBatchScan) {
        LastScan scan = getScanFromScannerId(scannerId);
        if (scan != null) {
            scan.isAutoBatchScan = isBatchScan;
        }
    }

    synchronized boolean isScanningTooFrequently() {
        if (mLastScans.size() < mAdapterService.getScanQuotaCount()) {
            return false;
@@ -477,6 +494,8 @@ import java.util.Objects;
                return "FIRST_MATCH";
            case ScanSettings.CALLBACK_TYPE_MATCH_LOST:
                return "LOST";
            case ScanSettings.CALLBACK_TYPE_ALL_MATCHES_AUTO_BATCH:
                return "ALL_MATCHES_AUTO_BATCH";
            default:
                return callbackType == (ScanSettings.CALLBACK_TYPE_FIRST_MATCH
                    | ScanSettings.CALLBACK_TYPE_MATCH_LOST) ? "[FIRST_MATCH | LOST]" : "UNKNOWN: "
@@ -591,6 +610,8 @@ import java.util.Objects;
                }
                if (scan.isBatchScan) {
                    sb.append("Batch Scan");
                } else if (scan.isAutoBatchScan) {
                    sb.append("Auto Batch Scan");
                } else {
                    sb.append("Regular Scan");
                }
@@ -639,6 +660,8 @@ import java.util.Objects;
                }
                if (scan.isBatchScan) {
                    sb.append("Batch Scan");
                } else if (scan.isAutoBatchScan) {
                    sb.append("Auto Batch Scan");
                } else {
                    sb.append("Regular Scan");
                }
+19 −3
Original line number Diff line number Diff line
@@ -1921,7 +1921,9 @@ public class GattService extends ProfileService {
                continue;
            }

            if ((settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_ALL_MATCHES) == 0) {
            int callbackType = settings.getCallbackType();
            if (!(callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES
                    || callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES_AUTO_BATCH)) {
                if (VDBG) {
                    Log.d(TAG, "Skipping client: CALLBACK_TYPE_ALL_MATCHES");
                }
@@ -2612,7 +2614,7 @@ public class GattService extends ProfileService {
            Log.d(TAG, "onBatchScanReports() - scannerId=" + scannerId + ", status=" + status
                    + ", reportType=" + reportType + ", numRecords=" + numRecords);
        }
        mScanManager.callbackDone(scannerId, status);

        Set<ScanResult> results = parseBatchScanResults(numRecords, reportType, recordData);
        if (reportType == ScanManager.SCAN_RESULT_TYPE_TRUNCATED) {
            // We only support single client for truncated mode.
@@ -2664,13 +2666,27 @@ public class GattService extends ProfileService {
                deliverBatchScan(client, results);
            }
        }
        mScanManager.callbackDone(scannerId, status);
    }

    private void sendBatchScanResults(ScannerMap.App app, ScanClient client,
            ArrayList<ScanResult> results) {
        try {
            if (app.callback != null) {
                if (mScanManager.isAutoBatchScanClientEnabled(client)) {
                    if (DBG) {
                        Log.d(TAG, "sendBatchScanResults() to onScanResult()" + client);
                    }
                    for (ScanResult result : results) {
                        app.appScanStats.addResult(client.scannerId);
                        app.callback.onScanResult(result);
                    }
                } else {
                    if (DBG) {
                        Log.d(TAG, "sendBatchScanResults() to onBatchScanResults()" + client);
                    }
                    app.callback.onBatchScanResults(results);
                }
            } else {
                sendResultsByPendingIntent(app.info, results,
                        ScanSettings.CALLBACK_TYPE_ALL_MATCHES);
+101 −1
Original line number Diff line number Diff line
@@ -354,6 +354,10 @@ public class ScanManager {
        return mBluetoothAdapterProxy.isOffloadedScanFilteringSupported();
    }

    boolean isAutoBatchScanClientEnabled(ScanClient client) {
        return mScanNative.isAutoBatchScanClientEnabled(client);
    }

    // Handler class that handles BLE scan operations.
    private class ClientHandler extends Handler {

@@ -442,8 +446,16 @@ public class ScanManager {
                return;
            }

            if (!mScanNative.isExemptFromAutoBatchScanUpdate(client)) {
                if (mScreenOn) {
                    clearAutoBatchScanClient(client);
                } else {
                    setAutoBatchScanClient(client);
                }
            }

            // Begin scan operations.
            if (isBatchClient(client)) {
            if (isBatchClient(client) || isAutoBatchScanClientEnabled(client)) {
                mBatchClients.add(client);
                mScanNative.startBatchScan(client);
            } else {
@@ -501,6 +513,9 @@ public class ScanManager {
                    mScanNative.configureRegularScanParams();
                }
            } else {
                if (isAutoBatchScanClientEnabled(client)) {
                    handleFlushBatchResults(client);
                }
                mScanNative.stopBatchScan(client);
            }
            if (client.appDied) {
@@ -512,7 +527,13 @@ public class ScanManager {
        }

        void handleFlushBatchResults(ScanClient client) {
            if (DBG) {
                Log.d(TAG, "handleFlushBatchResults() " + client);
            }
            if (!mBatchClients.contains(client)) {
                if (DBG) {
                    Log.d(TAG, "There is no batch scan client to flush " + client);
                }
                return;
            }
            mScanNative.flushBatchResults(client.scannerId);
@@ -549,6 +570,7 @@ public class ScanManager {
            }
            handleSuspendScans();
            updateRegularScanClientsScreenOff();
            updateRegularScanToBatchScanClients();
        }

        void handleConnectingState() {
@@ -618,6 +640,62 @@ public class ScanManager {
            }
        }

        private void updateRegularScanToBatchScanClients() {
            boolean updatedScanParams = false;
            for (ScanClient client : mRegularScanClients) {
                if (!mScanNative.isExemptFromAutoBatchScanUpdate(client)) {
                    if (DBG) {
                        Log.d(TAG, "Updating regular scan to batch scan" + client);
                    }
                    handleStopScan(client);
                    setAutoBatchScanClient(client);
                    handleStartScan(client);
                    updatedScanParams = true;
                }
            }
            if (updatedScanParams) {
                mScanNative.configureRegularScanParams();
            }
        }

        private void updateBatchScanToRegularScanClients() {
            boolean updatedScanParams = false;
            for (ScanClient client : mBatchClients) {
                if (!mScanNative.isExemptFromAutoBatchScanUpdate(client)) {
                    if (DBG) {
                        Log.d(TAG, "Updating batch scan to regular scan" + client);
                    }
                    handleStopScan(client);
                    clearAutoBatchScanClient(client);
                    handleStartScan(client);
                    updatedScanParams = true;
                }
            }
            if (updatedScanParams) {
                mScanNative.configureRegularScanParams();
            }
        }

        private void setAutoBatchScanClient(ScanClient client) {
            if (isAutoBatchScanClientEnabled(client)) {
                return;
            }
            client.updateScanMode(ScanSettings.SCAN_MODE_SCREEN_OFF);
            if (client.stats != null) {
                client.stats.setAutoBatchScan(client.scannerId, true);
            }
        }

        private void clearAutoBatchScanClient(ScanClient client) {
            if (!isAutoBatchScanClientEnabled(client)) {
                return;
            }
            client.updateScanMode(client.scanModeApp);
            if (client.stats != null) {
                client.stats.setAutoBatchScan(client.scannerId, false);
            }
        }

        private void updateRegularScanClientsScreenOff() {
            boolean updatedScanParams = false;
            for (ScanClient client : mRegularScanClients) {
@@ -782,6 +860,7 @@ public class ScanManager {
            if (DBG) {
                Log.d(TAG, "handleScreenOn()");
            }
            updateBatchScanToRegularScanClients();
            handleResumeScans();
            updateRegularScanClientsScreenOn();
        }
@@ -1039,6 +1118,19 @@ public class ScanManager {
            return isOpportunisticScanClient(client) || isFirstMatchScanClient(client);
        }

        private boolean isExemptFromAutoBatchScanUpdate(ScanClient client) {
            return isOpportunisticScanClient(client) || !isAllMatchesAutoBatchScanClient(client);
        }

        private boolean isAutoBatchScanClientEnabled(ScanClient client) {
            return client.stats != null && client.stats.isAutoBatchScan(client.scannerId);
        }

        private boolean isAllMatchesAutoBatchScanClient(ScanClient client) {
            return client.settings.getCallbackType()
                    == ScanSettings.CALLBACK_TYPE_ALL_MATCHES_AUTO_BATCH;
        }

        private boolean isOpportunisticScanClient(ScanClient client) {
            return client.settings.getScanMode() == ScanSettings.SCAN_MODE_OPPORTUNISTIC;
        }
@@ -1148,6 +1240,8 @@ public class ScanManager {
                        resolver,
                        Settings.Global.BLE_SCAN_BALANCED_WINDOW_MS,
                        SCAN_MODE_BALANCED_WINDOW_MS);
                case ScanSettings.SCAN_MODE_SCREEN_OFF:
                    return mAdapterService.getScreenOffLowPowerWindowMillis();
                default:
                    return Settings.Global.getInt(
                        resolver,
@@ -1164,6 +1258,8 @@ public class ScanManager {
                        resolver,
                        Settings.Global.BLE_SCAN_BALANCED_INTERVAL_MS,
                        SCAN_MODE_BALANCED_INTERVAL_MS);
                case ScanSettings.SCAN_MODE_SCREEN_OFF:
                    return mAdapterService.getScreenOffLowPowerIntervalMillis();
                default:
                    return Settings.Global.getInt(
                        resolver,
@@ -1526,6 +1622,10 @@ public class ScanManager {
                    || (settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_MATCH_LOST) != 0) {
                return DELIVERY_MODE_ON_FOUND_LOST;
            }
            if (isAllMatchesAutoBatchScanClient(client)) {
                return isAutoBatchScanClientEnabled(client) ? DELIVERY_MODE_BATCH
                        : DELIVERY_MODE_IMMEDIATE;
            }
            return settings.getReportDelayMillis() == 0 ? DELIVERY_MODE_IMMEDIATE
                    : DELIVERY_MODE_BATCH;
        }
+114 −8
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.bluetooth.gatt;

import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES;
import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES_AUTO_BATCH;
import static android.bluetooth.le.ScanSettings.SCAN_MODE_OPPORTUNISTIC;
import static android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_POWER;
import static android.bluetooth.le.ScanSettings.SCAN_MODE_BALANCED;
@@ -87,6 +89,7 @@ public class ScanManagerTest {
    private ScanManager mScanManager;
    private Handler mHandler;
    private CountDownLatch mLatch;
    private long mScanReportDelay;

    @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();
    @Mock private AdapterService mAdapterService;
@@ -139,6 +142,8 @@ public class ScanManagerTest {

        mLatch = new CountDownLatch(1);
        assertThat(mLatch).isNotNull();

        mScanReportDelay = DEFAULT_SCAN_REPORT_DELAY_MS;
    }

    @After
@@ -174,9 +179,9 @@ public class ScanManagerTest {
    }

    private ScanClient createScanClient(int id, boolean isFiltered, int scanMode,
            boolean isBatch) {
            boolean isBatch, boolean isAutoBatch) {
        List<ScanFilter> scanFilterList = createScanFilterList(isFiltered);
        ScanSettings scanSettings = createScanSettings(scanMode, isBatch);
        ScanSettings scanSettings = createScanSettings(scanMode, isBatch, isAutoBatch);

        ScanClient client = new ScanClient(id, scanSettings, scanFilterList);
        client.stats = new AppScanStats("Test", null, null, mService);
@@ -185,7 +190,7 @@ public class ScanManagerTest {
    }

    private ScanClient createScanClient(int id, boolean isFiltered, int scanMode) {
        return createScanClient(id, isFiltered, scanMode, false);
        return createScanClient(id, isFiltered, scanMode, false, false);
    }

    private List<ScanFilter> createScanFilterList(boolean isFiltered) {
@@ -197,12 +202,17 @@ public class ScanManagerTest {
        return scanFilterList;
    }

    private ScanSettings createScanSettings(int scanMode, boolean isBatch) {
    private ScanSettings createScanSettings(int scanMode, boolean isBatch, boolean isAutoBatch) {

        ScanSettings scanSettings = null;
        if (isBatch) {
        if (isBatch && isAutoBatch) {
            int autoCallbackType = CALLBACK_TYPE_ALL_MATCHES_AUTO_BATCH;
            scanSettings = new ScanSettings.Builder().setScanMode(scanMode)
                    .setReportDelay(mScanReportDelay).setCallbackType(autoCallbackType)
                    .build();
        } else if (isBatch) {
            scanSettings = new ScanSettings.Builder().setScanMode(scanMode)
                    .setReportDelay(DEFAULT_SCAN_REPORT_DELAY_MS).build();
                    .setReportDelay(mScanReportDelay).build();
        } else {
            scanSettings = new ScanSettings.Builder().setScanMode(scanMode).build();
        }
@@ -806,6 +816,7 @@ public class ScanManagerTest {
        // Set filtered and batch scan flag
        final boolean isFiltered = false;
        final boolean isBatch = true;
        final boolean isAutoBatch = false;
        // Set scan mode map {original scan mode (ScanMode) : expected scan mode (expectedScanMode)}
        SparseIntArray scanModeMap = new SparseIntArray();
        scanModeMap.put(SCAN_MODE_LOW_POWER, SCAN_MODE_LOW_POWER);
@@ -822,7 +833,7 @@ public class ScanManagerTest {
            // Turn off screen
            sendMessageWaitForProcessed(createScreenOnOffMessage(false));
            // Create scan client
            ScanClient client = createScanClient(i, isFiltered, ScanMode, isBatch);
            ScanClient client = createScanClient(i, isFiltered, ScanMode, isBatch, isAutoBatch);
            // Start scan
            sendMessageWaitForProcessed(createStartStopScanMessage(true, client));
            assertThat(mScanManager.getRegularScanQueue().contains(client)).isFalse();
@@ -842,6 +853,7 @@ public class ScanManagerTest {
        // Set filtered and batch scan flag
        final boolean isFiltered = true;
        final boolean isBatch = true;
        final boolean isAutoBatch = false;
        // Set scan mode map {original scan mode (ScanMode) : expected scan mode (expectedScanMode)}
        SparseIntArray scanModeMap = new SparseIntArray();
        scanModeMap.put(SCAN_MODE_LOW_POWER, SCAN_MODE_LOW_POWER);
@@ -858,7 +870,94 @@ public class ScanManagerTest {
            // Turn off screen
            sendMessageWaitForProcessed(createScreenOnOffMessage(false));
            // Create scan client
            ScanClient client = createScanClient(i, isFiltered, ScanMode, isBatch);
            ScanClient client = createScanClient(i, isFiltered, ScanMode, isBatch, isAutoBatch);
            // Start scan
            sendMessageWaitForProcessed(createStartStopScanMessage(true, client));
            assertThat(mScanManager.getRegularScanQueue().contains(client)).isFalse();
            assertThat(mScanManager.getSuspendedScanQueue().contains(client)).isFalse();
            assertThat(mScanManager.getBatchScanQueue().contains(client)).isTrue();
            assertThat(mScanManager.getBatchScanParams().scanMode).isEqualTo(expectedScanMode);
            // Turn on screen
            sendMessageWaitForProcessed(createScreenOnOffMessage(true));
            assertThat(mScanManager.getRegularScanQueue().contains(client)).isFalse();
            assertThat(mScanManager.getSuspendedScanQueue().contains(client)).isFalse();
            assertThat(mScanManager.getBatchScanQueue().contains(client)).isTrue();
            assertThat(mScanManager.getBatchScanParams().scanMode).isEqualTo(expectedScanMode);
        }
    }

    @Test
    public void testUnfilteredAutoBatchScan() {
        // Set filtered and batch scan flag
        final boolean isFiltered = false;
        final boolean isBatch = true;
        final boolean isAutoBatch = true;
        // Set report delay for auto batch scan callback type
        mScanReportDelay = ScanSettings.AUTO_BATCH_MIN_REPORT_DELAY_MILLIS;
        // Set scan mode map {original scan mode (ScanMode) : expected scan mode (expectedScanMode)}
        SparseIntArray scanModeMap = new SparseIntArray();
        scanModeMap.put(SCAN_MODE_LOW_POWER, SCAN_MODE_SCREEN_OFF);
        scanModeMap.put(SCAN_MODE_BALANCED, SCAN_MODE_SCREEN_OFF);
        scanModeMap.put(SCAN_MODE_LOW_LATENCY, SCAN_MODE_SCREEN_OFF);
        scanModeMap.put(SCAN_MODE_AMBIENT_DISCOVERY, SCAN_MODE_SCREEN_OFF);

        for (int i = 0; i < scanModeMap.size(); i++) {
            int ScanMode = scanModeMap.keyAt(i);
            int expectedScanMode = scanModeMap.get(ScanMode);
            Log.d(TAG, "ScanMode: " + String.valueOf(ScanMode)
                    + " expectedScanMode: " + String.valueOf(expectedScanMode));

            // Turn off screen
            sendMessageWaitForProcessed(createScreenOnOffMessage(false));
            // Create scan client
            ScanClient client = createScanClient(i, isFiltered, ScanMode, isBatch, isAutoBatch);
            // Start scan
            sendMessageWaitForProcessed(createStartStopScanMessage(true, client));
            assertThat(mScanManager.getRegularScanQueue().contains(client)).isFalse();
            assertThat(mScanManager.getSuspendedScanQueue().contains(client)).isTrue();
            assertThat(mScanManager.getBatchScanQueue().contains(client)).isFalse();
            assertThat(mScanManager.getBatchScanParams()).isNull();
            // Turn on screen
            sendMessageWaitForProcessed(createScreenOnOffMessage(true));
            assertThat(mScanManager.getRegularScanQueue().contains(client)).isTrue();
            assertThat(client.settings.getScanMode()).isEqualTo(ScanMode);
            assertThat(mScanManager.getSuspendedScanQueue().contains(client)).isFalse();
            assertThat(mScanManager.getBatchScanQueue().contains(client)).isFalse();
            assertThat(mScanManager.getBatchScanParams()).isNull();
            // Turn off screen
            sendMessageWaitForProcessed(createScreenOnOffMessage(false));
            assertThat(mScanManager.getRegularScanQueue().contains(client)).isFalse();
            assertThat(mScanManager.getSuspendedScanQueue().contains(client)).isTrue();
            assertThat(mScanManager.getBatchScanQueue().contains(client)).isFalse();
            assertThat(mScanManager.getBatchScanParams()).isNull();
        }
    }

    @Test
    public void testFilteredAutoBatchScan() {
        // Set filtered and batch scan flag
        final boolean isFiltered = true;
        final boolean isBatch = true;
        final boolean isAutoBatch = true;
        // Set report delay for auto batch scan callback type
        mScanReportDelay = ScanSettings.AUTO_BATCH_MIN_REPORT_DELAY_MILLIS;
        // Set scan mode map {original scan mode (ScanMode) : expected scan mode (expectedScanMode)}
        SparseIntArray scanModeMap = new SparseIntArray();
        scanModeMap.put(SCAN_MODE_LOW_POWER, SCAN_MODE_SCREEN_OFF);
        scanModeMap.put(SCAN_MODE_BALANCED, SCAN_MODE_SCREEN_OFF);
        scanModeMap.put(SCAN_MODE_LOW_LATENCY, SCAN_MODE_SCREEN_OFF);
        scanModeMap.put(SCAN_MODE_AMBIENT_DISCOVERY, SCAN_MODE_SCREEN_OFF);

        for (int i = 0; i < scanModeMap.size(); i++) {
            int ScanMode = scanModeMap.keyAt(i);
            int expectedScanMode = scanModeMap.get(ScanMode);
            Log.d(TAG, "ScanMode: " + String.valueOf(ScanMode)
                    + " expectedScanMode: " + String.valueOf(expectedScanMode));

            // Turn off screen
            sendMessageWaitForProcessed(createScreenOnOffMessage(false));
            // Create scan client
            ScanClient client = createScanClient(i, isFiltered, ScanMode, isBatch, isAutoBatch);
            // Start scan
            sendMessageWaitForProcessed(createStartStopScanMessage(true, client));
            assertThat(mScanManager.getRegularScanQueue().contains(client)).isFalse();
@@ -867,6 +966,13 @@ public class ScanManagerTest {
            assertThat(mScanManager.getBatchScanParams().scanMode).isEqualTo(expectedScanMode);
            // Turn on screen
            sendMessageWaitForProcessed(createScreenOnOffMessage(true));
            assertThat(mScanManager.getRegularScanQueue().contains(client)).isTrue();
            assertThat(client.settings.getScanMode()).isEqualTo(ScanMode);
            assertThat(mScanManager.getSuspendedScanQueue().contains(client)).isFalse();
            assertThat(mScanManager.getBatchScanQueue().contains(client)).isFalse();
            assertThat(mScanManager.getBatchScanParams()).isNull();
            // Turn off screen
            sendMessageWaitForProcessed(createScreenOnOffMessage(false));
            assertThat(mScanManager.getRegularScanQueue().contains(client)).isFalse();
            assertThat(mScanManager.getSuspendedScanQueue().contains(client)).isFalse();
            assertThat(mScanManager.getBatchScanQueue().contains(client)).isTrue();
+2 −0
Original line number Diff line number Diff line
@@ -1396,7 +1396,9 @@ package android.bluetooth.le {
    method public int getScanMode();
    method public int getScanResultType();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final long AUTO_BATCH_MIN_REPORT_DELAY_MILLIS = 600000L; // 0x927c0L
    field public static final int CALLBACK_TYPE_ALL_MATCHES = 1; // 0x1
    field public static final int CALLBACK_TYPE_ALL_MATCHES_AUTO_BATCH = 8; // 0x8
    field public static final int CALLBACK_TYPE_FIRST_MATCH = 2; // 0x2
    field public static final int CALLBACK_TYPE_MATCH_LOST = 4; // 0x4
    field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.ScanSettings> CREATOR;
Loading