Merge "Add new database table and proto for battery usage reattribution (2/5)" into main
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceController.java
index b623ba8..6ba7183 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceController.java
@@ -72,7 +72,7 @@
     private static final String PREF_KEY = "calls_and_alarms";
 
     @VisibleForTesting
-    protected enum ChangeCallAudioType {
+    enum ChangeCallAudioType {
         UNKNOWN,
         CONNECTED_EARLIER,
         CONNECTED_LATER
@@ -90,7 +90,9 @@
     Map<Integer, List<CachedBluetoothDevice>> mGroupedConnectedDevices = new HashMap<>();
     private List<AudioSharingDeviceItem> mDeviceItemsInSharingSession = new ArrayList<>();
     private final AtomicBoolean mCallbacksRegistered = new AtomicBoolean(false);
-    private final BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback =
+
+    @VisibleForTesting
+    final BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback =
             new BluetoothLeBroadcastAssistant.Callback() {
                 @Override
                 public void onSearchStarted(int reason) {}
@@ -276,7 +278,7 @@
 
     /** Test only: set callback registration status in tests. */
     @VisibleForTesting
-    public void setCallbacksRegistered(boolean registered) {
+    void setCallbacksRegistered(boolean registered) {
         mCallbacksRegistered.set(registered);
     }
 
@@ -385,7 +387,7 @@
     }
 
     @VisibleForTesting
-    protected void logCallAudioDeviceChange(int currentGroupId, CachedBluetoothDevice target) {
+    void logCallAudioDeviceChange(int currentGroupId, CachedBluetoothDevice target) {
         var unused =
                 ThreadUtils.postOnBackgroundThread(
                         () -> {
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCompatibilityPreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCompatibilityPreferenceController.java
index 581ad62..42a9038 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCompatibilityPreferenceController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCompatibilityPreferenceController.java
@@ -60,7 +60,7 @@
     private final AtomicBoolean mCallbacksRegistered = new AtomicBoolean(false);
 
     @VisibleForTesting
-    protected final BluetoothLeBroadcast.Callback mBroadcastCallback =
+    final BluetoothLeBroadcast.Callback mBroadcastCallback =
             new BluetoothLeBroadcast.Callback() {
                 @Override
                 public void onBroadcastStarted(int reason, int broadcastId) {
@@ -219,7 +219,7 @@
 
     /** Test only: set callbacks registration state for test setup. */
     @VisibleForTesting
-    public void setCallbacksRegistered(boolean registered) {
+    void setCallbacksRegistered(boolean registered) {
         mCallbacksRegistered.set(registered);
     }
 
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragment.java
index c7d7407..ad41e8a 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragment.java
@@ -103,7 +103,7 @@
 
     /** Test only: set mock controllers for the {@link AudioSharingDashboardFragment} */
     @VisibleForTesting
-    protected void setControllers(
+    void setControllers(
             AudioSharingDeviceVolumeGroupController volumeGroupController,
             AudioSharingCallAudioPreferenceController callAudioController,
             AudioSharingPlaySoundPreferenceController playSoundController,
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
index 51a8e11..b932a7e 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
@@ -87,7 +87,8 @@
     @Nullable private AudioSharingDialogHandler mDialogHandler;
     private AtomicBoolean mIntentHandled = new AtomicBoolean(false);
 
-    private BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback =
+    @VisibleForTesting
+    BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback =
             new BluetoothLeBroadcastAssistant.Callback() {
                 @Override
                 public void onSearchStarted(int reason) {}
@@ -368,23 +369,23 @@
     }
 
     @VisibleForTesting
-    public void setBluetoothDeviceUpdater(@Nullable BluetoothDeviceUpdater bluetoothDeviceUpdater) {
+    void setBluetoothDeviceUpdater(@Nullable BluetoothDeviceUpdater bluetoothDeviceUpdater) {
         mBluetoothDeviceUpdater = bluetoothDeviceUpdater;
     }
 
     @VisibleForTesting
-    public void setDialogHandler(@Nullable AudioSharingDialogHandler dialogHandler) {
+    void setDialogHandler(@Nullable AudioSharingDialogHandler dialogHandler) {
         mDialogHandler = dialogHandler;
     }
 
     @VisibleForTesting
-    public void setHostFragment(@Nullable DashboardFragment fragment) {
+    void setHostFragment(@Nullable DashboardFragment fragment) {
         mFragment = fragment;
     }
 
     /** Test only: set intent handle state for test. */
     @VisibleForTesting
-    public void setIntentHandled(boolean handled) {
+    void setIntentHandled(boolean handled) {
         mIntentHandled.set(handled);
     }
 
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupController.java
index 4a067ac..ee2ba7b 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupController.java
@@ -79,13 +79,10 @@
     private Map<Integer, Integer> mValueMap = new HashMap<Integer, Integer>();
     private AtomicBoolean mCallbacksRegistered = new AtomicBoolean(false);
 
-    private BluetoothVolumeControl.Callback mVolumeControlCallback =
+    @VisibleForTesting
+    BluetoothVolumeControl.Callback mVolumeControlCallback =
             new BluetoothVolumeControl.Callback() {
                 @Override
-                public void onVolumeOffsetChanged(
-                        @NonNull BluetoothDevice device, int volumeOffset) {}
-
-                @Override
                 public void onDeviceVolumeChanged(
                         @NonNull BluetoothDevice device,
                         @IntRange(from = -255, to = 255) int volume) {
@@ -117,7 +114,8 @@
                 }
             };
 
-    private BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback =
+    @VisibleForTesting
+    BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback =
             new BluetoothLeBroadcastAssistant.Callback() {
                 @Override
                 public void onSearchStarted(int reason) {}
@@ -323,26 +321,26 @@
     }
 
     @VisibleForTesting
-    public void setDeviceUpdater(@Nullable AudioSharingDeviceVolumeControlUpdater updater) {
+    void setDeviceUpdater(@Nullable AudioSharingDeviceVolumeControlUpdater updater) {
         mBluetoothDeviceUpdater = updater;
     }
 
     /** Test only: set callback registration status in tests. */
     @VisibleForTesting
-    public void setCallbacksRegistered(boolean registered) {
+    void setCallbacksRegistered(boolean registered) {
         mCallbacksRegistered.set(registered);
     }
 
     /** Test only: set volume map in tests. */
     @VisibleForTesting
-    public void setVolumeMap(@Nullable Map<Integer, Integer> map) {
+    void setVolumeMap(@Nullable Map<Integer, Integer> map) {
         mValueMap.clear();
         mValueMap.putAll(map);
     }
 
     /** Test only: set value for private preferenceGroup in tests. */
     @VisibleForTesting
-    public void setPreferenceGroup(@Nullable PreferenceGroup group) {
+    void setPreferenceGroup(@Nullable PreferenceGroup group) {
         mPreferenceGroup = group;
         mPreference = group;
     }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFactory.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFactory.java
index 165beae..2ee286d 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFactory.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFactory.java
@@ -345,4 +345,6 @@
             return dialog;
         }
     }
+
+    private AudioSharingDialogFactory() {}
 }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java
index 3d111fd..e787be3 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java
@@ -100,7 +100,8 @@
 
     /** Test only: get the event data passed to the dialog. */
     @VisibleForTesting
-    protected @NonNull Pair<Integer, Object>[] getEventData() {
+    @NonNull
+    Pair<Integer, Object>[] getEventData() {
         return sEventData;
     }
 
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java
index 5458a9f..8d69cf6 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java
@@ -28,6 +28,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 import androidx.fragment.app.DialogFragment;
 import androidx.fragment.app.Fragment;
 
@@ -61,7 +62,8 @@
     private final MetricsFeatureProvider mMetricsFeatureProvider;
     private List<BluetoothDevice> mTargetSinks = new ArrayList<>();
 
-    private final BluetoothLeBroadcast.Callback mBroadcastCallback =
+    @VisibleForTesting
+    final BluetoothLeBroadcast.Callback mBroadcastCallback =
             new BluetoothLeBroadcast.Callback() {
                 @Override
                 public void onBroadcastStarted(int reason, int broadcastId) {
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java
index 5f6d84a..dcd8a3b 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java
@@ -145,9 +145,17 @@
         return sNewDevice;
     }
 
+    /** Test only: get the {@link DialogEventListener} passed to the dialog. */
+    @VisibleForTesting
+    @Nullable
+    DialogEventListener getListener() {
+        return sListener;
+    }
+
     /** Test only: get the event data passed to the dialog. */
     @VisibleForTesting
-    protected @NonNull Pair<Integer, Object>[] getEventData() {
+    @NonNull
+    Pair<Integer, Object>[] getEventData() {
         return sEventData;
     }
 
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java
index 7eebbcb..ec669bf 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java
@@ -110,9 +110,17 @@
         return sNewDevice;
     }
 
+    /** Test only: get the {@link DialogEventListener} passed to the dialog. */
+    @VisibleForTesting
+    @Nullable
+    DialogEventListener getListener() {
+        return sListener;
+    }
+
     /** Test only: get the event data passed to the dialog. */
     @VisibleForTesting
-    protected @NonNull Pair<Integer, Object>[] getEventData() {
+    @NonNull
+    Pair<Integer, Object>[] getEventData() {
         return sEventData;
     }
 
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPlaySoundPreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPlaySoundPreferenceController.java
index 46c7f9c..11b195c 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPlaySoundPreferenceController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPlaySoundPreferenceController.java
@@ -105,7 +105,7 @@
     }
 
     @VisibleForTesting
-    protected void setRingtone(Ringtone ringtone) {
+    void setRingtone(Ringtone ringtone) {
         mRingtone = ringtone;
     }
 
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPreferenceController.java
index d27d3a2..0244889 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPreferenceController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPreferenceController.java
@@ -52,7 +52,7 @@
     private final Executor mExecutor;
 
     @VisibleForTesting
-    protected final BluetoothLeBroadcast.Callback mBroadcastCallback =
+    final BluetoothLeBroadcast.Callback mBroadcastCallback =
             new BluetoothLeBroadcast.Callback() {
                 @Override
                 public void onBroadcastStarted(int reason, int broadcastId) {
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java
index beac4b0..59593ba 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java
@@ -142,7 +142,8 @@
 
     /** Test only: get the event data passed to the dialog. */
     @VisibleForTesting
-    protected @NonNull Pair<Integer, Object>[] getEventData() {
+    @NonNull
+    Pair<Integer, Object>[] getEventData() {
         return sEventData;
     }
 
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
index 5022579..89d2c95 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
@@ -117,7 +117,7 @@
             };
 
     @VisibleForTesting
-    protected final BluetoothLeBroadcast.Callback mBroadcastCallback =
+    final BluetoothLeBroadcast.Callback mBroadcastCallback =
             new BluetoothLeBroadcast.Callback() {
                 @Override
                 public void onBroadcastStarted(int reason, int broadcastId) {
@@ -392,7 +392,7 @@
 
     /** Test only: set callback registration status in tests. */
     @VisibleForTesting
-    public void setCallbacksRegistered(boolean registered) {
+    void setCallbacksRegistered(boolean registered) {
         mCallbacksRegistered.set(registered);
     }
 
diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
index f63b649..0132273 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
@@ -39,6 +39,9 @@
     /** Check whether the battery tips card is enabled in the battery usage page */
     boolean isBatteryTipsEnabled();
 
+    /** Check whether force expire the app optimization mode. */
+    boolean isForceExpireAppOptimizationModeEnabled();
+
     /** Check whether to log the optimization mode of app entry in period job */
     boolean isAppOptimizationModeLogged();
 
diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
index cc6659a..1675ce6 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
@@ -84,6 +84,11 @@
     }
 
     @Override
+    public boolean isForceExpireAppOptimizationModeEnabled() {
+        return false;
+    }
+
+    @Override
     public boolean isAppOptimizationModeLogged() {
         return false;
     }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/AppOptModeSharedPreferencesUtils.kt b/src/com/android/settings/fuelgauge/batteryusage/AppOptModeSharedPreferencesUtils.kt
index 1111bd4..afff1c9 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/AppOptModeSharedPreferencesUtils.kt
+++ b/src/com/android/settings/fuelgauge/batteryusage/AppOptModeSharedPreferencesUtils.kt
@@ -25,6 +25,7 @@
 import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action
 import com.android.settings.fuelgauge.BatteryOptimizeUtils
 import com.android.settings.fuelgauge.BatteryUtils
+import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
 
 /** A util to store and update app optimization mode expiration event data. */
 object AppOptModeSharedPreferencesUtils {
@@ -74,10 +75,14 @@
     @JvmStatic
     fun resetExpiredAppOptModeBeforeTimestamp(context: Context, queryTimestampMs: Long) =
         synchronized(appOptimizationModeLock) {
+            val forceExpireEnabled =
+                featureFactory
+                    .powerUsageFeatureProvider.isForceExpireAppOptimizationModeEnabled
             val eventsMap = getAppOptModeEventsMap(context)
             val expirationUids = ArrayList<Int>(eventsMap.size)
             for ((uid, event) in eventsMap) {
-                if (event.expirationTime > queryTimestampMs) {
+                // Not reset the mode if forceExpireEnabled is false and not expired.
+                if (!forceExpireEnabled && event.expirationTime > queryTimestampMs) {
                     continue
                 }
                 updateBatteryOptimizationMode(
diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventDao.java b/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventDao.java
deleted file mode 100644
index 2497801..0000000
--- a/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventDao.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2022 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.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.fuelgauge.batteryusage.db;
-
-import android.database.Cursor;
-
-import androidx.room.Dao;
-import androidx.room.Insert;
-import androidx.room.OnConflictStrategy;
-import androidx.room.Query;
-
-import java.util.List;
-
-/** Data access object for accessing {@link AppUsageEventEntity} in the database. */
-@Dao
-public interface AppUsageEventDao {
-
-    /** Inserts a {@link AppUsageEventEntity} data into the database. */
-    @Insert(onConflict = OnConflictStrategy.REPLACE)
-    void insert(AppUsageEventEntity event);
-
-    /** Inserts {@link AppUsageEventEntity} data into the database. */
-    @Insert(onConflict = OnConflictStrategy.REPLACE)
-    void insertAll(List<AppUsageEventEntity> events);
-
-    /** Lists all recorded data after a specific timestamp. */
-    @Query("SELECT * FROM AppUsageEventEntity WHERE timestamp > :timestamp ORDER BY timestamp DESC")
-    List<AppUsageEventEntity> getAllAfter(long timestamp);
-
-    /** Gets the {@link Cursor} of all recorded data after a specific timestamp of the users. */
-    @Query(
-            "SELECT * FROM AppUsageEventEntity WHERE timestamp >= :timestamp"
-                    + " AND userId IN (:userIds) ORDER BY timestamp ASC")
-    Cursor getAllForUsersAfter(List<Long> userIds, long timestamp);
-
-    /** Gets the {@link Cursor} of the latest timestamp of the specific user. */
-    @Query("SELECT MAX(timestamp) as timestamp FROM AppUsageEventEntity WHERE userId = :userId")
-    Cursor getLatestTimestampOfUser(long userId);
-
-    /** Deletes all recorded data before a specific timestamp. */
-    @Query("DELETE FROM AppUsageEventEntity WHERE timestamp <= :timestamp")
-    void clearAllBefore(long timestamp);
-
-    /** Deletes all recorded data after a specific timestamp. */
-    @Query("DELETE FROM AppUsageEventEntity WHERE timestamp >= :timestamp")
-    void clearAllAfter(long timestamp);
-
-    /** Clears all recorded data in the database. */
-    @Query("DELETE FROM AppUsageEventEntity")
-    void clearAll();
-}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventDao.kt b/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventDao.kt
new file mode 100644
index 0000000..fa5fbc7
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventDao.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 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.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.fuelgauge.batteryusage.db
+
+import android.database.Cursor
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+
+/** Data access object for accessing [AppUsageEventEntity] in the database. */
+@Dao
+interface AppUsageEventDao {
+    /** Inserts a [AppUsageEventEntity] data into the database. */
+    @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(event: AppUsageEventEntity)
+
+    /** Inserts [AppUsageEventEntity] data into the database. */
+    @Insert(onConflict = OnConflictStrategy.REPLACE)
+    fun insertAll(events: List<AppUsageEventEntity>)
+
+    /** Lists all recorded data after a specific timestamp. */
+    @Query("SELECT * FROM AppUsageEventEntity WHERE timestamp > :timestamp ORDER BY timestamp DESC")
+    fun getAllAfter(timestamp: Long): List<AppUsageEventEntity>
+
+    /** Gets the [Cursor] of all recorded data after a specific timestamp of the users. */
+    @Query(
+        "SELECT * FROM AppUsageEventEntity WHERE timestamp >= :timestamp" +
+            " AND userId IN (:userIds) ORDER BY timestamp ASC"
+    )
+    fun getAllForUsersAfter(userIds: List<Long>, timestamp: Long): Cursor
+
+    /** Gets the [Cursor] of the latest timestamp of the specific user. */
+    @Query("SELECT MAX(timestamp) as timestamp FROM AppUsageEventEntity WHERE userId = :userId")
+    fun getLatestTimestampOfUser(userId: Long): Cursor
+
+    /** Deletes all recorded data before a specific timestamp. */
+    @Query("DELETE FROM AppUsageEventEntity WHERE timestamp <= :timestamp")
+    fun clearAllBefore(timestamp: Long)
+
+    /** Deletes all recorded data after a specific timestamp. */
+    @Query("DELETE FROM AppUsageEventEntity WHERE timestamp >= :timestamp")
+    fun clearAllAfter(timestamp: Long)
+
+    /** Clears all recorded data in the database. */
+    @Query("DELETE FROM AppUsageEventEntity") fun clearAll()
+}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDao.java b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDao.java
deleted file mode 100644
index 19d2043..0000000
--- a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDao.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.fuelgauge.batteryusage.db;
-
-import android.database.Cursor;
-
-import androidx.room.Dao;
-import androidx.room.Insert;
-import androidx.room.OnConflictStrategy;
-import androidx.room.Query;
-
-import java.util.List;
-
-/** Data access object for accessing {@link BatteryEventEntity} in the database. */
-@Dao
-public interface BatteryEventDao {
-    /** Inserts a {@link BatteryEventEntity} data into the database. */
-    @Insert(onConflict = OnConflictStrategy.REPLACE)
-    void insert(BatteryEventEntity event);
-
-    /** Gets all recorded data. */
-    @Query("SELECT * FROM BatteryEventEntity ORDER BY timestamp DESC")
-    List<BatteryEventEntity> getAll();
-
-    /** Gets the {@link Cursor} of the last full charge time . */
-    @Query(
-            "SELECT MAX(timestamp) FROM BatteryEventEntity"
-                    + " WHERE batteryEventType = 3") // BatteryEventType.FULL_CHARGED = 3
-    Cursor getLastFullChargeTimestamp();
-
-    /** Gets the {@link Long} of the last full charge time . */
-    @Query(
-            "SELECT MAX(timestamp) FROM BatteryEventEntity"
-                    + " WHERE batteryEventType = 3") // BatteryEventType.FULL_CHARGED = 3
-    Long getLastFullChargeTimestampForLog();
-
-    /** Gets the {@link Cursor} of all recorded data after a specific timestamp. */
-    @Query(
-            "SELECT * FROM BatteryEventEntity"
-                    + " WHERE timestamp >= :timestamp AND batteryEventType IN (:batteryEventTypes)"
-                    + " ORDER BY timestamp DESC")
-    Cursor getAllAfter(long timestamp, List<Integer> batteryEventTypes);
-
-    /** Gets all recorded data after a specific timestamp for log.*/
-    @Query(
-            "SELECT * FROM BatteryEventEntity "
-                    + "WHERE timestamp >= :timestamp ORDER BY timestamp DESC")
-    List<BatteryEventEntity> getAllAfterForLog(long timestamp);
-
-    /** Deletes all recorded data before a specific timestamp. */
-    @Query("DELETE FROM BatteryEventEntity WHERE timestamp <= :timestamp")
-    void clearAllBefore(long timestamp);
-
-    /** Deletes all recorded data after a specific timestamp. */
-    @Query("DELETE FROM BatteryEventEntity WHERE timestamp >= :timestamp")
-    void clearAllAfter(long timestamp);
-
-    /** Clears all recorded data in the database. */
-    @Query("DELETE FROM BatteryEventEntity")
-    void clearAll();
-}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDao.kt b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDao.kt
new file mode 100644
index 0000000..bac97d0
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDao.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 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.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.fuelgauge.batteryusage.db
+
+import android.database.Cursor
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+
+/** Data access object for accessing [BatteryEventEntity] in the database. */
+@Dao
+interface BatteryEventDao {
+    /** Inserts a [BatteryEventEntity] data into the database. */
+    @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(event: BatteryEventEntity)
+
+    /** Gets all recorded data. */
+    @Query("SELECT * FROM BatteryEventEntity ORDER BY timestamp DESC")
+    fun getAll(): List<BatteryEventEntity>
+
+    /** Gets the [Cursor] of the last full charge time. */
+    @Query(
+        "SELECT MAX(timestamp) FROM BatteryEventEntity" +
+            " WHERE batteryEventType = 3" // BatteryEventType.FULL_CHARGED = 3
+    )
+    fun getLastFullChargeTimestamp(): Cursor
+
+    /** Gets the [Long] of the last full charge time. */
+    @Query(
+        "SELECT MAX(timestamp) FROM BatteryEventEntity" +
+            " WHERE batteryEventType = 3" // BatteryEventType.FULL_CHARGED = 3
+    )
+    fun getLastFullChargeTimestampForLog(): Long?
+
+    /** Gets the [Cursor] of all recorded data after a specific timestamp. */
+    @Query(
+        "SELECT * FROM BatteryEventEntity" +
+            " WHERE timestamp >= :timestamp AND batteryEventType IN (:batteryEventTypes)" +
+            " ORDER BY timestamp DESC"
+    )
+    fun getAllAfter(timestamp: Long, batteryEventTypes: List<Int>): Cursor
+
+    /** Gets all recorded data after a specific timestamp for log. */
+    @Query(
+        "SELECT * FROM BatteryEventEntity " +
+            "WHERE timestamp >= :timestamp ORDER BY timestamp DESC"
+    )
+    fun getAllAfterForLog(timestamp: Long): List<BatteryEventEntity>
+
+    /** Deletes all recorded data before a specific timestamp. */
+    @Query("DELETE FROM BatteryEventEntity WHERE timestamp <= :timestamp")
+    fun clearAllBefore(timestamp: Long)
+
+    /** Deletes all recorded data after a specific timestamp. */
+    @Query("DELETE FROM BatteryEventEntity WHERE timestamp >= :timestamp")
+    fun clearAllAfter(timestamp: Long)
+
+    /** Clears all recorded data in the database. */
+    @Query("DELETE FROM BatteryEventEntity") fun clearAll()
+}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDao.java b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDao.java
deleted file mode 100644
index 049251e..0000000
--- a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDao.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2022 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.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.fuelgauge.batteryusage.db;
-
-import android.database.Cursor;
-
-import androidx.room.Dao;
-import androidx.room.Insert;
-import androidx.room.OnConflictStrategy;
-import androidx.room.Query;
-
-import java.util.List;
-
-/** Data access object for accessing {@link BatteryState} in the database. */
-@Dao
-public interface BatteryStateDao {
-
-    /** Inserts a {@link BatteryState} data into the database. */
-    @Insert(onConflict = OnConflictStrategy.REPLACE)
-    void insert(BatteryState state);
-
-    /** Inserts {@link BatteryState} data into the database. */
-    @Insert(onConflict = OnConflictStrategy.REPLACE)
-    void insertAll(List<BatteryState> states);
-
-    /** Gets the {@link Cursor} of the latest record timestamp no later than the given timestamp. */
-    @Query("SELECT MAX(timestamp) FROM BatteryState WHERE timestamp <= :timestamp")
-    Cursor getLatestTimestampBefore(long timestamp);
-
-    /** Lists all recorded battery states after a specific timestamp. */
-    @Query("SELECT * FROM BatteryState WHERE timestamp >= :timestamp ORDER BY timestamp ASC")
-    Cursor getBatteryStatesAfter(long timestamp);
-
-    /** Lists all recorded data after a specific timestamp. */
-    @Query("SELECT * FROM BatteryState WHERE timestamp > :timestamp ORDER BY timestamp DESC")
-    List<BatteryState> getAllAfter(long timestamp);
-
-    /** Get the count of distinct timestamp after a specific timestamp. */
-    @Query("SELECT COUNT(DISTINCT timestamp) FROM BatteryState WHERE timestamp > :timestamp")
-    int getDistinctTimestampCount(long timestamp);
-
-    /** Lists all distinct timestamps after a specific timestamp. */
-    @Query("SELECT DISTINCT timestamp FROM BatteryState WHERE timestamp > :timestamp")
-    List<Long> getDistinctTimestamps(long timestamp);
-
-    /** Deletes all recorded data before a specific timestamp. */
-    @Query("DELETE FROM BatteryState WHERE timestamp <= :timestamp")
-    void clearAllBefore(long timestamp);
-
-    /** Deletes all recorded data after a specific timestamp. */
-    @Query("DELETE FROM BatteryState WHERE timestamp >= :timestamp")
-    void clearAllAfter(long timestamp);
-
-    /** Clears all recorded data in the database. */
-    @Query("DELETE FROM BatteryState")
-    void clearAll();
-}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDao.kt b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDao.kt
new file mode 100644
index 0000000..6d31e07
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDao.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2024 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.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.fuelgauge.batteryusage.db
+
+import android.database.Cursor
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+
+/** Data access object for accessing [BatteryState] in the database. */
+@Dao
+interface BatteryStateDao {
+    /** Inserts a [BatteryState] data into the database. */
+    @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(state: BatteryState)
+
+    /** Inserts [BatteryState] data into the database. */
+    @Insert(onConflict = OnConflictStrategy.REPLACE) fun insertAll(states: List<BatteryState>)
+
+    /** Gets the [Cursor] of the latest record timestamp no later than the given timestamp. */
+    @Query("SELECT MAX(timestamp) FROM BatteryState WHERE timestamp <= :timestamp")
+    fun getLatestTimestampBefore(timestamp: Long): Cursor
+
+    /** Lists all recorded battery states after a specific timestamp. */
+    @Query("SELECT * FROM BatteryState WHERE timestamp >= :timestamp ORDER BY timestamp ASC")
+    fun getBatteryStatesAfter(timestamp: Long): Cursor
+
+    /** Lists all recorded data after a specific timestamp. */
+    @Query("SELECT * FROM BatteryState WHERE timestamp > :timestamp ORDER BY timestamp DESC")
+    fun getAllAfter(timestamp: Long): List<BatteryState>
+
+    /** Get the count of distinct timestamp after a specific timestamp. */
+    @Query("SELECT COUNT(DISTINCT timestamp) FROM BatteryState WHERE timestamp > :timestamp")
+    fun getDistinctTimestampCount(timestamp: Long): Int
+
+    /** Lists all distinct timestamps after a specific timestamp. */
+    @Query("SELECT DISTINCT timestamp FROM BatteryState WHERE timestamp > :timestamp")
+    fun getDistinctTimestamps(timestamp: Long): List<Long>
+
+    /** Deletes all recorded data before a specific timestamp. */
+    @Query("DELETE FROM BatteryState WHERE timestamp <= :timestamp")
+    fun clearAllBefore(timestamp: Long)
+
+    /** Deletes all recorded data after a specific timestamp. */
+    @Query("DELETE FROM BatteryState WHERE timestamp >= :timestamp")
+    fun clearAllAfter(timestamp: Long)
+
+    /** Clears all recorded data in the database. */
+    @Query("DELETE FROM BatteryState") fun clearAll()
+}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryUsageSlotDao.java b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryUsageSlotDao.java
deleted file mode 100644
index d53b0cf..0000000
--- a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryUsageSlotDao.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.fuelgauge.batteryusage.db;
-
-import android.database.Cursor;
-
-import androidx.room.Dao;
-import androidx.room.Insert;
-import androidx.room.OnConflictStrategy;
-import androidx.room.Query;
-
-import java.util.List;
-
-/** Data access object for accessing {@link BatteryUsageSlotEntity} in the database. */
-@Dao
-public interface BatteryUsageSlotDao {
-    /** Inserts a {@link BatteryUsageSlotEntity} data into the database. */
-    @Insert(onConflict = OnConflictStrategy.REPLACE)
-    void insert(BatteryUsageSlotEntity event);
-
-    /** Gets all recorded data. */
-    @Query("SELECT * FROM BatteryUsageSlotEntity ORDER BY timestamp ASC")
-    List<BatteryUsageSlotEntity> getAll();
-
-    /** Gets the {@link Cursor} of all recorded data after a specific timestamp. */
-    @Query(
-            "SELECT * FROM BatteryUsageSlotEntity WHERE timestamp >= :timestamp"
-                    + " ORDER BY timestamp ASC")
-    Cursor getAllAfter(long timestamp);
-
-    /** Gets all recorded data after a specific timestamp for log.*/
-    @Query(
-            "SELECT * FROM BatteryUsageSlotEntity WHERE timestamp >= :timestamp"
-                    + " ORDER BY timestamp DESC")
-    List<BatteryUsageSlotEntity> getAllAfterForLog(long timestamp);
-
-    /** Deletes all recorded data before a specific timestamp. */
-    @Query("DELETE FROM BatteryUsageSlotEntity WHERE timestamp <= :timestamp")
-    void clearAllBefore(long timestamp);
-
-    /** Deletes all recorded data after a specific timestamp. */
-    @Query("DELETE FROM BatteryUsageSlotEntity WHERE timestamp >= :timestamp")
-    void clearAllAfter(long timestamp);
-
-    /** Clears all recorded data in the database. */
-    @Query("DELETE FROM BatteryUsageSlotEntity")
-    void clearAll();
-}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryUsageSlotDao.kt b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryUsageSlotDao.kt
new file mode 100644
index 0000000..434c61a
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryUsageSlotDao.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 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.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.fuelgauge.batteryusage.db
+
+import android.database.Cursor
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+
+/** Data access object for accessing [BatteryUsageSlotEntity] in the database. */
+@Dao
+interface BatteryUsageSlotDao {
+    /** Inserts a [BatteryUsageSlotEntity] data into the database. */
+    @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(event: BatteryUsageSlotEntity)
+
+    /** Gets all recorded data. */
+    @Query("SELECT * FROM BatteryUsageSlotEntity ORDER BY timestamp ASC")
+    fun getAll(): List<BatteryUsageSlotEntity>
+
+    /** Gets the [Cursor] of all recorded data after a specific timestamp. */
+    @Query(
+        "SELECT * FROM BatteryUsageSlotEntity WHERE timestamp >= :timestamp" +
+            " ORDER BY timestamp ASC"
+    )
+    fun getAllAfter(timestamp: Long): Cursor
+
+    /** Gets all recorded data after a specific timestamp for log. */
+    @Query(
+        "SELECT * FROM BatteryUsageSlotEntity WHERE timestamp >= :timestamp" +
+            " ORDER BY timestamp DESC"
+    )
+    fun getAllAfterForLog(timestamp: Long): List<BatteryUsageSlotEntity>
+
+    /** Deletes all recorded data before a specific timestamp. */
+    @Query("DELETE FROM BatteryUsageSlotEntity WHERE timestamp <= :timestamp")
+    fun clearAllBefore(timestamp: Long)
+
+    /** Deletes all recorded data after a specific timestamp. */
+    @Query("DELETE FROM BatteryUsageSlotEntity WHERE timestamp >= :timestamp")
+    fun clearAllAfter(timestamp: Long)
+
+    /** Clears all recorded data in the database. */
+    @Query("DELETE FROM BatteryUsageSlotEntity") fun clearAll()
+}
diff --git a/src/com/android/settings/network/telephony/DataSubscriptionRepository.kt b/src/com/android/settings/network/telephony/DataSubscriptionRepository.kt
index 99f639b..62e7e98 100644
--- a/src/com/android/settings/network/telephony/DataSubscriptionRepository.kt
+++ b/src/com/android/settings/network/telephony/DataSubscriptionRepository.kt
@@ -51,46 +51,41 @@
             )
             .map { it.getIntExtra(SUBSCRIPTION_KEY, SubscriptionManager.INVALID_SUBSCRIPTION_ID) }
             .onStart { emit(SubscriptionManager.getDefaultDataSubscriptionId()) }
+            .distinctUntilChanged()
             .conflate()
             .flowOn(Dispatchers.Default)
 
     fun activeDataSubscriptionIdFlow(): Flow<Int> =
-        telephonyManager.telephonyCallbackFlow {
-            object : TelephonyCallback(), TelephonyCallback.ActiveDataSubscriptionIdListener {
-                override fun onActiveDataSubscriptionIdChanged(subId: Int) {
-                    trySend(subId)
-                    Log.d(TAG, "activeDataSubscriptionIdFlow: $subId")
+        telephonyManager
+            .telephonyCallbackFlow {
+                object : TelephonyCallback(), TelephonyCallback.ActiveDataSubscriptionIdListener {
+                    override fun onActiveDataSubscriptionIdChanged(subId: Int) {
+                        trySend(subId)
+                        Log.d(TAG, "activeDataSubscriptionIdFlow: $subId")
+                    }
                 }
             }
-        }
+            .distinctUntilChanged()
 
     fun dataSummaryFlow(): Flow<String> =
         combine(defaultDataSubscriptionIdFlow(), activeDataSubscriptionIdFlow()) {
-                defaultSubId,
-                activeSubId ->
-                DataSubscriptionIds(defaultSubId, activeSubId)
+                defaultDataSubId,
+                activeDataSubId ->
+                getDataSummary(defaultDataSubId, activeDataSubId)
             }
-            .distinctUntilChanged()
-            .map { it.getDataSummary() }
             .conflate()
             .flowOn(Dispatchers.Default)
 
-    private data class DataSubscriptionIds(
-        val defaultSubId: Int,
-        val activeSubId: Int,
-    )
-
-    private fun DataSubscriptionIds.getDataSummary(): String {
-        val activeSubInfo = subscriptionManager.getActiveSubscriptionInfo(activeSubId) ?: return ""
+    private fun getDataSummary(defaultDataSubId: Int, activeDataSubId: Int): String {
+        if (defaultDataSubId == activeDataSubId) return getDisplayName(defaultDataSubId)
+        val activeSubInfo =
+            subscriptionManager.getActiveSubscriptionInfo(activeDataSubId)
+                ?: return getDisplayName(defaultDataSubId)
         if (!SubscriptionUtil.isSubscriptionVisible(subscriptionManager, context, activeSubInfo)) {
-            return getDisplayName(defaultSubId)
+            return getDisplayName(defaultDataSubId)
         }
-        val uniqueName = getDisplayName(activeSubId)
-        return if (activeSubId == defaultSubId) {
-            uniqueName
-        } else {
-            context.getString(R.string.mobile_data_temp_using, uniqueName)
-        }
+        // non-DDS is active
+        return context.getString(R.string.mobile_data_temp_using, getDisplayName(activeDataSubId))
     }
 
     companion object {
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingBluetoothDeviceUpdaterTest.java
index 23be208..785b1dc 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingBluetoothDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingBluetoothDeviceUpdaterTest.java
@@ -58,6 +58,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -143,6 +144,12 @@
         mDeviceUpdater.setPrefContext(mContext);
     }
 
+    @After
+    public void tearDown() {
+        ShadowThreadUtils.reset();
+        ShadowBluetoothUtils.reset();
+    }
+
     @Test
     public void onProfileConnectionStateChanged_leaDeviceConnected_flagOff_removesPref() {
         setupPreferenceMapWithDevice();
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioDialogFragmentTest.java
index 979f149..7f0c1c9 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioDialogFragmentTest.java
@@ -33,6 +33,7 @@
 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
 import com.android.settingslib.flags.Flags;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -66,16 +67,16 @@
 
     private Fragment mParent;
     private AudioSharingCallAudioDialogFragment mFragment;
-    private ShadowBluetoothAdapter mShadowBluetoothAdapter;
 
     @Before
     public void setUp() {
         ShadowAlertDialogCompat.reset();
-        mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
-        mShadowBluetoothAdapter.setEnabled(true);
-        mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
+        ShadowBluetoothAdapter shadowBluetoothAdapter =
+                Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+        shadowBluetoothAdapter.setEnabled(true);
+        shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
                 BluetoothStatusCodes.FEATURE_SUPPORTED);
-        mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
+        shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
                 BluetoothStatusCodes.FEATURE_SUPPORTED);
         mFragment = new AudioSharingCallAudioDialogFragment();
         mParent = new Fragment();
@@ -83,6 +84,11 @@
                 mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null);
     }
 
+    @After
+    public void tearDown() {
+        ShadowAlertDialogCompat.reset();
+    }
+
     @Test
     public void getMetricsCategory_correctValue() {
         assertThat(mFragment.getMetricsCategory())
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceControllerTest.java
index af817d2..c72b5a5 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceControllerTest.java
@@ -25,7 +25,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import static org.robolectric.Shadows.shadowOf;
@@ -35,6 +35,7 @@
 import android.bluetooth.BluetoothCsipSetCoordinator;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothLeBroadcastAssistant;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
 import android.bluetooth.BluetoothLeBroadcastReceiveState;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothStatusCodes;
@@ -77,6 +78,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -130,6 +132,7 @@
     @Mock private CachedBluetoothDevice mCachedDevice2;
     @Mock private CachedBluetoothDevice mCachedDevice3;
     @Mock private BluetoothLeBroadcastReceiveState mState;
+    @Mock private BluetoothLeBroadcastMetadata mSource;
     @Mock private ContentResolver mContentResolver;
     private AudioSharingCallAudioPreferenceController mController;
     @Spy private ContentObserver mContentObserver;
@@ -142,6 +145,7 @@
 
     @Before
     public void setUp() {
+        ShadowAlertDialogCompat.reset();
         mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
         mShadowBluetoothAdapter.setEnabled(true);
         mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
@@ -179,17 +183,24 @@
         when(mScreen.findPreference(PREF_KEY)).thenReturn(mPreference);
     }
 
+    @After
+    public void tearDown() {
+        ShadowAlertDialogCompat.reset();
+        ShadowThreadUtils.reset();
+        ShadowBluetoothUtils.reset();
+    }
+
     @Test
     public void onStart_flagOff_doNothing() {
         mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
         mController.onStart(mLifecycleOwner);
-        verify(mBtEventManager, times(0)).registerCallback(mController);
-        verify(mContentResolver, times(0))
+        verify(mBtEventManager, never()).registerCallback(mController);
+        verify(mContentResolver, never())
                 .registerContentObserver(
                         Settings.Secure.getUriFor(SETTINGS_KEY_FALLBACK_DEVICE_GROUP_ID),
                         false,
                         mContentObserver);
-        verify(mAssistant, times(0))
+        verify(mAssistant, never())
                 .registerServiceCallBack(any(), any(BluetoothLeBroadcastAssistant.Callback.class));
     }
 
@@ -212,9 +223,9 @@
         mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
         mController.setCallbacksRegistered(true);
         mController.onStop(mLifecycleOwner);
-        verify(mBtEventManager, times(0)).unregisterCallback(mController);
-        verify(mContentResolver, times(0)).unregisterContentObserver(mContentObserver);
-        verify(mAssistant, times(0))
+        verify(mBtEventManager, never()).unregisterCallback(mController);
+        verify(mContentResolver, never()).unregisterContentObserver(mContentObserver);
+        verify(mAssistant, never())
                 .unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
     }
 
@@ -223,9 +234,9 @@
         mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
         mController.setCallbacksRegistered(false);
         mController.onStop(mLifecycleOwner);
-        verify(mBtEventManager, times(0)).unregisterCallback(mController);
-        verify(mContentResolver, times(0)).unregisterContentObserver(mContentObserver);
-        verify(mAssistant, times(0))
+        verify(mBtEventManager, never()).unregisterCallback(mController);
+        verify(mContentResolver, never()).unregisterContentObserver(mContentObserver);
+        verify(mAssistant, never())
                 .unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
     }
 
@@ -500,4 +511,80 @@
                         AudioSharingCallAudioPreferenceController.ChangeCallAudioType.UNKNOWN
                                 .ordinal());
     }
+
+    @Test
+    public void testBluetoothLeBroadcastAssistantCallbacks_updateSummary() {
+        when(mCachedDevice1.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID1);
+        when(mCachedDevice1.getDevice()).thenReturn(mDevice1);
+        when(mCachedDevice1.getName()).thenReturn(TEST_DEVICE_NAME1);
+        when(mCacheManager.findDevice(mDevice1)).thenReturn(mCachedDevice1);
+        Settings.Secure.putInt(
+                mContentResolver, TEST_SETTINGS_KEY, BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
+        when(mBroadcast.isEnabled(any())).thenReturn(true);
+        when(mAssistant.getDevicesMatchingConnectionStates(
+                        new int[] {BluetoothProfile.STATE_CONNECTED}))
+                .thenReturn(ImmutableList.of());
+        mController.displayPreference(mScreen);
+        shadowOf(Looper.getMainLooper()).idle();
+        assertThat(mPreference.getSummary().toString()).isEmpty();
+
+        // onReceiveStateChanged will update summary
+        Settings.Secure.putInt(mContentResolver, TEST_SETTINGS_KEY, TEST_DEVICE_GROUP_ID1);
+        when(mAssistant.getDevicesMatchingConnectionStates(
+                        new int[] {BluetoothProfile.STATE_CONNECTED}))
+                .thenReturn(ImmutableList.of(mDevice1));
+        when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mState));
+        mController.mBroadcastAssistantCallback.onReceiveStateChanged(
+                mDevice1, /* sourceId= */ 1, mState);
+        shadowOf(Looper.getMainLooper()).idle();
+        assertThat(mPreference.getSummary().toString())
+                .isEqualTo(
+                        mContext.getString(
+                                R.string.audio_sharing_call_audio_description, TEST_DEVICE_NAME1));
+    }
+
+    @Test
+    public void testBluetoothLeBroadcastAssistantCallbacks_doNothing() {
+        when(mCachedDevice1.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID1);
+        when(mCachedDevice1.getDevice()).thenReturn(mDevice1);
+        when(mCachedDevice1.getName()).thenReturn(TEST_DEVICE_NAME1);
+        when(mCacheManager.findDevice(mDevice1)).thenReturn(mCachedDevice1);
+        Settings.Secure.putInt(
+                mContentResolver, TEST_SETTINGS_KEY, BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
+        when(mBroadcast.isEnabled(any())).thenReturn(true);
+        when(mAssistant.getDevicesMatchingConnectionStates(
+                        new int[] {BluetoothProfile.STATE_CONNECTED}))
+                .thenReturn(ImmutableList.of());
+        mController.displayPreference(mScreen);
+        shadowOf(Looper.getMainLooper()).idle();
+        assertThat(mPreference.getSummary().toString()).isEmpty();
+
+        Settings.Secure.putInt(mContentResolver, TEST_SETTINGS_KEY, TEST_DEVICE_GROUP_ID1);
+        when(mAssistant.getDevicesMatchingConnectionStates(
+                        new int[] {BluetoothProfile.STATE_CONNECTED}))
+                .thenReturn(ImmutableList.of(mDevice1));
+        when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mState));
+        mController.mBroadcastAssistantCallback.onSearchStarted(/* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSearchStartFailed(/* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSearchStopped(/* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSearchStopFailed(/* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSourceAdded(
+                mDevice1, /* sourceId= */ 1, /* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSourceAddFailed(
+                mDevice1, mSource, /* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSourceRemoved(
+                mDevice1, /* sourceId= */ 1, /* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSourceRemoveFailed(
+                mDevice1, /* sourceId= */ 1, /* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSourceModified(
+                mDevice1, /* sourceId= */ 1, /* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSourceModifyFailed(
+                mDevice1, /* sourceId= */ 1, /* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSourceFound(mSource);
+        mController.mBroadcastAssistantCallback.onSourceLost(/* broadcastId= */ 1);
+        shadowOf(Looper.getMainLooper()).idle();
+
+        // Above callbacks won't update summary.
+        assertThat(mPreference.getSummary().toString()).isEmpty();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCompatibilityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCompatibilityPreferenceControllerTest.java
index 19221a6..74210cd 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCompatibilityPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCompatibilityPreferenceControllerTest.java
@@ -61,6 +61,7 @@
 import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.flags.Flags;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -128,6 +129,12 @@
         when(mScreen.findPreference(PREF_KEY)).thenReturn(mPreference);
     }
 
+    @After
+    public void tearDown() {
+        ShadowThreadUtils.reset();
+        ShadowBluetoothUtils.reset();
+    }
+
     @Test
     public void onStart_flagOn_registerCallback() {
         mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingConfirmDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingConfirmDialogFragmentTest.java
index e5facc1..32f666f 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingConfirmDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingConfirmDialogFragmentTest.java
@@ -61,7 +61,7 @@
 
     @Before
     public void setUp() {
-        cleanUpDialogs();
+        ShadowAlertDialogCompat.reset();
         ShadowBluetoothAdapter shadowBluetoothAdapter =
                 Shadow.extract(BluetoothAdapter.getDefaultAdapter());
         shadowBluetoothAdapter.setEnabled(true);
@@ -77,7 +77,7 @@
 
     @After
     public void tearDown() {
-        cleanUpDialogs();
+        ShadowAlertDialogCompat.reset();
     }
 
     @Test
@@ -118,12 +118,4 @@
         shadowMainLooper().idle();
         assertThat(dialog.isShowing()).isFalse();
     }
-
-    private void cleanUpDialogs() {
-        AlertDialog latestAlertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
-        if (latestAlertDialog != null) {
-            latestAlertDialog.dismiss();
-            ShadowAlertDialogCompat.reset();
-        }
-    }
 }
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceControllerTest.java
index 14bca08..18f75ba 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceControllerTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -35,6 +36,8 @@
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothLeBroadcastAssistant;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
+import android.bluetooth.BluetoothLeBroadcastReceiveState;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothStatusCodes;
 import android.content.Context;
@@ -74,6 +77,7 @@
 
 import com.google.common.collect.ImmutableList;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -85,6 +89,8 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.shadow.api.Shadow;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.Executor;
 
 @RunWith(RobolectricTestRunner.class)
@@ -113,6 +119,8 @@
     @Mock private LocalBluetoothLeBroadcast mBroadcast;
     @Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
     @Mock private VolumeControlProfile mVolumeControl;
+    @Mock private BluetoothLeBroadcastReceiveState mState;
+    @Mock private BluetoothLeBroadcastMetadata mSource;
     @Mock private PreferenceScreen mScreen;
     @Mock private AudioSharingDialogHandler mDialogHandler;
     @Mock private DashboardFragment mFragment;
@@ -173,17 +181,22 @@
         mController.setHostFragment(mFragment);
     }
 
+    @After
+    public void tearDown() {
+        ShadowBluetoothUtils.reset();
+    }
+
     @Test
     public void onStart_flagOff_doNothing() {
         mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
         mController.onStart(mLifecycleOwner);
-        verify(mEventManager, times(0)).registerCallback(any(BluetoothCallback.class));
-        verify(mDialogHandler, times(0)).registerCallbacks(any(Executor.class));
-        verify(mAssistant, times(0))
+        verify(mEventManager, never()).registerCallback(any(BluetoothCallback.class));
+        verify(mDialogHandler, never()).registerCallbacks(any(Executor.class));
+        verify(mAssistant, never())
                 .registerServiceCallBack(
                         any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class));
-        verify(mBluetoothDeviceUpdater, times(0)).registerCallback();
-        verify(mBluetoothDeviceUpdater, times(0)).refreshPreference();
+        verify(mBluetoothDeviceUpdater, never()).registerCallback();
+        verify(mBluetoothDeviceUpdater, never()).refreshPreference();
     }
 
     @Test
@@ -203,11 +216,11 @@
     public void onStop_flagOff_doNothing() {
         mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
         mController.onStop(mLifecycleOwner);
-        verify(mEventManager, times(0)).unregisterCallback(any(BluetoothCallback.class));
-        verify(mDialogHandler, times(0)).unregisterCallbacks();
-        verify(mAssistant, times(0))
+        verify(mEventManager, never()).unregisterCallback(any(BluetoothCallback.class));
+        verify(mDialogHandler, never()).unregisterCallbacks();
+        verify(mAssistant, never())
                 .unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
-        verify(mBluetoothDeviceUpdater, times(0)).unregisterCallback();
+        verify(mBluetoothDeviceUpdater, never()).unregisterCallback();
     }
 
     @Test
@@ -227,7 +240,7 @@
         mController.displayPreference(mScreen);
         assertThat(mPreferenceGroup.isVisible()).isFalse();
         assertThat(mAudioSharingPreference.isVisible()).isFalse();
-        verify(mBluetoothDeviceUpdater, times(0)).forceUpdate();
+        verify(mBluetoothDeviceUpdater, never()).forceUpdate();
     }
 
     @Test
@@ -401,8 +414,8 @@
         doReturn(intent).when(mActivity).getIntent();
         mController.displayPreference(mScreen);
 
-        verify(mDeviceManager, times(0)).findDevice(any(BluetoothDevice.class));
-        verify(mDialogHandler, times(0))
+        verify(mDeviceManager, never()).findDevice(any(BluetoothDevice.class));
+        verify(mDialogHandler, never())
                 .handleDeviceConnected(any(CachedBluetoothDevice.class), anyBoolean());
     }
 
@@ -418,8 +431,8 @@
         when(mDevice.isConnected()).thenReturn(false);
         mController.displayPreference(mScreen);
 
-        verify(mDeviceManager, times(0)).findDevice(any(BluetoothDevice.class));
-        verify(mDialogHandler, times(0))
+        verify(mDeviceManager, never()).findDevice(any(BluetoothDevice.class));
+        verify(mDialogHandler, never())
                 .handleDeviceConnected(any(CachedBluetoothDevice.class), anyBoolean());
     }
 
@@ -436,8 +449,8 @@
         mController.setIntentHandled(true);
         mController.displayPreference(mScreen);
 
-        verify(mDeviceManager, times(0)).findDevice(any(BluetoothDevice.class));
-        verify(mDialogHandler, times(0))
+        verify(mDeviceManager, never()).findDevice(any(BluetoothDevice.class));
+        verify(mDialogHandler, never())
                 .handleDeviceConnected(any(CachedBluetoothDevice.class), anyBoolean());
     }
 
@@ -484,4 +497,56 @@
 
         verify(mDialogHandler).handleDeviceConnected(mCachedDevice, true);
     }
+
+    @Test
+    public void testBluetoothLeBroadcastAssistantCallbacks_updateGroup() {
+        // onReceiveStateChanged with unconnected state will do nothing
+        when(mState.getBisSyncState()).thenReturn(new ArrayList<>());
+        mController.mBroadcastAssistantCallback.onReceiveStateChanged(
+                mDevice, /* sourceId= */ 1, mState);
+        shadowOf(Looper.getMainLooper()).idle();
+        verify(mBluetoothDeviceUpdater, never()).forceUpdate();
+        verify(mDialogHandler, never()).closeOpeningDialogsForLeaDevice(mCachedDevice);
+
+        // onReceiveStateChanged with connected state will update group preference and handle
+        // stale dialogs
+        List<Long> bisSyncState = new ArrayList<>();
+        bisSyncState.add(1L);
+        when(mState.getBisSyncState()).thenReturn(bisSyncState);
+        mController.mBroadcastAssistantCallback.onReceiveStateChanged(
+                mDevice, /* sourceId= */ 1, mState);
+        shadowOf(Looper.getMainLooper()).idle();
+        verify(mBluetoothDeviceUpdater).forceUpdate();
+        verify(mDialogHandler).closeOpeningDialogsForLeaDevice(mCachedDevice);
+
+        // onSourceRemoved will update group preference
+        mController.mBroadcastAssistantCallback.onSourceRemoved(
+                mDevice, /* sourceId= */ 1, /* reason= */ 1);
+        shadowOf(Looper.getMainLooper()).idle();
+        verify(mBluetoothDeviceUpdater, times(2)).forceUpdate();
+    }
+
+    @Test
+    public void testBluetoothLeBroadcastAssistantCallbacks_doNothing() {
+        mController.mBroadcastAssistantCallback.onSearchStarted(/* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSearchStartFailed(/* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSearchStopped(/* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSearchStopFailed(/* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSourceAdded(
+                mDevice, /* sourceId= */ 1, /* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSourceAddFailed(
+                mDevice, mSource, /* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSourceRemoveFailed(
+                mDevice, /* sourceId= */ 1, /* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSourceModified(
+                mDevice, /* sourceId= */ 1, /* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSourceModifyFailed(
+                mDevice, /* sourceId= */ 1, /* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSourceFound(mSource);
+        mController.mBroadcastAssistantCallback.onSourceLost(/* broadcastId= */ 1);
+        shadowOf(Looper.getMainLooper()).idle();
+
+        // Above callbacks won't update group preference
+        verify(mBluetoothDeviceUpdater, never()).forceUpdate();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeControlUpdaterTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeControlUpdaterTest.java
index 7a21f02..e7cfb85 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeControlUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeControlUpdaterTest.java
@@ -22,6 +22,7 @@
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoInteractions;
@@ -41,6 +42,7 @@
 import androidx.preference.Preference;
 import androidx.test.core.app.ApplicationProvider;
 
+import com.android.settings.bluetooth.BluetoothDevicePreference;
 import com.android.settings.bluetooth.Utils;
 import com.android.settings.connecteddevice.DevicePreferenceCallback;
 import com.android.settings.testutils.FakeFeatureFactory;
@@ -56,6 +58,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -80,6 +83,9 @@
     private static final String TEST_SETTINGS_KEY =
             "bluetooth_le_broadcast_fallback_active_group_id";
     private static final int TEST_DEVICE_GROUP_ID = 1;
+    private static final int TEST_VOLUME_VALUE = 255;
+    private static final int TEST_MAX_STREAM_VALUE = 10;
+    private static final int TEST_MIN_STREAM_VALUE = 0;
 
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
 
@@ -131,6 +137,11 @@
         mDeviceUpdater.setPrefContext(mContext);
     }
 
+    @After
+    public void tearDown() {
+        ShadowBluetoothUtils.reset();
+    }
+
     @Test
     public void onProfileConnectionStateChanged_leaDeviceConnected_noSharing_removesPref() {
         setupPreferenceMapWithDevice();
@@ -249,10 +260,11 @@
                 (AudioSharingDeviceVolumePreference) captor.getValue();
 
         SeekBar seekBar = mock(SeekBar.class);
-        when(seekBar.getProgress()).thenReturn(255);
+        when(seekBar.getProgress()).thenReturn(TEST_VOLUME_VALUE);
         preference.onStopTrackingTouch(seekBar);
 
-        verify(mVolumeControl).setDeviceVolume(mBluetoothDevice, 255, true);
+        verify(mVolumeControl)
+                .setDeviceVolume(mBluetoothDevice, TEST_VOLUME_VALUE, /* isGroupOp= */ true);
         verifyNoInteractions(mAudioManager);
         verify(mFeatureFactory.metricsFeatureProvider)
                 .action(
@@ -273,14 +285,17 @@
 
         Settings.Secure.putInt(
                 mContext.getContentResolver(), TEST_SETTINGS_KEY, TEST_DEVICE_GROUP_ID);
-        when(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)).thenReturn(10);
-        when(mAudioManager.getStreamMinVolume(AudioManager.STREAM_MUSIC)).thenReturn(0);
+        when(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
+                .thenReturn(TEST_MAX_STREAM_VALUE);
+        when(mAudioManager.getStreamMinVolume(AudioManager.STREAM_MUSIC))
+                .thenReturn(TEST_MIN_STREAM_VALUE);
         SeekBar seekBar = mock(SeekBar.class);
-        when(seekBar.getProgress()).thenReturn(255);
+        when(seekBar.getProgress()).thenReturn(TEST_VOLUME_VALUE);
         preference.onStopTrackingTouch(seekBar);
 
         verifyNoInteractions(mVolumeControl);
-        verify(mAudioManager).setStreamVolume(AudioManager.STREAM_MUSIC, 10, 0);
+        verify(mAudioManager)
+                .setStreamVolume(AudioManager.STREAM_MUSIC, TEST_MAX_STREAM_VALUE, /* flags= */ 0);
         verify(mFeatureFactory.metricsFeatureProvider)
                 .action(
                         mContext,
@@ -289,6 +304,22 @@
     }
 
     @Test
+    public void testOnSeekBarChangeListener_doNothing() {
+        ArgumentCaptor<Preference> captor = ArgumentCaptor.forClass(Preference.class);
+        setupPreferenceMapWithDevice();
+
+        verify(mDevicePreferenceCallback).onDeviceAdded(captor.capture());
+        assertThat(captor.getValue() instanceof AudioSharingDeviceVolumePreference).isTrue();
+        AudioSharingDeviceVolumePreference preference =
+                (AudioSharingDeviceVolumePreference) captor.getValue();
+        SeekBar seekBar = mock(SeekBar.class);
+        preference.onProgressChanged(seekBar, TEST_VOLUME_VALUE, /* fromUser= */ false);
+
+        verifyNoInteractions(mAudioManager);
+        verifyNoInteractions(mVolumeControl);
+    }
+
+    @Test
     public void getLogTag_returnsCorrectTag() {
         assertThat(mDeviceUpdater.getLogTag()).isEqualTo(TAG);
     }
@@ -298,6 +329,35 @@
         assertThat(mDeviceUpdater.getPreferenceKey()).isEqualTo(PREF_KEY);
     }
 
+    @Test
+    public void addPreferenceWithSortType_doNothing() {
+        mDeviceUpdater.addPreference(
+                mCachedBluetoothDevice, BluetoothDevicePreference.SortType.TYPE_DEFAULT);
+        // Verify AudioSharingDeviceVolumeControlUpdater overrides BluetoothDeviceUpdater and won't
+        // trigger add preference.
+        verifyNoInteractions(mDevicePreferenceCallback);
+    }
+
+    @Test
+    public void launchDeviceDetails_doNothing() {
+        Preference preference = mock(Preference.class);
+        mDeviceUpdater.launchDeviceDetails(preference);
+        // Verify AudioSharingDeviceVolumeControlUpdater overrides BluetoothDeviceUpdater and won't
+        // launch device details
+        verifyNoInteractions(preference);
+    }
+
+    @Test
+    public void refreshPreference_doNothing() {
+        setupPreferenceMapWithDevice();
+        verify(mDevicePreferenceCallback).onDeviceAdded(any(Preference.class));
+        when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(ImmutableList.of());
+        mDeviceUpdater.refreshPreference();
+        // Verify AudioSharingDeviceVolumeControlUpdater overrides BluetoothDeviceUpdater and won't
+        // refresh preference map
+        verify(mDevicePreferenceCallback, never()).onDeviceRemoved(any(Preference.class));
+    }
+
     private void setupPreferenceMapWithDevice() {
         // Add device to preferenceMap
         when(mBroadcast.isEnabled(null)).thenReturn(true);
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupControllerTest.java
index 7c8709c..f2f0a2f 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupControllerTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -34,6 +35,8 @@
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothLeBroadcastAssistant;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
+import android.bluetooth.BluetoothLeBroadcastReceiveState;
 import android.bluetooth.BluetoothStatusCodes;
 import android.bluetooth.BluetoothVolumeControl;
 import android.content.ContentResolver;
@@ -69,6 +72,7 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -81,6 +85,8 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.shadow.api.Shadow;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.Executor;
 
 @RunWith(RobolectricTestRunner.class)
@@ -114,6 +120,8 @@
     @Mock private LocalBluetoothProfileManager mProfileManager;
     @Mock private LocalBluetoothLeBroadcast mBroadcast;
     @Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
+    @Mock private BluetoothLeBroadcastReceiveState mState;
+    @Mock private BluetoothLeBroadcastMetadata mSource;
     @Mock private AudioSharingDeviceVolumeControlUpdater mDeviceUpdater;
     @Mock private VolumeControlProfile mVolumeControl;
     @Mock private PreferenceScreen mScreen;
@@ -164,6 +172,7 @@
         doReturn(TEST_DEVICE_GROUP_ID1).when(mCachedDevice1).getGroupId();
         doReturn(mDevice1).when(mCachedDevice1).getDevice();
         doReturn(ImmutableSet.of()).when(mCachedDevice1).getMemberDevice();
+        when(mCachedDeviceManager.findDevice(mDevice1)).thenReturn(mCachedDevice1);
         when(mPreference1.getCachedDevice()).thenReturn(mCachedDevice1);
         doReturn(TEST_DEVICE_NAME2).when(mCachedDevice2).getName();
         doReturn(TEST_DEVICE_GROUP_ID2).when(mCachedDevice2).getGroupId();
@@ -181,17 +190,23 @@
         mContentObserver = mController.getSettingsObserver();
     }
 
+    @After
+    public void tearDown() {
+        ShadowThreadUtils.reset();
+        ShadowBluetoothUtils.reset();
+    }
+
     @Test
     public void onStart_flagOff_doNothing() {
         mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
         mController.onStart(mLifecycleOwner);
-        verify(mAssistant, times(0))
+        verify(mAssistant, never())
                 .registerServiceCallBack(
                         any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class));
-        verify(mDeviceUpdater, times(0)).registerCallback();
-        verify(mVolumeControl, times(0))
+        verify(mDeviceUpdater, never()).registerCallback();
+        verify(mVolumeControl, never())
                 .registerCallback(any(Executor.class), any(BluetoothVolumeControl.Callback.class));
-        verify(mContentResolver, times(0))
+        verify(mContentResolver, never())
                 .registerContentObserver(
                         Settings.Secure.getUriFor(SETTINGS_KEY_FALLBACK_DEVICE_GROUP_ID),
                         false,
@@ -216,15 +231,32 @@
     }
 
     @Test
+    public void onAudioSharingProfilesConnected_flagOn_registerCallbacks() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+        mController.onAudioSharingProfilesConnected();
+        verify(mAssistant)
+                .registerServiceCallBack(
+                        any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class));
+        verify(mDeviceUpdater).registerCallback();
+        verify(mVolumeControl)
+                .registerCallback(any(Executor.class), any(BluetoothVolumeControl.Callback.class));
+        verify(mContentResolver)
+                .registerContentObserver(
+                        Settings.Secure.getUriFor(SETTINGS_KEY_FALLBACK_DEVICE_GROUP_ID),
+                        false,
+                        mContentObserver);
+    }
+
+    @Test
     public void onStop_flagOff_doNothing() {
         mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
         mController.onStop(mLifecycleOwner);
-        verify(mAssistant, times(0))
+        verify(mAssistant, never())
                 .unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
-        verify(mDeviceUpdater, times(0)).unregisterCallback();
-        verify(mVolumeControl, times(0))
+        verify(mDeviceUpdater, never()).unregisterCallback();
+        verify(mVolumeControl, never())
                 .unregisterCallback(any(BluetoothVolumeControl.Callback.class));
-        verify(mContentResolver, times(0)).unregisterContentObserver(mContentObserver);
+        verify(mContentResolver, never()).unregisterContentObserver(mContentObserver);
     }
 
     @Test
@@ -232,12 +264,12 @@
         mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
         mController.setCallbacksRegistered(false);
         mController.onStop(mLifecycleOwner);
-        verify(mAssistant, times(0))
+        verify(mAssistant, never())
                 .unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
-        verify(mDeviceUpdater, times(0)).unregisterCallback();
-        verify(mVolumeControl, times(0))
+        verify(mDeviceUpdater, never()).unregisterCallback();
+        verify(mVolumeControl, never())
                 .unregisterCallback(any(BluetoothVolumeControl.Callback.class));
-        verify(mContentResolver, times(0)).unregisterContentObserver(mContentObserver);
+        verify(mContentResolver, never()).unregisterContentObserver(mContentObserver);
     }
 
     @Test
@@ -257,7 +289,7 @@
         mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
         mController.displayPreference(mScreen);
         assertThat(mPreferenceGroup.isVisible()).isFalse();
-        verify(mDeviceUpdater, times(0)).forceUpdate();
+        verify(mDeviceUpdater, never()).forceUpdate();
     }
 
     @Test
@@ -324,7 +356,7 @@
         mPreferenceGroup.addPreference(mPreference1);
         mController.setPreferenceGroup(mPreferenceGroup);
         mController.onDeviceRemoved(mPreference1);
-        verify(mPreferenceGroup, times(0)).setVisible(false);
+        verify(mPreferenceGroup, never()).setVisible(false);
         assertThat(mPreferenceGroup.isVisible()).isTrue();
     }
 
@@ -344,7 +376,7 @@
         mController.updateVisibility();
         shadowOf(Looper.getMainLooper()).idle();
 
-        verify(mPreferenceGroup, times(0)).setVisible(anyBoolean());
+        verify(mPreferenceGroup, never()).setVisible(anyBoolean());
     }
 
     @Test
@@ -411,4 +443,64 @@
         verify(mPreference1).setOrder(0);
         verify(mPreference2).setOrder(1);
     }
+
+    @Test
+    public void onDeviceVolumeChanged_updatePreference() {
+        when(mPreference1.getProgress()).thenReturn(TEST_MAX_VOLUME_VALUE);
+        mController.setPreferenceGroup(mPreferenceGroup);
+        mController.onDeviceAdded(mPreference1);
+        shadowOf(Looper.getMainLooper()).idle();
+        assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1);
+
+        mController.mVolumeControlCallback.onDeviceVolumeChanged(mDevice1, TEST_VOLUME_VALUE);
+        shadowOf(Looper.getMainLooper()).idle();
+
+        verify(mPreference1).setProgress(TEST_VOLUME_VALUE);
+    }
+
+    @Test
+    public void testBluetoothLeBroadcastAssistantCallbacks_updateGroup() {
+        when(mState.getBisSyncState()).thenReturn(new ArrayList<>());
+        // onReceiveStateChanged with unconnected state will do nothing
+        mController.mBroadcastAssistantCallback.onReceiveStateChanged(
+                mDevice1, /* sourceId= */ 1, mState);
+        verify(mDeviceUpdater, never()).forceUpdate();
+
+        // onReceiveStateChanged with connected state will update group preference
+        List<Long> bisSyncState = new ArrayList<>();
+        bisSyncState.add(1L);
+        when(mState.getBisSyncState()).thenReturn(bisSyncState);
+        mController.mBroadcastAssistantCallback.onReceiveStateChanged(
+                mDevice1, /* sourceId= */ 1, mState);
+        verify(mDeviceUpdater).forceUpdate();
+
+        // onSourceRemoved will update group preference
+        mController.mBroadcastAssistantCallback.onSourceRemoved(
+                mDevice1, /* sourceId= */ 1, /* reason= */ 1);
+        verify(mDeviceUpdater, times(2)).forceUpdate();
+    }
+
+    @Test
+    public void testBluetoothLeBroadcastAssistantCallbacks_doNothing() {
+        mController.mBroadcastAssistantCallback.onSearchStarted(/* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSearchStartFailed(/* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSearchStopped(/* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSearchStopFailed(/* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSourceAdded(
+                mDevice1, /* sourceId= */ 1, /* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSourceAddFailed(
+                mDevice1, mSource, /* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSourceRemoveFailed(
+                mDevice1, /* sourceId= */ 1, /* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSourceModified(
+                mDevice1, /* sourceId= */ 1, /* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSourceModifyFailed(
+                mDevice1, /* sourceId= */ 1, /* reason= */ 1);
+        mController.mBroadcastAssistantCallback.onSourceFound(mSource);
+        mController.mBroadcastAssistantCallback.onSourceLost(/* broadcastId= */ 1);
+        shadowOf(Looper.getMainLooper()).idle();
+
+        // Above callbacks won't update group preference
+        verify(mDeviceUpdater, never()).forceUpdate();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragmentTest.java
index c63a1a9..39709c1 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragmentTest.java
@@ -45,6 +45,7 @@
 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
 import com.android.settingslib.flags.Flags;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -112,6 +113,11 @@
                 mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null);
     }
 
+    @After
+    public void tearDown() {
+        ShadowAlertDialogCompat.reset();
+    }
+
     @Test
     public void getMetricsCategory_correctValue() {
         assertThat(mFragment.getMetricsCategory())
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandlerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandlerTest.java
index 633bc06..4c060d4 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandlerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandlerTest.java
@@ -19,7 +19,11 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.times;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import static org.robolectric.Shadows.shadowOf;
@@ -27,6 +31,8 @@
 import android.app.settings.SettingsEnums;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeBroadcast;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
 import android.bluetooth.BluetoothLeBroadcastReceiveState;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothStatusCodes;
@@ -38,7 +44,6 @@
 import androidx.fragment.app.DialogFragment;
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentActivity;
-import androidx.test.core.app.ApplicationProvider;
 
 import com.android.settings.bluetooth.Utils;
 import com.android.settings.testutils.FakeFeatureFactory;
@@ -57,6 +62,7 @@
 import com.google.common.collect.Iterables;
 import com.google.common.truth.Correspondence;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -71,6 +77,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 @RunWith(RobolectricTestRunner.class)
 @Config(
@@ -82,6 +89,7 @@
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
     @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
+    private static final int TEST_SOURCE_ID = 1;
     private static final String TEST_DEVICE_NAME1 = "test1";
     private static final String TEST_DEVICE_NAME2 = "test2";
     private static final String TEST_DEVICE_NAME3 = "test3";
@@ -109,15 +117,22 @@
     @Mock private BluetoothDevice mDevice3;
     @Mock private BluetoothDevice mDevice4;
     @Mock private LeAudioProfile mLeAudioProfile;
-    private Fragment mParentFragment;
     @Mock private BluetoothLeBroadcastReceiveState mState;
+    @Mock private BluetoothLeBroadcastMetadata mMetadata;
+    private Fragment mParentFragment;
     private Context mContext;
     private AudioSharingDialogHandler mHandler;
     private FakeFeatureFactory mFeatureFactory;
 
     @Before
     public void setup() {
-        mContext = ApplicationProvider.getApplicationContext();
+        mParentFragment = new Fragment();
+        FragmentController.setupFragment(
+                mParentFragment,
+                FragmentActivity.class,
+                0 /* containerViewId */,
+                null /* bundle */);
+        mContext = mParentFragment.getContext();
         ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager;
         mLocalBtManager = Utils.getLocalBtManager(mContext);
         ShadowBluetoothAdapter shadowBluetoothAdapter =
@@ -135,6 +150,7 @@
         List<Long> bisSyncState = new ArrayList<>();
         bisSyncState.add(1L);
         when(mState.getBisSyncState()).thenReturn(bisSyncState);
+        when(mState.getSourceId()).thenReturn(TEST_SOURCE_ID);
         when(mLeAudioProfile.isEnabled(any())).thenReturn(true);
         when(mCachedDevice1.getName()).thenReturn(TEST_DEVICE_NAME1);
         when(mCachedDevice1.getDevice()).thenReturn(mDevice1);
@@ -158,15 +174,14 @@
         when(mCacheManager.findDevice(mDevice2)).thenReturn(mCachedDevice2);
         when(mCacheManager.findDevice(mDevice3)).thenReturn(mCachedDevice3);
         when(mCacheManager.findDevice(mDevice4)).thenReturn(mCachedDevice4);
-        mParentFragment = new Fragment();
-        FragmentController.setupFragment(
-                mParentFragment,
-                FragmentActivity.class,
-                0 /* containerViewId */,
-                null /* bundle */);
         mHandler = new AudioSharingDialogHandler(mContext, mParentFragment);
     }
 
+    @After
+    public void tearDown() {
+        ShadowBluetoothUtils.reset();
+    }
+
     @Test
     public void handleUserTriggeredNonLeaDeviceConnected_noSharing_setActive() {
         setUpBroadcast(false);
@@ -269,6 +284,10 @@
                                 AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT
                                         .ordinal(),
                                 2));
+        AudioSharingJoinDialogFragment.DialogEventListener listener = fragment.getListener();
+        assertThat(listener).isNotNull();
+        listener.onShareClick();
+        verify(mBroadcast).startPrivateBroadcast();
     }
 
     @Test
@@ -309,6 +328,10 @@
                                 AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT
                                         .ordinal(),
                                 1));
+        AudioSharingJoinDialogFragment.DialogEventListener listener = fragment.getListener();
+        assertThat(listener).isNotNull();
+        listener.onShareClick();
+        verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false);
     }
 
     @Test
@@ -351,6 +374,11 @@
                                 AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT
                                         .ordinal(),
                                 1));
+        AudioSharingDisconnectDialogFragment.DialogEventListener listener = fragment.getListener();
+        assertThat(listener).isNotNull();
+        listener.onItemClick(AudioSharingUtils.buildAudioSharingDeviceItem(mCachedDevice3));
+        verify(mAssistant).removeSource(mDevice3, TEST_SOURCE_ID);
+        verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false);
     }
 
     @Test
@@ -363,7 +391,7 @@
         when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of());
         mHandler.handleDeviceConnected(mCachedDevice2, /* userTriggered= */ false);
         shadowOf(Looper.getMainLooper()).idle();
