Merge "Fix WifiQrCode scheme inconsistency issue" into main
diff --git a/res/drawable/battery_tips_all_rounded_bg.xml b/res/drawable/battery_tips_all_rounded_bg.xml
deleted file mode 100644
index ba164b9..0000000
--- a/res/drawable/battery_tips_all_rounded_bg.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  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.
-  -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-    <solid android:color="@color/settingslib_materialColorSurfaceBright" />
-    <corners android:radius="@dimen/battery_tips_card_corner_radius_normal" />
-</shape>
\ No newline at end of file
diff --git a/res/drawable/battery_tips_all_rounded_bg_ripple.xml b/res/drawable/battery_tips_all_rounded_bg_ripple.xml
deleted file mode 100644
index 3180570..0000000
--- a/res/drawable/battery_tips_all_rounded_bg_ripple.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  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.
-  -->
-
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="?android:attr/colorControlHighlight">
-    <item android:drawable="@drawable/battery_tips_all_rounded_bg"/>
-</ripple>
\ No newline at end of file
diff --git a/res/drawable/battery_tips_half_rounded_bottom_bg.xml b/res/drawable/battery_tips_half_rounded_bottom_bg.xml
deleted file mode 100644
index 7766de6..0000000
--- a/res/drawable/battery_tips_half_rounded_bottom_bg.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  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.
-  -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-    <solid android:color="@color/settingslib_dialog_background"/>
-    <corners
-        android:topLeftRadius="@dimen/battery_tips_card_corner_radius_small"
-        android:topRightRadius="@dimen/battery_tips_card_corner_radius_small"
-        android:bottomLeftRadius="@dimen/battery_tips_card_corner_radius_normal"
-        android:bottomRightRadius="@dimen/battery_tips_card_corner_radius_normal"
-        />
-</shape>
\ No newline at end of file
diff --git a/res/drawable/battery_tips_half_rounded_top_bg.xml b/res/drawable/battery_tips_half_rounded_top_bg.xml
deleted file mode 100644
index aba1a4f..0000000
--- a/res/drawable/battery_tips_half_rounded_top_bg.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  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.
-  -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-    <solid android:color="@color/settingslib_dialog_background"/>
-    <corners
-        android:topLeftRadius="@dimen/battery_tips_card_corner_radius_normal"
-        android:topRightRadius="@dimen/battery_tips_card_corner_radius_normal"
-        android:bottomLeftRadius="@dimen/battery_tips_card_corner_radius_small"
-        android:bottomRightRadius="@dimen/battery_tips_card_corner_radius_small"
-        />
-</shape>
\ No newline at end of file
diff --git a/res/layout/battery_tips_card.xml b/res/layout/battery_tips_card.xml
deleted file mode 100644
index 18c326f..0000000
--- a/res/layout/battery_tips_card.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/battery_tips_card"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_marginStart="?android:attr/listPreferredItemPaddingStart"
-    android:layout_marginEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:background="@drawable/battery_tips_all_rounded_bg_ripple"
-    android:orientation="vertical"
-    android:padding="20dp">
-
-    <ImageView
-        android:id="@+id/icon"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical|start"
-        android:contentDescription="@string/battery_usage_anomaly_content_description"
-        android:src="@drawable/ic_battery_tips_lightbulb" />
-
-    <TextView
-        android:id="@+id/title"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="8dp"
-        android:textAlignment="viewStart"
-        android:textAppearance="@style/TextAppearance.Material3.TitleMedium"
-        android:textColor="@color/settingslib_materialColorOnSurface" />
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="8dp"
-        android:gravity="end"
-        android:orientation="horizontal">
-
-        <com.google.android.material.button.MaterialButton
-            android:id="@+id/dismiss_button"
-            style="@style/Widget.Material3.Button.TextButton"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="end|center_vertical"
-            android:layout_marginEnd="8dp"
-            android:paddingHorizontal="16dp"
-            android:text="@string/battery_tips_card_dismiss_button"
-            android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle2"
-            android:textColor="@color/color_accent_selector" />
-
-        <com.google.android.material.button.MaterialButton
-            android:id="@+id/main_button"
-            style="@style/Widget.Material3.Button"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="end|center_vertical"
-            android:paddingHorizontal="16dp"
-            android:text="@string/battery_tips_card_action_button"
-            android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle2"
-            android:textColor="@color/settingslib_materialColorOnPrimary"
-            app:backgroundTint="@color/color_accent_selector" />
-    </LinearLayout>
-</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/dialog_custom_body_audio_sharing.xml b/res/layout/dialog_custom_body_audio_sharing.xml
index 528bfbb..ba7f643 100644
--- a/res/layout/dialog_custom_body_audio_sharing.xml
+++ b/res/layout/dialog_custom_body_audio_sharing.xml
@@ -44,6 +44,7 @@
             android:layout_height="wrap_content"
             android:layout_gravity="center"
             android:contentDescription="@null"
+            android:paddingBottom="24dp"
             android:visibility="gone" />
 
         <androidx.recyclerview.widget.RecyclerView
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index a24dc86..598ec3e 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -1555,26 +1555,26 @@
     <!-- Descriptions of the icons in zen_mode_icon_options. Should describe the associated image
          [CHAR LIMIT=NONE] -->
     <string-array name="zen_mode_icon_options_descriptions">
-        <item>Bedtime</item>
-        <item>Driving</item>
-        <item>Immersive</item>
+        <item>Half-moon</item>
+        <item>Car</item>
+        <item>Person\'s mind</item>
         <item>Calendar</item>
-        <item>Time</item>
-        <item>Beach</item>
-        <item>Camping</item>
-        <item>Theater</item>
-        <item>Gaming</item>
-        <item>Gym</item>
-        <item>Ball sports</item>
-        <item>Martial arts</item>
+        <item>Clock</item>
+        <item>Beach umbrella</item>
+        <item>Tent</item>
+        <item>Film reel</item>
+        <item>Game controller</item>
+        <item>Gym dumbbell</item>
+        <item>Person throwing ball</item>
+        <item>Person kicking</item>
         <item>Swimming</item>
-        <item>Hiking</item>
+        <item>Person hiking</item>
         <item>Golf</item>
-        <item>Workshop</item>
-        <item>Work</item>
+        <item>Workshop tools</item>
+        <item>Briefcase</item>
         <item>Star</item>
-        <item>Zen</item>
-        <item>Managed</item>
+        <item>Lotus flower</item>
+        <item>Supervisor</item>
     </string-array>
 
     <!-- Packages that will not show Display over other apps permission -->
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1d60218..6c018c2 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -937,7 +937,7 @@
     <!-- Message showing that multiple fingerprints, face, and the current watch is set up. Shown for a menu item that launches fingerprint, face,  and active unlock settings or enrollment. [CHAR LIMIT=80]-->
     <string name="security_settings_fingerprint_multiple_face_watch_preference_summary">Face, fingerprints, and <xliff:g id="watch" example="Dani's Watch">%s</xliff:g> added</string>
     <!-- Description for mandatory biometrics prompt-->
-    <string name="mandatory_biometrics_prompt_description">This is needed since Identity Check is on</string>
+    <string name="mandatory_biometrics_prompt_description">Identity Check is on</string>
     <!-- RemoteAuth unlock enrollment and settings --><skip />
     <!-- Title shown for menu item that launches watch unlock settings. [CHAR LIMIT=40] -->
     <string name ="security_settings_remoteauth_preference_title">Remote Authenticator Unlock</string>
diff --git a/res/xml/mobile_network_settings.xml b/res/xml/mobile_network_settings.xml
index 539c145..eb80ac8 100644
--- a/res/xml/mobile_network_settings.xml
+++ b/res/xml/mobile_network_settings.xml
@@ -152,6 +152,7 @@
             android:summary="@string/contact_discovery_opt_in_summary"
             settings:controller="com.android.settings.network.telephony.ContactDiscoveryPreferenceController"/>
 
+        <!-- Settings search is handled by PreferredNetworkModeSearchItem. -->
         <ListPreference
             android:key="preferred_network_mode_key"
             android:title="@string/preferred_network_mode_title"
@@ -159,8 +160,10 @@
             android:entries="@array/preferred_network_mode_choices"
             android:entryValues="@array/preferred_network_mode_values"
             android:dialogTitle="@string/preferred_network_mode_dialogtitle"
+            settings:searchable="false"
             settings:controller="com.android.settings.network.telephony.PreferredNetworkModePreferenceController"/>
 
+        <!-- Settings search is handled by EnabledNetworkModeSearchItem. -->
         <ListPreference
             android:key="enabled_networks_key"
             android:title="@string/preferred_network_mode_title"
@@ -168,6 +171,7 @@
             android:entries="@array/enabled_networks_choices"
             android:entryValues="@array/enabled_networks_values"
             android:dialogTitle="@string/preferred_network_mode_dialogtitle"
+            settings:searchable="false"
             settings:controller="com.android.settings.network.telephony.EnabledNetworkModePreferenceController"/>
 
         <Preference
diff --git a/res/xml/power_usage_advanced.xml b/res/xml/power_usage_advanced.xml
index c129453..f0104e5 100644
--- a/res/xml/power_usage_advanced.xml
+++ b/res/xml/power_usage_advanced.xml
@@ -27,7 +27,7 @@
             "com.android.settings.fuelgauge.batteryusage.BatteryTipsController"
         settings:isPreferenceVisible="false">
 
-        <com.android.settings.fuelgauge.batteryusage.BatteryTipsCardPreference
+        <com.android.settings.widget.TipCardPreference
             android:key="battery_tips_card"
             settings:isPreferenceVisible="false" />
 
diff --git a/res/xml/sound_settings.xml b/res/xml/sound_settings.xml
index 15312c8..393009e 100644
--- a/res/xml/sound_settings.xml
+++ b/res/xml/sound_settings.xml
@@ -105,6 +105,14 @@
         settings:keywords="@string/keywords_sounds_and_notifications_interruptions"
         settings:controller="com.android.settings.notification.zen.ZenModePreferenceController"/>
 
+    <com.android.settingslib.RestrictedPreference
+        android:key="modes_notifications"
+        android:order="-130"
+        android:title="@string/zen_modes_list_title"
+        settings:useAdminDisabledSummary="true"
+        android:fragment="com.android.settings.notification.modes.ZenModesListFragment"
+        settings:controller="com.android.settings.notification.modes.ZenModesLinkPreferenceController"/>
+
     <!-- Phone ringtone -->
     <com.android.settings.DefaultRingtonePreference
         android:key="phone_ringtone"
diff --git a/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragment.java b/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragment.java
index bdf7857..d04b34a 100644
--- a/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragment.java
@@ -20,6 +20,7 @@
 import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE;
 import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS;
 import static android.provider.Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED;
+import static android.provider.Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS;
 import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED;
 import static android.provider.Settings.Secure.ACCESSIBILITY_QS_TARGETS;
 import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
@@ -93,7 +94,8 @@
             Settings.Secure.getUriFor(ACCESSIBILITY_BUTTON_MODE);
     private static final Uri BUTTON_SHORTCUT_SETTING =
             Settings.Secure.getUriFor(ACCESSIBILITY_BUTTON_TARGETS);
-
+    private static final Uri GESTURE_SHORTCUT_SETTING =
+            Settings.Secure.getUriFor(ACCESSIBILITY_GESTURE_TARGETS);
     private static final Uri TRIPLE_TAP_SHORTCUT_SETTING =
             Settings.Secure.getUriFor(ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
     private static final Uri TWO_FINGERS_DOUBLE_TAP_SHORTCUT_SETTING =
@@ -107,6 +109,7 @@
             VOLUME_KEYS_SHORTCUT_SETTING,
             BUTTON_SHORTCUT_MODE_SETTING,
             BUTTON_SHORTCUT_SETTING,
+            GESTURE_SHORTCUT_SETTING,
             TRIPLE_TAP_SHORTCUT_SETTING,
             TWO_FINGERS_DOUBLE_TAP_SHORTCUT_SETTING,
             QUICK_SETTINGS_SHORTCUT_SETTING,
@@ -173,6 +176,8 @@
                 } else if (BUTTON_SHORTCUT_MODE_SETTING.equals(uri)
                         || BUTTON_SHORTCUT_SETTING.equals(uri)) {
                     refreshSoftwareShortcutControllers();
+                } else if (GESTURE_SHORTCUT_SETTING.equals(uri)) {
+                    refreshPreferenceController(GestureShortcutOptionController.class);
                 } else if (TRIPLE_TAP_SHORTCUT_SETTING.equals(uri)) {
                     refreshPreferenceController(TripleTapShortcutOptionController.class);
                 } else if (TWO_FINGERS_DOUBLE_TAP_SHORTCUT_SETTING.equals(uri)) {
diff --git a/src/com/android/settings/accessibility/shortcuts/FloatingButtonShortcutOptionController.java b/src/com/android/settings/accessibility/shortcuts/FloatingButtonShortcutOptionController.java
index 2242cab..20ff12d 100644
--- a/src/com/android/settings/accessibility/shortcuts/FloatingButtonShortcutOptionController.java
+++ b/src/com/android/settings/accessibility/shortcuts/FloatingButtonShortcutOptionController.java
@@ -50,7 +50,14 @@
 
     @Override
     protected boolean isShortcutAvailable() {
-        return AccessibilityUtil.isFloatingMenuEnabled(mContext);
+        if (android.provider.Flags.a11yStandaloneGestureEnabled()) {
+            // FAB should be available when in gesture navigation mode,
+            // or if we're in the FAB button mode while in navbar navigation mode.
+            return AccessibilityUtil.isGestureNavigateEnabled(mContext)
+                    || AccessibilityUtil.isFloatingMenuEnabled(mContext);
+        } else {
+            return AccessibilityUtil.isFloatingMenuEnabled(mContext);
+        }
     }
 
     @Nullable
diff --git a/src/com/android/settings/accessibility/shortcuts/GestureShortcutOptionController.java b/src/com/android/settings/accessibility/shortcuts/GestureShortcutOptionController.java
index e65aab9..3c19b5d 100644
--- a/src/com/android/settings/accessibility/shortcuts/GestureShortcutOptionController.java
+++ b/src/com/android/settings/accessibility/shortcuts/GestureShortcutOptionController.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.accessibility.shortcuts;
 
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
+
 import android.content.Context;
 import android.text.SpannableStringBuilder;
 
@@ -52,10 +54,21 @@
     }
 
     @Override
+    protected int getShortcutType() {
+        return android.provider.Flags.a11yStandaloneGestureEnabled()
+                ? GESTURE : super.getShortcutType();
+    }
+
+    @Override
     protected boolean isShortcutAvailable() {
-        return !isInSetupWizard()
-                && !AccessibilityUtil.isFloatingMenuEnabled(mContext)
-                && AccessibilityUtil.isGestureNavigateEnabled(mContext);
+        if (android.provider.Flags.a11yStandaloneGestureEnabled()) {
+            return !isInSetupWizard()
+                    && AccessibilityUtil.isGestureNavigateEnabled(mContext);
+        } else {
+            return !isInSetupWizard()
+                    && AccessibilityUtil.isGestureNavigateEnabled(mContext)
+                    && !AccessibilityUtil.isFloatingMenuEnabled(mContext);
+        }
     }
 
     @Override
@@ -68,9 +81,8 @@
 
         final SpannableStringBuilder sb = new SpannableStringBuilder();
         sb.append(instruction);
-        if (!isInSetupWizard()) {
-            sb.append("\n\n");
-            sb.append(getCustomizeAccessibilityButtonLink());
+        if (!isInSetupWizard() && !android.provider.Flags.a11yStandaloneGestureEnabled()) {
+            sb.append("\n\n").append(getCustomizeAccessibilityButtonLink());
         }
 
         return sb;
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
index 65d9366..526ae8f6 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
@@ -42,6 +42,8 @@
 import android.os.Handler;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
 import android.text.InputFilter;
 import android.text.Spanned;
 import android.text.TextUtils;
@@ -109,6 +111,9 @@
     private static final int RESULT_FINISHED = BiometricEnrollBase.RESULT_FINISHED;
     private static final int RESULT_SKIP = BiometricEnrollBase.RESULT_SKIP;
     private static final int RESULT_TIMEOUT = BiometricEnrollBase.RESULT_TIMEOUT;
+    @VisibleForTesting
+    static final VibrationEffect SUCCESS_VIBRATION_EFFECT =
+            VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
 
     @Override
     public Intent getIntent() {
@@ -285,6 +290,7 @@
         private FingerprintAuthenticateSidecar mAuthenticateSidecar;
         private FingerprintRemoveSidecar mRemovalSidecar;
         private HashMap<Integer, String> mFingerprintsRenaming;
+        private Vibrator mVibrator;
 
         @Nullable
         private UdfpsEnrollCalibrator mCalibrator;
@@ -367,8 +373,8 @@
                         break;
                     case MSG_FINGER_AUTH_HELP: {
                         // Not used
+                        break;
                     }
-                    break;
                 }
             }
         };
@@ -511,6 +517,7 @@
                     addFirstFingerprint(null);
                 }
             }
+            mVibrator = getContext().getSystemService(Vibrator.class);
             final PreferenceScreen root = getPreferenceScreen();
             root.removeAll();
             addPreferencesFromResource(getPreferenceScreenResId());
@@ -1095,6 +1102,7 @@
         }
 
         private void highlightFingerprintItem(int fpId) {
+            mVibrator.vibrate(SUCCESS_VIBRATION_EFFECT);
             String prefName = genKey(fpId);
             FingerprintPreference fpref = (FingerprintPreference) findPreference(prefName);
             final Drawable highlight = getHighlightDrawable();
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
index 2b74684..0897a43 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
@@ -89,7 +89,7 @@
     private LocalBluetoothManager mManager;
     private LocalBluetoothProfileManager mProfileManager;
     private CachedBluetoothDevice mCachedDevice;
-    private List<CachedBluetoothDevice> mAllOfCachedDevices;
+    private Set<CachedBluetoothDevice> mCachedDeviceGroup;
     private Map<String, List<CachedBluetoothDevice>> mProfileDeviceMap =
             new HashMap<String, List<CachedBluetoothDevice>>();
     private boolean mIsLeContactSharingEnabled = false;
@@ -105,7 +105,7 @@
         mManager = manager;
         mProfileManager = mManager.getProfileManager();
         mCachedDevice = device;
-        mAllOfCachedDevices = Utils.getAllOfCachedBluetoothDevices(mManager, mCachedDevice);
+        mCachedDeviceGroup = Utils.findAllCachedBluetoothDevicesByGroupId(mManager, mCachedDevice);
     }
 
     @Override
