Merge "[Clean up] Create a new packages for class BroadcastProviderManager and DiscoveryProviderManager" into tm-mainline-prod
diff --git a/nearby/service/java/com/android/server/nearby/NearbyService.java b/nearby/service/java/com/android/server/nearby/NearbyService.java
index 0b12521..9a4834c 100644
--- a/nearby/service/java/com/android/server/nearby/NearbyService.java
+++ b/nearby/service/java/com/android/server/nearby/NearbyService.java
@@ -43,9 +43,9 @@
 import com.android.server.nearby.common.locator.LocatorContextWrapper;
 import com.android.server.nearby.fastpair.FastPairManager;
 import com.android.server.nearby.injector.Injector;
+import com.android.server.nearby.managers.BroadcastProviderManager;
+import com.android.server.nearby.managers.DiscoveryProviderManager;
 import com.android.server.nearby.presence.PresenceManager;
-import com.android.server.nearby.provider.BroadcastProviderManager;
-import com.android.server.nearby.provider.DiscoveryProviderManager;
 import com.android.server.nearby.provider.FastPairDataProvider;
 import com.android.server.nearby.util.identity.CallerIdentity;
 import com.android.server.nearby.util.permissions.BroadcastPermissions;
@@ -58,10 +58,10 @@
     public static final Boolean MANUAL_TEST = false;
 
     private final Context mContext;
-    private Injector mInjector;
     private final FastPairManager mFastPairManager;
     private final PresenceManager mPresenceManager;
     private final NearbyConfiguration mNearbyConfiguration;
+    private Injector mInjector;
     private final BroadcastReceiver mBluetoothReceiver =
             new BroadcastReceiver() {
                 @Override
@@ -79,8 +79,8 @@
                     }
                 }
             };
-    private DiscoveryProviderManager mProviderManager;
-    private BroadcastProviderManager mBroadcastProviderManager;
+    private final DiscoveryProviderManager mProviderManager;
+    private final BroadcastProviderManager mBroadcastProviderManager;
 
     public NearbyService(Context context) {
         mContext = context;
diff --git a/nearby/service/java/com/android/server/nearby/provider/BroadcastProviderManager.java b/nearby/service/java/com/android/server/nearby/managers/BroadcastProviderManager.java
similarity index 96%
rename from nearby/service/java/com/android/server/nearby/provider/BroadcastProviderManager.java
rename to nearby/service/java/com/android/server/nearby/managers/BroadcastProviderManager.java
index bc69631..024bff8 100644
--- a/nearby/service/java/com/android/server/nearby/provider/BroadcastProviderManager.java
+++ b/nearby/service/java/com/android/server/nearby/managers/BroadcastProviderManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.nearby.provider;
+package com.android.server.nearby.managers;
 
 import android.annotation.Nullable;
 import android.content.Context;
@@ -31,6 +31,7 @@
 import com.android.server.nearby.presence.Advertisement;
 import com.android.server.nearby.presence.ExtendedAdvertisement;
 import com.android.server.nearby.presence.FastAdvertisement;
+import com.android.server.nearby.provider.BleBroadcastProvider;
 import com.android.server.nearby.util.ForegroundThread;
 
 import java.util.concurrent.Executor;
diff --git a/nearby/service/java/com/android/server/nearby/provider/DiscoveryProviderManager.java b/nearby/service/java/com/android/server/nearby/managers/DiscoveryProviderManager.java
similarity index 96%
rename from nearby/service/java/com/android/server/nearby/provider/DiscoveryProviderManager.java
rename to nearby/service/java/com/android/server/nearby/managers/DiscoveryProviderManager.java
index cd97bb2..59db5d8 100644
--- a/nearby/service/java/com/android/server/nearby/provider/DiscoveryProviderManager.java
+++ b/nearby/service/java/com/android/server/nearby/managers/DiscoveryProviderManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.nearby.provider;
+package com.android.server.nearby.managers;
 
 import static android.nearby.ScanRequest.SCAN_TYPE_NEARBY_PRESENCE;
 
@@ -39,6 +39,11 @@
 import com.android.server.nearby.injector.Injector;
 import com.android.server.nearby.metrics.NearbyMetrics;
 import com.android.server.nearby.presence.PresenceDiscoveryResult;
+import com.android.server.nearby.provider.AbstractDiscoveryProvider;
+import com.android.server.nearby.provider.BleDiscoveryProvider;
+import com.android.server.nearby.provider.ChreCommunication;
+import com.android.server.nearby.provider.ChreDiscoveryProvider;
+import com.android.server.nearby.provider.PrivacyFilter;
 import com.android.server.nearby.util.identity.CallerIdentity;
 import com.android.server.nearby.util.permissions.DiscoveryPermissions;
 
@@ -386,7 +391,10 @@
         return false;
     }
 