-        verify(mCachedDevice2, times(0)).setActive();
+        verify(mCachedDevice2, never()).setActive();
     }
 
     @Test
@@ -415,7 +443,7 @@
         when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of());
         mHandler.handleDeviceConnected(mCachedDevice1, /* userTriggered= */ false);
         shadowOf(Looper.getMainLooper()).idle();
-        verify(mCachedDevice1, times(0)).setActive();
+        verify(mCachedDevice1, never()).setActive();
     }
 
     @Test
@@ -455,6 +483,10 @@
                                 AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT
                                         .ordinal(),
                                 2));
+        AudioSharingJoinDialogFragment.DialogEventListener listener = fragment.getListener();
+        assertThat(listener).isNotNull();
+        listener.onShareClick();
+        verify(mBroadcast).startPrivateBroadcast();
     }
 
     @Test
@@ -495,6 +527,10 @@
                                 AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT
                                         .ordinal(),
                                 1));
+        AudioSharingJoinDialogFragment.DialogEventListener listener = fragment.getListener();
+        assertThat(listener).isNotNull();
+        listener.onShareClick();
+        verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false);
     }
 
     @Test
@@ -536,6 +572,11 @@
                                 AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT
                                         .ordinal(),
                                 1));
+        AudioSharingDisconnectDialogFragment.DialogEventListener listener = fragment.getListener();
+        assertThat(listener).isNotNull();
+        listener.onItemClick(AudioSharingUtils.buildAudioSharingDeviceItem(mCachedDevice3));
+        verify(mAssistant).removeSource(mDevice3, TEST_SOURCE_ID);
+        verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false);
     }
 
     @Test