@@ -310,10 +310,10 @@
     private List<LocalBluetoothProfile> getProfiles() {
         List<LocalBluetoothProfile> result = new ArrayList<>();
         mProfileDeviceMap.clear();
-        if (mAllOfCachedDevices == null || mAllOfCachedDevices.isEmpty()) {
+        if (mCachedDeviceGroup == null || mCachedDeviceGroup.isEmpty()) {
             return result;
         }
-        for (CachedBluetoothDevice cachedItem : mAllOfCachedDevices) {
+        for (CachedBluetoothDevice cachedItem : mCachedDeviceGroup) {
             List<LocalBluetoothProfile> tmpResult = cachedItem.getUiAccessibleProfiles();
             for (LocalBluetoothProfile profile : tmpResult) {
                 if (mProfileDeviceMap.containsKey(profile.toString())) {
@@ -514,7 +514,7 @@
 
     @Override
     public void onPause() {
-        for (CachedBluetoothDevice item : mAllOfCachedDevices) {
+        for (CachedBluetoothDevice item : mCachedDeviceGroup) {
             item.unregisterCallback(this);
         }
         mProfileManager.removeServiceListener(this);
@@ -523,7 +523,7 @@
     @Override
     public void onResume() {
         updateLeAudioConfig();
-        for (CachedBluetoothDevice item : mAllOfCachedDevices) {
+        for (CachedBluetoothDevice item : mCachedDeviceGroup) {
             item.registerCallback(this);
         }
         mProfileManager.addServiceListener(this);
@@ -545,11 +545,11 @@
 
     @Override
     public void onDeviceAttributesChanged() {
-        for (CachedBluetoothDevice item : mAllOfCachedDevices) {
+        for (CachedBluetoothDevice item : mCachedDeviceGroup) {
             item.unregisterCallback(this);
         }
-        mAllOfCachedDevices = Utils.getAllOfCachedBluetoothDevices(mManager, mCachedDevice);
-        for (CachedBluetoothDevice item : mAllOfCachedDevices) {
+        mCachedDeviceGroup = Utils.findAllCachedBluetoothDevicesByGroupId(mManager, mCachedDevice);
+        for (CachedBluetoothDevice item : mCachedDeviceGroup) {
             item.registerCallback(this);
         }
 
diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
index ac0c63b..209c900 100644
--- a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
+++ b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
@@ -46,6 +46,7 @@
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.widget.GearPreference;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.utils.ThreadUtils;
 
@@ -55,6 +56,7 @@
 import java.util.Set;
 import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
 
 /**
  * BluetoothDevicePreference is the preference type used to display each remote
@@ -76,7 +78,10 @@
     }
 
     private final CachedBluetoothDevice mCachedDevice;
+    private Set<CachedBluetoothDevice> mCachedDeviceGroup;
+
     private final UserManager mUserManager;
+    private final LocalBluetoothManager mLocalBtManager;
 
     private Set<BluetoothDevice> mBluetoothDevices;
     @VisibleForTesting
@@ -113,6 +118,21 @@
         @Override
         public void onDeviceAttributesChanged() {
             onPreferenceAttributesChanged();
+            Set<CachedBluetoothDevice> newCachedDeviceGroup = new HashSet<>(
+                    Utils.findAllCachedBluetoothDevicesByGroupId(mLocalBtManager, mCachedDevice));
+            if (!mCachedDeviceGroup.equals(newCachedDeviceGroup)) {
+                for (CachedBluetoothDevice cachedBluetoothDevice : mCachedDeviceGroup) {
+                    cachedBluetoothDevice.unregisterCallback(this);
+                }
+                unregisterMetadataChangedListener();
+
+                mCachedDeviceGroup = newCachedDeviceGroup;
+
+                for (CachedBluetoothDevice cachedBluetoothDevice : mCachedDeviceGroup) {
+                    cachedBluetoothDevice.registerCallback(getContext().getMainExecutor(), this);
+                }
+                registerMetadataChangedListener();
+            }
         }
     }
 
@@ -121,6 +141,7 @@
         super(context, null);
         mResources = getContext().getResources();
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        mLocalBtManager = Utils.getLocalBluetoothManager(context);
         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
         mShowDevicesWithoutNames = showDeviceWithoutNames;
 
@@ -131,6 +152,8 @@
         }
 
         mCachedDevice = cachedDevice;
+        mCachedDeviceGroup = new HashSet<>(
+                Utils.findAllCachedBluetoothDevicesByGroupId(mLocalBtManager, mCachedDevice));
         mCallback = new BluetoothDevicePreferenceCallback();
         mId = sNextId.getAndIncrement();
         mType = type;
@@ -164,7 +187,9 @@
     protected void onPrepareForRemoval() {
         super.onPrepareForRemoval();
         if (!mIsCallbackRemoved) {
-            mCachedDevice.unregisterCallback(mCallback);
+            for (CachedBluetoothDevice cachedBluetoothDevice : mCachedDeviceGroup) {
+                cachedBluetoothDevice.unregisterCallback(mCallback);
+            }
             unregisterMetadataChangedListener();
             mIsCallbackRemoved = true;
         }
@@ -178,7 +203,9 @@
     public void onAttached() {
         super.onAttached();
         if (mIsCallbackRemoved) {
-            mCachedDevice.registerCallback(mCallback);
+            for (CachedBluetoothDevice cachedBluetoothDevice : mCachedDeviceGroup) {
+                cachedBluetoothDevice.registerCallback(getContext().getMainExecutor(), mCallback);
+            }
             registerMetadataChangedListener();
             mIsCallbackRemoved = false;
         }
@@ -189,7 +216,9 @@
     public void onDetached() {
         super.onDetached();
         if (!mIsCallbackRemoved) {
-            mCachedDevice.unregisterCallback(mCallback);
+            for (CachedBluetoothDevice cachedBluetoothDevice : mCachedDeviceGroup) {
+                cachedBluetoothDevice.unregisterCallback(mCallback);
+            }
             unregisterMetadataChangedListener();
             mIsCallbackRemoved = true;
         }
@@ -200,16 +229,11 @@
             Log.d(TAG, "No mBluetoothAdapter");
             return;
         }
-        if (mBluetoothDevices == null) {
-            mBluetoothDevices = new HashSet<>();
-        }
-        mBluetoothDevices.clear();
-        if (mCachedDevice.getDevice() != null) {
-            mBluetoothDevices.add(mCachedDevice.getDevice());
-        }
-        for (CachedBluetoothDevice cbd : mCachedDevice.getMemberDevice()) {
-            mBluetoothDevices.add(cbd.getDevice());
-        }
+
+        mBluetoothDevices = mCachedDeviceGroup.stream()
+                .map(CachedBluetoothDevice::getDevice)
+                .collect(Collectors.toCollection(HashSet::new));
+
         if (mBluetoothDevices.isEmpty()) {
             Log.d(TAG, "No BT device to register.");
             return;
diff --git a/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java
index 4be4d63..a5e9cde 100644
--- a/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java
+++ b/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java
@@ -47,7 +47,7 @@
 import com.android.settingslib.core.lifecycle.events.OnStop;
 import com.android.settingslib.widget.LayoutPreference;
 
-import java.util.List;
+import java.util.Set;
 
 /**
  * This class adds a header with device name and status (connected/disconnected, etc.).
@@ -90,7 +90,7 @@
     LayoutPreference mLayoutPreference;
     LocalBluetoothManager mManager;
     private CachedBluetoothDevice mCachedDevice;
-    private List<CachedBluetoothDevice> mAllOfCachedDevices;
+    private Set<CachedBluetoothDevice> mCachedDeviceGroup;
     @VisibleForTesting
     Handler mHandler = new Handler(Looper.getMainLooper());
     @VisibleForTesting
@@ -128,7 +128,7 @@
             return;
         }
         mIsRegisterCallback = true;
-        for (CachedBluetoothDevice item : mAllOfCachedDevices) {
+        for (CachedBluetoothDevice item : mCachedDeviceGroup) {
             item.registerCallback(this);
         }
         refresh();
@@ -139,7 +139,7 @@
         if (!mIsRegisterCallback) {
             return;
         }
-        for (CachedBluetoothDevice item : mAllOfCachedDevices) {
+        for (CachedBluetoothDevice item : mCachedDeviceGroup) {
             item.unregisterCallback(this);
         }
 
@@ -155,7 +155,7 @@
         mCachedDevice = cachedBluetoothDevice;
         mManager = bluetoothManager;
         mProfileManager = bluetoothManager.getProfileManager();
-        mAllOfCachedDevices = Utils.getAllOfCachedBluetoothDevices(mManager, mCachedDevice);
+        mCachedDeviceGroup = Utils.findAllCachedBluetoothDevicesByGroupId(mManager, mCachedDevice);
     }
 
     @VisibleForTesting
@@ -230,7 +230,7 @@
         // Init the battery layouts.
         hideAllOfBatteryLayouts();
         LeAudioProfile leAudioProfile = mProfileManager.getLeAudioProfile();
-        if (mAllOfCachedDevices.isEmpty()) {
+        if (mCachedDeviceGroup.isEmpty()) {
             Log.e(TAG, "There is no LeAudioProfile.");
             return;
         }
@@ -244,7 +244,7 @@
             return;
         }
 
-        for (CachedBluetoothDevice cachedDevice : mAllOfCachedDevices) {
+        for (CachedBluetoothDevice cachedDevice : mCachedDeviceGroup) {
             int deviceId = leAudioProfile.getAudioLocation(cachedDevice.getDevice());
             Log.d(TAG, "LeAudioDevices:" + cachedDevice.getDevice().getAnonymizedAddress()
                     + ", deviceId:" + deviceId);
@@ -300,15 +300,15 @@
 
     @Override
     public void onDeviceAttributesChanged() {
-        for (CachedBluetoothDevice item : mAllOfCachedDevices) {
+        for (CachedBluetoothDevice item : mCachedDeviceGroup) {
             item.unregisterCallback(this);
         }
-        mAllOfCachedDevices = Utils.getAllOfCachedBluetoothDevices(mManager, mCachedDevice);
-        for (CachedBluetoothDevice item : mAllOfCachedDevices) {
+        mCachedDeviceGroup = Utils.findAllCachedBluetoothDevicesByGroupId(mManager, mCachedDevice);
+        for (CachedBluetoothDevice item : mCachedDeviceGroup) {
             item.registerCallback(this);
         }
 
-        if (!mAllOfCachedDevices.isEmpty()) {
+        if (!mCachedDeviceGroup.isEmpty()) {
             refresh();
         }
     }
diff --git a/src/com/android/settings/bluetooth/Utils.java b/src/com/android/settings/bluetooth/Utils.java
index f6288b2..b1d9de7 100644
--- a/src/com/android/settings/bluetooth/Utils.java
+++ b/src/com/android/settings/bluetooth/Utils.java
@@ -48,8 +48,9 @@
 
 import com.google.common.base.Supplier;
 
-import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.FutureTask;
 
@@ -239,12 +240,12 @@
      * @param cachedBluetoothDevice The main cachedBluetoothDevice.
      * @return all cachedBluetoothDevices with the same groupId.
      */
-    public static List<CachedBluetoothDevice> getAllOfCachedBluetoothDevices(
+    public static Set<CachedBluetoothDevice> findAllCachedBluetoothDevicesByGroupId(
             LocalBluetoothManager localBtMgr,
             CachedBluetoothDevice cachedBluetoothDevice) {
-        List<CachedBluetoothDevice> cachedBluetoothDevices = new ArrayList<>();
+        Set<CachedBluetoothDevice> cachedBluetoothDevices = new HashSet<>();
         if (cachedBluetoothDevice == null) {
-            Log.e(TAG, "getAllOfCachedBluetoothDevices: no cachedBluetoothDevice");
+            Log.e(TAG, "findAllCachedBluetoothDevicesByGroupId: no cachedBluetoothDevice");
             return cachedBluetoothDevices;
         }
         int deviceGroupId = cachedBluetoothDevice.getGroupId();
@@ -254,7 +255,7 @@
         }
 
         if (localBtMgr == null) {
-            Log.e(TAG, "getAllOfCachedBluetoothDevices: no LocalBluetoothManager");
+            Log.e(TAG, "findAllCachedBluetoothDevicesByGroupId: no LocalBluetoothManager");
             return cachedBluetoothDevices;
         }
         CachedBluetoothDevice mainDevice =
@@ -262,16 +263,14 @@
                         .filter(cachedDevice -> cachedDevice.getGroupId() == deviceGroupId)
                         .findFirst().orElse(null);
         if (mainDevice == null) {
-            Log.e(TAG, "getAllOfCachedBluetoothDevices: groupId = " + deviceGroupId
+            Log.e(TAG, "findAllCachedBluetoothDevicesByGroupId: groupId = " + deviceGroupId
                     + ", no main device.");
             return cachedBluetoothDevices;
         }
         cachedBluetoothDevice = mainDevice;
         cachedBluetoothDevices.add(cachedBluetoothDevice);
-        for (CachedBluetoothDevice member : cachedBluetoothDevice.getMemberDevice()) {
-            cachedBluetoothDevices.add(member);
-        }
-        Log.d(TAG, "getAllOfCachedBluetoothDevices: groupId = " + deviceGroupId
+        cachedBluetoothDevices.addAll(cachedBluetoothDevice.getMemberDevice());
+        Log.d(TAG, "findAllCachedBluetoothDevicesByGroupId: groupId = " + deviceGroupId
                 + " , cachedBluetoothDevice = " + cachedBluetoothDevice
                 + " , deviceList = " + cachedBluetoothDevices);
         return cachedBluetoothDevices;
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java
index 6f62ed9..1ae541c 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java
@@ -30,6 +30,9 @@
 import androidx.fragment.app.FragmentManager;
 
 import com.android.settings.R;
+import com.android.settings.bluetooth.BluetoothPairingDetail;
+import com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsQrCodeFragment;
+import com.android.settings.core.SubSettingLauncher;
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
 import com.android.settingslib.bluetooth.BluetoothUtils;
 
@@ -130,9 +133,27 @@
             builder.setTitle(R.string.audio_sharing_share_dialog_title)
                     .setCustomImage(R.drawable.audio_sharing_guidance)
                     .setCustomMessage(R.string.audio_sharing_dialog_connect_device_content)
-                    .setNegativeButton(
-                            R.string.audio_sharing_close_button_label,
-                            (dig, which) -> onCancelClick());
+                    .setCustomPositiveButton(
+                            R.string.audio_sharing_pair_button_label,
+                            v -> {
+                                dismiss();
+                                new SubSettingLauncher(getContext())
+                                        .setDestination(BluetoothPairingDetail.class.getName())
+                                        .setSourceMetricsCategory(getMetricsCategory())
+                                        .launch();
+                                logDialogPositiveBtnClick();
+                            })
+                    .setCustomNegativeButton(
+                            R.string.audio_sharing_qrcode_button_label,
+                            v -> {
+                                dismiss();
+                                new SubSettingLauncher(getContext())
+                                        .setTitleRes(R.string.audio_streams_qr_code_page_title)
+                                        .setDestination(AudioStreamsQrCodeFragment.class.getName())
+                                        .setSourceMetricsCategory(getMetricsCategory())
+                                        .launch();
+                                logDialogNegativeBtnClick();
+                            });
         } else if (deviceItems.size() == 1) {
             AudioSharingDeviceItem deviceItem = Iterables.getOnlyElement(deviceItems);
             builder.setTitle(
@@ -145,11 +166,7 @@
                             v -> {
                                 if (sListener != null) {
                                     sListener.onItemClick(deviceItem);
-                                    mMetricsFeatureProvider.action(
-                                            getContext(),
-                                            SettingsEnums
-                                            .ACTION_AUDIO_SHARING_DIALOG_POSITIVE_BTN_CLICKED,
-                                            sEventData);
+                                    logDialogPositiveBtnClick();
                                 }
                                 dismiss();
                             })
@@ -165,6 +182,7 @@
                                     (AudioSharingDeviceItem item) -> {
                                         if (sListener != null) {
                                             sListener.onItemClick(item);
+                                            logDialogPositiveBtnClick();
                                         }
                                         dismiss();
                                     },
@@ -178,11 +196,22 @@
     private void onCancelClick() {
         if (sListener != null) {
             sListener.onCancelClick();
-            mMetricsFeatureProvider.action(
-                    getContext(),
-                    SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_NEGATIVE_BTN_CLICKED,
-                    sEventData);
+            logDialogNegativeBtnClick();
         }
         dismiss();
     }
+
+    private void logDialogPositiveBtnClick() {
+        mMetricsFeatureProvider.action(
+                getContext(),
+                SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_POSITIVE_BTN_CLICKED,
+                sEventData);
+    }
+
+    private void logDialogNegativeBtnClick() {
+        mMetricsFeatureProvider.action(
+                getContext(),
+                SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_NEGATIVE_BTN_CLICKED,
+                sEventData);
+    }
 }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java
index 4ee405d..14c19de 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java
@@ -499,7 +499,7 @@
     private void removeSourceForGroup(
             int groupId, Map<Integer, List<BluetoothDevice>> groupedDevices) {
         if (mAssistant == null) {
-            Log.d(TAG, "Fail to add source due to null profiles, group = " + groupId);
+            Log.d(TAG, "Fail to remove source due to null profiles, group = " + groupId);
             return;
         }
         if (!groupedDevices.containsKey(groupId)) {
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiver.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiver.java
index b43a544..371613f 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiver.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiver.java
@@ -81,9 +81,15 @@
                 break;
             case ACTION_LE_AUDIO_SHARING_STOP:
                 LocalBluetoothManager manager = Utils.getLocalBtManager(context);
-                AudioSharingUtils.stopBroadcasting(manager);
-                metricsFeatureProvider.action(
-                        context, SettingsEnums.ACTION_STOP_AUDIO_SHARING_FROM_NOTIFICATION);
+                if (BluetoothUtils.isBroadcasting(manager)) {
+                    AudioSharingUtils.stopBroadcasting(manager);
+                    metricsFeatureProvider.action(
+                            context, SettingsEnums.ACTION_STOP_AUDIO_SHARING_FROM_NOTIFICATION);
+                } else {
+                    cancelSharingNotification(context);
+                    metricsFeatureProvider.action(
+                            context, SettingsEnums.ACTION_CANCEL_AUDIO_SHARING_NOTIFICATION);
+                }
                 break;
             default:
                 Log.w(TAG, "Received unexpected intent " + intent.getAction());
diff --git a/src/com/android/settings/datetime/AutoTimePreferenceController.java b/src/com/android/settings/datetime/AutoTimePreferenceController.java
index 434eba9..2942acb 100644
--- a/src/com/android/settings/datetime/AutoTimePreferenceController.java
+++ b/src/com/android/settings/datetime/AutoTimePreferenceController.java
@@ -39,6 +39,11 @@
     public AutoTimePreferenceController(Context context, String preferenceKey) {
         super(context, preferenceKey);
         mTimeManager = context.getSystemService(TimeManager.class);
+        // This is a no-op implementation of UpdateTimeAndDateCallback to avoid a NPE when
+        // setTimeAndDateCallback() isn't called, e.g. for slices and other cases where the
+        // controller is instantiated outside of the context of the real Date & Time settings
+        // screen.
+        mCallback  = (c) -> {};
     }
 
     public void setDateAndTimeCallback(UpdateTimeAndDateCallback callback) {
diff --git a/src/com/android/settings/datetime/AutoTimeZonePreferenceController.java b/src/com/android/settings/datetime/AutoTimeZonePreferenceController.java
index 011cc97..3a1f995 100644
--- a/src/com/android/settings/datetime/AutoTimeZonePreferenceController.java
+++ b/src/com/android/settings/datetime/AutoTimeZonePreferenceController.java
@@ -40,6 +40,11 @@
     public AutoTimeZonePreferenceController(Context context, String preferenceKey) {
         super(context, preferenceKey);
         mTimeManager = context.getSystemService(TimeManager.class);
+        // This is a no-op implementation of UpdateTimeAndDateCallback to avoid a NPE when
+        // setTimeAndDateCallback() isn't called, e.g. for slices and other cases where the
+        // controller is instantiated outside of the context of the real Date & Time settings
+        // screen.
+        mCallback  = (c) -> {};
     }
 
     /**
diff --git a/src/com/android/settings/datetime/TimeFormatPreferenceController.java b/src/com/android/settings/datetime/TimeFormatPreferenceController.java
index 22f7509..19805ad 100644
--- a/src/com/android/settings/datetime/TimeFormatPreferenceController.java
+++ b/src/com/android/settings/datetime/TimeFormatPreferenceController.java
@@ -43,6 +43,11 @@
     public TimeFormatPreferenceController(Context context, String key) {
         super(context, key);
         mDummyDate = Calendar.getInstance();
+        // This is a no-op implementation of UpdateTimeAndDateCallback to avoid a NPE when
+        // setTimeAndDateCallback() isn't called, e.g. for slices and other cases where the
+        // controller is instantiated outside of the context of the real Date & Time settings
+        // screen.
+        mUpdateTimeAndDateCallback  = (c) -> {};
     }
 
     /**
diff --git a/src/com/android/settings/fuelgauge/batteryusage/AnomalyEventWrapper.java b/src/com/android/settings/fuelgauge/batteryusage/AnomalyEventWrapper.java
index be5de06..c62728b 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/AnomalyEventWrapper.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/AnomalyEventWrapper.java
@@ -28,10 +28,11 @@
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
 import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.widget.TipCardPreference;
 
 import java.util.function.Function;
 
-final class AnomalyEventWrapper {
+class AnomalyEventWrapper {
     private static final String TAG = "AnomalyEventWrapper";
 
     private final Context mContext;
@@ -235,16 +236,16 @@
         return mHighlightSlotPair;
     }
 
-    boolean updateTipsCardPreference(BatteryTipsCardPreference preference) {
+    boolean updateTipsCardPreference(TipCardPreference preference) {
         final String titleString = getTitleString();
         if (TextUtils.isEmpty(titleString)) {
             return false;
         }
         preference.setTitle(titleString);
-        preference.setIconResourceId(getIconResId());
-        preference.setButtonColorResourceId(getColorResId());
-        preference.setMainButtonLabel(getMainBtnString());
-        preference.setDismissButtonLabel(getDismissBtnString());
+        preference.setIconResId(getIconResId());
+        preference.setTintColorResId(getColorResId());
+        preference.setPrimaryButtonText(getDismissBtnString());
+        preference.setSecondaryButtonText(getMainBtnString());
         return true;
     }
 
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java
deleted file mode 100644
index bbd1099..0000000
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java
+++ /dev/null
@@ -1,150 +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;
-
-import android.content.Context;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceViewHolder;
-
-import com.android.settings.R;
-
-import com.google.android.material.button.MaterialButton;
-
-/** A preference for displaying the battery tips card view. */
-public class BatteryTipsCardPreference extends Preference implements View.OnClickListener {
-
-    private static final String TAG = "BatteryTipsCardPreference";
-
-    interface OnConfirmListener {
-        void onConfirm();
-    }
-
-    interface OnRejectListener {
-        void onReject();
-    }
-
-    private OnConfirmListener mOnConfirmListener;
-    private OnRejectListener mOnRejectListener;
-    private int mIconResourceId = 0;
-    private int mButtonColorResourceId = 0;
-
-    @VisibleForTesting CharSequence mMainButtonLabel;
-    @VisibleForTesting CharSequence mDismissButtonLabel;
-
-    public BatteryTipsCardPreference(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        setLayoutResource(R.layout.battery_tips_card);
-        setViewId(R.id.battery_tips_card);
-        setSelectable(false);
-    }
-
-    public void setOnConfirmListener(OnConfirmListener listener) {
-        mOnConfirmListener = listener;
-    }
-
-    public void setOnRejectListener(OnRejectListener listener) {
-        mOnRejectListener = listener;
-    }
-
-    /**
-     * Sets the icon in tips card.
-     */
-    public void setIconResourceId(int resourceId) {
-        if (mIconResourceId != resourceId) {
-            mIconResourceId = resourceId;
-            notifyChanged();
-        }
-    }
-
-    /**
-     * Sets the background color for main button and the text color for dismiss button.
-     */
-    public void setButtonColorResourceId(int resourceId) {
-        if (mButtonColorResourceId != resourceId) {
-            mButtonColorResourceId = resourceId;
-            notifyChanged();
-        }
-    }
-
-    /**
-     * Sets the label of main button in tips card.
-     */
-    public void setMainButtonLabel(CharSequence label) {
-        if (!TextUtils.equals(mMainButtonLabel, label)) {
-            mMainButtonLabel = label;
-            notifyChanged();
-        }
-    }
-
-    /**
-     * Sets the label of dismiss button in tips card.
-     */
-    public void setDismissButtonLabel(CharSequence label) {
-        if (!TextUtils.equals(mDismissButtonLabel, label)) {
-            mDismissButtonLabel = label;
-            notifyChanged();
-        }
-    }
-
-    @Override
-    public void onClick(View view) {
-        final int viewId = view.getId();
-        if (viewId == R.id.main_button || viewId == R.id.battery_tips_card) {
-            if (mOnConfirmListener != null) {
-                mOnConfirmListener.onConfirm();
-            }
-        } else if (viewId == R.id.dismiss_button) {
-            if (mOnRejectListener != null) {
-                mOnRejectListener.onReject();
-            }
-        }
-    }
-
-    @Override
-    public void onBindViewHolder(PreferenceViewHolder view) {
-        super.onBindViewHolder(view);
-
-        ((TextView) view.findViewById(R.id.title)).setText(getTitle());
-
-        final LinearLayout tipsCard = (LinearLayout) view.findViewById(R.id.battery_tips_card);
-        tipsCard.setOnClickListener(this);
-        final MaterialButton mainButton = (MaterialButton) view.findViewById(R.id.main_button);
-        mainButton.setOnClickListener(this);
-        mainButton.setText(mMainButtonLabel);
-        final MaterialButton dismissButton =
-                (MaterialButton) view.findViewById(R.id.dismiss_button);
-        dismissButton.setOnClickListener(this);
-        dismissButton.setText(mDismissButtonLabel);
-        if (mButtonColorResourceId != 0) {
-            final int colorInt = getContext().getColor(mButtonColorResourceId);
-            mainButton.setBackgroundColor(colorInt);
-            dismissButton.setTextColor(colorInt);
-        }
-
-        if (mIconResourceId != 0) {
-            ((ImageView) view.findViewById(R.id.icon)).setImageResource(mIconResourceId);
-        }
-    }
-}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsController.java
index 821c868..405b786 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsController.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsController.java
@@ -25,6 +25,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.widget.TipCardPreference;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 /** Controls the update for battery tips card */
@@ -50,10 +51,9 @@
         void onAnomalyReject();
     }
 
-    private OnAnomalyConfirmListener mOnAnomalyConfirmListener;
-    private OnAnomalyRejectListener mOnAnomalyRejectListener;
-
-    @VisibleForTesting BatteryTipsCardPreference mCardPreference;
+    @VisibleForTesting OnAnomalyConfirmListener mOnAnomalyConfirmListener;
+    @VisibleForTesting OnAnomalyRejectListener mOnAnomalyRejectListener;
+    @VisibleForTesting TipCardPreference mCardPreference;
     @VisibleForTesting AnomalyEventWrapper mAnomalyEventWrapper = null;
     @VisibleForTesting Boolean mIsAcceptable = false;
 
@@ -117,42 +117,20 @@
             return;
         }
 
-        // Set battery tips card listener
-        mCardPreference.setOnConfirmListener(
+        mCardPreference.setPrimaryButtonAction(
                 () -> {
-                    mCardPreference.setVisible(false);
-                    if (mOnAnomalyConfirmListener != null) {
-                        mOnAnomalyConfirmListener.onAnomalyConfirm();
-                    } else if (mAnomalyEventWrapper.updateSystemSettingsIfAvailable()
-                            || mAnomalyEventWrapper.launchSubSetting()) {
-                        mMetricsFeatureProvider.action(
-                                /* attribution= */ SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                                /* action= */ SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT,
-                                /* pageId= */ SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                                /* key= */ ANOMALY_KEY,
-                                /* value= */ anomalyKeyNumber);
-                    }
+                    onBatteryTipsCardDismiss(anomalyKeyNumber);
+                    return null;
                 });
-        mCardPreference.setOnRejectListener(
+        mCardPreference.setSecondaryButtonAction(
                 () -> {
-                    mCardPreference.setVisible(false);
-                    if (mOnAnomalyRejectListener != null) {
-                        mOnAnomalyRejectListener.onAnomalyReject();
-                    }
-                    // For anomaly events with same record key, dismissed until next time full
-                    // charged.
-                    final String dismissRecordKey = mAnomalyEventWrapper.getDismissRecordKey();
-                    if (!TextUtils.isEmpty(dismissRecordKey)) {
-                        DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, dismissRecordKey);
-                    }
-                    mMetricsFeatureProvider.action(
-                            /* attribution= */ SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                            /* action= */ SettingsEnums.ACTION_BATTERY_TIPS_CARD_DISMISS,
-                            /* pageId= */ SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                            /* key= */ ANOMALY_KEY,
-                            /* value= */ anomalyKeyNumber);
+                    onBatteryTipsCardAccept(anomalyKeyNumber);
+                    return null;
                 });
 
+        mCardPreference.setPrimaryButtonVisibility(true);
+        mCardPreference.setSecondaryButtonVisibility(true);
+        mCardPreference.buildContent();
         mCardPreference.setVisible(true);
         mMetricsFeatureProvider.action(
                 /* attribution= */ SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
@@ -161,4 +139,37 @@
                 /* key= */ ANOMALY_KEY,
                 /* value= */ anomalyKeyNumber);
     }
+
+    private void onBatteryTipsCardDismiss(final int anomalyKeyNumber) {
+        mCardPreference.setVisible(false);
+        if (mOnAnomalyRejectListener != null) {
+            mOnAnomalyRejectListener.onAnomalyReject();
+        }
+        // For anomaly events with same record key, dismissed until next time full charged.
+        final String dismissRecordKey = mAnomalyEventWrapper.getDismissRecordKey();
+        if (!TextUtils.isEmpty(dismissRecordKey)) {
+            DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, dismissRecordKey);
+        }
+        mMetricsFeatureProvider.action(
+                /* attribution= */ SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
+                /* action= */ SettingsEnums.ACTION_BATTERY_TIPS_CARD_DISMISS,
+                /* pageId= */ SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
+                /* key= */ ANOMALY_KEY,
+                /* value= */ anomalyKeyNumber);
+    }
+
+    private void onBatteryTipsCardAccept(final int anomalyKeyNumber) {
+        mCardPreference.setVisible(false);
+        if (mOnAnomalyConfirmListener != null) {
+            mOnAnomalyConfirmListener.onAnomalyConfirm();
+        } else if (mAnomalyEventWrapper.updateSystemSettingsIfAvailable()
+                || mAnomalyEventWrapper.launchSubSetting()) {
+            mMetricsFeatureProvider.action(
+                    /* attribution= */ SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
+                    /* action= */ SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT,
+                    /* pageId= */ SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
+                    /* key= */ ANOMALY_KEY,
+                    /* value= */ anomalyKeyNumber);
+        }
+    }
 }
diff --git a/src/com/android/settings/network/ims/VolteQueryImsState.java b/src/com/android/settings/network/ims/VolteQueryImsState.java
index b09a130..5acf2db 100644
--- a/src/com/android/settings/network/ims/VolteQueryImsState.java
+++ b/src/com/android/settings/network/ims/VolteQueryImsState.java
@@ -19,6 +19,7 @@
 import static android.telephony.ims.ProvisioningManager.KEY_VOIMS_OPT_IN_STATUS;
 
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.telecom.TelecomManager;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.SubscriptionManager;
@@ -143,6 +144,11 @@
      * @return true when VoIMS opt-in has been enabled, otherwise false
      */
     public boolean isVoImsOptInEnabled() {
+        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS)) {
+            // If the device does not have PackageManager.FEATURE_TELEPHONY_IMS,
+            // ProvisioningManager.getProvisioningIntValue() could not be called.
+            return false;
+        }
         int voImsOptInStatus = ProvisioningManager.createForSubscriptionId(mSubId)
                 .getProvisioningIntValue(KEY_VOIMS_OPT_IN_STATUS);
         return voImsOptInStatus == ProvisioningManager.PROVISIONING_VALUE_ENABLED;
diff --git a/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java b/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java
index e92d999..7d5230d 100644
--- a/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java
+++ b/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java
@@ -19,9 +19,11 @@
 import static androidx.lifecycle.Lifecycle.Event.ON_START;
 import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
 
+import static com.android.settings.network.telephony.EnabledNetworkModePreferenceControllerHelperKt.getNetworkModePreferenceType;
 import static com.android.settings.network.telephony.EnabledNetworkModePreferenceControllerHelperKt.setAllowedNetworkTypes;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionInfo;
@@ -44,6 +46,7 @@
 
 import com.android.internal.telephony.flags.Flags;
 import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
 import com.android.settings.network.AllowedNetworkTypesListener;
 import com.android.settings.network.CarrierConfigCache;
 import com.android.settings.network.SubscriptionsChangeListener;
@@ -61,16 +64,17 @@
  * Preference controller for "Enabled network mode"
  */
 public class EnabledNetworkModePreferenceController extends
-        TelephonyBasePreferenceController implements
+        BasePreferenceController implements
         ListPreference.OnPreferenceChangeListener, LifecycleObserver,
         SubscriptionsChangeListener.SubscriptionsChangeListenerClient {
 
     private static final String LOG_TAG = "EnabledNetworkMode";
+
+    private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     private AllowedNetworkTypesListener mAllowedNetworkTypesListener;
     private Preference mPreference;
     private PreferenceScreen mPreferenceScreen;
     private TelephonyManager mTelephonyManager;
-    private CarrierConfigCache mCarrierConfigCache;
     private PreferenceEntriesBuilder mBuilder;
     private SubscriptionsChangeListener mSubscriptionsListener;
     private int mCallState = TelephonyManager.CALL_STATE_IDLE;
@@ -81,36 +85,16 @@
     public EnabledNetworkModePreferenceController(Context context, String key) {
         super(context, key);
         mSubscriptionsListener = new SubscriptionsChangeListener(context, this);
-        mCarrierConfigCache = CarrierConfigCache.getInstance(context);
         if (mTelephonyCallback == null) {
             mTelephonyCallback = new PhoneCallStateTelephonyCallback();
         }
     }
 
     @Override
-    public int getAvailabilityStatus(int subId) {
-        boolean visible;
-
-        final PersistableBundle carrierConfig = mCarrierConfigCache.getConfigForSubId(subId);
-        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-            visible = false;
-        } else if (carrierConfig == null
-                || !CarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfig)) {
-            visible = false;
-        } else if (carrierConfig.getBoolean(
-                CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL)
-                || carrierConfig.getBoolean(
-                CarrierConfigManager.KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL)) {
-            visible = false;
-        } else if (carrierConfig.getBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL)) {
-            visible = false;
-        } else if (!isCallStateIdle()) {
-            return AVAILABLE_UNSEARCHABLE;
-        } else {
-            visible = true;
-        }
-
-        return visible ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+    public int getAvailabilityStatus() {
+        return getNetworkModePreferenceType(mContext, mSubId)
+                == NetworkModePreferenceType.EnabledNetworkMode
+                ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
     }
 
     protected boolean isCallStateIdle() {
@@ -953,9 +937,14 @@
         }
     }
 
+    /**
+     * Returns the resources associated with Subscription.
+     *
+     * @return Resources associated with Subscription.
+     */
     @VisibleForTesting
-    PhoneCallStateTelephonyCallback getTelephonyCallback() {
-        return mTelephonyCallback;
+    Resources getResourcesForSubId() {
+        return SubscriptionManager.getResourcesForSubId(mContext, mSubId);
     }
 
     @Override
diff --git a/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceControllerHelper.kt b/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceControllerHelper.kt
index eab5d74..d297e1c 100644
--- a/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceControllerHelper.kt
+++ b/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceControllerHelper.kt
@@ -16,9 +16,15 @@
 
 package com.android.settings.network.telephony
 
+import android.content.Context
+import android.telephony.CarrierConfigManager
+import android.telephony.SubscriptionManager
 import android.telephony.TelephonyManager
 import androidx.lifecycle.LifecycleOwner
 import androidx.lifecycle.lifecycleScope
+import com.android.settings.R
+import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem
+import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchResult
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 
@@ -33,3 +39,62 @@
         )
     }
 }
+
+enum class NetworkModePreferenceType {
+    EnabledNetworkMode,
+    PreferredNetworkMode,
+    None,
+}
+
+fun getNetworkModePreferenceType(context: Context, subId: Int): NetworkModePreferenceType {
+    if (!SubscriptionManager.isValidSubscriptionId(subId)) return NetworkModePreferenceType.None
+    data class Config(
+        val carrierConfigApplied: Boolean,
+        val hideCarrierNetworkSettings: Boolean,
+        val hidePreferredNetworkType: Boolean,
+        val worldPhone: Boolean,
+    )
+
+    val config =
+        CarrierConfigRepository(context).transformConfig(subId) {
+            Config(
+                carrierConfigApplied =
+                    getBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL),
+                hideCarrierNetworkSettings =
+                    getBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL),
+                hidePreferredNetworkType =
+                    getBoolean(CarrierConfigManager.KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL),
+                worldPhone = getBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL),
+            )
+        }
+
+    return when {
+        !config.carrierConfigApplied ||
+            config.hideCarrierNetworkSettings ||
+            config.hidePreferredNetworkType -> NetworkModePreferenceType.None
+        config.worldPhone -> NetworkModePreferenceType.PreferredNetworkMode
+        else -> NetworkModePreferenceType.EnabledNetworkMode
+    }
+}
+
+class PreferredNetworkModeSearchItem(private val context: Context) :
+    MobileNetworkSettingsSearchItem {
+    private val title: String = context.getString(R.string.preferred_network_mode_title)
+
+    override fun getSearchResult(subId: Int): MobileNetworkSettingsSearchResult? =
+        when (getNetworkModePreferenceType(context, subId)) {
+            NetworkModePreferenceType.PreferredNetworkMode ->
+                MobileNetworkSettingsSearchResult(
+                    key = "preferred_network_mode_key",
+                    title = title,
+                )
+
+            NetworkModePreferenceType.EnabledNetworkMode ->
+                MobileNetworkSettingsSearchResult(
+                    key = "enabled_networks_key",
+                    title = title,
+                )
+
+            else -> null
+        }
+}
diff --git a/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt b/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt
index c929d5c..220218c 100644
--- a/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt
+++ b/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt
@@ -20,11 +20,13 @@
 import android.telephony.SubscriptionManager
 import android.telephony.TelephonyManager
 import android.telephony.data.ApnSetting
+import androidx.annotation.VisibleForTesting
 import androidx.lifecycle.LifecycleOwner
 import androidx.preference.PreferenceScreen
 import com.android.settings.R
 import com.android.settings.Settings.MobileNetworkActivity.EXTRA_MMS_MESSAGE
 import com.android.settings.core.TogglePreferenceController
+import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchResult
 import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem
 import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
 import kotlinx.coroutines.flow.combine
@@ -109,7 +111,7 @@
         }
 
         class MmsMessageSearchItem(
-            context: Context,
+            private val context: Context,
             private val getDefaultDataSubId: () -> Int = {
                 SubscriptionManager.getDefaultDataSubscriptionId()
             },
@@ -117,12 +119,18 @@
             private var telephonyManager: TelephonyManager =
                 context.getSystemService(TelephonyManager::class.java)!!
 
-            override val key: String = EXTRA_MMS_MESSAGE
-            override val title: String = context.getString(R.string.mms_message_title)
-
-            override fun isAvailable(subId: Int): Boolean =
+            @VisibleForTesting
+            fun isAvailable(subId: Int): Boolean =
                 getAvailabilityStatus(
                     telephonyManager.createForSubscriptionId(subId), subId, getDefaultDataSubId)
+
+            override fun getSearchResult(subId: Int): MobileNetworkSettingsSearchResult? {
+                if (!isAvailable(subId)) return null
+                return MobileNetworkSettingsSearchResult(
+                    key = EXTRA_MMS_MESSAGE,
+                    title = context.getString(R.string.mms_message_title),
+                )
+            }
         }
     }
 }
diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt b/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt
index 4e97d31..58661f0 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt
+++ b/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt
@@ -39,15 +39,14 @@
     private val searchItemsFactory: (context: Context) -> List<MobileNetworkSettingsSearchItem> =
         ::createSearchItems,
 ) {
+    data class MobileNetworkSettingsSearchResult(
+        val key: String,
+        val title: String,
+        val keywords: String? = null,
+    )
+
     interface MobileNetworkSettingsSearchItem {
-        val key: String
-
-        val title: String
-
-        val keywords: String?
-            get() = null
-
-        fun isAvailable(subId: Int): Boolean
+        fun getSearchResult(subId: Int): MobileNetworkSettingsSearchResult?
     }
 
     fun createSearchIndexableData(): SearchIndexableData {
@@ -71,13 +70,15 @@
         searchItem: MobileNetworkSettingsSearchItem,
         subInfos: List<SubscriptionInfo>
     ): List<SearchIndexableRaw> =
-        subInfos
-            .filter { searchItem.isAvailable(it.subscriptionId) }
-            .map { subInfo -> searchIndexableRaw(context, searchItem, subInfo) }
+        subInfos.mapNotNull { subInfo ->
+            searchItem.getSearchResult(subInfo.subscriptionId)?.let { searchResult ->
+                searchIndexableRaw(context, searchResult, subInfo)
+            }
+        }
 
     private fun searchIndexableRaw(
         context: Context,
-        searchItem: MobileNetworkSettingsSearchItem,
+        searchResult: MobileNetworkSettingsSearchResult,
         subInfo: SubscriptionInfo,
     ): SearchIndexableRaw {
         val key =
@@ -85,7 +86,7 @@
                 .setFragment(
                     SpaSearchLandingFragment.newBuilder()
                         .setFragmentName(MobileNetworkSettings::class.java.name)
-                        .setPreferenceKey(searchItem.key)
+                        .setPreferenceKey(searchResult.key)
                         .putArguments(
                             Settings.EXTRA_SUB_ID,
                             BundleValue.newBuilder().setIntValue(subInfo.subscriptionId).build()))
@@ -94,8 +95,8 @@
         return createSearchIndexableRaw(
             context = context,
             spaSearchLandingKey = key,
-            itemTitle = searchItem.title,
-            keywords = searchItem.keywords,
+            itemTitle = searchResult.title,
+            keywords = searchResult.keywords,
             indexableClass = MobileNetworkSettings::class.java,
             pageTitle = "$simsTitle > ${subInfo.displayName}",
         )
@@ -115,6 +116,7 @@
             listOf(
                 MmsMessageSearchItem(context),
                 NrAdvancedCallingSearchItem(context),
+                PreferredNetworkModeSearchItem(context),
                 RoamingSearchItem(context),
                 WifiCallingSearchItem(context),
             )
diff --git a/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceController.kt b/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceController.kt
index 5c94e84..0d8766e 100644
--- a/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceController.kt
+++ b/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceController.kt
@@ -25,6 +25,7 @@
 import androidx.compose.ui.res.stringResource
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.settings.R
+import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchResult
 import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem
 import com.android.settings.spa.preference.ComposePreferenceController
 import com.android.settingslib.spa.widget.preference.SwitchPreference
@@ -79,12 +80,17 @@
     companion object {
         class NrAdvancedCallingSearchItem(private val context: Context) :
             MobileNetworkSettingsSearchItem {
-            override val key = "nr_advanced_calling"
-            override val title: String = context.getString(R.string.nr_advanced_calling_title)
-            override val keywords: String = context.getString(R.string.keywords_nr_advanced_calling)
 
-            override fun isAvailable(subId: Int): Boolean =
-                VoNrRepository(context, subId).isVoNrAvailable()
+            fun isAvailable(subId: Int): Boolean = VoNrRepository(context, subId).isVoNrAvailable()
+
+            override fun getSearchResult(subId: Int): MobileNetworkSettingsSearchResult? {
+                if (!isAvailable(subId)) return null
+                return MobileNetworkSettingsSearchResult(
+                    key = "nr_advanced_calling",
+                    title = context.getString(R.string.nr_advanced_calling_title),
+                    keywords = context.getString(R.string.keywords_nr_advanced_calling),
+                )
+            }
         }
     }
 }
diff --git a/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceController.java b/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceController.java
index bdfeace..210cd87 100644
--- a/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceController.java
+++ b/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceController.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.network.telephony;
 
+import static com.android.settings.network.telephony.EnabledNetworkModePreferenceControllerHelperKt.getNetworkModePreferenceType;
+
 import android.content.Context;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
@@ -27,16 +29,18 @@
 import androidx.preference.Preference;
 
 import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
 import com.android.settings.network.CarrierConfigCache;
 import com.android.settings.network.telephony.TelephonyConstants.TelephonyManagerConstants;
 
 /**
  * Preference controller for "Preferred network mode"
  */
-public class PreferredNetworkModePreferenceController extends TelephonyBasePreferenceController
+public class PreferredNetworkModePreferenceController extends BasePreferenceController
         implements ListPreference.OnPreferenceChangeListener {
     private static final String TAG = "PrefNetworkModeCtrl";
 
+    private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     private CarrierConfigCache mCarrierConfigCache;
     private TelephonyManager mTelephonyManager;
     private boolean mIsGlobalCdma;
@@ -47,25 +51,10 @@
     }
 
     @Override
-    public int getAvailabilityStatus(int subId) {
-        final PersistableBundle carrierConfig = mCarrierConfigCache.getConfigForSubId(subId);
-        boolean visible;
-        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-            visible = false;
-        } else if (carrierConfig == null) {
-            visible = false;
-        } else if (carrierConfig.getBoolean(
-                CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL)
-                || carrierConfig.getBoolean(
-                CarrierConfigManager.KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL)) {
-            visible = false;
-        } else if (carrierConfig.getBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL)) {
-            visible = true;
-        } else {
-            visible = false;
-        }
-
-        return visible ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+    public int getAvailabilityStatus() {
+        return getNetworkModePreferenceType(mContext, mSubId)
+                == NetworkModePreferenceType.PreferredNetworkMode
+                ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/network/telephony/RoamingPreferenceController.kt b/src/com/android/settings/network/telephony/RoamingPreferenceController.kt
index 7633677..a5ac7d6 100644
--- a/src/com/android/settings/network/telephony/RoamingPreferenceController.kt
+++ b/src/com/android/settings/network/telephony/RoamingPreferenceController.kt
@@ -29,6 +29,7 @@
 import androidx.fragment.app.FragmentManager
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.settings.R
+import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchResult
 import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem
 import com.android.settings.spa.preference.ComposePreferenceController
 import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
@@ -98,16 +99,21 @@
     companion object {
         private const val DIALOG_TAG = "MobileDataDialog"
 
-        class RoamingSearchItem(context: Context) : MobileNetworkSettingsSearchItem {
-            override val key = "button_roaming_key"
-            override val title: String = context.getString(R.string.roaming)
-
+        class RoamingSearchItem(private val context: Context) : MobileNetworkSettingsSearchItem {
             private val carrierConfigRepository = CarrierConfigRepository(context)
 
-            override fun isAvailable(subId: Int): Boolean =
+            fun isAvailable(subId: Int): Boolean =
                 SubscriptionManager.isValidSubscriptionId(subId) &&
                     !carrierConfigRepository.getBoolean(
                         subId, CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL)
+
+            override fun getSearchResult(subId: Int): MobileNetworkSettingsSearchResult? {
+                if (!isAvailable(subId)) return null
+                return MobileNetworkSettingsSearchResult(
+                    key = "button_roaming_key",
+                    title = context.getString(R.string.roaming),
+                )
+            }
         }
     }
 }
diff --git a/src/com/android/settings/network/telephony/TelephonyBasePreferenceController.java b/src/com/android/settings/network/telephony/TelephonyBasePreferenceController.java
index 3972f39..ee15520 100644
--- a/src/com/android/settings/network/telephony/TelephonyBasePreferenceController.java
+++ b/src/com/android/settings/network/telephony/TelephonyBasePreferenceController.java
@@ -17,9 +17,6 @@
 package com.android.settings.network.telephony;
 
 import android.content.Context;
-import android.content.res.Resources;
-import android.os.PersistableBundle;
-import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 
 import com.android.settings.core.BasePreferenceController;
@@ -59,29 +56,4 @@
     public void unsetAvailabilityStatus() {
         mSetSessionCount.getAndDecrement();
     }
-
-    /**
-     * Get carrier config based on specific subscription id.
-     *
-     * @param subId is the subscription id
-     * @return {@link PersistableBundle} of carrier config, or {@code null} when carrier config
-     * is not available.
-     */
-    public PersistableBundle getCarrierConfigForSubId(int subId) {
-        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
-            return null;
-        }
-        final CarrierConfigManager carrierConfigMgr =
-                mContext.getSystemService(CarrierConfigManager.class);
-        return carrierConfigMgr.getConfigForSubId(subId);
-    }
-
-    /**
-     * Returns the resources associated with Subscription.
-     *
-     * @return Resources associated with Subscription.
-     */
-    public Resources getResourcesForSubId() {
-        return SubscriptionManager.getResourcesForSubId(mContext, mSubId);
-    }
 }
diff --git a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt
index 7e8e58c..e04763a 100644
--- a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt
+++ b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt
@@ -27,6 +27,7 @@
 import androidx.preference.PreferenceScreen
 import com.android.settings.R
 import com.android.settings.core.BasePreferenceController
+import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchResult
 import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem
 import com.android.settings.network.telephony.wificalling.WifiCallingRepository
 import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
@@ -132,12 +133,17 @@
         class WifiCallingSearchItem(
             private val context: Context,
         ) : MobileNetworkSettingsSearchItem {
-            override val key: String = "wifi_calling"
-            override val title: String = context.getString(R.string.wifi_calling_settings_title)
-
-            override fun isAvailable(subId: Int): Boolean = runBlocking {
+            private fun isAvailable(subId: Int): Boolean = runBlocking {
                 WifiCallingRepository(context, subId).wifiCallingReadyFlow().first()
             }
+
+            override fun getSearchResult(subId: Int): MobileNetworkSettingsSearchResult? {
+                if (!isAvailable(subId)) return null
+                return MobileNetworkSettingsSearchResult(
+                    key = "wifi_calling",
+                    title = context.getString(R.string.wifi_calling_settings_title),
+                )
+            }
         }
     }
 }