-    class ScanListenerDeathRecipient implements IBinder.DeathRecipient {
+    /**
+     * Class to make listener unregister after the binder is dead.
+     */
+    public class ScanListenerDeathRecipient implements IBinder.DeathRecipient {
         public IScanListener listener;
 
         ScanListenerDeathRecipient(IScanListener listener) {
diff --git a/nearby/service/java/com/android/server/nearby/provider/AbstractDiscoveryProvider.java b/nearby/service/java/com/android/server/nearby/provider/AbstractDiscoveryProvider.java
index 21b4d7c..64c55f0 100644
--- a/nearby/service/java/com/android/server/nearby/provider/AbstractDiscoveryProvider.java
+++ b/nearby/service/java/com/android/server/nearby/provider/AbstractDiscoveryProvider.java
@@ -41,14 +41,6 @@
     protected final Executor mExecutor;
     protected Listener mListener;
 
-    /** Interface for listening to discovery providers. */
-    public interface Listener {
-        /**
-         * Called when a provider has a new nearby device available. May be invoked from any thread.
-         */
-        void onNearbyDeviceDiscovered(NearbyDeviceParcelable nearbyDevice);
-    }
-
     protected AbstractDiscoveryProvider(Context context, Executor executor) {
         mContext = context;
         mExecutor = executor;
@@ -86,10 +78,18 @@
      * as a discovery provider should not be controlling itself. Using this method from subclasses
      * could also result in deadlock.
      */
-    protected DiscoveryProviderController getController() {
+    public DiscoveryProviderController getController() {
         return mController;
     }
 
+    /** Interface for listening to discovery providers. */
+    public interface Listener {
+        /**
+         * Called when a provider has a new nearby device available. May be invoked from any thread.
+         */
+        void onNearbyDeviceDiscovered(NearbyDeviceParcelable nearbyDevice);
+    }
+
     private class Controller implements DiscoveryProviderController {
 
         private boolean mStarted = false;
@@ -104,7 +104,6 @@
         public boolean isStarted() {
             return mStarted;
         }
-
         @Override
         public void start() {
             if (mStarted) {
@@ -125,6 +124,12 @@
             mExecutor.execute(AbstractDiscoveryProvider.this::onStop);
         }
 
+        @ScanRequest.ScanMode
+        @Override
+        public int getProviderScanMode() {
+            return mScanMode;
+        }
+
         @Override
         public void setProviderScanMode(@ScanRequest.ScanMode int scanMode) {
             if (mScanMode == scanMode) {
@@ -135,12 +140,6 @@
             mExecutor.execute(AbstractDiscoveryProvider.this::invalidateScanMode);
         }
 
-        @ScanRequest.ScanMode
-        @Override
-        public int getProviderScanMode() {
-            return mScanMode;
-        }
-
         @Override
         public void setProviderScanFilters(List<ScanFilter> filters) {
             mExecutor.execute(() -> onSetScanFilters(filters));
diff --git a/nearby/service/java/com/android/server/nearby/provider/BleBroadcastProvider.java b/nearby/service/java/com/android/server/nearby/provider/BleBroadcastProvider.java
index 2b9fdb9..5632ab5 100644
--- a/nearby/service/java/com/android/server/nearby/provider/BleBroadcastProvider.java
+++ b/nearby/service/java/com/android/server/nearby/provider/BleBroadcastProvider.java
@@ -45,7 +45,7 @@
     /**
      * Listener for Broadcast status changes.
      */
-    interface BroadcastListener {
+    public interface BroadcastListener {
         void onStatusChanged(int status);
     }
 
@@ -56,13 +56,16 @@
     private boolean mIsAdvertising;
     @VisibleForTesting
     AdvertisingSetCallback mAdvertisingSetCallback;
-    BleBroadcastProvider(Injector injector, Executor executor) {
+    public BleBroadcastProvider(Injector injector, Executor executor) {
         mInjector = injector;
         mExecutor = executor;
         mAdvertisingSetCallback = getAdvertisingSetCallback();
     }
 
-    void start(@BroadcastRequest.BroadcastVersion int version, byte[] advertisementPackets,
+    /**
+     * Starts to broadcast with given bytes.
+     */
+    public void start(@BroadcastRequest.BroadcastVersion int version, byte[] advertisementPackets,
             BroadcastListener listener) {
         if (mIsAdvertising) {
             stop();
@@ -113,7 +116,10 @@
         }
     }
 
-    void stop() {
+    /**
+     * Stops current advertisement.
+     */
+    public void stop() {
         if (mIsAdvertising) {
             BluetoothAdapter adapter = mInjector.getBluetoothAdapter();
             if (adapter != null) {
diff --git a/nearby/service/java/com/android/server/nearby/provider/ChreDiscoveryProvider.java b/nearby/service/java/com/android/server/nearby/provider/ChreDiscoveryProvider.java
index 98cac59..1b82af3 100644
--- a/nearby/service/java/com/android/server/nearby/provider/ChreDiscoveryProvider.java
+++ b/nearby/service/java/com/android/server/nearby/provider/ChreDiscoveryProvider.java
@@ -142,7 +142,7 @@
     }
 
     @VisibleForTesting
-    List<ScanFilter> getFiltersLocked() {
+    public List<ScanFilter> getFiltersLocked() {
         synchronized (mLock) {
             return mScanFilters == null ? null : List.copyOf(mScanFilters);
         }
diff --git a/nearby/service/java/com/android/server/nearby/provider/DiscoveryProviderController.java b/nearby/service/java/com/android/server/nearby/provider/DiscoveryProviderController.java
index fa1a874..71ffda5 100644
--- a/nearby/service/java/com/android/server/nearby/provider/DiscoveryProviderController.java
+++ b/nearby/service/java/com/android/server/nearby/provider/DiscoveryProviderController.java
@@ -23,7 +23,7 @@
 import java.util.List;
 
 /** Interface for controlling discovery providers. */
-interface DiscoveryProviderController {
+public interface DiscoveryProviderController {
 
     /**
      * Sets the listener which can expect to receive all state updates from after this point. May be
diff --git a/nearby/tests/unit/src/com/android/server/nearby/provider/BroadcastProviderManagerTest.java b/nearby/tests/unit/src/com/android/server/nearby/managers/BroadcastProviderManagerTest.java
similarity index 94%
rename from nearby/tests/unit/src/com/android/server/nearby/provider/BroadcastProviderManagerTest.java
rename to nearby/tests/unit/src/com/android/server/nearby/managers/BroadcastProviderManagerTest.java
index 58b8e07..d0008f5 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/provider/BroadcastProviderManagerTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/managers/BroadcastProviderManagerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.nearby.provider;
+package com.android.server.nearby.managers;
 
 import static android.Manifest.permission.READ_DEVICE_CONFIG;
 import static android.Manifest.permission.WRITE_DEVICE_CONFIG;
@@ -40,6 +40,8 @@
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.server.nearby.provider.BleBroadcastProvider;
+
 import com.google.common.util.concurrent.MoreExecutors;
 
 import org.junit.Before;
@@ -52,7 +54,7 @@
 import java.util.Collections;
 
 /**
- * Unit test for {@link BroadcastProviderManager}.
+ * Unit test for {@link com.android.server.nearby.managers.BroadcastProviderManager}.
  */
 public class BroadcastProviderManagerTest {
     private static final byte[] IDENTITY = new byte[]{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
@@ -84,7 +86,8 @@
                 "true", false);
 
         mContext = ApplicationProvider.getApplicationContext();
-        mBroadcastProviderManager = new BroadcastProviderManager(MoreExecutors.directExecutor(),
+        mBroadcastProviderManager = new BroadcastProviderManager(
+                MoreExecutors.directExecutor(),
                 mBleBroadcastProvider);
 
         PrivateCredential privateCredential =
diff --git a/nearby/tests/unit/src/com/android/server/nearby/provider/DiscoveryProviderManagerTest.java b/nearby/tests/unit/src/com/android/server/nearby/managers/DiscoveryProviderManagerTest.java
similarity index 90%
rename from nearby/tests/unit/src/com/android/server/nearby/provider/DiscoveryProviderManagerTest.java
rename to nearby/tests/unit/src/com/android/server/nearby/managers/DiscoveryProviderManagerTest.java
index 5ffaffb..542ceed 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/provider/DiscoveryProviderManagerTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/managers/DiscoveryProviderManagerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.nearby.provider;
+package com.android.server.nearby.managers;
 
 import static android.nearby.PresenceCredential.IDENTITY_TYPE_PRIVATE;
 import static android.nearby.ScanRequest.SCAN_TYPE_NEARBY_PRESENCE;
@@ -37,6 +37,10 @@
 import android.os.IBinder;
 
 import com.android.server.nearby.injector.Injector;
+import com.android.server.nearby.provider.BleDiscoveryProvider;
+import com.android.server.nearby.provider.ChreCommunication;
+import com.android.server.nearby.provider.ChreDiscoveryProvider;
+import com.android.server.nearby.provider.DiscoveryProviderController;
 import com.android.server.nearby.util.identity.CallerIdentity;
 
 import org.junit.Before;
@@ -56,257 +60,33 @@
     private static final int UID = 1234;
     private static final int PID = 5678;
     private static final String PACKAGE_NAME = "android.nearby.test";
-
-    @Mock Injector mInjector;
-    @Mock Context mContext;
-    @Mock AppOpsManager mAppOpsManager;
-    @Mock BleDiscoveryProvider mBleDiscoveryProvider;
-    @Mock ChreDiscoveryProvider mChreDiscoveryProvider;
-    @Mock DiscoveryProviderController mBluetoothController;
-    @Mock DiscoveryProviderController mChreController;
-    @Mock IScanListener mScanListener;
-    @Mock CallerIdentity mCallerIdentity;
-    @Mock DiscoveryProviderManager.ScanListenerDeathRecipient mScanListenerDeathRecipient;
-    @Mock IBinder mIBinder;
-
+    private static final int RSSI = -60;
+    @Mock
+    Injector mInjector;
+    @Mock
+    Context mContext;
+    @Mock
+    AppOpsManager mAppOpsManager;
+    @Mock
+    BleDiscoveryProvider mBleDiscoveryProvider;
+    @Mock
+    ChreDiscoveryProvider mChreDiscoveryProvider;
+    @Mock
+    DiscoveryProviderController mBluetoothController;
+    @Mock
+    DiscoveryProviderController mChreController;
+    @Mock
+    IScanListener mScanListener;
+    @Mock
+    CallerIdentity mCallerIdentity;
+    @Mock
+    DiscoveryProviderManager.ScanListenerDeathRecipient mScanListenerDeathRecipient;
+    @Mock
+    IBinder mIBinder;
     private DiscoveryProviderManager mDiscoveryProviderManager;
     private Map<IBinder, DiscoveryProviderManager.ScanListenerRecord>
             mScanTypeScanListenerRecordMap;
 
-    private static final int RSSI = -60;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        when(mInjector.getAppOpsManager()).thenReturn(mAppOpsManager);
-        when(mBleDiscoveryProvider.getController()).thenReturn(mBluetoothController);
-        when(mChreDiscoveryProvider.getController()).thenReturn(mChreController);
-
-        mScanTypeScanListenerRecordMap = new HashMap<>();
-        mDiscoveryProviderManager =
-                new DiscoveryProviderManager(mContext, mInjector, mBleDiscoveryProvider,
-                        mChreDiscoveryProvider,
-                        mScanTypeScanListenerRecordMap);
-        mCallerIdentity = CallerIdentity
-                .forTest(UID, PID, PACKAGE_NAME, /* attributionTag= */ null);
-    }
-
-    @Test
-    public void testOnNearbyDeviceDiscovered() {
-        NearbyDeviceParcelable nearbyDeviceParcelable = new NearbyDeviceParcelable.Builder()
-                .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
-                .build();
-        mDiscoveryProviderManager.onNearbyDeviceDiscovered(nearbyDeviceParcelable);
-    }
-
-    @Test
-    public void testInvalidateProviderScanMode() {
-        mDiscoveryProviderManager.invalidateProviderScanMode();
-    }
-
-    @Test
-    public void testStartProviders_chreOnlyChreAvailable_bleProviderNotStarted() {
-        when(mChreDiscoveryProvider.available()).thenReturn(true);
-
-        ScanRequest scanRequest = new ScanRequest.Builder()
-                .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
-                .addScanFilter(getChreOnlyPresenceScanFilter()).build();
-        DiscoveryProviderManager.ScanListenerRecord record =
-                new DiscoveryProviderManager.ScanListenerRecord(scanRequest, mScanListener,
-                        mCallerIdentity, mScanListenerDeathRecipient);
-        mScanTypeScanListenerRecordMap.put(mIBinder, record);
-
-        Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
-        verify(mBluetoothController, never()).start();
-        assertThat(start).isTrue();
-    }
-
-    @Test
-    public void testStartProviders_chreOnlyChreAvailable_multipleFilters_bleProviderNotStarted() {
-        when(mChreDiscoveryProvider.available()).thenReturn(true);
-
-        ScanRequest scanRequest = new ScanRequest.Builder()
-                .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
-                .addScanFilter(getChreOnlyPresenceScanFilter())
-                .addScanFilter(getPresenceScanFilter()).build();
-        DiscoveryProviderManager.ScanListenerRecord record =
-                new DiscoveryProviderManager.ScanListenerRecord(scanRequest, mScanListener,
-                        mCallerIdentity, mScanListenerDeathRecipient);
-        mScanTypeScanListenerRecordMap.put(mIBinder, record);
-
-        Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
-        verify(mBluetoothController, never()).start();
-        assertThat(start).isTrue();
-    }
-
-    @Test
-    public void testStartProviders_chreOnlyChreUnavailable_bleProviderNotStarted() {
-        when(mChreDiscoveryProvider.available()).thenReturn(false);
-
-        ScanRequest scanRequest = new ScanRequest.Builder()
-                .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
-                .addScanFilter(getChreOnlyPresenceScanFilter()).build();
-        DiscoveryProviderManager.ScanListenerRecord record =
-                new DiscoveryProviderManager.ScanListenerRecord(scanRequest, mScanListener,
-                        mCallerIdentity, mScanListenerDeathRecipient);
-        mScanTypeScanListenerRecordMap.put(mIBinder, record);
-
-        Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
-        verify(mBluetoothController, never()).start();
-        assertThat(start).isFalse();
-    }
-
-    @Test
-    public void testStartProviders_notChreOnlyChreAvailable_bleProviderNotStarted() {
-        when(mChreDiscoveryProvider.available()).thenReturn(true);
-
-        ScanRequest scanRequest = new ScanRequest.Builder()
-                .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
-                .addScanFilter(getPresenceScanFilter()).build();
-        DiscoveryProviderManager.ScanListenerRecord record =
-                new DiscoveryProviderManager.ScanListenerRecord(scanRequest, mScanListener,
-                        mCallerIdentity, mScanListenerDeathRecipient);
-        mScanTypeScanListenerRecordMap.put(mIBinder, record);
-
-        Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
-        verify(mBluetoothController, never()).start();
-        assertThat(start).isTrue();
-    }
-
-    @Test
-    public void testStartProviders_notChreOnlyChreUnavailable_bleProviderStarted() {
-        when(mChreDiscoveryProvider.available()).thenReturn(false);
-
-        ScanRequest scanRequest = new ScanRequest.Builder()
-                .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
-                .addScanFilter(getPresenceScanFilter()).build();
-        DiscoveryProviderManager.ScanListenerRecord record =
-                new DiscoveryProviderManager.ScanListenerRecord(scanRequest, mScanListener,
-                        mCallerIdentity, mScanListenerDeathRecipient);
-        mScanTypeScanListenerRecordMap.put(mIBinder, record);
-
-        Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
-        verify(mBluetoothController, atLeastOnce()).start();
-        assertThat(start).isTrue();
-    }
-
-    @Test
-    public void testStartProviders_chreOnlyChreUndetermined_bleProviderNotStarted() {
-        when(mChreDiscoveryProvider.available()).thenReturn(null);
-
-        ScanRequest scanRequest = new ScanRequest.Builder()
-                .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
-                .addScanFilter(getChreOnlyPresenceScanFilter()).build();
-        DiscoveryProviderManager.ScanListenerRecord record =
-                new DiscoveryProviderManager.ScanListenerRecord(scanRequest, mScanListener,
-                        mCallerIdentity, mScanListenerDeathRecipient);
-        mScanTypeScanListenerRecordMap.put(mIBinder, record);
-
-        Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
-        verify(mBluetoothController, never()).start();
-        assertThat(start).isNull();
-    }
-
-    @Test
-    public void testStartProviders_notChreOnlyChreUndetermined_bleProviderStarted() {
-        when(mChreDiscoveryProvider.available()).thenReturn(null);
-
-        ScanRequest scanRequest = new ScanRequest.Builder()
-                .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
-                .addScanFilter(getPresenceScanFilter()).build();
-        DiscoveryProviderManager.ScanListenerRecord record =
-                new DiscoveryProviderManager.ScanListenerRecord(scanRequest, mScanListener,
-                        mCallerIdentity, mScanListenerDeathRecipient);
-        mScanTypeScanListenerRecordMap.put(mIBinder, record);
-
-        Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
-        verify(mBluetoothController, atLeastOnce()).start();
-        assertThat(start).isTrue();
-    }
-
-    @Test
-    public void test_stopChreProvider_clearFilters() throws Exception {
-        // Cannot use mocked ChreDiscoveryProvider,
-        // so we cannot use class variable mDiscoveryProviderManager
-        ExecutorService executor = Executors.newSingleThreadExecutor();
-        DiscoveryProviderManager manager =
-                new DiscoveryProviderManager(mContext, mInjector, mBleDiscoveryProvider,
-                        new ChreDiscoveryProvider(
-                                mContext,
-                                new ChreCommunication(mInjector, mContext, executor), executor),
-                mScanTypeScanListenerRecordMap);
-        ScanRequest scanRequest = new ScanRequest.Builder()
-                .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
-                .addScanFilter(getPresenceScanFilter()).build();
-        DiscoveryProviderManager.ScanListenerRecord record =
-                new DiscoveryProviderManager.ScanListenerRecord(scanRequest, mScanListener,
-                        mCallerIdentity, mScanListenerDeathRecipient);
-        mScanTypeScanListenerRecordMap.put(mIBinder, record);
-        manager.startChreProvider(List.of(getPresenceScanFilter()));
-        // This is an asynchronized process. The filters will be set in executor thread. So we need
-        // to wait for some time to get the correct result.
-        Thread.sleep(200);
-
-        assertThat(manager.mChreDiscoveryProvider.getController().isStarted())
-                .isTrue();
-        assertThat(manager.mChreDiscoveryProvider.getFiltersLocked()).isNotNull();
-
-        manager.stopChreProvider();
-        Thread.sleep(200);
-        // The filters should be cleared right after.
-        assertThat(manager.mChreDiscoveryProvider.getController().isStarted())
-                .isFalse();
-        assertThat(manager.mChreDiscoveryProvider.getFiltersLocked()).isEmpty();
-    }
-
-    @Test
-    public void test_restartChreProvider() throws Exception {
-        // Cannot use mocked ChreDiscoveryProvider,
-        // so we cannot use class variable mDiscoveryProviderManager
-        ExecutorService executor = Executors.newSingleThreadExecutor();
-        DiscoveryProviderManager manager =
-                new DiscoveryProviderManager(mContext, mInjector, mBleDiscoveryProvider,
-                        new ChreDiscoveryProvider(
-                                mContext,
-                                new ChreCommunication(mInjector, mContext, executor), executor),
-                        mScanTypeScanListenerRecordMap);
-        ScanRequest scanRequest = new ScanRequest.Builder()
-                .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
-                .addScanFilter(getPresenceScanFilter()).build();
-        DiscoveryProviderManager.ScanListenerRecord record =
-                new DiscoveryProviderManager.ScanListenerRecord(scanRequest, mScanListener,
-                        mCallerIdentity, mScanListenerDeathRecipient);
-        mScanTypeScanListenerRecordMap.put(mIBinder, record);
-        manager.startChreProvider(List.of(getPresenceScanFilter()));
-        // This is an asynchronized process. The filters will be set in executor thread. So we need
-        // to wait for some time to get the correct result.
-        Thread.sleep(200);
-
-        assertThat(manager.mChreDiscoveryProvider.getController().isStarted())
-                .isTrue();
-        assertThat(manager.mChreDiscoveryProvider.getFiltersLocked()).isNotNull();
-
-        // We want to make sure quickly restart the provider the filters should
-        // be reset correctly.
-        // See b/255922206, there can be a race condition that filters get cleared because onStop()
-        // get executed after onStart() if they are called from different threads.
-        manager.stopChreProvider();
-        manager.mChreDiscoveryProvider.getController().setProviderScanFilters(
-                List.of(getPresenceScanFilter()));
-        manager.startChreProvider(List.of(getPresenceScanFilter()));
-        Thread.sleep(200);
-        assertThat(manager.mChreDiscoveryProvider.getController().isStarted())
-                .isTrue();
-        assertThat(manager.mChreDiscoveryProvider.getFiltersLocked()).isNotNull();
-
-        // Wait for enough time
-        Thread.sleep(1000);
-
-        assertThat(manager.mChreDiscoveryProvider.getController().isStarted())
-                .isTrue();
-        assertThat(manager.mChreDiscoveryProvider.getFiltersLocked()).isNotNull();
-    }
-
     private static PresenceScanFilter getPresenceScanFilter() {
         final byte[] secretId = new byte[]{1, 2, 3, 4};
         final byte[] authenticityKey = new byte[]{0, 1, 1, 1};
@@ -349,4 +129,247 @@
                 .addExtendedProperty(scanModeElement)
                 .build();
     }
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        when(mInjector.getAppOpsManager()).thenReturn(mAppOpsManager);
+        when(mBleDiscoveryProvider.getController()).thenReturn(mBluetoothController);
+        when(mChreDiscoveryProvider.getController()).thenReturn(mChreController);
+
+        mScanTypeScanListenerRecordMap = new HashMap<>();
+        mDiscoveryProviderManager =
+                new DiscoveryProviderManager(mContext, mInjector,
+                        mBleDiscoveryProvider,
+                        mChreDiscoveryProvider,
+                        mScanTypeScanListenerRecordMap);
+        mCallerIdentity = CallerIdentity
+                .forTest(UID, PID, PACKAGE_NAME, /* attributionTag= */ null);
+    }
+
+    @Test
+    public void testOnNearbyDeviceDiscovered() {
+        NearbyDeviceParcelable nearbyDeviceParcelable = new NearbyDeviceParcelable.Builder()
+                .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
+                .build();
+        mDiscoveryProviderManager.onNearbyDeviceDiscovered(nearbyDeviceParcelable);
+    }
+
+    @Test
+    public void testInvalidateProviderScanMode() {
+        mDiscoveryProviderManager.invalidateProviderScanMode();
+    }
+
+    @Test
+    public void testStartProviders_chreOnlyChreAvailable_bleProviderNotStarted() {
+        when(mChreDiscoveryProvider.available()).thenReturn(true);
+
+        ScanRequest scanRequest = new ScanRequest.Builder()
+                .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
+                .addScanFilter(getChreOnlyPresenceScanFilter()).build();
+        DiscoveryProviderManager.ScanListenerRecord record =
+                new DiscoveryProviderManager.ScanListenerRecord(
+                        scanRequest, mScanListener,
+                        mCallerIdentity, mScanListenerDeathRecipient);
+        mScanTypeScanListenerRecordMap.put(mIBinder, record);
+
+        Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
+        verify(mBluetoothController, never()).start();
+        assertThat(start).isTrue();
+    }
+
+    @Test
+    public void testStartProviders_chreOnlyChreAvailable_multipleFilters_bleProviderNotStarted() {
+        when(mChreDiscoveryProvider.available()).thenReturn(true);
+
+        ScanRequest scanRequest = new ScanRequest.Builder()
+                .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
+                .addScanFilter(getChreOnlyPresenceScanFilter())
+                .addScanFilter(getPresenceScanFilter()).build();
+        DiscoveryProviderManager.ScanListenerRecord record =
+                new DiscoveryProviderManager.ScanListenerRecord(
+                        scanRequest, mScanListener,
+                        mCallerIdentity, mScanListenerDeathRecipient);
+        mScanTypeScanListenerRecordMap.put(mIBinder, record);
+
+        Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
+        verify(mBluetoothController, never()).start();
+        assertThat(start).isTrue();
+    }
+
+    @Test
+    public void testStartProviders_chreOnlyChreUnavailable_bleProviderNotStarted() {
+        when(mChreDiscoveryProvider.available()).thenReturn(false);
+
+        ScanRequest scanRequest = new ScanRequest.Builder()
+                .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
+                .addScanFilter(getChreOnlyPresenceScanFilter()).build();
+        DiscoveryProviderManager.ScanListenerRecord record =
+                new DiscoveryProviderManager.ScanListenerRecord(
+                        scanRequest, mScanListener,
+                        mCallerIdentity, mScanListenerDeathRecipient);
+        mScanTypeScanListenerRecordMap.put(mIBinder, record);
+
+        Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
+        verify(mBluetoothController, never()).start();
+        assertThat(start).isFalse();
+    }
+
+    @Test
+    public void testStartProviders_notChreOnlyChreAvailable_bleProviderNotStarted() {
+        when(mChreDiscoveryProvider.available()).thenReturn(true);
+
+        ScanRequest scanRequest = new ScanRequest.Builder()
+                .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
+                .addScanFilter(getPresenceScanFilter()).build();
+        DiscoveryProviderManager.ScanListenerRecord record =
+                new DiscoveryProviderManager.ScanListenerRecord(
+                        scanRequest, mScanListener,
+                        mCallerIdentity, mScanListenerDeathRecipient);
+        mScanTypeScanListenerRecordMap.put(mIBinder, record);
+
+        Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
+        verify(mBluetoothController, never()).start();
+        assertThat(start).isTrue();
+    }
+
+    @Test
+    public void testStartProviders_notChreOnlyChreUnavailable_bleProviderStarted() {
+        when(mChreDiscoveryProvider.available()).thenReturn(false);
+
+        ScanRequest scanRequest = new ScanRequest.Builder()
+                .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
+                .addScanFilter(getPresenceScanFilter()).build();
+        DiscoveryProviderManager.ScanListenerRecord record =
+                new DiscoveryProviderManager.ScanListenerRecord(
+                        scanRequest, mScanListener,
+                        mCallerIdentity, mScanListenerDeathRecipient);
+        mScanTypeScanListenerRecordMap.put(mIBinder, record);
+
+        Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
+        verify(mBluetoothController, atLeastOnce()).start();
+        assertThat(start).isTrue();
+    }
+
+    @Test
+    public void testStartProviders_chreOnlyChreUndetermined_bleProviderNotStarted() {
+        when(mChreDiscoveryProvider.available()).thenReturn(null);
+
+        ScanRequest scanRequest = new ScanRequest.Builder()
+                .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
+                .addScanFilter(getChreOnlyPresenceScanFilter()).build();
+        DiscoveryProviderManager.ScanListenerRecord record =
+                new DiscoveryProviderManager.ScanListenerRecord(
+                        scanRequest, mScanListener,
+                        mCallerIdentity, mScanListenerDeathRecipient);
+        mScanTypeScanListenerRecordMap.put(mIBinder, record);
+
+        Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
+        verify(mBluetoothController, never()).start();
+        assertThat(start).isNull();
+    }
+
+    @Test
+    public void testStartProviders_notChreOnlyChreUndetermined_bleProviderStarted() {
+        when(mChreDiscoveryProvider.available()).thenReturn(null);
+
+        ScanRequest scanRequest = new ScanRequest.Builder()
+                .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
+                .addScanFilter(getPresenceScanFilter()).build();
+        DiscoveryProviderManager.ScanListenerRecord record =
+                new DiscoveryProviderManager.ScanListenerRecord(
+                        scanRequest, mScanListener,
+                        mCallerIdentity, mScanListenerDeathRecipient);
+        mScanTypeScanListenerRecordMap.put(mIBinder, record);
+
+        Boolean start = mDiscoveryProviderManager.startProviders(scanRequest);
+        verify(mBluetoothController, atLeastOnce()).start();
+        assertThat(start).isTrue();
+    }
+
+    @Test
+    public void test_stopChreProvider_clearFilters() throws Exception {
+        // Cannot use mocked ChreDiscoveryProvider,
+        // so we cannot use class variable mDiscoveryProviderManager
+        ExecutorService executor = Executors.newSingleThreadExecutor();
+        DiscoveryProviderManager manager =
+                new DiscoveryProviderManager(mContext, mInjector,
+                        mBleDiscoveryProvider,
+                        new ChreDiscoveryProvider(
+                                mContext,
+                                new ChreCommunication(mInjector, mContext, executor), executor),
+                        mScanTypeScanListenerRecordMap);
+        ScanRequest scanRequest = new ScanRequest.Builder()
+                .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
+                .addScanFilter(getPresenceScanFilter()).build();
+        DiscoveryProviderManager.ScanListenerRecord record =
+                new DiscoveryProviderManager.ScanListenerRecord(
+                        scanRequest, mScanListener,
+                        mCallerIdentity, mScanListenerDeathRecipient);
+        mScanTypeScanListenerRecordMap.put(mIBinder, record);
+        manager.startChreProvider(List.of(getPresenceScanFilter()));
+        // This is an asynchronized process. The filters will be set in executor thread. So we need
+        // to wait for some time to get the correct result.
+        Thread.sleep(200);
+
+        assertThat(manager.mChreDiscoveryProvider.getController().isStarted())
+                .isTrue();
+        assertThat(manager.mChreDiscoveryProvider.getFiltersLocked()).isNotNull();
+
+        manager.stopChreProvider();
+        Thread.sleep(200);
+        // The filters should be cleared right after.
+        assertThat(manager.mChreDiscoveryProvider.getController().isStarted())
+                .isFalse();
+        assertThat(manager.mChreDiscoveryProvider.getFiltersLocked()).isEmpty();
+    }
+
+    @Test
+    public void test_restartChreProvider() throws Exception {
+        // Cannot use mocked ChreDiscoveryProvider,
+        // so we cannot use class variable mDiscoveryProviderManager
+        ExecutorService executor = Executors.newSingleThreadExecutor();
+        DiscoveryProviderManager manager =
+                new DiscoveryProviderManager(mContext, mInjector,
+                        mBleDiscoveryProvider,
+                        new ChreDiscoveryProvider(
+                                mContext,
+                                new ChreCommunication(mInjector, mContext, executor), executor),
+                        mScanTypeScanListenerRecordMap);
+        ScanRequest scanRequest = new ScanRequest.Builder()
+                .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
+                .addScanFilter(getPresenceScanFilter()).build();
+        DiscoveryProviderManager.ScanListenerRecord record =
+                new DiscoveryProviderManager.ScanListenerRecord(scanRequest, mScanListener,
+                        mCallerIdentity, mScanListenerDeathRecipient);
+        mScanTypeScanListenerRecordMap.put(mIBinder, record);
+        manager.startChreProvider(List.of(getPresenceScanFilter()));
+        // This is an asynchronized process. The filters will be set in executor thread. So we need
+        // to wait for some time to get the correct result.
+        Thread.sleep(200);
+
+        assertThat(manager.mChreDiscoveryProvider.getController().isStarted())
+                .isTrue();
+        assertThat(manager.mChreDiscoveryProvider.getFiltersLocked()).isNotNull();
+
+        // We want to make sure quickly restart the provider the filters should
+        // be reset correctly.
+        // See b/255922206, there can be a race condition that filters get cleared because onStop()
+        // get executed after onStart() if they are called from different threads.
+        manager.stopChreProvider();
+        manager.mChreDiscoveryProvider.getController().setProviderScanFilters(
+                List.of(getPresenceScanFilter()));
+        manager.startChreProvider(List.of(getPresenceScanFilter()));
+        Thread.sleep(200);
+        assertThat(manager.mChreDiscoveryProvider.getController().isStarted())
+                .isTrue();
+        assertThat(manager.mChreDiscoveryProvider.getFiltersLocked()).isNotNull();
+
+        // Wait for enough time
+        Thread.sleep(1000);
+
+        assertThat(manager.mChreDiscoveryProvider.getController().isStarted())
+                .isTrue();
+        assertThat(manager.mChreDiscoveryProvider.getFiltersLocked()).isNotNull();
+    }
 }