@@ -588,7 +629,106 @@
                         SettingsEnums.DIALOG_STOP_AUDIO_SHARING);
     }
 
+    @Test
+    public void closeOpeningDialogsOtherThan() {
+        setUpBroadcast(true);
+        ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice3);
+        when(mAssistant.getDevicesMatchingConnectionStates(
+                        new int[] {BluetoothProfile.STATE_CONNECTED}))
+                .thenReturn(deviceList);
+        when(mAssistant.getAllSources(mDevice3)).thenReturn(ImmutableList.of(mState));
+        mHandler.handleDeviceConnected(mCachedDevice2, /* userTriggered= */ true);
+        shadowOf(Looper.getMainLooper()).idle();
+        List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
+        assertThat(childFragments)
+                .comparingElementsUsing(TAG_EQUALS)
+                .containsExactly(AudioSharingStopDialogFragment.tag());
+
+        deviceList = ImmutableList.of(mDevice1, mDevice3);
+        when(mAssistant.getDevicesMatchingConnectionStates(
+                        new int[] {BluetoothProfile.STATE_CONNECTED}))
+                .thenReturn(deviceList);
+        when(mAssistant.getAllSources(mDevice1)).thenReturn(ImmutableList.of());
+        mHandler.handleDeviceConnected(mCachedDevice1, /* userTriggered= */ false);
+        shadowOf(Looper.getMainLooper()).idle();
+        childFragments = mParentFragment.getChildFragmentManager().getFragments();
+        assertThat(childFragments)
+                .comparingElementsUsing(TAG_EQUALS)
+                .containsExactly(AudioSharingJoinDialogFragment.tag());
+
+        verify(mFeatureFactory.metricsFeatureProvider)
+                .action(
+                        mContext,
+                        SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_AUTO_DISMISS,
+                        SettingsEnums.DIALOG_STOP_AUDIO_SHARING);
+    }
+
+    @Test
+    public void registerCallbacks() {
+        Executor executor = mock(Executor.class);
+        mHandler.registerCallbacks(executor);
+        verify(mBroadcast)
+                .registerServiceCallBack(eq(executor), any(BluetoothLeBroadcast.Callback.class));
+    }
+
+    @Test
+    public void unregisterCallbacks() {
+        mHandler.unregisterCallbacks();
+        verify(mBroadcast).unregisterServiceCallBack(any(BluetoothLeBroadcast.Callback.class));
+    }
+
+    @Test
+    public void onPlaybackStarted_addSource() {
+        setUpBroadcast(false);
+        ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice1, mDevice3);
+        when(mAssistant.getDevicesMatchingConnectionStates(
+                        new int[] {BluetoothProfile.STATE_CONNECTED}))
+                .thenReturn(deviceList);
+        when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of());
+        mHandler.handleDeviceConnected(mCachedDevice1, /* userTriggered= */ true);
+        shadowOf(Looper.getMainLooper()).idle();
+        List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
+        assertThat(childFragments)
+                .comparingElementsUsing(TAG_EQUALS)
+                .containsExactly(AudioSharingJoinDialogFragment.tag());
+        AudioSharingJoinDialogFragment fragment =
+                (AudioSharingJoinDialogFragment) Iterables.getOnlyElement(childFragments);
+        AudioSharingJoinDialogFragment.DialogEventListener listener = fragment.getListener();
+        assertThat(listener).isNotNull();
+        listener.onShareClick();
+
+        setUpBroadcast(true);
+        mHandler.mBroadcastCallback.onPlaybackStarted(/* reason= */ 1, /* broadcastId= */ 1);
+        shadowOf(Looper.getMainLooper()).idle();
+
+        verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false);
+        verify(mAssistant).addSource(mDevice3, mMetadata, /* isGroupOp= */ false);
+    }
+
+    @Test
+    public void testBluetoothLeBroadcastCallbacks_doNothing() {
+        mHandler.mBroadcastCallback.onBroadcastStarted(/* reason= */ 1, /* broadcastId= */ 1);
+        mHandler.mBroadcastCallback.onBroadcastStopped(/* reason= */ 1, /* broadcastId= */ 1);
+        mHandler.mBroadcastCallback.onBroadcastMetadataChanged(/* reason= */ 1, mMetadata);
+        mHandler.mBroadcastCallback.onBroadcastUpdated(/* reason= */ 1, /* broadcastId= */ 1);
+        mHandler.mBroadcastCallback.onPlaybackStarted(/* reason= */ 1, /* broadcastId= */ 1);
+        mHandler.mBroadcastCallback.onPlaybackStopped(/* reason= */ 1, /* broadcastId= */ 1);
+        mHandler.mBroadcastCallback.onBroadcastStartFailed(/* reason= */ 1);
+        mHandler.mBroadcastCallback.onBroadcastStopFailed(/* reason= */ 1);
+        mHandler.mBroadcastCallback.onBroadcastUpdateFailed(/* reason= */ 1, /* broadcastId= */ 1);
+
+        verify(mAssistant, never())
+                .addSource(
+                        any(BluetoothDevice.class),
+                        any(BluetoothLeBroadcastMetadata.class),
+                        anyBoolean());
+        verify(mAssistant, never()).removeSource(any(BluetoothDevice.class), anyInt());
+    }
+
     private void setUpBroadcast(boolean isBroadcasting) {
         when(mBroadcast.isEnabled(any())).thenReturn(isBroadcasting);
+        if (isBroadcasting) {
+            when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
+        }
     }
 }
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragmentTest.java
index 481c78d..6b984af 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragmentTest.java
@@ -47,6 +47,7 @@
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.flags.Flags;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -104,11 +105,7 @@
 
     @Before
     public void setUp() {
-        AlertDialog latestAlertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
-        if (latestAlertDialog != null) {
-            latestAlertDialog.dismiss();
-            ShadowAlertDialogCompat.reset();
-        }
+        ShadowAlertDialogCompat.reset();
         ShadowBluetoothAdapter shadowBluetoothAdapter =
                 Shadow.extract(BluetoothAdapter.getDefaultAdapter());
         shadowBluetoothAdapter.setEnabled(true);
@@ -131,6 +128,11 @@
                 mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null);
     }
 