diff --git a/src/com/android/settings/notification/modes/SetupInterstitialActivity.java b/src/com/android/settings/notification/modes/SetupInterstitialActivity.java
index 028e15b..225ded0 100644
--- a/src/com/android/settings/notification/modes/SetupInterstitialActivity.java
+++ b/src/com/android/settings/notification/modes/SetupInterstitialActivity.java
@@ -115,18 +115,21 @@
         if (intent == null) {
             Log.w(TAG, "no intent found for modes interstitial");
             finish();
+            return;
         }
 
         String modeId = intent.getStringExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID);
         if (modeId == null) {
             Log.w(TAG, "no mode id included in intent: " + intent);
             finish();
+            return;
         }
 
         ZenMode mode = mBackend.getMode(modeId);
         if (mode == null) {
             Log.w(TAG, "mode not found for mode id: " + modeId);
             finish();
+            return;
         }
         setTitle(mode.getName());
 
@@ -237,11 +240,11 @@
             // Don't come back to this activity after sending the user to the modes page, if
             // they happen to go back. Forward the activity result in case we got here (indirectly)
             // from some app that is waiting for the result.
-            finish();
             if (updated) {
                 ZenSubSettingLauncher.forMode(this, modeId)
                         .addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT).launch();
             }
+            finish();
         };
     }
 
