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

Commit 0f4dffa4 authored by Sarvesh Kalwit's avatar Sarvesh Kalwit
Browse files

Implement MSFT extension logic in ScanManager

Bug: 360387731
Bug: 360392001
Bug: 365787977
Flag: com.android.bluetooth.flags.le_scan_msft_support
Test: atest BluetoothInstrumentationTests:ScanManagerTest
Change-Id: I7ef0770537d126c1ec1012353317ba968d6c8a1b
parent 43b80335
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -2829,7 +2829,7 @@ static int register_com_android_bluetooth_gatt_scan(JNIEnv* env) {
          {"gattClientScanFilterEnableNative", "(IZ)V", (void*)gattClientScanFilterEnableNative},
          {"gattSetScanParametersNative", "(IIII)V", (void*)gattSetScanParametersNative},
          // MSFT HCI Extension functions.
          {"gattClientIsMsftSupportedNative", "()Z", (void*)gattClientIsMsftSupportedNative},
          {"gattClientIsMsftSupportedNative", "()Z", (bool*)gattClientIsMsftSupportedNative},
          {"gattClientMsftAdvMonitorAddNative",
           "(Lcom/android/bluetooth/le_scan/MsftAdvMonitor$Monitor;[Lcom/android/bluetooth/le_scan/"
           "MsftAdvMonitor$Pattern;Lcom/android/bluetooth/le_scan/MsftAdvMonitor$Address;I)V",
+107 −2
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.Settings;
import android.util.Log;
import android.util.SparseBooleanArray;
@@ -1005,6 +1006,12 @@ public class ScanManager {
        // The logic is AND for each filter field.
        private static final int LIST_LOGIC_TYPE = 0x1111111;
        private static final int FILTER_LOGIC_TYPE = 1;

        // MSFT-based hardware scan offload sysprop
        private static final String MSFT_HCI_EXT_ENABLED = "bluetooth.core.le.use_msft_hci_ext";
        // Hardcoded min number of hardware adv monitor slots for MSFT-enabled controllers
        private static final int MIN_NUM_MSFT_MONITOR_SLOTS = 20;

        // Filter indices that are available to user. It's sad we need to maintain filter index.
        private final Deque<Integer> mFilterIndexStack;
        // Map of scannerId and Filter indices used by client.
@@ -1020,6 +1027,11 @@ public class ScanManager {
        private final PendingIntent mBatchScanIntervalIntent;
        private final ScanNativeInterface mNativeInterface;

        // Whether or not MSFT-based scanning hardware offload is available on this device
        private final boolean mIsMsftSupported;
        // Whether or not MSFT-based scanning is currently enabled in the controller
        private boolean scanEnabledMsft = false;

        ScanNative(TransitionalScanHelper scanHelper) {
            mNativeInterface = ScanObjectsFactory.getInstance().getScanNativeInterface();
            mNativeInterface.init(scanHelper);
@@ -1053,6 +1065,11 @@ public class ScanManager {
                        }
                    });
            mContext.registerReceiver(mBatchAlarmReceiver.get(), filter);

            mIsMsftSupported =
                    Flags.leScanMsftSupport()
                            && SystemProperties.getBoolean(MSFT_HCI_EXT_ENABLED, false)
                            && mNativeInterface.gattClientIsMsftSupported();
        }

        private void callbackDone(int scannerId, int status) {
@@ -1147,14 +1164,17 @@ public class ScanManager {
        }

        void startRegularScan(ScanClient client) {
            if (isFilteringSupported()
            if ((isFilteringSupported() || mIsMsftSupported)
                    && mFilterIndexStack.isEmpty()
                    && mClientFilterIndexMap.isEmpty()) {
                initFilterIndexStack();
            }
            if (isFilteringSupported()) {
                configureScanFilters(client);
            } else if (mIsMsftSupported) {
                addFiltersMsft(client);
            }

            // Start scan native only for the first client.
            if (numRegularScanClients() == 1
                    && client.settings != null
@@ -1402,7 +1422,12 @@ public class ScanManager {
                    Log.w(TAG, "There is no scan radio to stop");
                }
            }

            if (!mIsMsftSupported) {
                removeScanFilters(client.scannerId);
            } else {
                removeFiltersMsft(client);
            }
        }

        void regularScanTimeout(ScanClient client) {
@@ -1692,6 +1717,11 @@ public class ScanManager {
        private void initFilterIndexStack() {
            int maxFiltersSupported =
                    AdapterService.getAdapterService().getNumOfOffloadedScanFilterSupported();
            if (!isFilteringSupported() && mIsMsftSupported) {
                // Hardcoded minimum number of hardware adv monitor slots, because this value
                // cannot be queried from the controller for MSFT enabled devices
                maxFiltersSupported = MIN_NUM_MSFT_MONITOR_SLOTS;
            }
            // Start from index 4 as:
            // index 0 is reserved for ALL_PASS filter in Settings app.
            // index 1 is reserved for ALL_PASS filter for regular scan apps.
@@ -1954,6 +1984,81 @@ public class ScanManager {
        private void unregisterScanner(int scannerId) {
            mNativeInterface.unregisterScanner(scannerId);
        }

        private void addFiltersMsft(ScanClient client) {
            // Do not add any filters set by opportunistic scan clients
            if (isOpportunisticScanClient(client)) {
                return;
            }

            if (client == null
                    || client.filters == null
                    || client.filters.isEmpty()
                    || client.filters.size() > mFilterIndexStack.size()) {
                // Use all-pass filter
                updateScanMsft();
                return;
            }

            Deque<Integer> clientFilterIndices = new ArrayDeque<>();
            for (ScanFilter filter : client.filters) {
                int filterIndex = mFilterIndexStack.pop();
                MsftAdvMonitor monitor = new MsftAdvMonitor(filter);

                resetCountDownLatch();
                mNativeInterface.gattClientMsftAdvMonitorAdd(
                        monitor.getMonitor(),
                        monitor.getPatterns(),
                        monitor.getAddress(),
                        filterIndex);
                waitForCallback();

                clientFilterIndices.add(filterIndex);
            }
            mClientFilterIndexMap.put(client.scannerId, clientFilterIndices);

            updateScanMsft();
        }

        private void removeFiltersMsft(ScanClient client) {
            Deque<Integer> clientFilterIndices = mClientFilterIndexMap.remove(client.scannerId);
            if (clientFilterIndices != null) {
                mFilterIndexStack.addAll(clientFilterIndices);
                for (int filterIndex : clientFilterIndices) {
                    resetCountDownLatch();
                    mNativeInterface.gattClientMsftAdvMonitorRemove(filterIndex);
                    waitForCallback();
                }
            }

            updateScanMsft();
        }

        private void updateScanMsft() {
            boolean shouldEnableScanMsft =
                    !mRegularScanClients.stream()
                            .anyMatch(
                                    c ->
                                            c.settings != null
                                                    && c.settings.getScanMode()
                                                            != ScanSettings.SCAN_MODE_OPPORTUNISTIC
                                                    && !this.mClientFilterIndexMap.containsKey(
                                                            c.scannerId));
            if (scanEnabledMsft != shouldEnableScanMsft) {
                resetCountDownLatch();
                mNativeInterface.gattClientMsftAdvMonitorEnable(shouldEnableScanMsft);
                waitForCallback();
                scanEnabledMsft = shouldEnableScanMsft;

                // Restart scanning, since enabling/disabling may have changed
                // the filter policy
                Log.d(TAG, "Restarting MSFT scan");
                mNativeInterface.gattClientScan(false);
                if (numRegularScanClients() > 0) {
                    mNativeInterface.gattClientScan(true);
                }
            }
        }
    }

    @VisibleForTesting
+51 −3
Original line number Diff line number Diff line
@@ -182,6 +182,36 @@ public class ScanNativeInterface {
        gattClientScanFilterEnableNative(clientIf, enable);
    }

    /** Check if MSFT HCI extension is supported */
    public boolean gattClientIsMsftSupported() {
        return gattClientIsMsftSupportedNative();
    }

    /** Add a MSFT Advertisement Monitor */
    public void gattClientMsftAdvMonitorAdd(
            MsftAdvMonitor.Monitor msft_adv_monitor,
            MsftAdvMonitor.Pattern[] msft_adv_monitor_patterns,
            MsftAdvMonitor.Address msft_adv_monitor_address,
            int filter_index) {
        gattClientMsftAdvMonitorAddNative(
                msft_adv_monitor,
                msft_adv_monitor_patterns,
                msft_adv_monitor_address,
                filter_index);
    }

    /** Remove a MSFT Advertisement Monitor */
    public void gattClientMsftAdvMonitorRemove(int filter_index) {
        int monitor_handle = mScanHelper.msftMonitorHandleFromFilterIndex(filter_index);
        if (monitor_handle < 0) return;
        gattClientMsftAdvMonitorRemoveNative(filter_index, monitor_handle);
    }

    /** Enable a MSFT Advertisement Monitor */
    public void gattClientMsftAdvMonitorEnable(boolean enable) {
        gattClientMsftAdvMonitorEnableNative(enable);
    }

    /** Configure BLE batch scan storage */
    public void gattClientConfigBatchScanStorage(
            int clientIf,
@@ -385,9 +415,27 @@ public class ScanNativeInterface {
        mScanHelper.onScanParamSetupCompleted(status, scannerId);
    }

    void onMsftAdvMonitorAdd(int filter_index, int monitor_handle, int status) {}
    void onMsftAdvMonitorAdd(int filter_index, int monitor_handle, int status) {
        if (mScanHelper == null) {
            Log.e(TAG, "Scan helper is null!");
            return;
        }
        mScanHelper.onMsftAdvMonitorAdd(filter_index, monitor_handle, status);
    }

    void onMsftAdvMonitorRemove(int filter_index, int status) {}
    void onMsftAdvMonitorRemove(int filter_index, int status) {
        if (mScanHelper == null) {
            Log.e(TAG, "Scan helper is null!");
            return;
        }
        mScanHelper.onMsftAdvMonitorRemove(filter_index, status);
    }

    void onMsftAdvMonitorEnable(int status) {}
    void onMsftAdvMonitorEnable(int status) {
        if (mScanHelper == null) {
            Log.e(TAG, "Scan helper is null!");
            return;
        }
        mScanHelper.onMsftAdvMonitorEnable(status);
    }
}
+45 −0
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@ import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -142,6 +143,7 @@ public class TransitionalScanHelper {
    private AdapterService mAdapterService;

    private ScannerMap mScannerMap = new ScannerMap();
    private HashMap<Integer, Integer> mFilterIndexToMsftAdvMonitorMap = new HashMap<>();
    private String mExposureNotificationPackage;

    public ScannerMap getScannerMap() {
@@ -1048,6 +1050,49 @@ public class TransitionalScanHelper {
        }
    }

    public int msftMonitorHandleFromFilterIndex(int filter_index) {
        if (!mFilterIndexToMsftAdvMonitorMap.containsKey(filter_index)) {
            Log.e(TAG, "Monitor with filter_index'" + filter_index + "' does not exist");
            return -1;
        }
        return mFilterIndexToMsftAdvMonitorMap.get(filter_index);
    }

    public void onMsftAdvMonitorAdd(int filter_index, int monitor_handle, int status) {
        if (status != 0) {
            Log.e(
                    TAG,
                    "Error adding advertisement monitor with filter index '" + filter_index + "'");
            return;
        }
        if (mFilterIndexToMsftAdvMonitorMap.containsKey(filter_index)) {
            Log.e(TAG, "Monitor with filter_index'" + filter_index + "' already added");
            return;
        }
        mFilterIndexToMsftAdvMonitorMap.put(filter_index, monitor_handle);
    }

    public void onMsftAdvMonitorRemove(int filter_index, int status) {
        if (status != 0) {
            Log.e(
                    TAG,
                    "Error removing advertisement monitor with filter index '"
                            + filter_index
                            + "'");
        }
        if (!mFilterIndexToMsftAdvMonitorMap.containsKey(filter_index)) {
            Log.e(TAG, "Monitor with filter_index'" + filter_index + "' does not exist");
            return;
        }
        mFilterIndexToMsftAdvMonitorMap.remove(filter_index);
    }

    public void onMsftAdvMonitorEnable(int status) {
        if (status != 0) {
            Log.e(TAG, "Error enabling advertisement monitor");
        }
    }

    /**************************************************************************
     * GATT Service functions - Shared CLIENT/SERVER
     *************************************************************************/
+50 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import static com.android.bluetooth.le_scan.ScanManager.SCAN_MODE_SCREEN_OFF_LOW

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -63,6 +64,7 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemProperties;
import android.os.WorkSource;
import android.os.test.TestLooper;
import android.platform.test.annotations.EnableFlags;
@@ -127,6 +129,9 @@ public class ScanManagerTest {
    private static final String TEST_APP_NAME = "Test";
    private static final String TEST_PACKAGE_NAME = "com.test.package";

    // MSFT-based hardware scan offload sysprop
    private static final String MSFT_HCI_EXT_ENABLED = "bluetooth.core.le.use_msft_hci_ext";

    private Context mTargetContext;
    private ScanManager mScanManager;
    private Handler mHandler;
@@ -2142,4 +2147,49 @@ public class ScanManagerTest {
                    .gattSetScanParameters(anyInt(), anyInt(), anyInt(), eq(expectedPhy));
        }
    }

    @Test
    @EnableFlags(Flags.FLAG_LE_SCAN_MSFT_SUPPORT)
    public void testMsftScan() {
        final boolean isFiltered = true;
        final boolean isEmptyFilter = false;

        boolean isMsftEnabled = SystemProperties.getBoolean(MSFT_HCI_EXT_ENABLED, false);
        SystemProperties.set(MSFT_HCI_EXT_ENABLED, Boolean.toString(true));
        try {
            when(mScanNativeInterface.gattClientIsMsftSupported()).thenReturn(true);
            when(mBluetoothAdapterProxy.isOffloadedScanFilteringSupported()).thenReturn(false);

            // Create new ScanManager since sysprop and MSFT support are only checked when
            // ScanManager
            // is created
            mScanManager =
                    new ScanManager(
                            mMockGattService,
                            mMockScanHelper,
                            mAdapterService,
                            mBluetoothAdapterProxy,
                            mTestLooper.getLooper());
            mHandler = mScanManager.getClientHandler();
            assertThat(mHandler).isNotNull();

            // Turn on screen
            sendMessageWaitForProcessed(createScreenOnOffMessage(true));
            // Create scan client
            ScanClient client = createScanClient(0, isFiltered, isEmptyFilter, SCAN_MODE_LOW_POWER);
            // Start scan
            sendMessageWaitForProcessed(createStartStopScanMessage(true, client));

            // Verify MSFT APIs
            verify(mScanNativeInterface, atLeastOnce())
                    .gattClientMsftAdvMonitorAdd(
                            any(MsftAdvMonitor.Monitor.class),
                            any(MsftAdvMonitor.Pattern[].class),
                            any(MsftAdvMonitor.Address.class),
                            anyInt());
            verify(mScanNativeInterface, atLeastOnce()).gattClientMsftAdvMonitorEnable(eq(true));
        } finally {
            SystemProperties.set(MSFT_HCI_EXT_ENABLED, Boolean.toString(isMsftEnabled));
        }
    }
}