+    @After
+    public void tearDown() {
+        ShadowAlertDialogCompat.reset();
+    }
+
     @Test
     public void getMetricsCategory_correctValue() {
         assertThat(mFragment.getMetricsCategory())
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragmentTest.java
index c7b21ad..13ea965 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragmentTest.java
@@ -48,6 +48,7 @@
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
 import com.android.settingslib.flags.Flags;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -104,11 +105,7 @@
 
     @Before
     public void setUp() {
-        AlertDialog latestAlertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
-        if (latestAlertDialog != null) {
-            latestAlertDialog.dismiss();
-            ShadowAlertDialogCompat.reset();
-        }
+        ShadowAlertDialogCompat.reset();
         ShadowBluetoothAdapter shadowBluetoothAdapter =
                 Shadow.extract(BluetoothAdapter.getDefaultAdapter());
         shadowBluetoothAdapter.setEnabled(true);
@@ -129,6 +126,12 @@
                 mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null);
     }
 
+    @After
+    public void tearDown() {
+        ShadowAlertDialogCompat.reset();
+        ShadowBluetoothUtils.reset();
+    }
+
     @Test
     public void getMetricsCategory_notInSharing_correctValue() {
         when(mBroadcast.isEnabled(null)).thenReturn(false);
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPreferenceControllerTest.java
index 046a4ce..d9c883e 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPreferenceControllerTest.java
@@ -56,6 +56,7 @@
 import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.flags.Flags;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -114,6 +115,12 @@
         when(mScreen.findPreference(PREF_KEY)).thenReturn(mPreference);
     }
 