diff --git a/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java b/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java
index eec6808..c5300da 100644
--- a/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java
+++ b/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java
@@ -514,7 +514,7 @@
             return msgFormat.format(args);
         } else {
             int automaticModeCount = (int) modes.stream()
-                    .filter(m -> !m.isManualDnd() && !m.isCustomManual())
+                    .filter(m -> m.isEnabled() && !m.isManualDnd() && !m.isCustomManual())
                     .count();
 
             MessageFormat msgFormat = new MessageFormat(
diff --git a/src/com/android/settings/notification/modes/ZenModesListItemPreference.java b/src/com/android/settings/notification/modes/ZenModesListItemPreference.java
index 9de56b1..e09d04c 100644
--- a/src/com/android/settings/notification/modes/ZenModesListItemPreference.java
+++ b/src/com/android/settings/notification/modes/ZenModesListItemPreference.java
@@ -74,15 +74,16 @@
         }
 
         setTitle(mZenMode.getName());
+        String dynamicDescription = zenMode.getDynamicDescription(mContext);
         CharSequence statusText = switch (mZenMode.getStatus()) {
             case ENABLED_AND_ACTIVE ->
-                    Strings.isNullOrEmpty(mZenMode.getTriggerDescription())
+                    Strings.isNullOrEmpty(dynamicDescription)
                             ? mContext.getString(R.string.zen_mode_active_text)
                             : mContext.getString(
                                     R.string.zen_mode_format_status_and_trigger,
                                     mContext.getString(R.string.zen_mode_active_text),
-                                    mZenMode.getRule().getTriggerDescription());
-            case ENABLED -> mZenMode.getRule().getTriggerDescription();
+                                    dynamicDescription);
+            case ENABLED -> dynamicDescription;
             case DISABLED_BY_USER -> mContext.getString(R.string.zen_mode_disabled_by_user);
             case DISABLED_BY_OTHER -> mContext.getString(R.string.zen_mode_disabled_needs_setup);
         };
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
index 4f35532..72dbed4 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
@@ -104,6 +104,7 @@
     private boolean mForceVerifyPath = false;
     private boolean mGoingToBackground;
     private boolean mWaitingForBiometricCallback;
+    private int mBiometricsAuthenticators;
 
     private Executor mExecutor = (runnable -> {
         mHandler.post(runnable);
@@ -122,8 +123,14 @@
                     Log.i(TAG, "Finishing, user no longer valid: " + mUserId);
                     finish();
                 } else {
-                    // All other errors go to some version of CC
-                    showConfirmCredentials();
+                    if ((mBiometricsAuthenticators
+                            & BiometricManager.Authenticators.DEVICE_CREDENTIAL) != 0) {
+                        // All other errors go to some version of CC
+                        showConfirmCredentials();
+                    } else {
+                        Log.i(TAG, "Finishing, device credential not requested");
+                        finish();
+                    }
                 }
             } else if (mWaitingForBiometricCallback) { // mGoingToBackground is true
                 mWaitingForBiometricCallback = false;
@@ -188,7 +195,7 @@
         mDetails = intent.getCharSequenceExtra(KeyguardManager.EXTRA_DESCRIPTION);
         String alternateButton = intent.getStringExtra(
                 KeyguardManager.EXTRA_ALTERNATE_BUTTON_LABEL);
-        final int authenticators = intent.getIntExtra(BIOMETRIC_PROMPT_AUTHENTICATORS,
+        mBiometricsAuthenticators = intent.getIntExtra(BIOMETRIC_PROMPT_AUTHENTICATORS,
                 BiometricManager.Authenticators.DEVICE_CREDENTIAL
                         | BiometricManager.Authenticators.BIOMETRIC_WEAK);
         final String negativeButtonText = intent.getStringExtra(
@@ -229,7 +236,7 @@
         promptInfo.setTitle(mTitle);
         promptInfo.setDescription(mDetails);
         promptInfo.setDisallowBiometricsIfPolicyExists(mCheckDevicePolicyManager);
-        promptInfo.setAuthenticators(authenticators);
+        promptInfo.setAuthenticators(mBiometricsAuthenticators);
         promptInfo.setNegativeButtonText(negativeButtonText);
 
         if (android.multiuser.Flags.enablePrivateSpaceFeatures()
diff --git a/src/com/android/settings/password/ConfirmLockPassword.java b/src/com/android/settings/password/ConfirmLockPassword.java
index b9b1810..ead79d7 100644
--- a/src/com/android/settings/password/ConfirmLockPassword.java
+++ b/src/com/android/settings/password/ConfirmLockPassword.java
@@ -64,6 +64,7 @@
 import com.android.internal.widget.LockscreenCredential;
 import com.android.internal.widget.TextViewInputDisabler;
 import com.android.settings.R;
+import com.android.settings.SetupRedactionInterstitial;
 import com.android.settingslib.animation.AppearAnimationUtils;
 import com.android.settingslib.animation.DisappearAnimationUtils;
 
@@ -723,6 +724,7 @@
             Intent result = new Intent();
             if (mRemoteValidation && containsGatekeeperPasswordHandle(resultData)) {
                 result.putExtra(EXTRA_KEY_GK_PW_HANDLE, getGatekeeperPasswordHandle(resultData));
+                SetupRedactionInterstitial.setEnabled(getContext(), true);
             }
             mCredentialCheckResultTracker.setResult(/* matched= */ true, result,
                     /* timeoutMs= */ 0, mEffectiveUserId);
diff --git a/src/com/android/settings/password/ConfirmLockPattern.java b/src/com/android/settings/password/ConfirmLockPattern.java
index 3415478..28d790e 100644
--- a/src/com/android/settings/password/ConfirmLockPattern.java
+++ b/src/com/android/settings/password/ConfirmLockPattern.java
@@ -54,6 +54,7 @@
 import com.android.internal.widget.LockPatternView.Cell;
 import com.android.internal.widget.LockscreenCredential;
 import com.android.settings.R;
+import com.android.settings.SetupRedactionInterstitial;
 import com.android.settingslib.animation.AppearAnimationCreator;
 import com.android.settingslib.animation.AppearAnimationUtils;
 import com.android.settingslib.animation.DisappearAnimationUtils;
@@ -751,6 +752,7 @@
             Intent result = new Intent();
             if (mRemoteValidation && containsGatekeeperPasswordHandle(resultData)) {
                 result.putExtra(EXTRA_KEY_GK_PW_HANDLE, getGatekeeperPasswordHandle(resultData));
+                SetupRedactionInterstitial.setEnabled(getContext(), true);
             }
             mCredentialCheckResultTracker.setResult(/* matched= */ true, result,
                     /* timeoutMs= */ 0, mEffectiveUserId);
diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java
index ec60f44..8720160 100644
--- a/src/com/android/settings/slices/SettingsSliceProvider.java
+++ b/src/com/android/settings/slices/SettingsSliceProvider.java
@@ -120,6 +120,16 @@
      * permission can use them.
      */
     private static final List<Uri> PUBLICLY_SUPPORTED_CUSTOM_SLICE_URIS =
+            android.app.Flags.modesUi()
+                    ?
+                    Arrays.asList(
+                            CustomSliceRegistry.BLUETOOTH_URI,
+                            CustomSliceRegistry.FLASHLIGHT_SLICE_URI,
+                            CustomSliceRegistry.LOCATION_SLICE_URI,
+                            CustomSliceRegistry.MOBILE_DATA_SLICE_URI,
+                            CustomSliceRegistry.WIFI_CALLING_URI,
+                            CustomSliceRegistry.WIFI_SLICE_URI
+                    ) :
             Arrays.asList(
                     CustomSliceRegistry.BLUETOOTH_URI,
                     CustomSliceRegistry.FLASHLIGHT_SLICE_URI,
@@ -184,7 +194,9 @@
         }
 
         if (CustomSliceRegistry.ZEN_MODE_SLICE_URI.equals(sliceUri)) {
-            registerIntentToUri(ZenModeSliceBuilder.INTENT_FILTER, sliceUri);
+            if (!android.app.Flags.modesUi()) {
+                registerIntentToUri(ZenModeSliceBuilder.INTENT_FILTER, sliceUri);
+            }
             return;
         } else if (CustomSliceRegistry.BLUETOOTH_URI.equals(sliceUri)) {
             registerIntentToUri(BluetoothSliceBuilder.INTENT_FILTER, sliceUri);
@@ -256,7 +268,8 @@
                         .getSlicesFeatureProvider()
                         .getNewWifiCallingSliceHelper(getContext())
                         .createWifiCallingSlice(sliceUri);
-            } else if (CustomSliceRegistry.ZEN_MODE_SLICE_URI.equals(sliceUri)) {
+            } else if (!android.app.Flags.modesUi()
+                    && CustomSliceRegistry.ZEN_MODE_SLICE_URI.equals(sliceUri)) {
                 return ZenModeSliceBuilder.getSlice(getContext());
             } else if (CustomSliceRegistry.BLUETOOTH_URI.equals(sliceUri)) {
                 return BluetoothSliceBuilder.getSlice(getContext());
diff --git a/src/com/android/settings/widget/TipCardPreference.kt b/src/com/android/settings/widget/TipCardPreference.kt
index 0ae7d2a..0ca0272 100644
--- a/src/com/android/settings/widget/TipCardPreference.kt
+++ b/src/com/android/settings/widget/TipCardPreference.kt
@@ -19,6 +19,7 @@
 import android.content.Context
 import android.content.res.Resources
 import android.util.AttributeSet
+import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.res.vectorResource
 import com.android.settings.spa.preference.ComposePreference
@@ -37,6 +38,9 @@
     /** A icon resource id for displaying icon on tips card. */
     var iconResId: Int? = null
 
+    /** A color resource id for displaying icon and button text on tips card. */
+    var tintColorResId: Int? = null
+
     /** The primary button's text. */
     var primaryButtonText: String = ""
 
@@ -85,6 +89,8 @@
                     title = title?.toString() ?: "",
                     text = summary?.toString() ?: "",
                     buttons = listOfNotNull(configPrimaryButton(), configSecondaryButton()),
+                    tintColor = tintColorResId?.let { Color(context.getColor(it)) }
+                        ?: Color.Unspecified,
                     onDismiss = onDismiss,
                     imageVector =
                     iconResId
diff --git a/tests/robotests/src/com/android/settings/accessibility/shortcuts/FloatingButtonShortcutOptionControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/shortcuts/FloatingButtonShortcutOptionControllerTest.java
index 20f5d5d..0952b09 100644
--- a/tests/robotests/src/com/android/settings/accessibility/shortcuts/FloatingButtonShortcutOptionControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/shortcuts/FloatingButtonShortcutOptionControllerTest.java
@@ -16,37 +16,46 @@
 
 package com.android.settings.accessibility.shortcuts;
 
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
-
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.spy;
+
 import android.content.ComponentName;
 import android.content.Context;
-import android.provider.Settings;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Flags;
 
 import androidx.preference.PreferenceManager;
 import androidx.preference.PreferenceScreen;
 import androidx.test.core.app.ApplicationProvider;
 
 import com.android.settings.R;
+import com.android.settings.testutils.AccessibilityTestUtils;
+import com.android.settings.testutils.shadow.SettingsShadowResources;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
 
 import java.util.Set;
 
 /**
  * Tests for {@link FloatingButtonShortcutOptionController}
  */
+@Config(shadows = SettingsShadowResources.class)
 @RunWith(RobolectricTestRunner.class)
 public class FloatingButtonShortcutOptionControllerTest {
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     private static final String PREF_KEY = "prefKey";
     private static final String TARGET =
             new ComponentName("FakePackage", "FakeClass").flattenToString();
-    private final Context mContext = ApplicationProvider.getApplicationContext();
+    private final Context mContext = spy(ApplicationProvider.getApplicationContext());
     private FloatingButtonShortcutOptionController mController;
     private ShortcutOptionPreference mShortcutOptionPreference;
 
@@ -61,7 +70,6 @@
         mShortcutOptionPreference.setKey(PREF_KEY);
         mPreferenceScreen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
         mPreferenceScreen.addPreference(mShortcutOptionPreference);
-        setFloatingButtonEnabled(true);
     }
 
     @Test
@@ -95,23 +103,26 @@
 
     @Test
     public void isShortcutAvailable_floatingMenuEnabled_returnTrue() {
-        setFloatingButtonEnabled(true);
+        AccessibilityTestUtils.setSoftwareShortcutMode(
+                mContext, /* gestureNavEnabled= */ false, /* floatingButtonEnabled= */ true);
 
         assertThat(mController.isShortcutAvailable()).isTrue();
     }
 
     @Test
     public void isShortcutAvailable_floatingMenuDisabled_returnFalse() {
-        setFloatingButtonEnabled(false);
+        AccessibilityTestUtils.setSoftwareShortcutMode(
+                mContext, /* gestureNavEnabled= */ false, /* floatingButtonEnabled= */ false);
 
         assertThat(mController.isShortcutAvailable()).isFalse();
     }
 
-    private void setFloatingButtonEnabled(boolean enable) {
-        int mode = enable
-                ? ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU : ACCESSIBILITY_BUTTON_MODE_GESTURE;
+    @Test
+    @EnableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
+    public void isShortcutAvailable_gestureNavigationMode_returnsTrue() {
+        AccessibilityTestUtils.setSoftwareShortcutMode(
+                mContext, /* gestureNavEnabled= */ true, /* floatingButtonEnabled= */ false);
 
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_BUTTON_MODE, mode);
+        assertThat(mController.isShortcutAvailable()).isTrue();
     }
 }
diff --git a/tests/robotests/src/com/android/settings/accessibility/shortcuts/GestureShortcutOptionControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/shortcuts/GestureShortcutOptionControllerTest.java
index 0149cc3..1d46cae 100644
--- a/tests/robotests/src/com/android/settings/accessibility/shortcuts/GestureShortcutOptionControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/shortcuts/GestureShortcutOptionControllerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.accessibility.shortcuts;
 
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
 import static com.android.settings.testutils.AccessibilityTestUtils.setupMockAccessibilityManager;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -25,6 +27,10 @@
 
 import android.content.ComponentName;
 import android.content.Context;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Flags;
 import android.view.accessibility.AccessibilityManager;
 
 import androidx.preference.PreferenceManager;
@@ -37,6 +43,7 @@
 import com.android.settingslib.utils.StringUtil;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
@@ -50,6 +57,8 @@
 @Config(shadows = SettingsShadowResources.class)
 @RunWith(RobolectricTestRunner.class)
 public class GestureShortcutOptionControllerTest {
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
     private static final String PREF_KEY = "prefKey";
     private static final String TARGET =
             new ComponentName("FakePackage", "FakeClass").flattenToString();
@@ -137,6 +146,18 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
+    public void getSummary_standaloneGestureFlagOn_verifyNoCustomizeA11yButtonTest() {
+        enableTouchExploration(true);
+        String expected = StringUtil.getIcuPluralsString(
+                mContext,
+                /* count= */ 3,
+                R.string.accessibility_shortcut_edit_dialog_summary_gesture);
+
+        assertThat(mController.getSummary().toString()).isEqualTo(expected);
+    }
+
+    @Test
     public void isShortcutAvailable_inSuw_returnFalse() {
         mController.setInSetupWizard(true);
 
@@ -144,6 +165,7 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
     public void isShortcutAvailable_notInSuwUseGestureNavSystemUseFab_returnFalse() {
         mController.setInSetupWizard(false);
         AccessibilityTestUtils.setSoftwareShortcutMode(
@@ -179,6 +201,28 @@
         assertThat(mController.isShortcutAvailable()).isFalse();
     }
 
+    @EnableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
+    @Test
+    public void isShortcutAvailable_floatingMenuEnabled_gestureNavEnabled_returnsTrue() {
+        mController.setInSetupWizard(false);
+        AccessibilityTestUtils.setSoftwareShortcutMode(
+                mContext, /* gestureNavEnabled= */ true, /* floatingButtonEnabled= */ true);
+
+        assertThat(mController.isShortcutAvailable()).isTrue();
+    }
+
+    @EnableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
+    @Test
+    public void getShortcutType_gesture() {
+        assertThat(mController.getShortcutType()).isEqualTo(GESTURE);
+    }
+
+    @DisableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
+    @Test
+    public void getShortcutType_software() {
+        assertThat(mController.getShortcutType()).isEqualTo(SOFTWARE);
+    }
+
     private void enableTouchExploration(boolean enable) {
         AccessibilityManager am = setupMockAccessibilityManager(mContext);
         when(am.isTouchExplorationEnabled()).thenReturn(enable);
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java
index b3e1c5d..ca76c1e 100644
--- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java
@@ -54,6 +54,7 @@
 import android.os.CancellationSignal;
 import android.os.Looper;
 import android.os.UserHandle;
+import android.os.Vibrator;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
@@ -128,6 +129,9 @@
             mAuthenticationCallbackArgumentCaptor = ArgumentCaptor.forClass(
             FingerprintManager.AuthenticationCallback.class);
 
+    @Mock
+    private Vibrator mVibrator;
+
     private FingerprintAuthenticateSidecar mFingerprintAuthenticateSidecar;
     private FingerprintRemoveSidecar mFingerprintRemoveSidecar;
 
@@ -141,6 +145,7 @@
         doReturn(mContext).when(mFragment).getContext();
         doReturn(mBiometricManager).when(mContext).getSystemService(BiometricManager.class);
         doReturn(true).when(mFingerprintManager).isHardwareDetected();
+        doReturn(mVibrator).when(mContext).getSystemService(Vibrator.class);
         when(mBiometricManager.canAuthenticate(
                 BiometricManager.Authenticators.MANDATORY_BIOMETRICS))
                 .thenReturn(BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE);
@@ -288,6 +293,28 @@
     }
 
     @Test
+    @Ignore("b/353726774")
+    public void fingerprintVibratesOnAuthSuccess() {
+        setUpFragment(false);
+
+        doNothing().when(mFingerprintManager).authenticate(any(),
+                mCancellationSignalArgumentCaptor.capture(),
+                mAuthenticationCallbackArgumentCaptor.capture(), any(), anyInt());
+
+        mFingerprintAuthenticateSidecar.startAuthentication(1);
+
+        assertThat(mAuthenticationCallbackArgumentCaptor.getValue()).isNotNull();
+        assertThat(mCancellationSignalArgumentCaptor.getValue()).isNotNull();
+
+        mAuthenticationCallbackArgumentCaptor.getValue()
+                .onAuthenticationSucceeded(new FingerprintManager.AuthenticationResult(null,
+                        new Fingerprint("finger 1", 1, 1), 0 /* userId */, false));
+
+        shadowOf(Looper.getMainLooper()).idle();
+        verify(mVibrator).vibrate(FingerprintSettings.SUCCESS_VIBRATION_EFFECT);
+    }
+
+    @Test
     public void testNotIndexable_whenDisabled() {
         doReturn(mPackageManager).when(mContext).getPackageManager();
         doReturn(false)
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java
index 03113421..ba90ccf 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java
@@ -18,10 +18,10 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 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.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -32,22 +32,31 @@
 import android.graphics.drawable.Drawable;
 import android.os.UserManager;
 import android.util.Pair;
-import android.view.ContextThemeWrapper;
+
+import androidx.test.core.app.ApplicationProvider;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
+import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 import org.robolectric.util.ReflectionHelpers;
 
@@ -57,18 +66,21 @@
 import java.util.List;
 
 @RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowAlertDialogCompat.class})
+@Config(shadows = {ShadowAlertDialogCompat.class,
+        com.android.settings.testutils.shadow.ShadowBluetoothUtils.class})
 public class BluetoothDevicePreferenceTest {
     private static final boolean SHOW_DEVICES_WITHOUT_NAMES = true;
-    private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C";
-    private static final String MAC_ADDRESS_2 = "05:52:C7:0B:D8:3C";
-    private static final String MAC_ADDRESS_3 = "06:52:C7:0B:D8:3C";
-    private static final String MAC_ADDRESS_4 = "07:52:C7:0B:D8:3C";
+    private static final String TEST_MAC_ADDRESS = "04:52:C7:0B:D8:3C";
+    private static final String TEST_MAC_ADDRESS_1 = "05:52:C7:0B:D8:3C";
+    private static final String TEST_MAC_ADDRESS_2 = "06:52:C7:0B:D8:3C";
+    private static final String TEST_MAC_ADDRESS_3 = "07:52:C7:0B:D8:3C";
     private static final Comparator<BluetoothDevicePreference> COMPARATOR =
             Comparator.naturalOrder();
     private static final String FAKE_DESCRIPTION = "fake_description";
+    private static final int TEST_DEVICE_GROUP_ID = 1;
 
-    private Context mContext;
+    @Rule
+    public final MockitoRule mockito = MockitoJUnit.rule();
     @Mock
     private CachedBluetoothDevice mCachedBluetoothDevice;
     @Mock
@@ -89,35 +101,37 @@
     private Drawable mDrawable;
     @Mock
     private BluetoothAdapter mBluetoothAdapter;
+    @Mock
+    private LocalBluetoothManager mLocalBluetoothManager;
+    @Mock
+    private CachedBluetoothDeviceManager mDeviceManager;
 
+    private Context mContext = ApplicationProvider.getApplicationContext();
     private FakeFeatureFactory mFakeFeatureFactory;
     private MetricsFeatureProvider mMetricsFeatureProvider;
+
     private BluetoothDevicePreference mPreference;
     private List<BluetoothDevicePreference> mPreferenceList = new ArrayList<>();
 
     @Before
     public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        Context context = spy(RuntimeEnvironment.application.getApplicationContext());
-        mContext = new ContextThemeWrapper(context, R.style.Theme_Settings);
+        mContext.setTheme(R.style.Theme_Settings);
         mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
         mMetricsFeatureProvider = mFakeFeatureFactory.getMetricsFeatureProvider();
-        when(mCachedBluetoothDevice.getAddress()).thenReturn(MAC_ADDRESS);
-        when(mCachedBluetoothDevice.getDrawableWithDescription())
-                .thenReturn(new Pair<>(mDrawable, FAKE_DESCRIPTION));
-        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
-        when(mCachedDevice1.getAddress()).thenReturn(MAC_ADDRESS_2);
-        when(mCachedDevice1.getDrawableWithDescription())
-                .thenReturn(new Pair<>(mDrawable, FAKE_DESCRIPTION));
-        when(mCachedDevice1.getDevice()).thenReturn(mBluetoothDevice1);
-        when(mCachedDevice2.getAddress()).thenReturn(MAC_ADDRESS_3);
-        when(mCachedDevice2.getDrawableWithDescription())
-                .thenReturn(new Pair<>(mDrawable, FAKE_DESCRIPTION));
-        when(mCachedDevice2.getDevice()).thenReturn(mBluetoothDevice2);
-        when(mCachedDevice3.getAddress()).thenReturn(MAC_ADDRESS_4);
-        when(mCachedDevice3.getDrawableWithDescription())
-                .thenReturn(new Pair<>(mDrawable, FAKE_DESCRIPTION));
-        when(mCachedDevice3.getDevice()).thenReturn(mBluetoothDevice3);
+        ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
+        mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
+        when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
+        prepareCachedBluetoothDevice(mCachedBluetoothDevice, TEST_MAC_ADDRESS,
+                new Pair<>(mDrawable, FAKE_DESCRIPTION), TEST_DEVICE_GROUP_ID, mBluetoothDevice);
+        prepareCachedBluetoothDevice(mCachedDevice1, TEST_MAC_ADDRESS_1,
+                new Pair<>(mDrawable, FAKE_DESCRIPTION), TEST_DEVICE_GROUP_ID, mBluetoothDevice1);
+        prepareCachedBluetoothDevice(mCachedDevice2, TEST_MAC_ADDRESS_2,
+                new Pair<>(mDrawable, FAKE_DESCRIPTION), TEST_DEVICE_GROUP_ID, mBluetoothDevice2);
+        prepareCachedBluetoothDevice(mCachedDevice3, TEST_MAC_ADDRESS_3,
+                new Pair<>(mDrawable, FAKE_DESCRIPTION), TEST_DEVICE_GROUP_ID, mBluetoothDevice3);
+        when(mDeviceManager.getCachedDevicesCopy()).thenReturn(
+                ImmutableList.of(mCachedBluetoothDevice));
+
         mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
                 SHOW_DEVICES_WITHOUT_NAMES, BluetoothDevicePreference.SortType.TYPE_DEFAULT);
         mPreference.mBluetoothAdapter = mBluetoothAdapter;
@@ -301,7 +315,8 @@
         // callback is not removed.
         mPreference.onAttached();
 
-        verify(mCachedBluetoothDevice, times(1)).registerCallback(any());
+        verify(mCachedBluetoothDevice, times(1)).registerCallback(eq(mContext.getMainExecutor()),
+                any());
         verify(mBluetoothAdapter, times(1)).addOnMetadataChangedListener(any(), any(), any());
     }
 
@@ -313,7 +328,99 @@
         mPreference.onAttached();
 
         verify(mCachedBluetoothDevice, times(1)).unregisterCallback(any());
-        verify(mCachedBluetoothDevice, times(2)).registerCallback(any());
+        verify(mCachedBluetoothDevice, times(2)).registerCallback(eq(mContext.getMainExecutor()),
+                any());
         verify(mBluetoothAdapter, times(2)).addOnMetadataChangedListener(any(), any(), any());
     }
+
+    @Test
+    public void onDeviceAttributesChanged_updatePreference() {
+        when(mCachedBluetoothDevice.getName()).thenReturn("Name");
+        mPreference.onAttached();
+        final String updatedName = "updatedName";
+        when(mCachedBluetoothDevice.getName()).thenReturn(updatedName);
+
+        getCachedBluetoothDeviceCallback().onDeviceAttributesChanged();
+
+        assertThat(mPreference.getTitle().toString()).isEqualTo(updatedName);
+    }
+
+    @Test
+    public void onAttached_memberDevicesAdded_registerAllCallback() {
+        when(mCachedBluetoothDevice.getMemberDevice()).thenReturn(
+                ImmutableSet.of(mCachedDevice1, mCachedDevice2, mCachedDevice3));
+        when(mDeviceManager.getCachedDevicesCopy()).thenReturn(
+                ImmutableList.of(mCachedBluetoothDevice, mCachedDevice1, mCachedDevice2,
+                        mCachedDevice3));
+        mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
+                SHOW_DEVICES_WITHOUT_NAMES, BluetoothDevicePreference.SortType.TYPE_DEFAULT);
+
+        mPreference.onAttached();
+
+        verify(mCachedBluetoothDevice).registerCallback(eq(mContext.getMainExecutor()), any());
+        verify(mCachedDevice1).registerCallback(eq(mContext.getMainExecutor()), any());
+        verify(mCachedDevice2).registerCallback(eq(mContext.getMainExecutor()), any());
+        verify(mCachedDevice3).registerCallback(eq(mContext.getMainExecutor()), any());
+    }
+
+    @Test
+    public void onDetached_memberDevicesAdded_unregisterAllCallback() {
+        when(mCachedBluetoothDevice.getMemberDevice()).thenReturn(
+                ImmutableSet.of(mCachedDevice1, mCachedDevice2, mCachedDevice3));
+        when(mDeviceManager.getCachedDevicesCopy()).thenReturn(
+                ImmutableList.of(mCachedBluetoothDevice, mCachedDevice1, mCachedDevice2,
+                        mCachedDevice3));
+        mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
+                SHOW_DEVICES_WITHOUT_NAMES, BluetoothDevicePreference.SortType.TYPE_DEFAULT);
+
+        mPreference.onAttached();
+        mPreference.onDetached();
+
+        verify(mCachedBluetoothDevice).unregisterCallback(any());
+        verify(mCachedDevice1).unregisterCallback(any());
+        verify(mCachedDevice2).unregisterCallback(any());
+        verify(mCachedDevice3).unregisterCallback(any());
+    }
+
+    @Test
+    public void onDeviceAttributesChanged_memberDevicesChanged_registerOnlyExistDeviceCallback() {
+        when(mCachedBluetoothDevice.getMemberDevice()).thenReturn(
+                ImmutableSet.of(mCachedDevice1, mCachedDevice2, mCachedDevice3));
+        when(mDeviceManager.getCachedDevicesCopy()).thenReturn(
+                ImmutableList.of(mCachedBluetoothDevice, mCachedDevice1, mCachedDevice2,
+                        mCachedDevice3));
+        mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
+                SHOW_DEVICES_WITHOUT_NAMES, BluetoothDevicePreference.SortType.TYPE_DEFAULT);
+        mPreference.onAttached();
+        when(mCachedBluetoothDevice.getMemberDevice()).thenReturn(
+                ImmutableSet.of(mCachedDevice1, mCachedDevice2));
+        when(mDeviceManager.getCachedDevicesCopy()).thenReturn(
+                ImmutableList.of(mCachedBluetoothDevice, mCachedDevice1, mCachedDevice2));
+
+        getCachedBluetoothDeviceCallback().onDeviceAttributesChanged();
+
+        verify(mCachedBluetoothDevice, times(2)).registerCallback(eq(mContext.getMainExecutor()),
+                any());
+        verify(mCachedDevice1, times(2)).registerCallback(eq(mContext.getMainExecutor()), any());
+        verify(mCachedDevice2, times(2)).registerCallback(eq(mContext.getMainExecutor()), any());
+        verify(mCachedDevice3, times(1)).registerCallback(eq(mContext.getMainExecutor()), any());
+    }
+
+    private void prepareCachedBluetoothDevice(CachedBluetoothDevice cachedDevice, String address,
+            Pair<Drawable, String> drawableWithDescription, int groupId,
+            BluetoothDevice bluetoothDevice) {
+        when(cachedDevice.getAddress()).thenReturn(address);
+        when(cachedDevice.getDrawableWithDescription()).thenReturn(drawableWithDescription);
+        when(cachedDevice.getGroupId()).thenReturn(groupId);
+        when(cachedDevice.getDevice()).thenReturn(bluetoothDevice);
+    }
+
+    private CachedBluetoothDevice.Callback getCachedBluetoothDeviceCallback() {
+        ArgumentCaptor<CachedBluetoothDevice.Callback> callbackCaptor = ArgumentCaptor.forClass(
+                CachedBluetoothDevice.Callback.class);
+        verify(mCachedBluetoothDevice).registerCallback(eq(mContext.getMainExecutor()),
+                callbackCaptor.capture());
+
+        return callbackCaptor.getValue();
+    }
 }
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 20c225c..7227f37 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragmentTest.java
@@ -93,7 +93,6 @@
             new Pair[] {TEST_EVENT_DATA};
 
     private Fragment mParent;
-    private AudioSharingDialogFragment mFragment;
     private FakeFeatureFactory mFeatureFactory;
 
     @Before
@@ -107,7 +106,6 @@
         shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
                 BluetoothStatusCodes.FEATURE_SUPPORTED);
         mFeatureFactory = FakeFeatureFactory.setupForTest();
-        mFragment = new AudioSharingDialogFragment();
         mParent = new Fragment();
         FragmentController.setupFragment(
                 mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null);
@@ -120,7 +118,8 @@
 
     @Test
     public void getMetricsCategory_correctValue() {
-        assertThat(mFragment.getMetricsCategory())
+        AudioSharingDialogFragment fragment = new AudioSharingDialogFragment();
+        assertThat(fragment.getMetricsCategory())
                 .isEqualTo(SettingsEnums.DIALOG_AUDIO_SHARING_ADD_DEVICE);
     }
 
@@ -145,7 +144,7 @@
     }
 
     @Test