+    @After
+    public void tearDown() {
+        ShadowBluetoothUtils.reset();
+        ShadowThreadUtils.reset();
+    }
+
     @Test
     public void onStart_flagOn_registerCallback() {
         mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiverTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiverTest.java
index d542b98..deed229 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiverTest.java
@@ -50,6 +50,7 @@
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
 import com.android.settingslib.flags.Flags;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -103,6 +104,11 @@
         mFeatureFactory = FakeFeatureFactory.setupForTest();
     }
 
+    @After
+    public void tearDown() {
+        ShadowBluetoothUtils.reset();
+    }
+
     @Test
     public void broadcastReceiver_isRegistered() {
         List<ShadowApplication.Wrapper> registeredReceivers =
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragmentTest.java
index 7d46a18..b6babfb 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragmentTest.java
@@ -48,6 +48,7 @@
 
 import com.google.common.collect.ImmutableList;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -101,11 +102,7 @@
 
     @Before
     public void setUp() {
-        AlertDialog latestAlertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
-        if (latestAlertDialog != null) {
-            latestAlertDialog.dismiss();
-            ShadowAlertDialogCompat.reset();
-        }
+        ShadowAlertDialogCompat.reset();
         ShadowBluetoothAdapter shadowBluetoothAdapter =
                 Shadow.extract(BluetoothAdapter.getDefaultAdapter());
         shadowBluetoothAdapter.setEnabled(true);
@@ -126,6 +123,11 @@
                 mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null);
     }
 