-    public void onCreateDialog_flagOn_noConnectedDevice() {
+    public void onCreateDialog_flagOn_noExtraConnectedDevice() {
         mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
         AudioSharingDialogFragment.show(
                 mParent, new ArrayList<>(), EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST);
@@ -157,42 +156,67 @@
         assertThat(description).isNotNull();
         ImageView image = dialog.findViewById(R.id.description_image);
         assertThat(image).isNotNull();
-        Button shareBtn = dialog.findViewById(R.id.positive_btn);
-        assertThat(shareBtn).isNotNull();
-        Button cancelBtn = dialog.findViewById(R.id.negative_btn);
-        assertThat(cancelBtn).isNotNull();
+        Button positiveBtn = dialog.findViewById(R.id.positive_btn);
+        assertThat(positiveBtn).isNotNull();
+        Button negativeBtn = dialog.findViewById(R.id.negative_btn);
+        assertThat(negativeBtn).isNotNull();
         assertThat(dialog.isShowing()).isTrue();
         assertThat(description.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(description.getText().toString())
                 .isEqualTo(mParent.getString(R.string.audio_sharing_dialog_connect_device_content));
         assertThat(image.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(shareBtn.getVisibility()).isEqualTo(View.GONE);
-        assertThat(cancelBtn.getVisibility()).isEqualTo(View.GONE);
+        assertThat(positiveBtn.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(positiveBtn.getText().toString())
+                .isEqualTo(mParent.getString(R.string.audio_sharing_pair_button_label));
+        assertThat(negativeBtn.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(negativeBtn.getText().toString())
+                .isEqualTo(mParent.getString(R.string.audio_sharing_qrcode_button_label));
     }
 
     @Test
-    public void onCreateDialog_noConnectedDevice_dialogDismiss() {
+    public void onCreateDialog_noExtraConnectedDevice_pairNewDevice() {
         mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
         AudioSharingDialogFragment.show(
                 mParent, new ArrayList<>(), EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST);
         shadowMainLooper().idle();
         AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
         assertThat(dialog).isNotNull();
-        View btnView = dialog.findViewById(android.R.id.button2);
-        assertThat(btnView).isNotNull();
-        btnView.performClick();
+        Button pairBtn = dialog.findViewById(R.id.positive_btn);
+        assertThat(pairBtn).isNotNull();
+        pairBtn.performClick();
         shadowMainLooper().idle();
 
+        verify(mFeatureFactory.metricsFeatureProvider)
+                .action(
+                        any(Context.class),
+                        eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_POSITIVE_BTN_CLICKED),
+                        eq(TEST_EVENT_DATA));
         assertThat(dialog.isShowing()).isFalse();
+    }
+
+    @Test
+    public void onCreateDialog_noExtraConnectedDevice_showQRCode() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+        AudioSharingDialogFragment.show(
+                mParent, new ArrayList<>(), EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST);
+        shadowMainLooper().idle();
+        AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+        assertThat(dialog).isNotNull();
+        Button qrCodeBtn = dialog.findViewById(R.id.negative_btn);
+        assertThat(qrCodeBtn).isNotNull();
+        qrCodeBtn.performClick();
+        shadowMainLooper().idle();
+
         verify(mFeatureFactory.metricsFeatureProvider)
                 .action(
                         any(Context.class),
                         eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_NEGATIVE_BTN_CLICKED),
                         eq(TEST_EVENT_DATA));
+        assertThat(dialog.isShowing()).isFalse();
     }
 
     @Test
-    public void onCreateDialog_flagOn_singleConnectedDevice() {
+    public void onCreateDialog_flagOn_singleExtraConnectedDevice() {
         mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
         ArrayList<AudioSharingDeviceItem> list = new ArrayList<>();
         list.add(TEST_DEVICE_ITEM1);
@@ -207,10 +231,10 @@
         assertThat(description).isNotNull();
         ImageView image = dialog.findViewById(R.id.description_image);
         assertThat(image).isNotNull();
-        Button shareBtn = dialog.findViewById(R.id.positive_btn);
-        assertThat(shareBtn).isNotNull();
-        Button cancelBtn = dialog.findViewById(R.id.negative_btn);
-        assertThat(cancelBtn).isNotNull();
+        Button positiveBtn = dialog.findViewById(R.id.positive_btn);
+        assertThat(positiveBtn).isNotNull();
+        Button negativeBtn = dialog.findViewById(R.id.negative_btn);
+        assertThat(negativeBtn).isNotNull();
         assertThat(dialog.isShowing()).isTrue();
         assertThat(title.getText().toString())
                 .isEqualTo(
@@ -220,12 +244,16 @@
         assertThat(description.getText().toString())
                 .isEqualTo(mParent.getString(R.string.audio_sharing_dialog_share_content));
         assertThat(image.getVisibility()).isEqualTo(View.GONE);
-        assertThat(shareBtn.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(cancelBtn.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(positiveBtn.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(positiveBtn.getText().toString())
+                .isEqualTo(mParent.getString(R.string.audio_sharing_share_button_label));
+        assertThat(negativeBtn.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(negativeBtn.getText().toString())
+                .isEqualTo(mParent.getString(R.string.audio_sharing_no_thanks_button_label));
     }
 
     @Test
-    public void onCreateDialog_singleConnectedDevice_dialogDismiss() {
+    public void onCreateDialog_singleExtraConnectedDevice_dialogDismiss() {
         mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
         ArrayList<AudioSharingDeviceItem> list = new ArrayList<>();
         list.add(TEST_DEVICE_ITEM1);
@@ -248,7 +276,7 @@
     }
 
     @Test
-    public void onCreateDialog_singleConnectedDevice_shareClicked() {
+    public void onCreateDialog_singleExtraConnectedDevice_shareClicked() {
         mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
         ArrayList<AudioSharingDeviceItem> list = new ArrayList<>();
         list.add(TEST_DEVICE_ITEM1);
@@ -285,7 +313,7 @@
     }
 
     @Test
-    public void onCreateDialog_flagOn_multipleConnectedDevice() {
+    public void onCreateDialog_flagOn_multipleExtraConnectedDevice() {
         mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
         ArrayList<AudioSharingDeviceItem> list = new ArrayList<>();
         list.add(TEST_DEVICE_ITEM1);
@@ -313,12 +341,14 @@
         assertThat(image.getVisibility()).isEqualTo(View.GONE);
         assertThat(shareBtn.getVisibility()).isEqualTo(View.GONE);
         assertThat(cancelBtn.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(cancelBtn.getText().toString())
+                .isEqualTo(mParent.getString(com.android.settings.R.string.cancel));
         assertThat(recyclerView.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(recyclerView.getAdapter().getItemCount()).isEqualTo(3);
     }
 
     @Test
-    public void onCreateDialog_multipleConnectedDevice_dialogDismiss() {
+    public void onCreateDialog_multipleExtraConnectedDevice_dialogDismiss() {
         mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
         ArrayList<AudioSharingDeviceItem> list = new ArrayList<>();
         list.add(TEST_DEVICE_ITEM1);
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 deed229..db6eb8c 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiverTest.java
@@ -25,7 +25,7 @@
 
 import static org.mockito.ArgumentMatchers.any;
 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.verifyNoInteractions;
 import static org.mockito.Mockito.when;
@@ -164,8 +164,7 @@
         AudioSharingReceiver audioSharingReceiver = getAudioSharingReceiver(intent);
         audioSharingReceiver.onReceive(mContext, intent);
 
-        verify(mNm, times(1))
-                .notify(eq(R.drawable.ic_bt_le_audio_sharing), any(Notification.class));
+        verify(mNm).notify(eq(R.drawable.ic_bt_le_audio_sharing), any(Notification.class));
         verify(mFeatureFactory.metricsFeatureProvider)
                 .action(mContext, SettingsEnums.ACTION_SHOW_AUDIO_SHARING_NOTIFICATION);
     }
@@ -181,7 +180,7 @@
         AudioSharingReceiver audioSharingReceiver = getAudioSharingReceiver(intent);
         audioSharingReceiver.onReceive(mContext, intent);
 
-        verify(mNm, times(1)).cancel(R.drawable.ic_bt_le_audio_sharing);
+        verify(mNm).cancel(R.drawable.ic_bt_le_audio_sharing);
         verify(mFeatureFactory.metricsFeatureProvider)
                 .action(mContext, SettingsEnums.ACTION_CANCEL_AUDIO_SHARING_NOTIFICATION);
     }
@@ -199,8 +198,10 @@
     }
 
     @Test
-    public void broadcastReceiver_receiveAudioSharingStopIntent_stopBroadcast() {
+    public void
+            broadcastReceiver_receiveAudioSharingStopIntent_notInBroadcast_cancelNotification() {
         mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+        when(mBroadcast.isEnabled(null)).thenReturn(false);
         int broadcastId = 1;
         when(mBroadcast.getLatestBroadcastId()).thenReturn(broadcastId);
 
@@ -209,7 +210,25 @@
         AudioSharingReceiver audioSharingReceiver = getAudioSharingReceiver(intent);
         audioSharingReceiver.onReceive(mContext, intent);
 
-        verify(mBroadcast, times(1)).stopBroadcast(broadcastId);
+        verify(mBroadcast, never()).stopBroadcast(broadcastId);
+        verify(mNm).cancel(R.drawable.ic_bt_le_audio_sharing);
+        verify(mFeatureFactory.metricsFeatureProvider)
+                .action(mContext, SettingsEnums.ACTION_CANCEL_AUDIO_SHARING_NOTIFICATION);
+    }
+
+    @Test
+    public void broadcastReceiver_receiveAudioSharingStopIntent_inBroadcast_stopBroadcast() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+        when(mBroadcast.isEnabled(null)).thenReturn(true);
+        int broadcastId = 1;
+        when(mBroadcast.getLatestBroadcastId()).thenReturn(broadcastId);
+
+        Intent intent = new Intent(ACTION_LE_AUDIO_SHARING_STOP);
+        intent.setPackage(mContext.getPackageName());
+        AudioSharingReceiver audioSharingReceiver = getAudioSharingReceiver(intent);
+        audioSharingReceiver.onReceive(mContext, intent);
+
+        verify(mBroadcast).stopBroadcast(broadcastId);
         verify(mFeatureFactory.metricsFeatureProvider)
                 .action(mContext, SettingsEnums.ACTION_STOP_AUDIO_SHARING_FROM_NOTIFICATION);
     }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreferenceTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreferenceTest.java
deleted file mode 100644
index 5f86301..0000000
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreferenceTest.java
+++ /dev/null
@@ -1,293 +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;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-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.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.settings.SettingsEnums;
-import android.content.Context;
-import android.content.Intent;
-import android.provider.Settings;
-import android.util.Pair;
-import android.view.View;
-
-import com.android.settings.DisplaySettings;
-import com.android.settings.R;
-import com.android.settings.SettingsActivity;
-import com.android.settings.testutils.BatteryTestUtils;
-import com.android.settings.testutils.FakeFeatureFactory;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-import java.util.Map;
-import java.util.Optional;
-import java.util.TimeZone;
-
-@RunWith(RobolectricTestRunner.class)
-public final class BatteryTipsCardPreferenceTest {
-
-    private Context mContext;
-    private FakeFeatureFactory mFeatureFactory;
-    private BatteryTipsCardPreference mBatteryTipsCardPreference;
-    private PowerUsageAdvanced mPowerUsageAdvanced;
-    private BatteryTipsController mBatteryTipsController;
-    private BatteryChartPreferenceController mBatteryChartPreferenceController;
-
-    @Mock private View mFakeView;
-    @Mock private BatteryUsageBreakdownController mBatteryUsageBreakdownController;
-    @Mock private BatteryDiffEntry mFakeEntry;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"));
-        mContext = spy(RuntimeEnvironment.application);
-        mFeatureFactory = FakeFeatureFactory.setupForTest();
-        mBatteryTipsCardPreference = new BatteryTipsCardPreference(mContext, /* attrs= */ null);
-        mBatteryTipsController = new BatteryTipsController(mContext);
-        mBatteryChartPreferenceController =
-                spy(new BatteryChartPreferenceController(mContext, null, null));
-        mBatteryChartPreferenceController.mPrefContext = mContext;
-        mBatteryTipsController.mCardPreference = mBatteryTipsCardPreference;
-
-        mPowerUsageAdvanced = spy(new PowerUsageAdvanced());
-        doReturn(mContext).when(mPowerUsageAdvanced).getContext();
-        mPowerUsageAdvanced.mBatteryTipsController = mBatteryTipsController;
-        mPowerUsageAdvanced.mBatteryChartPreferenceController = mBatteryChartPreferenceController;
-        mPowerUsageAdvanced.mBatteryUsageBreakdownController = mBatteryUsageBreakdownController;
-        mPowerUsageAdvanced.mBatteryLevelData =
-                Optional.of(
-                        new BatteryLevelData(
-                                Map.of(
-                                        1694354400000L, 1, // 2023-09-10 22:00:00
-                                        1694361600000L, 2, // 2023-09-11 00:00:00
-                                        1694368800000L, 3))); // 2023-09-11 02:00:00
-        doReturn("TestEntriesKey").when(mFakeEntry).getKey();
-    }
-
-    @Test
-    public void constructor_returnExpectedResult() {
-        assertThat(mBatteryTipsCardPreference.getLayoutResource())
-                .isEqualTo(R.layout.battery_tips_card);
-    }
-
-    @Test
-    public void onClick_mainBtnOfSettingsAnomalyLaunchPage_getAdaptiveBrightnessLauncher() {
-        final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
-        PowerAnomalyEvent adaptiveBrightnessAnomaly =
-                BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent(/* changeSettings= */ false);
-        when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
-        when(mFakeView.getId()).thenReturn(R.id.main_button);
-        doNothing().when(mContext).startActivity(captor.capture());
-
-        mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(
-                adaptiveBrightnessAnomaly, adaptiveBrightnessAnomaly);
-        mBatteryTipsCardPreference.onClick(mFakeView);
-
-        assertThat(mBatteryTipsCardPreference.isVisible()).isFalse();
-        verify(mContext).startActivity(any(Intent.class));
-        final Intent intent = captor.getValue();
-        assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
-                .isEqualTo(DisplaySettings.class.getName());
-        assertThat(intent.getIntExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, -1))
-                .isEqualTo(SettingsEnums.DISPLAY);
-        verify(mFeatureFactory.metricsFeatureProvider)
-                .action(
-                        SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                        SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW,
-                        SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                        BatteryTipsController.ANOMALY_KEY,
-                        PowerAnomalyKey.KEY_BRIGHTNESS.getNumber());
-        verify(mFeatureFactory.metricsFeatureProvider)
-                .action(
-                        SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                        SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT,
-                        SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                        BatteryTipsController.ANOMALY_KEY,
-                        PowerAnomalyKey.KEY_BRIGHTNESS.getNumber());
-    }
-
-    @Test
-    public void onClick_mainBtnOfSettingsAnomalyChangeSettings_settingsChanged()
-            throws Settings.SettingNotFoundException {
-        Settings.System.putInt(
-                mContext.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS_MODE,
-                Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
-        final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
-        PowerAnomalyEvent adaptiveBrightnessAnomaly =
-                BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent(/* changeSettings= */ true);
-        when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
-        when(mFakeView.getId()).thenReturn(R.id.main_button);
-        doNothing().when(mContext).startActivity(captor.capture());
-
-        mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(
-                adaptiveBrightnessAnomaly, adaptiveBrightnessAnomaly);
-        mBatteryTipsCardPreference.onClick(mFakeView);
-
-        assertThat(mBatteryTipsCardPreference.isVisible()).isFalse();
-        assertThat(
-                        Settings.System.getInt(
-                                mContext.getContentResolver(),
-                                Settings.System.SCREEN_BRIGHTNESS_MODE))
-                .isEqualTo(Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
-        verify(mContext, never()).startActivity(any(Intent.class));
-        verify(mFeatureFactory.metricsFeatureProvider)
-                .action(
-                        SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                        SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW,
-                        SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                        BatteryTipsController.ANOMALY_KEY,
-                        PowerAnomalyKey.KEY_BRIGHTNESS.getNumber());
-        verify(mFeatureFactory.metricsFeatureProvider)
-                .action(
-                        SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                        SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT,
-                        SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                        BatteryTipsController.ANOMALY_KEY,
-                        PowerAnomalyKey.KEY_BRIGHTNESS.getNumber());
-    }
-
-    @Test
-    public void onClick_dismissBtnOfSettingsAnomaly_cardDismissAndLogged() {
-        final PowerAnomalyEvent screenTimeoutAnomaly =
-                BatteryTestUtils.createScreenTimeoutAnomalyEvent();
-        DatabaseUtils.removeDismissedPowerAnomalyKeys(mContext);
-        when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
-        when(mFakeView.getId()).thenReturn(R.id.dismiss_button);
-
-        mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(
-                screenTimeoutAnomaly, screenTimeoutAnomaly);
-        mBatteryTipsCardPreference.onClick(mFakeView);
-
-        assertThat(mBatteryTipsCardPreference.isVisible()).isFalse();
-        assertThat(DatabaseUtils.getDismissedPowerAnomalyKeys(mContext)).hasSize(1);
-        assertThat(DatabaseUtils.getDismissedPowerAnomalyKeys(mContext))
-                .contains(PowerAnomalyKey.KEY_SCREEN_TIMEOUT.name());
-        verify(mFeatureFactory.metricsFeatureProvider)
-                .action(
-                        SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                        SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW,
-                        SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                        BatteryTipsController.ANOMALY_KEY,
-                        PowerAnomalyKey.KEY_SCREEN_TIMEOUT.getNumber());
-        verify(mFeatureFactory.metricsFeatureProvider)
-                .action(
-                        SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                        SettingsEnums.ACTION_BATTERY_TIPS_CARD_DISMISS,
-                        SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                        BatteryTipsController.ANOMALY_KEY,
-                        PowerAnomalyKey.KEY_SCREEN_TIMEOUT.getNumber());
-    }
-
-    @Test
-    public void onClick_mainBtnOfAppsAnomaly_selectHighlightSlot() {
-        final PowerAnomalyEvent appsAnomaly = BatteryTestUtils.createAppAnomalyEvent();
-        when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
-        when(mFakeView.getId()).thenReturn(R.id.main_button);
-        doNothing().when(mBatteryChartPreferenceController).selectHighlightSlotIndex();
-        when(mPowerUsageAdvanced.findRelatedBatteryDiffEntry(any())).thenReturn(mFakeEntry);
-
-        mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(appsAnomaly, appsAnomaly);
-        assertHighlightSlotIndexPair(1, 0);
-        mBatteryTipsCardPreference.onClick(mFakeView);
-
-        assertThat(mBatteryTipsCardPreference.isVisible()).isFalse();
-        verify(mContext, never()).startActivity(any(Intent.class));
-        verify(mBatteryChartPreferenceController).onHighlightSlotIndexUpdate(eq(1), eq(0));
-        verify(mBatteryChartPreferenceController).selectHighlightSlotIndex();
-        verify(mFeatureFactory.metricsFeatureProvider)
-                .action(
-                        SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                        SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW,
-                        SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                        BatteryTipsController.ANOMALY_KEY,
-                        PowerAnomalyKey.KEY_APP_TOTAL_HIGHER_THAN_USUAL.getNumber());
-        verify(mFeatureFactory.metricsFeatureProvider)
-                .action(
-                        SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                        SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT,
-                        SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                        BatteryTipsController.ANOMALY_KEY,
-                        PowerAnomalyKey.KEY_APP_TOTAL_HIGHER_THAN_USUAL.getNumber());
-    }
-
-    @Test
-    public void onClick_dismissBtnOfAppsAnomaly_keepHighlightSlotIndex() {
-        final PowerAnomalyEvent appsAnomaly = BatteryTestUtils.createAppAnomalyEvent();
-        when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
-        when(mFakeView.getId()).thenReturn(R.id.dismiss_button);
-        when(mPowerUsageAdvanced.findRelatedBatteryDiffEntry(any())).thenReturn(mFakeEntry);
-
-        mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(appsAnomaly, appsAnomaly);
-        assertHighlightSlotIndexPair(1, 0);
-        mBatteryTipsCardPreference.onClick(mFakeView);
-
-        assertThat(mBatteryTipsCardPreference.isVisible()).isFalse();
-        verify(mContext, never()).startActivity(any(Intent.class));
-        verify(mBatteryChartPreferenceController).onHighlightSlotIndexUpdate(eq(1), eq(0));
-        verify(mBatteryChartPreferenceController, never()).selectHighlightSlotIndex();
-        verify(mFeatureFactory.metricsFeatureProvider)
-                .action(
-                        SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                        SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW,
-                        SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                        BatteryTipsController.ANOMALY_KEY,
-                        PowerAnomalyKey.KEY_APP_TOTAL_HIGHER_THAN_USUAL.getNumber());
-        verify(mFeatureFactory.metricsFeatureProvider)
-                .action(
-                        SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                        SettingsEnums.ACTION_BATTERY_TIPS_CARD_DISMISS,
-                        SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                        BatteryTipsController.ANOMALY_KEY,
-                        PowerAnomalyKey.KEY_APP_TOTAL_HIGHER_THAN_USUAL.getNumber());
-    }
-
-    private void assertHighlightSlotIndexPair(
-            int dailyHighlightSlotIndex, int hourlyHighlightSlotIndex) {
-        assertThat(mPowerUsageAdvanced.mBatteryLevelData.isPresent()).isTrue();
-        assertThat(mPowerUsageAdvanced.mHighlightEventWrapper.isPresent()).isTrue();
-        Pair<Integer, Integer> slotIndexPair =
-                mPowerUsageAdvanced
-                        .mHighlightEventWrapper
-                        .get()
-                        .getHighlightSlotPair(mPowerUsageAdvanced.mBatteryLevelData.get());
-        assertThat(slotIndexPair)
-                .isEqualTo(Pair.create(dailyHighlightSlotIndex, hourlyHighlightSlotIndex));
-        assertThat(mPowerUsageAdvanced.mBatteryChartPreferenceController.mDailyHighlightSlotIndex)
-                .isEqualTo(dailyHighlightSlotIndex);
-        assertThat(mPowerUsageAdvanced.mBatteryChartPreferenceController.mHourlyHighlightSlotIndex)
-                .isEqualTo(hourlyHighlightSlotIndex);
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsControllerTest.java
index 954437f..6c29036b 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsControllerTest.java
@@ -16,8 +16,13 @@
 
 package com.android.settings.fuelgauge.batteryusage;
 
+
+import static com.google.common.truth.Truth.assertThat;
+
 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;
 import static org.mockito.Mockito.when;
 
@@ -29,11 +34,11 @@
 import com.android.settings.R;
 import com.android.settings.testutils.BatteryTestUtils;
 import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.widget.TipCardPreference;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
@@ -47,134 +52,156 @@
     private Context mContext;
     private FakeFeatureFactory mFeatureFactory;
     private BatteryTipsController mBatteryTipsController;
-
-    @Mock private BatteryTipsCardPreference mBatteryTipsCardPreference;
+    private TipCardPreference mCardPreference;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         Locale.setDefault(new Locale("en_US"));
-        org.robolectric.shadows.ShadowSettings.set24HourTimeFormat(false);
         TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+
         mContext = spy(RuntimeEnvironment.application);
         final Resources resources = spy(mContext.getResources());
         resources.getConfiguration().setLocales(new LocaleList(new Locale("en_US")));
         doReturn(resources).when(mContext).getResources();
         mFeatureFactory = FakeFeatureFactory.setupForTest();
-        mBatteryTipsController = new BatteryTipsController(mContext);
-        mBatteryTipsController.mCardPreference = mBatteryTipsCardPreference;
+        mBatteryTipsController = spy(new BatteryTipsController(mContext));
+        mCardPreference = new TipCardPreference(mContext);
+        mBatteryTipsController.mCardPreference = mCardPreference;
     }
 
     @Test
     public void handleBatteryTipsCardUpdated_null_hidePreference() {
         mBatteryTipsController.handleBatteryTipsCardUpdated(/* powerAnomalyEvents= */ null, false);
 
-        verify(mBatteryTipsCardPreference).setVisible(false);
+        assertThat(mCardPreference.isVisible()).isFalse();
     }
 
     @Test
     public void handleBatteryTipsCardUpdated_adaptiveBrightnessAnomaly_showAnomaly() {
-        PowerAnomalyEvent event = BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent();
+        AnomalyEventWrapper anomalyEventWrapper =
+                spy(
+                        new AnomalyEventWrapper(
+                                mContext,
+                                BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent(true)));
         when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
 
-        mBatteryTipsController.handleBatteryTipsCardUpdated(
-                new AnomalyEventWrapper(mContext, event), false);
+        mBatteryTipsController.handleBatteryTipsCardUpdated(anomalyEventWrapper, false);
 
-        // Check pre-defined string
-        verify(mBatteryTipsCardPreference)
-                .setTitle("Turn on adaptive brightness to extend battery life");
-        verify(mBatteryTipsCardPreference).setIconResourceId(R.drawable.ic_battery_tips_lightbulb);
-        verify(mBatteryTipsCardPreference).setButtonColorResourceId(R.color.color_accent_selector);
-        verify(mBatteryTipsCardPreference).setMainButtonLabel("View Settings");
-        verify(mBatteryTipsCardPreference).setDismissButtonLabel("Got it");
-        // Check proto info
-        verify(mBatteryTipsCardPreference).setVisible(true);
-        verify(mFeatureFactory.metricsFeatureProvider)
-                .action(
-                        SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                        SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW,
-                        SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                        BatteryTipsController.ANOMALY_KEY,
-                        PowerAnomalyKey.KEY_BRIGHTNESS.getNumber());
+        assertThat(mCardPreference.getTitle())
+                .isEqualTo("Turn on adaptive brightness to extend battery life");
+        assertThat(mCardPreference.getPrimaryButtonText()).isEqualTo("Got it");
+        assertThat(mCardPreference.getSecondaryButtonText()).isEqualTo("View Settings");
+        assertThat(mCardPreference.getIconResId()).isEqualTo(R.drawable.ic_battery_tips_lightbulb);
+        assertThat(mCardPreference.getTintColorResId()).isEqualTo(R.color.color_accent_selector);
+        assertThat(mCardPreference.getPrimaryButtonVisibility()).isTrue();
+        assertThat(mCardPreference.getSecondaryButtonVisibility()).isTrue();
+        assertCardButtonActionAndMetrics(anomalyEventWrapper);
     }
 
     @Test
     public void handleBatteryTipsCardUpdated_screenTimeoutAnomaly_showAnomaly() {
-        PowerAnomalyEvent event = BatteryTestUtils.createScreenTimeoutAnomalyEvent();
+        AnomalyEventWrapper anomalyEventWrapper =
+                spy(
+                        new AnomalyEventWrapper(
+                                mContext, BatteryTestUtils.createScreenTimeoutAnomalyEvent(true)));
         when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
 
-        mBatteryTipsController.handleBatteryTipsCardUpdated(
-                new AnomalyEventWrapper(mContext, event), false);
+        mBatteryTipsController.handleBatteryTipsCardUpdated(anomalyEventWrapper, false);
 
-        verify(mBatteryTipsCardPreference).setTitle("Reduce screen timeout to extend battery life");
-        verify(mBatteryTipsCardPreference).setIconResourceId(R.drawable.ic_battery_tips_lightbulb);
-        verify(mBatteryTipsCardPreference).setButtonColorResourceId(R.color.color_accent_selector);
-        verify(mBatteryTipsCardPreference).setMainButtonLabel("View Settings");
-        verify(mBatteryTipsCardPreference).setDismissButtonLabel("Got it");
-        verify(mBatteryTipsCardPreference).setVisible(true);
-        verify(mFeatureFactory.metricsFeatureProvider)
-                .action(
-                        SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                        SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW,
-                        SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                        BatteryTipsController.ANOMALY_KEY,
-                        PowerAnomalyKey.KEY_SCREEN_TIMEOUT.getNumber());
+        assertThat(mCardPreference.getTitle())
+                .isEqualTo("Reduce screen timeout to extend battery life");
+        assertThat(mCardPreference.getPrimaryButtonText()).isEqualTo("Got it");
+        assertThat(mCardPreference.getSecondaryButtonText()).isEqualTo("View Settings");
+        assertThat(mCardPreference.getIconResId()).isEqualTo(R.drawable.ic_battery_tips_lightbulb);
+        assertThat(mCardPreference.getTintColorResId()).isEqualTo(R.color.color_accent_selector);
+        assertThat(mCardPreference.getPrimaryButtonVisibility()).isTrue();
+        assertThat(mCardPreference.getSecondaryButtonVisibility()).isTrue();
+        assertCardButtonActionAndMetrics(anomalyEventWrapper);
     }
 
     @Test
     public void handleBatteryTipsCardUpdated_screenTimeoutAnomalyHasTitle_showAnomaly() {
-        PowerAnomalyEvent event = BatteryTestUtils.createScreenTimeoutAnomalyEvent();
+        PowerAnomalyEvent anomalyEvent = BatteryTestUtils.createScreenTimeoutAnomalyEvent(true);
         String testTitle = "TestTitle";
-        event =
-                event.toBuilder()
+        anomalyEvent =
+                anomalyEvent.toBuilder()
                         .setWarningBannerInfo(
-                                event.getWarningBannerInfo().toBuilder()
+                                anomalyEvent.getWarningBannerInfo().toBuilder()
                                         .setTitleString(testTitle)
                                         .build())
                         .build();
+        AnomalyEventWrapper anomalyEventWrapper =
+                spy(new AnomalyEventWrapper(mContext, anomalyEvent));
         when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
 
-        mBatteryTipsController.handleBatteryTipsCardUpdated(
-                new AnomalyEventWrapper(mContext, event), false);
+        mBatteryTipsController.handleBatteryTipsCardUpdated(anomalyEventWrapper, false);
 
-        verify(mBatteryTipsCardPreference).setTitle(testTitle);
-        verify(mBatteryTipsCardPreference).setIconResourceId(R.drawable.ic_battery_tips_lightbulb);
-        verify(mBatteryTipsCardPreference).setButtonColorResourceId(R.color.color_accent_selector);
-        verify(mBatteryTipsCardPreference).setMainButtonLabel("View Settings");
-        verify(mBatteryTipsCardPreference).setDismissButtonLabel("Got it");
-        verify(mBatteryTipsCardPreference).setVisible(true);
-        verify(mFeatureFactory.metricsFeatureProvider)
-                .action(
-                        SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                        SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW,
-                        SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                        BatteryTipsController.ANOMALY_KEY,
-                        PowerAnomalyKey.KEY_SCREEN_TIMEOUT.getNumber());
+        assertThat(mCardPreference.getTitle()).isEqualTo(testTitle);
+        assertThat(mCardPreference.getPrimaryButtonText()).isEqualTo("Got it");
+        assertThat(mCardPreference.getSecondaryButtonText()).isEqualTo("View Settings");
+        assertThat(mCardPreference.getIconResId()).isEqualTo(R.drawable.ic_battery_tips_lightbulb);
+        assertThat(mCardPreference.getTintColorResId()).isEqualTo(R.color.color_accent_selector);
+        assertThat(mCardPreference.getPrimaryButtonVisibility()).isTrue();
+        assertThat(mCardPreference.getSecondaryButtonVisibility()).isTrue();
+        assertCardButtonActionAndMetrics(anomalyEventWrapper);
     }
 
     @Test
     public void handleBatteryTipsCardUpdated_appAnomaly_showAnomaly() {
-        PowerAnomalyEvent event = BatteryTestUtils.createAppAnomalyEvent();
+        AnomalyEventWrapper anomalyEventWrapper =
+                spy(new AnomalyEventWrapper(mContext, BatteryTestUtils.createAppAnomalyEvent()));
         when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
 
-        AnomalyEventWrapper eventWrapper = new AnomalyEventWrapper(mContext, event);
-        eventWrapper.setRelatedBatteryDiffEntry(new BatteryDiffEntry(mContext, "", "Chrome", 0));
-        mBatteryTipsController.handleBatteryTipsCardUpdated(eventWrapper, false);
+        anomalyEventWrapper.setRelatedBatteryDiffEntry(
+                new BatteryDiffEntry(mContext, "", "Chrome", 0));
+        mBatteryTipsController.setOnAnomalyConfirmListener(
+                () -> mBatteryTipsController.acceptTipsCard());
+        mBatteryTipsController.handleBatteryTipsCardUpdated(anomalyEventWrapper, true);
 
-        verify(mBatteryTipsCardPreference).setTitle("Chrome used more battery than usual");
-        verify(mBatteryTipsCardPreference)
-                .setIconResourceId(R.drawable.ic_battery_tips_warning_icon);
-        verify(mBatteryTipsCardPreference)
-                .setButtonColorResourceId(R.color.color_battery_anomaly_app_warning_selector);
-        verify(mBatteryTipsCardPreference).setMainButtonLabel("Check");
-        verify(mBatteryTipsCardPreference).setDismissButtonLabel("Got it");
-        verify(mBatteryTipsCardPreference).setVisible(true);
+        assertThat(mCardPreference.getTitle()).isEqualTo("Chrome used more battery than usual");
+        assertThat(mCardPreference.getPrimaryButtonText()).isEqualTo("Got it");
+        assertThat(mCardPreference.getSecondaryButtonText()).isEqualTo("Check");
+        assertThat(mCardPreference.getIconResId())
+                .isEqualTo(R.drawable.ic_battery_tips_warning_icon);
+        assertThat(mCardPreference.getTintColorResId())
+                .isEqualTo(R.color.color_battery_anomaly_app_warning_selector);
+        assertThat(mCardPreference.getPrimaryButtonVisibility()).isTrue();
+        assertThat(mCardPreference.getSecondaryButtonVisibility()).isTrue();
+        assertThat(mCardPreference.isVisible()).isTrue();
+        assertCardButtonActionAndMetrics(anomalyEventWrapper);
+    }
+
+    private void assertCardButtonActionAndMetrics(final AnomalyEventWrapper anomalyEventWrapper) {
+        when(anomalyEventWrapper.updateSystemSettingsIfAvailable()).thenReturn(true);
+
+        final int powerAnomalyKeyNumber = anomalyEventWrapper.getAnomalyKeyNumber();
+        assertCardMetrics(SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, powerAnomalyKeyNumber);
+        assertThat(mCardPreference.isVisible()).isTrue();
+
+        // Check accept button action
+        mCardPreference.setVisible(true);
+        mCardPreference.getSecondaryButtonAction().invoke();
+        assertCardMetrics(SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, powerAnomalyKeyNumber);
+        assertThat(mCardPreference.isVisible()).isFalse();
+        final boolean isAppAnomalyCard = powerAnomalyKeyNumber > 1;
+        verify(anomalyEventWrapper, isAppAnomalyCard ? never() : times(1))
+                .updateSystemSettingsIfAvailable();
+
+        // Check reject button action
+        mCardPreference.setVisible(true);
+        mCardPreference.getPrimaryButtonAction().invoke();
+        assertCardMetrics(SettingsEnums.ACTION_BATTERY_TIPS_CARD_DISMISS, powerAnomalyKeyNumber);
+        assertThat(mCardPreference.isVisible()).isFalse();
+    }
+
+    private void assertCardMetrics(final int action, final int powerAnomalyKeyNumber) {
         verify(mFeatureFactory.metricsFeatureProvider)
                 .action(
                         SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
-                        SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW,
+                        action,
                         SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL,
                         BatteryTipsController.ANOMALY_KEY,
-                        PowerAnomalyKey.KEY_APP_TOTAL_HIGHER_THAN_USUAL.getNumber());
+                        powerAnomalyKeyNumber);
     }
 }
diff --git a/tests/robotests/src/com/android/settings/notification/modes/SetupInterstitialActivityTest.java b/tests/robotests/src/com/android/settings/notification/modes/SetupInterstitialActivityTest.java
index e73610b..129e56e 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/SetupInterstitialActivityTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/SetupInterstitialActivityTest.java
@@ -82,6 +82,41 @@
     }
 
     @Test
+    public void invalidIntent_doesNotQueryBackend() {
+        // Mode is set up sensibly
+        ZenMode mode = new TestModeBuilder().setId(MODE_ID).setEnabled(false).build();
+        when(mBackend.getMode(MODE_ID)).thenReturn(mode);
+
+        // but the intent is lacking the zen mode extra
+        ActivityScenario<SetupInterstitialActivity> scenario =
+                ActivityScenario.launch(new Intent(Intent.ACTION_MAIN)
+                        .setClass(RuntimeEnvironment.getApplication(),
+                                SetupInterstitialActivity.class));
+        // creating the scenario takes it through onResume(), which would query the backend if
+        // it had mode data.
+        scenario.onActivity(activity -> {
+            assertThat(activity.isFinishing()).isTrue();
+            verify(mBackend, never()).getMode(any());
+        });
+        scenario.close();
+    }
+
+    @Test
+    public void invalidModeId_doesNotCrash() {
+        when(mBackend.getMode(MODE_ID)).thenReturn(null);
+        ActivityScenario<SetupInterstitialActivity> scenario =
+                ActivityScenario.launch(new Intent(Intent.ACTION_MAIN)
+                        .setClass(RuntimeEnvironment.getApplication(),
+                                SetupInterstitialActivity.class)
+                        .putExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID, MODE_ID));
+        // do nothing, but it would crash if attempting to work with a null mode at any point
+        scenario.onActivity(activity -> {
+            assertThat(activity.isFinishing()).isTrue();
+        });
+        scenario.close();
+    }
+
+    @Test
     public void enableButton_enablesModeAndRedirectsToModePage() {
         ZenMode mode = new TestModeBuilder().setId(MODE_ID).setEnabled(false).build();
         when(mBackend.getMode(MODE_ID)).thenReturn(mode);
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeSummaryHelperTest.java
similarity index 98%
rename from tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java
rename to tests/robotests/src/com/android/settings/notification/modes/ZenModeSummaryHelperTest.java
index 7ad7abc..a0e1d38 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeSummaryHelperTest.java
@@ -61,7 +61,7 @@
 
 @RunWith(RobolectricTestRunner.class)
 @EnableFlags(Flags.FLAG_MODES_UI)
-public class ZenModesSummaryHelperTest {
+public class ZenModeSummaryHelperTest {
     private static final int WORK_PROFILE_ID = 3;
 
     private Context mContext;
@@ -489,7 +489,7 @@
     }
 
     @Test
-    public void getModesSummary_noRulesActive_countsOnlyAutomaticModes() {
+    public void getModesSummary_noRulesActive_countsOnlyEnabledAutomaticModes() {
         ImmutableList<ZenMode> modes = ImmutableList.of(
                 TestModeBuilder.MANUAL_DND_INACTIVE, // Not automatic
                 new TestModeBuilder().setName("Auto 1").build(), // App provided automatic
@@ -500,6 +500,10 @@
                         .setConditionId(ZenModeConfig.toCustomManualConditionId())
                         .build(), // Custom manual, not automatic
                 new TestModeBuilder()
+                        .setName("Disabled 1")
+                        .setEnabled(false)
+                        .build(), // Would be automatic, but it's disabled.
+                new TestModeBuilder()
                         .setName("Sleep")
                         .setPackage(SystemZenRules.PACKAGE_ANDROID)
                         .setType(AutomaticZenRule.TYPE_SCHEDULE_TIME)
diff --git a/tests/robotests/src/com/android/settings/password/ConfirmLockPasswordTest.java b/tests/robotests/src/com/android/settings/password/ConfirmLockPasswordTest.java
index 4ac375f..9e8535a 100644
--- a/tests/robotests/src/com/android/settings/password/ConfirmLockPasswordTest.java
+++ b/tests/robotests/src/com/android/settings/password/ConfirmLockPasswordTest.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.password;
 
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+
 import static com.android.settings.password.ConfirmLockPassword.ConfirmLockPasswordFragment;
 import static com.android.settings.password.TestUtils.GUESS_INVALID_RESULT;
 import static com.android.settings.password.TestUtils.GUESS_VALID_RESULT;
@@ -42,6 +44,7 @@
 import android.Manifest;
 import android.app.KeyguardManager;
 import android.app.admin.ManagedSubscriptionsPolicy;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.os.UserHandle;
@@ -55,6 +58,7 @@
 
 import com.android.internal.widget.LockPatternUtils;
 import com.android.settings.R;
+import com.android.settings.SetupRedactionInterstitial;
 import com.android.settings.testutils.shadow.ShadowDevicePolicyManager;
 import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
 import com.android.settings.testutils.shadow.ShadowUserManager;
@@ -305,6 +309,32 @@
         assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isFalse();
     }
 
+    @Test
+    public void onChosenLockSaveFinished_setsRedactionInterstitial() throws Exception {
+        // This test verifies that the RedactionInterstitial is available. This is the screen
+        // responsible for allowing the user to show sensitive lockscreen content. This
+        // also allows the Settings tile for RedactionInterstitial to appear in the "anything else"
+        // page during SUW.
+        final ConfirmDeviceCredentialBaseActivity activity =
+                buildConfirmDeviceCredentialBaseActivity(
+                        ConfirmLockPassword.class,
+                        createRemoteLockscreenValidationIntent(
+                                KeyguardManager.PASSWORD, VALID_REMAINING_ATTEMPTS));
+        final ConfirmLockPasswordFragment fragment =
+                (ConfirmLockPasswordFragment) getConfirmDeviceCredentialBaseFragment(activity);
+        final Intent intent = new Intent();
+        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 1L);
+
+        fragment.onChosenLockSaveFinished(true, intent);
+
+        final ComponentName componentName =
+                new ComponentName(mContext, SetupRedactionInterstitial.class);
+        final int isEnabled = mContext.getPackageManager()
+                .getComponentEnabledSetting(componentName);
+
+        assertThat(isEnabled).isEqualTo(COMPONENT_ENABLED_STATE_ENABLED);
+    }
+
     private void triggerHandleNext(
             ConfirmLockPasswordFragment fragment, ImeAwareEditText passwordEntry) {
         passwordEntry.setText("Password");
diff --git a/tests/robotests/src/com/android/settings/password/ConfirmLockPatternTest.java b/tests/robotests/src/com/android/settings/password/ConfirmLockPatternTest.java
index 04a76b8..9e7ec06 100644
--- a/tests/robotests/src/com/android/settings/password/ConfirmLockPatternTest.java
+++ b/tests/robotests/src/com/android/settings/password/ConfirmLockPatternTest.java
@@ -16,6 +16,9 @@
 
 package com.android.settings.password;
 
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+
+import static com.android.settings.password.ConfirmLockPattern.ConfirmLockPatternFragment;
 import static com.android.settings.password.TestUtils.GUESS_INVALID_RESULT;
 import static com.android.settings.password.TestUtils.GUESS_VALID_RESULT;
 import static com.android.settings.password.TestUtils.LOCKOUT_RESULT;
@@ -39,6 +42,7 @@
 import android.Manifest;
 import android.app.KeyguardManager;
 import android.app.admin.ManagedSubscriptionsPolicy;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.os.UserHandle;
@@ -51,6 +55,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternView;
 import com.android.settings.R;
+import com.android.settings.SetupRedactionInterstitial;
 import com.android.settings.testutils.shadow.ShadowDevicePolicyManager;
 import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
 import com.android.settings.testutils.shadow.ShadowUserManager;
@@ -284,6 +289,32 @@
         assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isFalse();
     }
 
+    @Test
+    public void onChosenLockSaveFinished_setsRedactionInterstitial() throws Exception {
+        // This test verifies that the RedactionInterstitial is available. This is the screen
+        // responsible for allowing the user to show sensitive lockscreen content. This
+        // also allows the Settings tile for RedactionInterstitial to appear in the "anything else"
+        // page during SUW.
+        final ConfirmDeviceCredentialBaseActivity activity =
+                buildConfirmDeviceCredentialBaseActivity(
+                        ConfirmLockPattern.class,
+                        createRemoteLockscreenValidationIntent(
+                                KeyguardManager.PATTERN, VALID_REMAINING_ATTEMPTS));
+        final ConfirmLockPatternFragment fragment = (ConfirmLockPatternFragment)
+                getConfirmDeviceCredentialBaseFragment(activity);
+        final Intent intent = new Intent();
+        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 1L);
+
+        fragment.onChosenLockSaveFinished(true, intent);
+
+        final ComponentName componentName =
+                new ComponentName(mContext, SetupRedactionInterstitial.class);
+        final int isEnabled = mContext.getPackageManager()
+                .getComponentEnabledSetting(componentName);
+
+        assertThat(isEnabled).isEqualTo(COMPONENT_ENABLED_STATE_ENABLED);
+    }
+
     private void triggerOnPatternDetected(LockPatternView lockPatternView) {
         List<LockPatternView.Cell> pattern = List.of(LockPatternView.Cell.of(0, 0));
         lockPatternView.setPattern(LockPatternView.DisplayMode.Correct, pattern);
diff --git a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
index 84c8586..edb310e 100644
--- a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
@@ -30,7 +30,6 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 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;
@@ -131,12 +130,18 @@
             CustomSliceRegistry.LOCATION_SLICE_URI
     );
 
-    private static final List<Uri> SPECIAL_CASE_OEM_URIS = Arrays.asList(
-            CustomSliceRegistry.ZEN_MODE_SLICE_URI,
-            CustomSliceRegistry.FLASHLIGHT_SLICE_URI,
-            CustomSliceRegistry.MOBILE_DATA_SLICE_URI,
-            CustomSliceRegistry.WIFI_CALLING_URI
-    );
+    private static final List<Uri> SPECIAL_CASE_OEM_URIS = android.app.Flags.modesUi()
+            ? Arrays.asList(
+                    CustomSliceRegistry.FLASHLIGHT_SLICE_URI,
+                    CustomSliceRegistry.MOBILE_DATA_SLICE_URI,
+                    CustomSliceRegistry.WIFI_CALLING_URI
+            ) :
+            Arrays.asList(
+                CustomSliceRegistry.ZEN_MODE_SLICE_URI,
+                CustomSliceRegistry.FLASHLIGHT_SLICE_URI,
+                CustomSliceRegistry.MOBILE_DATA_SLICE_URI,
+                CustomSliceRegistry.WIFI_CALLING_URI
+            );
 
     @Before
     public void setUp() {
diff --git a/tests/robotests/src/com/android/settings/testutils/AccessibilityTestUtils.java b/tests/robotests/src/com/android/settings/testutils/AccessibilityTestUtils.java
index 5d895d9..4d69821 100644
--- a/tests/robotests/src/com/android/settings/testutils/AccessibilityTestUtils.java
+++ b/tests/robotests/src/com/android/settings/testutils/AccessibilityTestUtils.java
@@ -20,6 +20,8 @@
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -46,20 +48,15 @@
 
     public static void setSoftwareShortcutMode(
             Context context, boolean gestureNavEnabled, boolean floatingButtonEnabled) {
-        int mode = floatingButtonEnabled ? ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU : -1;
+        int buttonMode = floatingButtonEnabled ? ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU : -1;
+        int navMode = gestureNavEnabled ? NAV_BAR_MODE_GESTURAL : NAV_BAR_MODE_3BUTTON;
 
         Settings.Secure.putInt(context.getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_BUTTON_MODE, mode);
-
-        if (gestureNavEnabled) {
-            SettingsShadowResources.overrideResource(
-                    com.android.internal.R.integer.config_navBarInteractionMode,
-                    NAV_BAR_MODE_GESTURAL);
-        } else {
-            SettingsShadowResources.overrideResource(
-                    com.android.internal.R.integer.config_navBarInteractionMode,
-                    NAV_BAR_MODE_3BUTTON);
-        }
+                Settings.Secure.ACCESSIBILITY_BUTTON_MODE, buttonMode);
+        SettingsShadowResources.overrideResource(
+                com.android.internal.R.integer.config_navBarInteractionMode, navMode);
+        assertThat(context.getResources().getInteger(
+                com.android.internal.R.integer.config_navBarInteractionMode)).isEqualTo(navMode);
     }
 
     /**
diff --git a/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java b/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java
index a151632..951f2f0 100644
--- a/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java
+++ b/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java
@@ -264,18 +264,28 @@
 
     /** Create a power anomaly event proto of screen timeout. */
     public static PowerAnomalyEvent createScreenTimeoutAnomalyEvent() {
+        return createScreenTimeoutAnomalyEvent(false);
+    }
+
+    /** Create a power anomaly event proto of screen timeout. */
+    public static PowerAnomalyEvent createScreenTimeoutAnomalyEvent(boolean changeSettings) {
+        WarningBannerInfo.Builder warningBannerInfoBuilder =
+                WarningBannerInfo.newBuilder()
+                        .setMainButtonDestination(ScreenTimeoutSettings.class.getName())
+                        .setMainButtonSourceMetricsCategory(SettingsEnums.SCREEN_TIMEOUT)
+                        .setMainButtonSourceHighlightKey("60000");
+        if (changeSettings) {
+            warningBannerInfoBuilder
+                    .setMainButtonConfigSettingsName(Settings.System.SCREEN_OFF_TIMEOUT)
+                    .setMainButtonConfigSettingsValue(60000);
+        }
         return PowerAnomalyEvent.newBuilder()
                 .setEventId("ScreenTimeoutAnomaly")
                 .setType(PowerAnomalyType.TYPE_SETTINGS_BANNER)
                 .setKey(PowerAnomalyKey.KEY_SCREEN_TIMEOUT)
                 .setDismissRecordKey(PowerAnomalyKey.KEY_SCREEN_TIMEOUT.name())
                 .setScore(1.1f)
-                .setWarningBannerInfo(
-                        WarningBannerInfo.newBuilder()
-                                .setMainButtonDestination(ScreenTimeoutSettings.class.getName())
-                                .setMainButtonSourceMetricsCategory(SettingsEnums.SCREEN_TIMEOUT)
-                                .setMainButtonSourceHighlightKey("60000")
-                                .build())
+                .setWarningBannerInfo(warningBannerInfoBuilder.build())
                 .build();
     }
 
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceControllerHelperTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceControllerHelperTest.kt
new file mode 100644
index 0000000..8edc90f
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceControllerHelperTest.kt
@@ -0,0 +1,96 @@
+/*
+ * 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.telephony
+
+import android.content.Context
+import android.telephony.CarrierConfigManager
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.spy
+
+@RunWith(AndroidJUnit4::class)
+class EnabledNetworkModePreferenceControllerHelperTest {
+
+    private var context: Context = spy(ApplicationProvider.getApplicationContext()) {}
+
+    @Before
+    fun setUp() {
+        CarrierConfigRepository.resetForTest()
+        CarrierConfigRepository.setBooleanForTest(
+            SUB_ID, CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true)
+    }
+
+    @Test
+    fun getNetworkModePreferenceType_hideCarrierNetworkSettings_returnNone() {
+        CarrierConfigRepository.setBooleanForTest(
+            SUB_ID, CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, true)
+
+        val networkModePreferenceType = getNetworkModePreferenceType(context, SUB_ID)
+
+        assertThat(networkModePreferenceType).isEqualTo(NetworkModePreferenceType.None)
+    }
+
+    @Test
+    fun getNetworkModePreferenceType_hidePreferredNetworkType_returnNone() {
+        CarrierConfigRepository.setBooleanForTest(
+            SUB_ID, CarrierConfigManager.KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL, true)
+
+        val networkModePreferenceType = getNetworkModePreferenceType(context, SUB_ID)
+
+        assertThat(networkModePreferenceType).isEqualTo(NetworkModePreferenceType.None)
+    }
+
+    @Test
+    fun getNetworkModePreferenceType_carrierConfigNotReady_returnNone() {
+        CarrierConfigRepository.setBooleanForTest(
+            SUB_ID, CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, false)
+
+        val networkModePreferenceType = getNetworkModePreferenceType(context, SUB_ID)
+
+        assertThat(networkModePreferenceType).isEqualTo(NetworkModePreferenceType.None)
+    }
+
+    @Test
+    fun getNetworkModePreferenceType_isWorldPhone_returnPreferredNetworkMode() {
+        CarrierConfigRepository.setBooleanForTest(
+            SUB_ID, CarrierConfigManager.KEY_WORLD_PHONE_BOOL, true)
+
+        val networkModePreferenceType = getNetworkModePreferenceType(context, SUB_ID)
+
+        assertThat(networkModePreferenceType)
+            .isEqualTo(NetworkModePreferenceType.PreferredNetworkMode)
+    }
+
+    @Test
+    fun getNetworkModePreferenceType_notWorldPhone_returnEnabledNetworkMode() {
+        CarrierConfigRepository.setBooleanForTest(
+            SUB_ID, CarrierConfigManager.KEY_WORLD_PHONE_BOOL, false)
+
+        val networkModePreferenceType = getNetworkModePreferenceType(context, SUB_ID)
+
+        assertThat(networkModePreferenceType)
+            .isEqualTo(NetworkModePreferenceType.EnabledNetworkMode)
+    }
+
+    private companion object {
+        const val SUB_ID = 10
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndexTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndexTest.kt
index 5e7e83c..bf51208 100644
--- a/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndexTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndexTest.kt
@@ -25,6 +25,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.settings.R
 import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.Companion.isMobileNetworkSettingsSearchable
+import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchResult
 import com.android.settings.spa.SpaSearchLanding.BundleValue
 import com.android.settings.spa.SpaSearchLanding.SpaSearchLandingFragment
 import com.android.settings.spa.SpaSearchLanding.SpaSearchLandingKey
@@ -62,10 +63,12 @@
     private val mobileNetworkSettingsSearchIndex = MobileNetworkSettingsSearchIndex {
         listOf(
             object : MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem {
-                override val key = KEY
-                override val title = TITLE
-
-                override fun isAvailable(subId: Int) = subId == SUB_ID_1
+                override fun getSearchResult(subId: Int): MobileNetworkSettingsSearchResult? =
+                    if (subId == SUB_ID_1) {
+                        MobileNetworkSettingsSearchResult(key = KEY, title = TITLE)
+                    } else {
+                        null
+                    }
             })
     }
 
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppNotificationPreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppNotificationPreferenceTest.kt
index 37f3a11..7a0ec8c 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppNotificationPreferenceTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppNotificationPreferenceTest.kt
@@ -20,8 +20,8 @@
 import android.content.pm.ApplicationInfo
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.hasText
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithText
 import androidx.compose.ui.test.onRoot
@@ -34,36 +34,35 @@
 import com.android.settings.notification.app.AppNotificationSettings
 import com.android.settings.spa.notification.IAppNotificationRepository
 import com.android.settingslib.spa.testutils.delay
+import com.android.settingslib.spa.testutils.waitUntilExists
 import org.junit.After
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.MockitoSession
-import org.mockito.Spy
 import org.mockito.quality.Strictness
 
 @RunWith(AndroidJUnit4::class)
 class AppNotificationPreferenceTest {
-    @get:Rule
-    val composeTestRule = createComposeRule()
+    @get:Rule val composeTestRule = createComposeRule()
 
     private lateinit var mockSession: MockitoSession
 
-    @Spy
     private val context: Context = ApplicationProvider.getApplicationContext()
 
-    private val repository = object : IAppNotificationRepository {
-        override fun getNotificationSummary(app: ApplicationInfo) = SUMMARY
-    }
+    private val repository =
+        object : IAppNotificationRepository {
+            override fun getNotificationSummary(app: ApplicationInfo) = SUMMARY
+        }
 
     @Before
     fun setUp() {
-        mockSession = ExtendedMockito.mockitoSession()
-            .initMocks(this)
-            .mockStatic(AppInfoDashboardFragment::class.java)
-            .strictness(Strictness.LENIENT)
-            .startMocking()
+        mockSession =
+            ExtendedMockito.mockitoSession()
+                .mockStatic(AppInfoDashboardFragment::class.java)
+                .strictness(Strictness.LENIENT)
+                .startMocking()
     }
 
     @After
@@ -75,25 +74,26 @@
     fun title_displayed() {
         setContent(APP)
 
-        composeTestRule.onNodeWithText(context.getString(R.string.notifications_label))
-            .assertIsDisplayed()
+        composeTestRule.waitUntilExists(hasText(context.getString(R.string.notifications_label)))
     }
 
     @Test
     fun summary_displayed() {
         setContent(APP)
 
-        composeTestRule.onNodeWithText(SUMMARY).assertIsDisplayed()
+        composeTestRule.waitUntilExists(hasText(SUMMARY))
     }
 
     @Test
     fun whenNotInstalled_disable() {
-        setContent(ApplicationInfo().apply {
-            packageName = PACKAGE_NAME
-            uid = UID
-        })
+        setContent(
+            ApplicationInfo().apply {
+                packageName = PACKAGE_NAME
+                uid = UID
+            })
 
-        composeTestRule.onNodeWithText(context.getString(R.string.notifications_label))
+        composeTestRule
+            .onNodeWithText(context.getString(R.string.notifications_label))
             .assertIsNotEnabled()
     }
 
@@ -125,11 +125,12 @@
     private companion object {
         const val PACKAGE_NAME = "package.name"
         const val UID = 123
-        val APP = ApplicationInfo().apply {
-            packageName = PACKAGE_NAME
-            uid = UID
-            flags = ApplicationInfo.FLAG_INSTALLED
-        }
+        val APP =
+            ApplicationInfo().apply {
+                packageName = PACKAGE_NAME
+                uid = UID
+                flags = ApplicationInfo.FLAG_INSTALLED
+            }
         const val SUMMARY = "Summary"
     }
-}
\ No newline at end of file
+}
diff --git a/tests/unit/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceControllerTest.java
index b3d095e..adc8dc0 100644
--- a/tests/unit/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceControllerTest.java
@@ -18,9 +18,6 @@
 
 import static androidx.lifecycle.Lifecycle.Event.ON_START;
 
-import static com.android.settings.core.BasePreferenceController.AVAILABLE;
-import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE;
-import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
 import static com.android.settings.network.telephony.MobileNetworkUtils.getRafFromNetworkType;
 import static com.android.settings.network.telephony.TelephonyConstants.RadioAccessFamily.CDMA;
 import static com.android.settings.network.telephony.TelephonyConstants.RadioAccessFamily.EVDO;
@@ -33,8 +30,6 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
@@ -125,79 +120,6 @@
 
     @UiThreadTest
     @Test
-    public void getAvailabilityStatus_hideCarrierNetworkSettings_returnUnavailable() {
-        mPersistableBundle.putBoolean(
-                CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL,
-                true);
-
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
-    }
-
-    @UiThreadTest
-    @Test
-    public void getAvailabilityStatus_hidePreferredNetworkType_returnUnavailable() {
-        mPersistableBundle.putBoolean(CarrierConfigManager.KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL,
-                true);
-
-        when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
-        when(mServiceState.getDataRegistrationState()).thenReturn(
-                ServiceState.STATE_OUT_OF_SERVICE);
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
-
-        when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
-        when(mServiceState.getDataRegistrationState()).thenReturn(ServiceState.STATE_IN_SERVICE);
-
-        when(mServiceState.getRoaming()).thenReturn(false);
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
-
-        when(mServiceState.getRoaming()).thenReturn(true);
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
-    }
-
-    @UiThreadTest
-    @Test
-    public void getAvailabilityStatus_carrierConfigNotReady_returnUnavailable() {
-        mPersistableBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, false);
-
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
-    }
-
-    @UiThreadTest
-    @Test
-    public void getAvailabilityStatus_notWorldPhone_returnAvailable() {
-        mPersistableBundle.putBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL,
-                false);
-        mPersistableBundle.putBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL, false);
-
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
-    }
-
-    @UiThreadTest
-    @Test
-    public void getAvailabilityStatus_callStateIsIdle_returnAvailable() {
-        mockEnabledNetworkMode(TelephonyManagerConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA);
-        mController.getTelephonyCallback().onCallStateChanged(TelephonyManager.CALL_STATE_IDLE);
-
-        mController.updateState(mPreference);
-
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
-        assertTrue(mPreference.isEnabled());
-    }
-
-    @UiThreadTest
-    @Test
-    public void getAvailabilityStatus_duringCalling_returnAvailable() {
-        mockEnabledNetworkMode(TelephonyManagerConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA);
-        mController.getTelephonyCallback().onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
-
-        mController.updateState(mPreference);
-
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_UNSEARCHABLE);
-        assertFalse(mPreference.isEnabled());
-    }
-
-    @UiThreadTest
-    @Test
     public void updateState_LteWorldPhone_GlobalHasLte() {
         mPersistableBundle.putBoolean(CarrierConfigManager.KEY_WORLD_MODE_ENABLED_BOOL, true);
 
diff --git a/tests/unit/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceControllerTest.java
index 9dbfdde..f22ad3b 100644
--- a/tests/unit/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/network/telephony/PreferredNetworkModePreferenceControllerTest.java
@@ -16,8 +16,6 @@
 
 package com.android.settings.network.telephony;
 
-import static com.android.settings.core.BasePreferenceController.AVAILABLE;
-import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
 import static com.android.settings.network.telephony.TelephonyConstants.RadioAccessFamily.GSM;
 import static com.android.settings.network.telephony.TelephonyConstants.RadioAccessFamily.RAF_TD_SCDMA;
 import static com.android.settings.network.telephony.TelephonyConstants.RadioAccessFamily.WCDMA;
@@ -32,7 +30,6 @@
 
 import android.content.Context;
 import android.os.PersistableBundle;
-import android.telephony.CarrierConfigManager;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -92,43 +89,6 @@
     }
 
     @Test
-    public void getAvailabilityStatus_hideCarrierNetworkSettings_returnUnavailable() {
-        mPersistableBundle.putBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL,
-                true);
-
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
-    }
-
-    @Test
-    public void getAvailabilityStatus_worldPhone_returnAvailable() {
-        mPersistableBundle.putBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL,
-                false);
-        mPersistableBundle.putBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL, true);
-
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
-    }
-
-    @Test
-    public void getAvailabilityStatus_hidePreferredNetworkType_returnUnavailable() {
-        mPersistableBundle.putBoolean(CarrierConfigManager.KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL,
-                true);
-
-        when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
-        when(mServiceState.getDataRegistrationState()).thenReturn(
-                ServiceState.STATE_OUT_OF_SERVICE);
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
-
-        when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
-        when(mServiceState.getDataRegistrationState()).thenReturn(ServiceState.STATE_IN_SERVICE);
-
-        when(mServiceState.getRoaming()).thenReturn(false);
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
-
-        when(mServiceState.getRoaming()).thenReturn(true);
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
-    }
-
-    @Test
     public void updateState_updateByNetworkMode() {
         // NETWORK_MODE_TDSCDMA_GSM_WCDMA = RAF_TD_SCDMA | GSM | WCDMA
         when(mTelephonyManager.getAllowedNetworkTypesForReason(