+    @After
+    public void tearDown() {
+        ShadowAlertDialogCompat.reset();
+    }
+
     @Test
     public void getMetricsCategory_correctValue() {
         assertThat(mFragment.getMetricsCategory())
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java
index 8f85feb8..45d99c7 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java
@@ -22,11 +22,13 @@
 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.eq;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import static org.robolectric.Shadows.shadowOf;
@@ -36,6 +38,7 @@
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothLeBroadcast;
 import android.bluetooth.BluetoothLeBroadcastAssistant;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothStatusCodes;
 import android.content.BroadcastReceiver;
@@ -75,6 +78,7 @@
 import com.google.common.collect.Iterables;
 import com.google.common.truth.Correspondence;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -181,7 +185,7 @@
         doNothing()
                 .when(mAssistant)
                 .unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
-        mSwitchBar = new SettingsMainSwitchBar(mContext);
+        mSwitchBar = spy(new SettingsMainSwitchBar(mContext));
         mSwitchBar.setDisabledByAdmin(mock(RestrictedLockUtils.EnforcedAdmin.class));
         mOnAudioSharingStateChanged = false;
         mOnAudioSharingServiceConnected = false;
@@ -207,6 +211,12 @@
         mController.init(mParentFragment);
     }
 
+    @After
+    public void tearDown() {
+        ShadowBluetoothUtils.reset();
+        ShadowThreadUtils.reset();
+    }
+
     @Test
     public void bluetoothOff_switchDisabled() {
         mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
@@ -258,15 +268,15 @@
     public void onStart_flagOff_doNothing() {
         mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
         mController.onStart(mLifecycleOwner);
-        verify(mContext, times(0))
+        verify(mContext, never())
                 .registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class), anyInt());
-        verify(mBroadcast, times(0))
+        verify(mBroadcast, never())
                 .registerServiceCallBack(
                         any(Executor.class), any(BluetoothLeBroadcast.Callback.class));
-        verify(mAssistant, times(0))
+        verify(mAssistant, never())
                 .registerServiceCallBack(
                         any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class));
-        verify(mBtProfileManager, times(0)).addServiceListener(mController);
+        verify(mBtProfileManager, never()).addServiceListener(mController);
     }
 
     @Test
@@ -279,10 +289,10 @@
 
         verify(mContext)
                 .registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class), anyInt());
-        verify(mBroadcast, times(0))
+        verify(mBroadcast, never())
                 .registerServiceCallBack(
                         any(Executor.class), any(BluetoothLeBroadcast.Callback.class));
-        verify(mAssistant, times(0))
+        verify(mAssistant, never())
                 .registerServiceCallBack(
                         any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class));
         verify(mBtProfileManager).addServiceListener(mController);
@@ -305,7 +315,7 @@
         verify(mAssistant)
                 .registerServiceCallBack(
                         any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class));
-        verify(mBtProfileManager, times(0)).addServiceListener(mController);
+        verify(mBtProfileManager, never()).addServiceListener(mController);
         assertThat(mSwitchBar.isChecked()).isTrue();
         assertThat(mSwitchBar.isEnabled()).isTrue();
     }
@@ -314,12 +324,12 @@
     public void onStop_flagOff_doNothing() {
         mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
         mController.onStop(mLifecycleOwner);
-        verify(mContext, times(0)).unregisterReceiver(any(BroadcastReceiver.class));
-        verify(mBroadcast, times(0))
+        verify(mContext, never()).unregisterReceiver(any(BroadcastReceiver.class));
+        verify(mBroadcast, never())
                 .unregisterServiceCallBack(any(BluetoothLeBroadcast.Callback.class));
-        verify(mAssistant, times(0))
+        verify(mAssistant, never())
                 .unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
-        verify(mBtProfileManager, times(0)).removeServiceListener(mController);
+        verify(mBtProfileManager, never()).removeServiceListener(mController);
     }
 
     @Test
@@ -331,9 +341,9 @@
 
         verify(mContext).unregisterReceiver(any(BroadcastReceiver.class));
         verify(mBtProfileManager).removeServiceListener(mController);
-        verify(mBroadcast, times(0))
+        verify(mBroadcast, never())
                 .unregisterServiceCallBack(any(BluetoothLeBroadcast.Callback.class));
-        verify(mAssistant, times(0))
+        verify(mAssistant, never())
                 .unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
     }
 
@@ -358,7 +368,7 @@
         when(mBtnView.isEnabled()).thenReturn(true);
         when(mBroadcast.isEnabled(null)).thenReturn(true);
         mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
-        verify(mBroadcast, times(0)).startPrivateBroadcast();
+        verify(mBroadcast, never()).startPrivateBroadcast();
     }
 
     @Test
@@ -372,7 +382,7 @@
         doNothing().when(mBroadcast).startPrivateBroadcast();
         mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
         assertThat(mSwitchBar.isChecked()).isFalse();
-        verify(mBroadcast, times(0)).startPrivateBroadcast();
+        verify(mBroadcast, never()).startPrivateBroadcast();
     }
 
     @Test
@@ -407,7 +417,7 @@
         when(mBroadcast.isEnabled(null)).thenReturn(false);
         when(mBroadcast.getLatestBroadcastId()).thenReturn(1);
         mController.onCheckedChanged(mBtnView, /* isChecked= */ false);
-        verify(mBroadcast, times(0)).stopBroadcast(anyInt());
+        verify(mBroadcast, never()).stopBroadcast(anyInt());
     }
 
     @Test
@@ -465,4 +475,46 @@
                                         .ordinal(),
                                 1));
     }
+
+    @Test
+    public void testBluetoothLeBroadcastCallbacks_updateSwitch() {
+        mOnAudioSharingStateChanged = false;
+        mSwitchBar.setChecked(false);
+        when(mBroadcast.isEnabled(any())).thenReturn(false);
+        mController.mBroadcastCallback.onBroadcastStartFailed(/* reason= */ 1);
+        shadowOf(Looper.getMainLooper()).idle();
+        assertThat(mSwitchBar.isChecked()).isFalse();
+        assertThat(mOnAudioSharingStateChanged).isFalse();
+
+        when(mBroadcast.isEnabled(any())).thenReturn(true);
+        mController.mBroadcastCallback.onBroadcastStarted(/* reason= */ 1, /* broadcastId= */ 1);
+        shadowOf(Looper.getMainLooper()).idle();
+        assertThat(mSwitchBar.isChecked()).isTrue();
+        assertThat(mOnAudioSharingStateChanged).isTrue();
+
+        mOnAudioSharingStateChanged = false;
+        mController.mBroadcastCallback.onBroadcastStopFailed(/* reason= */ 1);
+        shadowOf(Looper.getMainLooper()).idle();
+        assertThat(mSwitchBar.isChecked()).isTrue();
+        assertThat(mOnAudioSharingStateChanged).isFalse();
+
+        when(mBroadcast.isEnabled(any())).thenReturn(false);
+        mController.mBroadcastCallback.onBroadcastStopped(/* reason= */ 1, /* broadcastId= */ 1);
+        shadowOf(Looper.getMainLooper()).idle();
+        assertThat(mSwitchBar.isChecked()).isFalse();
+        assertThat(mOnAudioSharingStateChanged).isTrue();
+    }
+
+    @Test
+    public void testBluetoothLeBroadcastCallbacks_doNothing() {
+        BluetoothLeBroadcastMetadata metadata = mock(BluetoothLeBroadcastMetadata.class);
+        mController.mBroadcastCallback.onBroadcastMetadataChanged(/* reason= */ 1, metadata);
+        mController.mBroadcastCallback.onBroadcastUpdated(/* reason= */ 1, /* broadcastId= */ 1);
+        mController.mBroadcastCallback.onPlaybackStarted(/* reason= */ 1, /* broadcastId= */ 1);
+        mController.mBroadcastCallback.onPlaybackStopped(/* reason= */ 1, /* broadcastId= */ 1);
+        mController.mBroadcastCallback.onBroadcastUpdateFailed(
+                /* reason= */ 1, /* broadcastId= */ 1);
+        verify(mSwitchBar, never()).setChecked(anyBoolean());
+        assertThat(mOnAudioSharingStateChanged).isFalse();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/StreamSettingsCategoryControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/StreamSettingsCategoryControllerTest.java
index 50dde0f..ee92b3b 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/StreamSettingsCategoryControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/StreamSettingsCategoryControllerTest.java
@@ -54,6 +54,7 @@
 import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.flags.Flags;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -117,6 +118,11 @@
         when(mScreen.findPreference(KEY)).thenReturn(mPreference);
     }
 
+    @After
+    public void tearDown() {
+        ShadowBluetoothUtils.reset();
+    }
+
     @Test
     public void bluetoothOff_updateVisibility() {
         mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImplTest.java
index 354afd0..6b32ff5 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImplTest.java
@@ -78,6 +78,11 @@
     }
 
     @Test
+    public void isForceExpireAppOptimizationModeEnabled_returnFalse() {
+        assertThat(mPowerFeatureProvider.isForceExpireAppOptimizationModeEnabled()).isFalse();
+    }
+
+    @Test
     public void isAppOptimizationModeLogged_returnFalse() {
         assertThat(mPowerFeatureProvider.isAppOptimizationModeLogged()).isFalse();
     }
diff --git a/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt
index 4dbe050..3621948 100644
--- a/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt
@@ -58,7 +58,6 @@
     private val apnEnable = context.resources.getString(R.string.carrier_enabled)
     private val apnProtocolOptions =
         context.resources.getStringArray(R.array.apn_protocol_entries).toList()
-    private val networkType = context.resources.getString(R.string.network_type)
     private val passwordTitle = context.resources.getString(R.string.apn_password)
     private val apnInit = ApnData(
         name = apnName,
@@ -168,48 +167,6 @@
     }
 
     @Test
-    fun network_type_displayed() {
-        composeTestRule.setContent {
-            ApnPage(apnInit, remember { apnData }, uri)
-        }
-        composeTestRule.onRoot().onChild().onChildAt(0)
-            .performScrollToNode(hasText(networkType, true))
-        composeTestRule.onNodeWithText(networkType, true).assertIsDisplayed()
-    }
-
-    @Test
-    fun network_type_changed() {
-        composeTestRule.setContent {
-            ApnPage(apnInit, remember { apnData }, uri)
-        }
-        composeTestRule.onRoot().onChild().onChildAt(0)
-            .performScrollToNode(hasText(networkType, true))
-        composeTestRule.onNodeWithText(networkType, true).performClick()
-        composeTestRule.onNodeWithText(NETWORK_TYPE_LTE, true).performClick()
-        composeTestRule.onNode(hasText(NETWORK_TYPE_UNSPECIFIED) and isFocused(), true)
-            .assertDoesNotExist()
-        composeTestRule.onNode(hasText(NETWORK_TYPE_LTE) and isFocused(), true).assertIsDisplayed()
-    }
-
-    @Test
-    fun network_type_changed_back2Default() {
-        composeTestRule.setContent {
-            ApnPage(apnInit, remember { apnData }, uri)
-        }
-        composeTestRule.onRoot().onChild().onChildAt(0)
-            .performScrollToNode(hasText(networkType, true))
-        composeTestRule.onNodeWithText(networkType, true).performClick()
-        composeTestRule.onNodeWithText(NETWORK_TYPE_LTE, true).performClick()
-        composeTestRule.onNode(hasText(NETWORK_TYPE_UNSPECIFIED) and isFocused(), true)
-            .assertDoesNotExist()
-        composeTestRule.onNode(hasText(NETWORK_TYPE_LTE) and isFocused(), true).assertIsDisplayed()
-        composeTestRule.onAllNodesWithText(NETWORK_TYPE_LTE, true).onLast().performClick()
-        composeTestRule.onNode(hasText(NETWORK_TYPE_UNSPECIFIED) and isFocused(), true)
-            .assertIsDisplayed()
-        composeTestRule.onNode(hasText(NETWORK_TYPE_LTE) and isFocused(), true).assertDoesNotExist()
-    }
-
-    @Test
     fun password_displayed() {
         composeTestRule.setContent {
             ApnPage(apnInit, remember { apnData }, uri)
@@ -218,9 +175,4 @@
             .performScrollToNode(hasText(passwordTitle, true))
         composeTestRule.onNodeWithText(passwordTitle, true).assertIsDisplayed()
     }
-
-    private companion object {
-        const val NETWORK_TYPE_UNSPECIFIED = "Unspecified"
-        const val NETWORK_TYPE_LTE = "LTE"
-    }
 }
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/network/apn/ApnNetworkTypeCheckBoxTest.kt b/tests/spa_unit/src/com/android/settings/network/apn/ApnNetworkTypeCheckBoxTest.kt
new file mode 100644
index 0000000..5a8e40a
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/apn/ApnNetworkTypeCheckBoxTest.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2024 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.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.apn
+
+import android.content.Context
+import android.telephony.TelephonyManager
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.hasText
+import androidx.compose.ui.test.isToggleable
+import androidx.compose.ui.test.junit4.ComposeTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.android.settingslib.spa.testutils.hasRole
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class ApnNetworkTypeCheckBoxTest {
+    @get:Rule val composeTestRule = createComposeRule()
+
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    private val apnData = ApnData()
+
+    @Test
+    fun networkType_displayed() {
+        composeTestRule.setContent { ApnNetworkTypeCheckBox(apnData) {} }
+
+        composeTestRule.onNodeWithText(context.getString(R.string.network_type)).assertIsDisplayed()
+    }
+
+    @Test
+    fun networkType_changed() {
+        composeTestRule.setContent { ApnNetworkTypeCheckBox(apnData) {} }
+
+        composeTestRule.onNodeWithText(context.getString(R.string.network_type)).performClick()
+        composeTestRule.onNode(hasText(LTE_TEXT) and isToggleable()).performClick()
+
+        composeTestRule
+            .onDropdownListWithText(context.getString(R.string.network_type_unspecified))
+            .assertDoesNotExist()
+        composeTestRule.onDropdownListWithText(LTE_TEXT).assertIsDisplayed()
+    }
+
+    @Test
+    fun networkType_changed_back2Default() {
+        composeTestRule.setContent { ApnNetworkTypeCheckBox(apnData) {} }
+
+        composeTestRule.onNodeWithText(context.getString(R.string.network_type)).performClick()
+        composeTestRule.onNode(hasText(LTE_TEXT) and isToggleable()).performClick()
+        composeTestRule.onNode(hasText(LTE_TEXT) and isToggleable()).performClick()
+
+        composeTestRule
+            .onDropdownListWithText(context.getString(R.string.network_type_unspecified))
+            .assertIsDisplayed()
+        composeTestRule.onDropdownListWithText(LTE_TEXT).assertDoesNotExist()
+    }
+
+    private fun ComposeTestRule.onDropdownListWithText(text: String) =
+        onNode(hasText(text) and hasRole(Role.DropdownList))
+
+    private companion object {
+        val LTE_TEXT = TelephonyManager.getNetworkTypeName(TelephonyManager.NETWORK_TYPE_LTE)
+    }
+}
diff --git a/tests/unit/src/com/android/settings/fuelgauge/batteryusage/AppOptModeSharedPreferencesUtilsTest.kt b/tests/unit/src/com/android/settings/fuelgauge/batteryusage/AppOptModeSharedPreferencesUtilsTest.kt
index 76ae491..94f082d 100644
--- a/tests/unit/src/com/android/settings/fuelgauge/batteryusage/AppOptModeSharedPreferencesUtilsTest.kt
+++ b/tests/unit/src/com/android/settings/fuelgauge/batteryusage/AppOptModeSharedPreferencesUtilsTest.kt
@@ -26,6 +26,7 @@
 import com.android.settings.fuelgauge.BatteryOptimizeUtils.MODE_UNKNOWN
 import com.android.settings.fuelgauge.BatteryOptimizeUtils.MODE_UNRESTRICTED
 import com.android.settings.fuelgauge.batteryusage.AppOptModeSharedPreferencesUtils.UNLIMITED_EXPIRE_TIME
+import com.android.settings.testutils.FakeFeatureFactory
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
 import org.junit.Before
@@ -51,9 +52,14 @@
     @Spy
     private var testBatteryOptimizeUtils = spy(BatteryOptimizeUtils(context, UID, PACKAGE_NAME))
 
+    private lateinit var featureFactory: FakeFeatureFactory
+
     @Before
     fun setup() {
         AppOptModeSharedPreferencesUtils.clearAll(context)
+        featureFactory = FakeFeatureFactory.setupForTest()
+        whenever(featureFactory.powerUsageFeatureProvider.isForceExpireAppOptimizationModeEnabled)
+            .thenReturn(false)
     }
 
     @After
@@ -127,6 +133,20 @@
     }
 
     @Test
+    fun resetExpiredAppOptModeBeforeTimestamp_forceExpiredData_verifyEmptyList() {
+        whenever(featureFactory.powerUsageFeatureProvider.isForceExpireAppOptimizationModeEnabled)
+            .thenReturn(true)
+        insertAppOptModeEventForTest(expirationTime = 1000L)
+
+        AppOptModeSharedPreferencesUtils.resetExpiredAppOptModeBeforeTimestamp(
+            context,
+            queryTimestampMs = 999L
+        )
+
+        assertThat(AppOptModeSharedPreferencesUtils.getAllEvents(context)).isEmpty()
+    }
+
+    @Test
     fun resetExpiredAppOptModeBeforeTimestamp_noExpiredData_verifyData() {
         insertAppOptModeEventForTest(expirationTime = 1000L)
 
diff --git a/tests/unit/src/com/android/settings/network/NetworkProviderCallsSmsFragmentTest.java b/tests/unit/src/com/android/settings/network/NetworkProviderCallsSmsFragmentTest.java
index cc9e116..fa61820 100644
--- a/tests/unit/src/com/android/settings/network/NetworkProviderCallsSmsFragmentTest.java
+++ b/tests/unit/src/com/android/settings/network/NetworkProviderCallsSmsFragmentTest.java
@@ -22,6 +22,7 @@
 
 import android.content.Context;
 import android.os.Looper;
+import android.platform.test.annotations.DisableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 
 import androidx.test.annotation.UiThreadTest;
@@ -54,11 +55,11 @@
         if (Looper.myLooper() == null) {
             Looper.prepare();
         }
-        mSetFlagsRule.disableFlags(Flags.FLAG_IS_DUAL_SIM_ONBOARDING_ENABLED);
     }
 
     @Test
     @UiThreadTest
+    @DisableFlags(Flags.FLAG_IS_DUAL_SIM_ONBOARDING_ENABLED)
     public void isPageSearchEnabled_shouldIncludeFragmentXml() {
         mPreferenceKeyList =
                 NetworkProviderCallsSmsFragment.SEARCH_INDEX_DATA_PROVIDER