Merge "Revert "Don't truncate the title in Color Correction options"" into main
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 223b473f..7b79611 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1370,6 +1370,12 @@
         </activity>
 
         <activity
+            android:name=".notification.modes.SetupInterstitialActivity"
+            android:exported="false"
+            android:theme="@style/Theme.Settings.NoActionBar">
+        </activity>
+
+        <activity
             android:name=".notification.zen.ZenSuggestionActivity"
             android:label="@string/zen_mode_settings_title"
             android:icon="@drawable/ic_suggestion_dnd"
diff --git a/aconfig/accessibility/accessibility_flags.aconfig b/aconfig/accessibility/accessibility_flags.aconfig
index 0338ed8..5c81cc9 100644
--- a/aconfig/accessibility/accessibility_flags.aconfig
+++ b/aconfig/accessibility/accessibility_flags.aconfig
@@ -72,6 +72,16 @@
 }
 
 flag {
+  name: "never_restrict_accessibility_activity"
+  namespace: "accessibility"
+  description: "Stops possibly restricting AccessibilityActivityPreferences"
+  bug: "331990900"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
   name: "remove_qs_tooltip_in_suw"
   namespace: "accessibility"
   description: "Don't show quick settings tooltip in SUW, since the user can't use quick settings there."
diff --git a/res/layout-land/mode_interstitial_layout.xml b/res/layout-land/mode_interstitial_layout.xml
new file mode 100644
index 0000000..1420185
--- /dev/null
+++ b/res/layout-land/mode_interstitial_layout.xml
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<LinearLayout
+    android:id="@+id/interstitial_page"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:fitsSystemWindows="true"
+    android:transitionGroup="true"
+    android:orientation="vertical">
+
+    <Toolbar
+        android:id="@+id/action_bar"
+        style="?android:attr/actionBarStyle"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:theme="?android:attr/actionBarTheme"
+        android:elevation="0dp"
+        android:background="@android:color/transparent"/>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:paddingTop="12dp"
+        android:paddingBottom="24dp"
+        android:paddingHorizontal="24dp"
+        android:clipChildren="true">
+
+        <ScrollView
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            app:layout_constrainedHeight="true"
+            app:layout_constraintBottom_toTopOf="@+id/enable_mode_button"
+            app:layout_constraintEnd_toStartOf="@+id/guideline"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent">
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                android:paddingEnd="12dp"
+                android:paddingStart="12dp">
+
+                <TextView
+                    android:id="@+id/mode_name_title"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:clickable="false"
+                    android:paddingVertical="12dp"
+                    android:textSize="36sp"
+                    android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Title" />
+
+                <TextView
+                    android:id="@+id/mode_name_subtitle"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:clickable="false"
+                    android:paddingBottom="12dp"
+                    android:text="@string/zen_mode_setup_page_summary"
+                    android:textSize="18sp"
+                    android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Subhead" />
+
+            </LinearLayout>
+
+        </ScrollView>
+
+        <Button
+            android:id="@+id/enable_mode_button"
+            style="@style/ActionPrimaryButton"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toStartOf="@+id/guideline"
+            android:paddingEnd="12dp" />
+
+        <!-- guideline to have text/button side & image side take up half the page each -->
+        <androidx.constraintlayout.widget.Guideline
+            android:id="@+id/guideline"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:orientation="vertical"
+            app:layout_constraintGuide_percent="0.5" />
+
+        <FrameLayout
+            android:id="@+id/image_frame"
+            android:layout_height="0dp"
+            android:layout_width="0dp"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toEndOf="@+id/guideline"
+            app:layout_constraintEnd_toEndOf="parent"
+            android:paddingHorizontal="12dp">
+
+            <ImageView
+                android:id="@+id/image"
+                android:clickable="false"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:adjustViewBounds="true"
+                android:scaleType="centerCrop"
+                android:layout_gravity="center" />
+
+        </FrameLayout>
+
+ />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/audio_sharing_password_dialog.xml b/res/layout/audio_sharing_password_dialog.xml
index f1a78bc..2bdf505 100644
--- a/res/layout/audio_sharing_password_dialog.xml
+++ b/res/layout/audio_sharing_password_dialog.xml
@@ -53,8 +53,8 @@
 
             <CheckBox
                 android:id="@+id/audio_sharing_stream_password_checkbox"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
+                android:layout_width="48dp"
+                android:layout_height="48dp"
                 android:layout_marginTop="24dp"
                 android:layout_marginEnd="20dp" />
         </LinearLayout>
diff --git a/res/layout/bluetooth_find_broadcast_password_dialog.xml b/res/layout/bluetooth_find_broadcast_password_dialog.xml
index 938f103..d758f61 100644
--- a/res/layout/bluetooth_find_broadcast_password_dialog.xml
+++ b/res/layout/bluetooth_find_broadcast_password_dialog.xml
@@ -26,7 +26,8 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:minHeight="48dp"
-        android:textAlignment="viewStart"/>
+        android:textAlignment="viewStart"
+        android:textColor="?android:attr/textColorPrimary"/>
     <EditText
         android:id="@+id/broadcast_edit_text"
         android:layout_width="match_parent"
@@ -34,11 +35,4 @@
         android:maxLength="16"
         android:minHeight="48dp"
         android:textAlignment="viewStart"/>
-    <TextView
-        android:id="@+id/broadcast_error_message"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="6dp"
-        style="@style/TextAppearance.ErrorText"
-        android:visibility="invisible"/>
 </LinearLayout>
\ No newline at end of file
diff --git a/res/layout/mode_interstitial_layout.xml b/res/layout/mode_interstitial_layout.xml
new file mode 100644
index 0000000..d0f80a7
--- /dev/null
+++ b/res/layout/mode_interstitial_layout.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<LinearLayout
+    android:id="@+id/interstitial_page"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:fitsSystemWindows="true"
+    android:transitionGroup="true"
+    android:orientation="vertical">
+
+    <Toolbar
+        android:id="@+id/action_bar"
+        style="?android:attr/actionBarStyle"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:theme="?android:attr/actionBarTheme"
+        android:elevation="0dp"
+        android:background="@android:color/transparent" />
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:paddingTop="12dp"
+        android:paddingBottom="64dp"
+        android:paddingLeft="24dp"
+        android:paddingRight="24dp"
+        android:orientation="vertical">
+
+        <!-- image goes here -->
+        <FrameLayout
+            android:id="@+id/image_frame"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            app:layout_constraintBottom_toTopOf="@+id/mode_name_title"
+            app:layout_constraintTop_toTopOf="parent">
+
+            <ImageView
+                android:id="@+id/image"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:adjustViewBounds="true"
+                android:clickable="false"
+                android:scaleType="centerCrop"
+                android:layout_gravity="center" />
+
+        </FrameLayout>
+
+        <TextView
+            android:id="@+id/mode_name_title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:clickable="false"
+            android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Title"
+            app:layout_constraintBottom_toTopOf="@+id/mode_name_subtitle"
+            android:textSize="36sp"
+            android:paddingVertical="12dp" />
+
+        <TextView
+            android:id="@+id/mode_name_subtitle"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:clickable="false"
+            android:text="@string/zen_mode_setup_page_summary"
+            android:textSize="18sp"
+            android:paddingBottom="12dp"
+            android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body1"
+            app:layout_constraintBottom_toTopOf="@+id/enable_mode_button" />
+
+        <Button
+            android:id="@+id/enable_mode_button"
+            style="@style/ActionPrimaryButton"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingVertical="12dp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintVertical_bias="1" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/navigation/privatespace_main_context_nav.xml b/res/navigation/privatespace_main_context_nav.xml
index 3eb57d3..e20eaff 100644
--- a/res/navigation/privatespace_main_context_nav.xml
+++ b/res/navigation/privatespace_main_context_nav.xml
@@ -35,6 +35,9 @@
         <action
             android:id="@+id/action_set_lock_fragment"
             app:destination="@id/ps_profile_lock_fragment"/>
+        <action
+            android:id="@+id/action_create_profile_error_restrict"
+            app:destination="@id/ps_profile_error_restricted_fragment"/>
     </fragment>
     <fragment android:id="@+id/ps_profile_error_fragment"
               android:name="com.android.settings.privatespace.PrivateProfileCreationError"
@@ -67,6 +70,9 @@
             android:id="@+id/action_lock_success_fragment"
             app:destination="@id/ps_pre_finish_delay_fragment"/>
     </fragment>
+    <fragment android:id="@+id/ps_profile_error_restricted_fragment"
+              android:name="com.android.settings.privatespace.PrivateProfileCreationRestrictedError"
+              android:label="fragment_ps_error_exit"/>
     <action android:id="@+id/action_pre_finish_delay_fragment"
             app:destination="@id/ps_pre_finish_delay_fragment"/>
     <action android:id="@+id/action_advance_login_error"
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index d0422af..a24dc86 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -1503,12 +1503,14 @@
 
     <!-- A locale list of not supporting Terms of Address. [DO NOT TRANSLATE] -->
     <string-array name="terms_of_address_unsupported_locales">
-        <item>fr-CA</item> <!-- French (Canada) -->
     </string-array>
 
     <!-- A language list of supporting Terms of Address. [DO NOT TRANSLATE] -->
     <string-array name="terms_of_address_supported_languages">
         <item>fr</item> <!-- French -->
+        <item>es</item> <!-- Spanish -->
+        <item>it</item> <!-- Italian -->
+        <item>de</item> <!-- German -->
     </string-array>
 
     <!-- Entries for private space auto lock option -->
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 5158ed6..b1dabe8 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -517,4 +517,6 @@
     <dimen name="zen_mode_circular_icon_margin_between">4dp</dimen>
     <dimen name="zen_mode_circular_icon_margin_vertical">8dp</dimen>
     <dimen name="zen_mode_circular_icon_text_size">18dp</dimen>
+    <!-- For images in SetupInterstitialActivity -->
+    <dimen name="zen_mode_interstitial_corner_radius">30dp</dimen>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 66b3b8c..dc986ad 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1374,6 +1374,12 @@
     <string name="private_space_error_screen_title">Couldn\u2019t set up a private space</string>
     <!-- Label for button to retry creating private space again on creation error. [CHAR LIMIT=30] -->
     <string name="private_space_tryagain_label">Try Again</string>
+    <!-- Label for button to exit private space setup on creation error. [CHAR LIMIT=30] -->
+    <string name="private_space_exit_label">Exit</string>
+    <!-- Description in Private space error page with a link to the Help Center page[CHAR LIMIT=NONE] -->
+    <string name="private_space_error_description">Private space isn\u2019t available.\nView possible causes</string>
+    <!-- Text in Private space error page that points to view possible error causes [CHAR LIMIT=40] -->
+    <string name="private_space_error_causes_text">View possible causes</string>
     <!-- Title for private space lock setup screen. [CHAR LIMIT=90] -->
     <string name="private_space_lockscreen_title">Choose a new lock for private space?</string>
     <!-- Summary for the private space lock setup screen. [CHAR LIMIT=NONE] -->
@@ -8008,6 +8014,12 @@
     <!-- Subtitle for the Do not Disturb slice. [CHAR LIMIT=50]-->
     <string name="zen_mode_slice_subtitle">Limit interruptions</string>
 
+    <!-- Priority Modes: Summary on a page prompting the user to set up/enable a mode [CHAR_LIMIT=NONE] -->
+    <string name="zen_mode_setup_page_summary">Block interruptions and distractions</string>
+
+    <!-- Priority Modes: Label on a button prompting the user to set up the mode with the given name. [CHAR_LIMIT=40] -->
+    <string name="zen_mode_setup_button_label">Set up <xliff:g id="mode" example="My Mode">%1$s</xliff:g></string>
+
     <!-- Do not disturb: Title for the Do not Disturb dialog to turn on Do not disturb. [CHAR LIMIT=50]-->
     <string name="zen_mode_settings_turn_on_dialog_title">Turn on Do Not Disturb</string>
 
@@ -9177,6 +9189,8 @@
     <!-- [CHAR LIMIT=120] Zen mode settings: Title for conversations settings page -->
     <string name="zen_mode_conversations_title">Conversations</string>
     <string name="zen_mode_from_all_conversations">All conversations</string>
+    <!-- [CHAR LIMIT=40] Version of zen_mode_from_all_conversations when it is a non-first member of a list -->
+    <string name="zen_mode_from_all_conversations_second">all conversations</string>
     <string name="zen_mode_from_important_conversations">Priority conversations</string>
     <!-- [CHAR LIMIT=40] Version of the above for "priority conversations" when it is a non-first member of a list -->
     <string name="zen_mode_from_important_conversations_second">priority conversations</string>
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index 88abadb..9032849 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -507,6 +507,11 @@
             android:title="@string/show_key_presses"
             android:summary="@string/show_key_presses_summary" />
 
+        <SwitchPreference
+            android:key="touchpad_visualizer"
+            android:title="@string/touchpad_visualizer"
+            android:summary="@string/touchpad_visualizer_summary" />
+
     </PreferenceCategory>
 
     <PreferenceCategory
diff --git a/res/xml/mobile_network_settings.xml b/res/xml/mobile_network_settings.xml
index bed6de8..539c145 100644
--- a/res/xml/mobile_network_settings.xml
+++ b/res/xml/mobile_network_settings.xml
@@ -85,9 +85,10 @@
             android:summary="@string/auto_data_switch_summary"
             settings:controller="com.android.settings.network.telephony.AutoDataSwitchPreferenceController"/>
 
+        <!-- Settings search is handled by RoamingSearchItem. -->
         <com.android.settings.spa.preference.ComposePreference
             android:key="button_roaming_key"
-            android:title="@string/roaming"
+            settings:searchable="false"
             settings:controller="com.android.settings.network.telephony.RoamingPreferenceController"/>
 
         <Preference
@@ -201,10 +202,12 @@
             android:title="@string/call_category"
             settings:controller="com.android.settings.network.telephony.CallingPreferenceCategoryController">
 
+            <!-- Settings search is handled by WifiCallingSearchItem. -->
             <Preference
                 android:key="wifi_calling"
                 android:title="@string/wifi_calling_settings_title"
                 android:summary="@string/wifi_calling_summary"
+                settings:searchable="false"
                 settings:controller="com.android.settings.network.telephony.WifiCallingPreferenceController">
                 <intent android:action="android.intent.action.MAIN"
                         android:targetPackage="com.android.settings"
@@ -293,10 +296,10 @@
             settings:controller=
                 "com.android.settings.network.telephony.NullAlgorithmsPreferenceController"/>
 
+        <!-- Settings search is handled by NrAdvancedCallingSearchItem. -->
         <com.android.settings.spa.preference.ComposePreference
             android:key="nr_advanced_calling"
-            android:title="@string/nr_advanced_calling_title"
-            settings:keywords="@string/keywords_nr_advanced_calling"
+            settings:searchable="false"
             settings:controller="com.android.settings.network.telephony.NrAdvancedCallingPreferenceController"/>
     </PreferenceCategory>
 
diff --git a/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceController.java b/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceController.java
index 3ba0f0d..833638b 100644
--- a/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceController.java
+++ b/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceController.java
@@ -29,7 +29,6 @@
 import android.os.Handler;
 import android.os.VibrationAttributes;
 import android.os.Vibrator;
-import android.os.vibrator.Flags;
 import android.provider.Settings;
 import android.util.Log;
 
@@ -110,8 +109,7 @@
 
     @Override
     public int getAvailabilityStatus() {
-        if (Flags.keyboardCategoryEnabled()
-                && mContext.getResources().getBoolean(
+        if (mContext.getResources().getBoolean(
                         com.android.internal.R.bool.config_keyboardVibrationSettingsSupported)) {
             return AVAILABLE;
         }
diff --git a/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java b/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java
index 2cabc76..c324130 100644
--- a/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java
+++ b/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java
@@ -128,7 +128,13 @@
             AccessibilityActivityPreference preference = new AccessibilityActivityPreference(
                     mContext, componentName.getPackageName(), activityInfo.applicationInfo.uid,
                     info);
-            setRestrictedPreferenceEnabled(preference, permittedServices, serviceEnabled);
+            if (Flags.neverRestrictAccessibilityActivity()) {
+                // Accessibility Activities do not have elevated privileges so restricting
+                // them based on ECM or device admin does not give any value.
+                preference.setEnabled(true);
+            } else {
+                setRestrictedPreferenceEnabled(preference, permittedServices, serviceEnabled);
+            }
             preferenceList.add(preference);
         }
         return preferenceList;
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherController.java b/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherController.java
index d1d00d8..e29dcb0 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherController.java
@@ -50,7 +50,6 @@
             CachedBluetoothDevice device,
             Lifecycle lifecycle) {
         super(context, fragment, device, lifecycle);
-        lifecycle.addObserver(this);
     }
 
     @Override
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
index 015d427..50c908d 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
@@ -106,7 +106,6 @@
         mProfileManager = mManager.getProfileManager();
         mCachedDevice = device;
         mAllOfCachedDevices = Utils.getAllOfCachedBluetoothDevices(mManager, mCachedDevice);
-        lifecycle.addObserver(this);
     }
 
     @Override
@@ -309,7 +308,7 @@
      * Helper to get the list of connectable and special profiles.
      */
     private List<LocalBluetoothProfile> getProfiles() {
-        List<LocalBluetoothProfile> result = new ArrayList<LocalBluetoothProfile>();
+        List<LocalBluetoothProfile> result = new ArrayList<>();
         mProfileDeviceMap.clear();
         if (mAllOfCachedDevices == null || mAllOfCachedDevices.isEmpty()) {
             return result;
@@ -320,8 +319,7 @@
                 if (mProfileDeviceMap.containsKey(profile.toString())) {
                     mProfileDeviceMap.get(profile.toString()).add(cachedItem);
                 } else {
-                    List<CachedBluetoothDevice> tmpCachedDeviceList =
-                            new ArrayList<CachedBluetoothDevice>();
+                    List<CachedBluetoothDevice> tmpCachedDeviceList = new ArrayList<>();
                     tmpCachedDeviceList.add(cachedItem);
                     mProfileDeviceMap.put(profile.toString(), tmpCachedDeviceList);
                     result.add(profile);
@@ -357,6 +355,10 @@
         }
         boolean hearingAidSupported = result.contains(
                 mManager.getProfileManager().getHearingAidProfile());
+        // Remove hearing aids toggle anyway since showing the toggle will confuse users
+        if (hearingAidSupported) {
+            result.remove(mManager.getProfileManager().getHearingAidProfile());
+        }
         if (leAudioSupported && !classicAudioSupported && !hearingAidSupported) {
             mIsLeAudioOnlyDevice = true;
         }
diff --git a/src/com/android/settings/bluetooth/BluetoothEnabler.java b/src/com/android/settings/bluetooth/BluetoothEnabler.java
index df5cc72..a5d0bc6 100644
--- a/src/com/android/settings/bluetooth/BluetoothEnabler.java
+++ b/src/com/android/settings/bluetooth/BluetoothEnabler.java
@@ -132,7 +132,7 @@
 
         new Thread(() -> {
             try {
-                mIsSatelliteOn.set(mSatelliteRepository.requestIsEnabled(
+                mIsSatelliteOn.set(mSatelliteRepository.requestIsSessionStarted(
                         Executors.newSingleThreadExecutor()).get(3000, TimeUnit.MILLISECONDS));
             } catch (InterruptedException | ExecutionException | TimeoutException e) {
                 Log.e(TAG, "Error to get satellite status : " + e);
diff --git a/src/com/android/settings/bluetooth/BluetoothPairingDetail.java b/src/com/android/settings/bluetooth/BluetoothPairingDetail.java
index ca53854..ebb07bd 100644
--- a/src/com/android/settings/bluetooth/BluetoothPairingDetail.java
+++ b/src/com/android/settings/bluetooth/BluetoothPairingDetail.java
@@ -79,7 +79,7 @@
         boolean isSatelliteOn = true;
         try {
             isSatelliteOn =
-                    satelliteRepository.requestIsEnabled(
+                    satelliteRepository.requestIsSessionStarted(
                             Executors.newSingleThreadExecutor()).get(3000, TimeUnit.MILLISECONDS);
         } catch (InterruptedException | ExecutionException | TimeoutException e) {
             Log.e(TAG, "Error to get satellite status : " + e);
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceController.java
index 33c1a6c..168fce5 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceController.java
@@ -66,7 +66,7 @@
 /** PreferenceController to control the dialog to choose the active device for calls and alarms */
 public class AudioSharingCallAudioPreferenceController extends AudioSharingBasePreferenceController
         implements BluetoothCallback {
-    private static final String TAG = "CallsAndAlarmsPreferenceController";
+    private static final String TAG = "CallAudioPrefController";
     private static final String PREF_KEY = "calls_and_alarms";
 
     @VisibleForTesting
@@ -85,7 +85,7 @@
     private final ContentObserver mSettingsObserver;
     private final MetricsFeatureProvider mMetricsFeatureProvider;
     @Nullable private Fragment mFragment;
-    Map<Integer, List<CachedBluetoothDevice>> mGroupedConnectedDevices = new HashMap<>();
+    Map<Integer, List<BluetoothDevice>> mGroupedConnectedDevices = new HashMap<>();
     private List<AudioSharingDeviceItem> mDeviceItemsInSharingSession = new ArrayList<>();
     private final AtomicBoolean mCallbacksRegistered = new AtomicBoolean(false);
 
@@ -210,17 +210,18 @@
                                                     "Skip set fallback active device: unchanged");
                                             return;
                                         }
-                                        List<CachedBluetoothDevice> devices =
+                                        List<BluetoothDevice> devices =
                                                 mGroupedConnectedDevices.getOrDefault(
                                                         item.getGroupId(), ImmutableList.of());
                                         CachedBluetoothDevice lead =
-                                                AudioSharingUtils.getLeadDevice(devices);
+                                                AudioSharingUtils.getLeadDevice(
+                                                        mCacheManager, devices);
                                         if (lead != null) {
                                             Log.d(
                                                     TAG,
                                                     "Set fallback active device: "
                                                             + lead.getDevice()
-                                                            .getAnonymizedAddress());
+                                                                    .getAnonymizedAddress());
                                             lead.setActive();
                                             logCallAudioDeviceChange(currentGroupId, lead);
                                         } else {
@@ -347,8 +348,8 @@
      */
     private void updateSummary() {
         updateDeviceItemsInSharingSession();
-        int fallbackActiveGroupId = BluetoothUtils.getPrimaryGroupIdForBroadcast(
-                mContext.getContentResolver());
+        int fallbackActiveGroupId =
+                BluetoothUtils.getPrimaryGroupIdForBroadcast(mContext.getContentResolver());
         if (fallbackActiveGroupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
             for (AudioSharingDeviceItem item : mDeviceItemsInSharingSession) {
                 if (item.getGroupId() == fallbackActiveGroupId) {
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java
index 472cb44..4ee405d 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.connecteddevice.audiosharing;
 
+import static java.util.stream.Collectors.toList;
+
 import android.app.settings.SettingsEnums;
 import android.bluetooth.BluetoothCsipSetCoordinator;
 import android.bluetooth.BluetoothDevice;
@@ -38,6 +40,7 @@
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.bluetooth.BluetoothUtils;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -49,14 +52,14 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 import java.util.concurrent.Executor;
 
 public class AudioSharingDialogHandler {
-    private static final String TAG = "AudioSharingDialogHandler";
+    private static final String TAG = "AudioSharingDlgHandler";
     private final Context mContext;
     private final Fragment mHostFragment;
     @Nullable private final LocalBluetoothManager mLocalBtManager;
+    @Nullable private final CachedBluetoothDeviceManager mDeviceManager;
     @Nullable private final LocalBluetoothLeBroadcast mBroadcast;
     @Nullable private final LocalBluetoothLeBroadcastAssistant mAssistant;
     private final MetricsFeatureProvider mMetricsFeatureProvider;
@@ -163,6 +166,7 @@
         mContext = context;
         mHostFragment = fragment;
         mLocalBtManager = Utils.getLocalBluetoothManager(context);
+        mDeviceManager = mLocalBtManager != null ? mLocalBtManager.getCachedDeviceManager() : null;
         mBroadcast =
                 mLocalBtManager != null
                         ? mLocalBtManager.getProfileManager().getLeAudioBroadcastProfile()
@@ -212,7 +216,7 @@
         if (isBroadcasting) {
             // Show stop audio sharing dialog when an ineligible (non LE audio) remote device
             // connected during a sharing session.
-            Map<Integer, List<CachedBluetoothDevice>> groupedDevices =
+            Map<Integer, List<BluetoothDevice>> groupedDevices =
                     AudioSharingUtils.fetchConnectedDevicesByGroupId(mLocalBtManager);
             List<AudioSharingDeviceItem> deviceItemsInSharingSession =
                     AudioSharingUtils.buildOrderedConnectedLeadAudioSharingDeviceItem(
@@ -256,19 +260,19 @@
             @NonNull CachedBluetoothDevice cachedDevice,
             boolean isBroadcasting,
             boolean userTriggered) {
-        Map<Integer, List<CachedBluetoothDevice>> groupedDevices =
+        Map<Integer, List<BluetoothDevice>> groupedDevices =
                 AudioSharingUtils.fetchConnectedDevicesByGroupId(mLocalBtManager);
         BluetoothDevice btDevice = cachedDevice.getDevice();
         String deviceAddress = btDevice == null ? "" : btDevice.getAnonymizedAddress();
+        int groupId = BluetoothUtils.getGroupId(cachedDevice);
         if (isBroadcasting) {
             // If another device within the same is already in the sharing session, add source to
             // the device automatically.
-            int groupId = BluetoothUtils.getGroupId(cachedDevice);
             if (groupedDevices.containsKey(groupId)
                     && groupedDevices.get(groupId).stream()
                             .anyMatch(
                                     device ->
-                                            BluetoothUtils.hasConnectedBroadcastSource(
+                                            BluetoothUtils.hasConnectedBroadcastSourceForBtDevice(
                                                     device, mLocalBtManager))) {
                 Log.d(
                         TAG,
@@ -352,14 +356,17 @@
         } else {
             // Build a list of AudioSharingDeviceItem for connected devices other than cachedDevice.
             List<AudioSharingDeviceItem> deviceItems = new ArrayList<>();
-            for (List<CachedBluetoothDevice> devices : groupedDevices.values()) {
+            for (Map.Entry<Integer, List<BluetoothDevice>> entry : groupedDevices.entrySet()) {
+                if (entry.getKey() == groupId) continue;
                 // Use random device in the group within the sharing session to represent the group.
-                CachedBluetoothDevice device = devices.get(0);
-                if (BluetoothUtils.getGroupId(device)
-                        == BluetoothUtils.getGroupId(cachedDevice)) {
-                    continue;
+                for (BluetoothDevice device : entry.getValue()) {
+                    CachedBluetoothDevice cDevice =
+                            mDeviceManager != null ? mDeviceManager.findDevice(device) : null;
+                    if (cDevice != null) {
+                        deviceItems.add(AudioSharingUtils.buildAudioSharingDeviceItem(cDevice));
+                        break;
+                    }
                 }
-                deviceItems.add(AudioSharingUtils.buildAudioSharingDeviceItem(device));
             }
             // Show audio sharing join dialog when the second eligible (LE audio) remote
             // device connect and no sharing session.
@@ -368,13 +375,10 @@
                         new AudioSharingJoinDialogFragment.DialogEventListener() {
                             @Override
                             public void onShareClick() {
-                                mTargetSinks = new ArrayList<>();
-                                for (List<CachedBluetoothDevice> devices :
-                                        groupedDevices.values()) {
-                                    for (CachedBluetoothDevice device : devices) {
-                                        mTargetSinks.add(device.getDevice());
-                                    }
-                                }
+                                mTargetSinks =
+                                        groupedDevices.values().stream()
+                                                .flatMap(items -> items.stream())
+                                                .collect(toList());
                                 Log.d(TAG, "Start broadcast with sinks = " + mTargetSinks.size());
                                 if (mBroadcast != null) {
                                     mBroadcast.startPrivateBroadcast();
@@ -493,7 +497,7 @@
     }
 
     private void removeSourceForGroup(
-            int groupId, Map<Integer, List<CachedBluetoothDevice>> groupedDevices) {
+            int groupId, Map<Integer, List<BluetoothDevice>> groupedDevices) {
         if (mAssistant == null) {
             Log.d(TAG, "Fail to add source due to null profiles, group = " + groupId);
             return;
@@ -503,8 +507,6 @@
             return;
         }
         groupedDevices.getOrDefault(groupId, ImmutableList.of()).stream()
-                .map(CachedBluetoothDevice::getDevice)
-                .filter(Objects::nonNull)
                 .forEach(
                         device -> {
                             for (BluetoothLeBroadcastReceiveState source :
@@ -515,7 +517,7 @@
     }
 
     private void addSourceForGroup(
-            int groupId, Map<Integer, List<CachedBluetoothDevice>> groupedDevices) {
+            int groupId, Map<Integer, List<BluetoothDevice>> groupedDevices) {
         if (mBroadcast == null || mAssistant == null) {
             Log.d(TAG, "Fail to add source due to null profiles, group = " + groupId);
             return;
@@ -525,8 +527,6 @@
             return;
         }
         groupedDevices.getOrDefault(groupId, ImmutableList.of()).stream()
-                .map(CachedBluetoothDevice::getDevice)
-                .filter(Objects::nonNull)
                 .forEach(
                         device ->
                                 mAssistant.addSource(
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
index 60b2ee5..8396e48 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
@@ -30,6 +30,9 @@
 import android.util.FeatureFlagUtils;
 import android.util.Log;
 import android.util.Pair;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
 import android.widget.CompoundButton;
 import android.widget.CompoundButton.OnCheckedChangeListener;
 
@@ -45,7 +48,6 @@
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.widget.SettingsMainSwitchBar;
 import com.android.settingslib.bluetooth.BluetoothUtils;
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -60,17 +62,15 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Objects;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.stream.Collectors;
 
 public class AudioSharingSwitchBarController extends BasePreferenceController
         implements DefaultLifecycleObserver,
-                OnCheckedChangeListener,
-                LocalBluetoothProfileManager.ServiceListener {
-    private static final String TAG = "AudioSharingSwitchBarCtl";
+        OnCheckedChangeListener,
+        LocalBluetoothProfileManager.ServiceListener {
+    private static final String TAG = "AudioSharingSwitchCtlr";
     private static final String PREF_KEY = "audio_sharing_main_switch";
 
     interface OnAudioSharingStateChangedListener {
@@ -100,7 +100,7 @@
     private final Executor mExecutor;
     private final MetricsFeatureProvider mMetricsFeatureProvider;
     private final OnAudioSharingStateChangedListener mListener;
-    private Map<Integer, List<CachedBluetoothDevice>> mGroupedConnectedDevices = new HashMap<>();
+    private Map<Integer, List<BluetoothDevice>> mGroupedConnectedDevices = new HashMap<>();
     private List<BluetoothDevice> mTargetActiveSinks = new ArrayList<>();
     private List<AudioSharingDeviceItem> mDeviceItemsForSharing = new ArrayList<>();
     @VisibleForTesting IntentFilter mIntentFilter;
@@ -284,6 +284,7 @@
                         : mProfileManager.getLeAudioBroadcastAssistantProfile();
         mExecutor = Executors.newSingleThreadExecutor();
         mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
+        mSwitchBar.getRootView().setAccessibilityDelegate(new MainSwitchAccessibilityDelegate());
     }
 
     @Override
@@ -337,8 +338,8 @@
             // FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST is always true in
             // prod. We can turn off the flag for debug purpose.
             if (FeatureFlagUtils.isEnabled(
-                            mContext,
-                            FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST)
+                    mContext,
+                    FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST)
                     && mAssistant.getAllConnectedDevices().isEmpty()) {
                 // Pop up dialog to ask users to connect at least one lea buds before audio sharing.
                 AudioSharingUtils.postOnMainThread(
@@ -450,13 +451,11 @@
         mDeviceItemsForSharing = new ArrayList<>(deviceItems);
         mTargetActiveSinks = new ArrayList<>();
         if (!deviceItems.isEmpty() && deviceItems.get(0).isActive()) {
-            for (CachedBluetoothDevice device :
+            // If active device exists for audio sharing, share to it
+            // automatically once the broadcast is started.
+            mTargetActiveSinks =
                     mGroupedConnectedDevices.getOrDefault(
-                            deviceItems.get(0).getGroupId(), ImmutableList.of())) {
-                // If active device exists for audio sharing, share to it
-                // automatically once the broadcast is started.
-                mTargetActiveSinks.add(device.getDevice());
-            }
+                            deviceItems.get(0).getGroupId(), ImmutableList.of());
             mDeviceItemsForSharing.remove(0);
         }
         if (mBroadcast != null) {
@@ -484,7 +483,7 @@
                             boolean isStateReady =
                                     isBluetoothOn()
                                             && AudioSharingUtils.isAudioSharingProfileReady(
-                                                    mProfileManager);
+                                            mProfileManager);
                             AudioSharingUtils.postOnMainThread(
                                     mContext,
                                     () -> {
@@ -537,12 +536,8 @@
                     @Override
                     public void onItemClick(@NonNull AudioSharingDeviceItem item) {
                         AudioSharingUtils.addSourceToTargetSinks(
-                                mGroupedConnectedDevices
-                                        .getOrDefault(item.getGroupId(), ImmutableList.of())
-                                        .stream()
-                                        .map(CachedBluetoothDevice::getDevice)
-                                        .filter(Objects::nonNull)
-                                        .collect(Collectors.toList()),
+                                mGroupedConnectedDevices.getOrDefault(
+                                        item.getGroupId(), ImmutableList.of()),
                                 mBtManager);
                         mGroupedConnectedDevices.clear();
                         mDeviceItemsForSharing.clear();
@@ -564,4 +559,19 @@
                     }
                 });
     }
+
+    private static final class MainSwitchAccessibilityDelegate extends View.AccessibilityDelegate {
+        @Override
+        public boolean onRequestSendAccessibilityEvent(
+                @NonNull ViewGroup host, @NonNull View view, @NonNull AccessibilityEvent event) {
+            if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
+                    && (event.getContentChangeTypes()
+                    & AccessibilityEvent.CONTENT_CHANGE_TYPE_ENABLED)
+                    != 0) {
+                Log.d(TAG, "Skip accessibility event for CONTENT_CHANGE_TYPE_ENABLED");
+                return false;
+            }
+            return super.onRequestSendAccessibilityEvent(host, view, event);
+        }
+    }
 }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java
index 50f9c9a..0c2dc36 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java
@@ -22,6 +22,8 @@
 import static com.android.settings.connecteddevice.audiosharing.AudioSharingUtils.MetricKey.METRIC_KEY_SOURCE_PAGE_ID;
 import static com.android.settings.connecteddevice.audiosharing.AudioSharingUtils.MetricKey.METRIC_KEY_USER_TRIGGERED;
 
+import static java.util.stream.Collectors.toList;
+
 import android.bluetooth.BluetoothCsipSetCoordinator;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothLeBroadcastMetadata;
@@ -44,10 +46,11 @@
 import com.android.settingslib.bluetooth.VolumeControlProfile;
 
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.stream.Collectors;
+import java.util.Objects;
 
 public class AudioSharingUtils {
     private static final String TAG = "AudioSharingUtils";
@@ -62,15 +65,15 @@
     }
 
     /**
-     * Fetch {@link CachedBluetoothDevice}s connected to the broadcast assistant. The devices are
-     * grouped by CSIP group id.
+     * Fetch {@link BluetoothDevice}s connected to the broadcast assistant. The devices are grouped
+     * by CSIP group id.
      *
      * @param localBtManager The BT manager to provide BT functions.
      * @return A map of connected devices grouped by CSIP group id.
      */
-    public static Map<Integer, List<CachedBluetoothDevice>> fetchConnectedDevicesByGroupId(
+    public static Map<Integer, List<BluetoothDevice>> fetchConnectedDevicesByGroupId(
             @Nullable LocalBluetoothManager localBtManager) {
-        Map<Integer, List<CachedBluetoothDevice>> groupedDevices = new HashMap<>();
+        Map<Integer, List<BluetoothDevice>> groupedDevices = new HashMap<>();
         if (localBtManager == null) {
             Log.d(TAG, "Skip fetchConnectedDevicesByGroupId due to bt manager is null");
             return groupedDevices;
@@ -99,7 +102,7 @@
             if (!groupedDevices.containsKey(groupId)) {
                 groupedDevices.put(groupId, new ArrayList<>());
             }
-            groupedDevices.get(groupId).add(cachedDevice);
+            groupedDevices.get(groupId).add(device);
         }
         if (DEBUG) {
             Log.d(TAG, "fetchConnectedDevicesByGroupId: " + groupedDevices);
@@ -122,11 +125,16 @@
      */
     public static List<CachedBluetoothDevice> buildOrderedConnectedLeadDevices(
             @Nullable LocalBluetoothManager localBtManager,
-            Map<Integer, List<CachedBluetoothDevice>> groupedConnectedDevices,
+            Map<Integer, List<BluetoothDevice>> groupedConnectedDevices,
             boolean filterByInSharing) {
         List<CachedBluetoothDevice> orderedDevices = new ArrayList<>();
-        for (List<CachedBluetoothDevice> devices : groupedConnectedDevices.values()) {
-            CachedBluetoothDevice leadDevice = getLeadDevice(devices);
+        if (localBtManager == null) {
+            Log.d(TAG, "Skip buildOrderedConnectedLeadDevices due to bt manager is null");
+            return orderedDevices;
+        }
+        CachedBluetoothDeviceManager deviceManager = localBtManager.getCachedDeviceManager();
+        for (List<BluetoothDevice> devices : groupedConnectedDevices.values()) {
+            CachedBluetoothDevice leadDevice = getLeadDevice(deviceManager, devices);
             if (leadDevice == null) {
                 Log.d(TAG, "Skip due to no lead device");
                 continue;
@@ -141,52 +149,39 @@
             }
             orderedDevices.add(leadDevice);
         }
-        orderedDevices.sort(
-                (CachedBluetoothDevice d1, CachedBluetoothDevice d2) -> {
-                    // Active above not inactive
-                    int comparison =
-                            (isActiveLeAudioDevice(d2) ? 1 : 0)
-                                    - (isActiveLeAudioDevice(d1) ? 1 : 0);
-                    if (comparison != 0) return comparison;
-                    // Bonded above not bonded
-                    comparison =
-                            (d2.getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0)
-                                    - (d1.getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0);
-                    if (comparison != 0) return comparison;
-                    // Bond timestamp available above unavailable
-                    comparison =
-                            (d2.getBondTimestamp() != null ? 1 : 0)
-                                    - (d1.getBondTimestamp() != null ? 1 : 0);
-                    if (comparison != 0) return comparison;
-                    // Order by bond timestamp if it is available
-                    // Otherwise order by device name
-                    return d1.getBondTimestamp() != null
-                            ? d1.getBondTimestamp().compareTo(d2.getBondTimestamp())
-                            : d1.getName().compareTo(d2.getName());
-                });
+        orderedDevices.sort(sCachedDeviceComparator);
         return orderedDevices;
     }
 
     /**
      * Get the lead device from a list of devices with same group id.
      *
+     * @param deviceManager CachedBluetoothDeviceManager
      * @param devices A list of devices with same group id.
      * @return The lead device
      */
     @Nullable
     public static CachedBluetoothDevice getLeadDevice(
-            @NonNull List<CachedBluetoothDevice> devices) {
-        if (devices.isEmpty()) return null;
-        for (CachedBluetoothDevice device : devices) {
-            if (!device.getMemberDevice().isEmpty()) {
-                return device;
+            @Nullable CachedBluetoothDeviceManager deviceManager,
+            @NonNull List<BluetoothDevice> devices) {
+        if (deviceManager == null || devices.isEmpty()) return null;
+        List<CachedBluetoothDevice> cachedDevices =
+                devices.stream()
+                        .map(device -> deviceManager.findDevice(device))
+                        .filter(Objects::nonNull)
+                        .collect(toList());
+        for (CachedBluetoothDevice cachedDevice : cachedDevices) {
+            if (!cachedDevice.getMemberDevice().isEmpty()) {
+                return cachedDevice;
             }
         }
-        CachedBluetoothDevice leadDevice = devices.get(0);
+        CachedBluetoothDevice leadDevice = cachedDevices.isEmpty() ? null : cachedDevices.get(0);
         Log.d(
                 TAG,
                 "No lead device in the group, pick arbitrary device as the lead: "
-                        + leadDevice.getDevice().getAnonymizedAddress());
+                        + (leadDevice == null
+                                ? "null"
+                                : leadDevice.getDevice().getAnonymizedAddress()));
         return leadDevice;
     }
 
@@ -206,13 +201,13 @@
     @NonNull
     public static List<AudioSharingDeviceItem> buildOrderedConnectedLeadAudioSharingDeviceItem(
             @Nullable LocalBluetoothManager localBtManager,
-            Map<Integer, List<CachedBluetoothDevice>> groupedConnectedDevices,
+            Map<Integer, List<BluetoothDevice>> groupedConnectedDevices,
             boolean filterByInSharing) {
         return buildOrderedConnectedLeadDevices(
                         localBtManager, groupedConnectedDevices, filterByInSharing)
                 .stream()
                 .map(AudioSharingUtils::buildAudioSharingDeviceItem)
-                .collect(Collectors.toList());
+                .collect(toList());
     }
 
     /** Build {@link AudioSharingDeviceItem} from {@link CachedBluetoothDevice}. */
@@ -361,4 +356,27 @@
             Pair.create(METRIC_KEY_CANDIDATE_DEVICE_COUNT.ordinal(), candidateDeviceCount)
         };
     }
+
+    private static final Comparator<CachedBluetoothDevice> sCachedDeviceComparator =
+            (CachedBluetoothDevice d1, CachedBluetoothDevice d2) -> {
+                // Active above not inactive
+                int comparison =
+                        (isActiveLeAudioDevice(d2) ? 1 : 0) - (isActiveLeAudioDevice(d1) ? 1 : 0);
+                if (comparison != 0) return comparison;
+                // Bonded above not bonded
+                comparison =
+                        (d2.getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0)
+                                - (d1.getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0);
+                if (comparison != 0) return comparison;
+                // Bond timestamp available above unavailable
+                comparison =
+                        (d2.getBondTimestamp() != null ? 1 : 0)
+                                - (d1.getBondTimestamp() != null ? 1 : 0);
+                if (comparison != 0) return comparison;
+                // Order by bond timestamp if it is available
+                // Otherwise order by device name
+                return d1.getBondTimestamp() != null
+                        ? d1.getBondTimestamp().compareTo(d2.getBondTimestamp())
+                        : d1.getName().compareTo(d2.getName());
+            };
 }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreference.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreference.java
index 071cf57..175e037 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreference.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreference.java
@@ -43,23 +43,12 @@
      * Update preference UI based on connection status
      *
      * @param isConnected Is this stream connected
-     * @param summary Summary text
-     * @param onPreferenceClickListener Click listener for the preference
      */
-    void setIsConnected(
-            boolean isConnected,
-            String summary,
-            @Nullable OnPreferenceClickListener onPreferenceClickListener) {
-        if (mIsConnected == isConnected
-                && getSummary() == summary
-                && getOnPreferenceClickListener() == onPreferenceClickListener) {
-            // Nothing to update.
-            return;
+    void setIsConnected(boolean isConnected) {
+        if (mIsConnected != isConnected) {
+            mIsConnected = isConnected;
+            notifyChanged();
         }
-        mIsConnected = isConnected;
-        setSummary(summary);
-        setOnPreferenceClickListener(onPreferenceClickListener);
-        notifyChanged();
     }
 
     @VisibleForTesting
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandler.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandler.java
index 4bb8475..758984f 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandler.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandler.java
@@ -16,8 +16,12 @@
 
 package com.android.settings.connecteddevice.audiosharing.audiostreams;
 
+import static android.text.Spanned.SPAN_EXCLUSIVE_INCLUSIVE;
+
 import android.os.Handler;
 import android.os.Looper;
+import android.text.SpannableString;
+import android.text.style.ForegroundColorSpan;
 import android.util.Log;
 
 import androidx.annotation.Nullable;
@@ -26,6 +30,7 @@
 import androidx.preference.Preference;
 
 import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.Utils;
 import com.android.settingslib.bluetooth.BluetoothUtils;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.utils.ThreadUtils;
@@ -68,15 +73,31 @@
 
         // Update UI
         ThreadUtils.postOnMainThread(
-                () ->
-                        preference.setIsConnected(
-                                newState
-                                        == AudioStreamsProgressCategoryController.AudioStreamState
-                                                .SOURCE_ADDED,
-                                getSummary() != EMPTY_STRING_RES
-                                        ? preference.getContext().getString(getSummary())
-                                        : "",
-                                getOnClickListener(controller)));
+                () -> {
+                    String summary =
+                            getSummary() != EMPTY_STRING_RES
+                                    ? preference.getContext().getString(getSummary())
+                                    : "";
+                    if (newState
+                            == AudioStreamsProgressCategoryController.AudioStreamState
+                                    .ADD_SOURCE_BAD_CODE) {
+                        SpannableString summarySpan = new SpannableString(summary);
+                        int colorError = Utils.getColorErrorDefaultColor(preference.getContext());
+                        summarySpan.setSpan(
+                                new ForegroundColorSpan(colorError),
+                                0,
+                                summary.length(),
+                                SPAN_EXCLUSIVE_INCLUSIVE);
+                        preference.setSummary(summarySpan);
+                    } else {
+                        preference.setSummary(summary);
+                    }
+                    preference.setIsConnected(
+                            newState
+                                    == AudioStreamsProgressCategoryController.AudioStreamState
+                                            .SOURCE_ADDED);
+                    preference.setOnPreferenceClickListener(getOnClickListener(controller));
+                });
     }
 
     /**
diff --git a/src/com/android/settings/core/BasePreferenceController.java b/src/com/android/settings/core/BasePreferenceController.java
index 5763d3b..3f91fb7 100644
--- a/src/com/android/settings/core/BasePreferenceController.java
+++ b/src/com/android/settings/core/BasePreferenceController.java
@@ -263,6 +263,16 @@
                 || availabilityStatus == DISABLED_DEPENDENT_SETTING);
     }
 
+    private boolean isAvailableForSearch() {
+        if (mIsForWork && mWorkProfileUser == null) {
+            return false;
+        }
+
+        final int availabilityStatus = getAvailabilityStatus();
+        return (availabilityStatus == AVAILABLE
+                || availabilityStatus == DISABLED_DEPENDENT_SETTING);
+    }
+
     /**
      * @return {@code false} if the setting is not applicable to the device. This covers both
      * settings which were only introduced in future versions of android, or settings that have
@@ -303,18 +313,12 @@
      * Called by SearchIndexProvider#getNonIndexableKeys
      */
     public void updateNonIndexableKeys(List<String> keys) {
-        final boolean shouldSuppressFromSearch = !isAvailable()
-                || getAvailabilityStatus() == AVAILABLE_UNSEARCHABLE;
-        if (shouldSuppressFromSearch) {
-            final String key = getPreferenceKey();
-            if (TextUtils.isEmpty(key)) {
-                Log.w(TAG, "Skipping updateNonIndexableKeys due to empty key " + toString());
-                return;
-            }
-            if (keys.contains(key)) {
-                Log.w(TAG, "Skipping updateNonIndexableKeys, key already in list. " + toString());
-                return;
-            }
+        final String key = getPreferenceKey();
+        if (TextUtils.isEmpty(key)) {
+            Log.w(TAG, "Skipping updateNonIndexableKeys due to empty key " + this);
+            return;
+        }
+        if (!keys.contains(key) && !isAvailableForSearch()) {
             keys.add(key);
         }
     }
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index db50676..666d24d 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -736,6 +736,7 @@
         controllers.add(new ShowTapsPreferenceController(context));
         controllers.add(new PointerLocationPreferenceController(context));
         controllers.add(new ShowKeyPressesPreferenceController(context));
+        controllers.add(new TouchpadVisualizerPreferenceController(context));
         controllers.add(new ShowSurfaceUpdatesPreferenceController(context));
         controllers.add(new ShowLayoutBoundsPreferenceController(context));
         controllers.add(new ShowHdrSdrRatioPreferenceController(context));
diff --git a/src/com/android/settings/development/OWNERS b/src/com/android/settings/development/OWNERS
index 081c44f..d780615 100644
--- a/src/com/android/settings/development/OWNERS
+++ b/src/com/android/settings/development/OWNERS
@@ -16,3 +16,6 @@
 # ADB
 per-file Adb*=set noparent
 per-file Adb*=file:platform/packages/modules/adb:/OWNERS
+
+#TouchpadVisualizerPreferenceController
+per-file TouchpadVisualizerPreferenceController.java=file:platform/frameworks/base:/INPUT_OWNERS
diff --git a/src/com/android/settings/development/TouchpadVisualizerPreferenceController.java b/src/com/android/settings/development/TouchpadVisualizerPreferenceController.java
new file mode 100644
index 0000000..f918d26
--- /dev/null
+++ b/src/com/android/settings/development/TouchpadVisualizerPreferenceController.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 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.development;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.SwitchPreference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.development.DeveloperOptionsPreferenceController;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import android.hardware.input.InputSettings;
+
+/** PreferenceController that controls the "Touchpad visualizer" developer option. */
+public class TouchpadVisualizerPreferenceController extends
+        DeveloperOptionsPreferenceController implements
+        Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
+
+    private static final String TOUCHPAD_VISUALIZER_KEY = "touchpad_visualizer";
+
+    @VisibleForTesting
+    static final int SETTING_VALUE_ON = 1;
+    @VisibleForTesting
+    static final int SETTING_VALUE_OFF = 0;
+
+    public TouchpadVisualizerPreferenceController(@NonNull Context context) {
+        super(context);
+    }
+
+    @Override
+    public @NonNull String getPreferenceKey() {
+        return TOUCHPAD_VISUALIZER_KEY;
+    }
+
+    @Override
+    public boolean isAvailable(){
+        return InputSettings.isTouchpadVisualizerFeatureFlagEnabled();
+    }
+
+    @Override
+    public boolean onPreferenceChange(@NonNull Preference preference, @Nullable Object newValue) {
+        final boolean isEnabled = newValue != null ? (Boolean) newValue : false;
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.TOUCHPAD_VISUALIZER,
+                isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF);
+        return true;
+    }
+
+    @Override
+    public void updateState(@NonNull Preference preference) {
+        int touchpadVisualizer = Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.TOUCHPAD_VISUALIZER, SETTING_VALUE_OFF);
+        ((SwitchPreference) mPreference).setChecked(touchpadVisualizer != SETTING_VALUE_OFF);
+    }
+
+    @Override
+    protected void onDeveloperOptionsSwitchDisabled() {
+        super.onDeveloperOptionsSwitchDisabled();
+        Settings.System.putInt(mContext.getContentResolver(), Settings.System.TOUCHPAD_VISUALIZER,
+                SETTING_VALUE_OFF);
+        ((SwitchPreference) mPreference).setChecked(false);
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryOptimizationModeCache.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryOptimizationModeCache.java
index 6b35fb9..2084b66 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryOptimizationModeCache.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryOptimizationModeCache.java
@@ -17,38 +17,47 @@
 package com.android.settings.fuelgauge.batteryusage;
 
 import android.content.Context;
-import android.util.ArrayMap;
+import android.util.SparseArray;
 
 import androidx.annotation.VisibleForTesting;
+import androidx.core.util.Pair;
 
 import com.android.settings.fuelgauge.BatteryOptimizeUtils;
 import com.android.settingslib.fuelgauge.PowerAllowlistBackend;
 
-import java.util.Map;
-
 /** A cache to log battery optimization mode of an app */
 final class BatteryOptimizationModeCache {
     private static final String TAG = "BatteryOptimizationModeCache";
 
-    @VisibleForTesting final Map<Integer, BatteryOptimizationMode> mBatteryOptimizeModeCacheMap;
+    /* Stores the battery optimization mode and mutable state for each UID. */
+    @VisibleForTesting
+    final SparseArray<Pair<BatteryOptimizationMode, Boolean>> mBatteryOptimizeModeCache;
 
     private final Context mContext;
 
     BatteryOptimizationModeCache(final Context context) {
         mContext = context;
-        mBatteryOptimizeModeCacheMap = new ArrayMap<>();
+        mBatteryOptimizeModeCache = new SparseArray<>();
         PowerAllowlistBackend.getInstance(mContext).refreshList();
     }
 
-    BatteryOptimizationMode getBatteryOptimizeMode(final int uid, final String packageName) {
-        if (!mBatteryOptimizeModeCacheMap.containsKey(uid)) {
+    Pair<BatteryOptimizationMode, Boolean> getBatteryOptimizeModeInfo(
+            final int uid, final String packageName) {
+        if (!mBatteryOptimizeModeCache.contains(uid)) {
             final BatteryOptimizeUtils batteryOptimizeUtils =
                     new BatteryOptimizeUtils(mContext, uid, packageName);
-            mBatteryOptimizeModeCacheMap.put(
+            mBatteryOptimizeModeCache.put(
                     uid,
-                    BatteryOptimizationMode.forNumber(
-                            batteryOptimizeUtils.getAppOptimizationMode(/* refreshList= */ false)));
+                    Pair.create(
+                            BatteryOptimizationMode.forNumber(
+                                    batteryOptimizeUtils.getAppOptimizationMode(
+                                            /* refreshList= */ false)),
+                            batteryOptimizeUtils.isOptimizeModeMutable()));
         }
-        return mBatteryOptimizeModeCacheMap.get(uid);
+        final Pair<BatteryOptimizationMode, Boolean> batteryOptimizeModeInfo =
+                mBatteryOptimizeModeCache.get(uid);
+        return batteryOptimizeModeInfo != null
+                ? batteryOptimizeModeInfo
+                : new Pair<>(BatteryOptimizationMode.MODE_UNKNOWN, false);
     }
 }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
index 8f4d4dd..391a57a 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
@@ -35,6 +35,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
+import androidx.core.util.Pair;
 
 import com.android.settings.fuelgauge.BatteryUtils;
 import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
@@ -542,9 +543,11 @@
         }
         // Log the battery optimization mode of AppEntry while converting to batteryUsageSlot.
         if (optimizationModeCache != null && !batteryDiffEntry.isSystemEntry()) {
-            builder.setAppOptimizationMode(
-                    optimizationModeCache.getBatteryOptimizeMode(
-                            (int) batteryDiffEntry.mUid, batteryDiffEntry.getPackageName()));
+            final Pair<BatteryOptimizationMode, Boolean> batteryOptimizationModeInfo =
+                    optimizationModeCache.getBatteryOptimizeModeInfo(
+                            (int) batteryDiffEntry.mUid, batteryDiffEntry.getPackageName());
+            builder.setAppOptimizationMode(batteryOptimizationModeInfo.first)
+                    .setIsAppOptimizationModeMutable(batteryOptimizationModeInfo.second);
         }
         return builder.build();
     }
diff --git a/src/com/android/settings/fuelgauge/protos/battery_usage_slot.proto b/src/com/android/settings/fuelgauge/protos/battery_usage_slot.proto
index 4e3e3c4..da2bd4f 100644
--- a/src/com/android/settings/fuelgauge/protos/battery_usage_slot.proto
+++ b/src/com/android/settings/fuelgauge/protos/battery_usage_slot.proto
@@ -40,4 +40,5 @@
   optional int64 screen_on_time = 16;
   optional int64 foreground_service_usage_time = 17;
   optional BatteryOptimizationMode app_optimization_mode = 18;
+  optional bool is_app_optimization_mode_mutable = 19;
 }
diff --git a/src/com/android/settings/network/AirplaneModePreferenceController.java b/src/com/android/settings/network/AirplaneModePreferenceController.java
index b1f6e50..d4bd4a3 100644
--- a/src/com/android/settings/network/AirplaneModePreferenceController.java
+++ b/src/com/android/settings/network/AirplaneModePreferenceController.java
@@ -162,7 +162,8 @@
     public void onResume() {
         try {
             mIsSatelliteOn.set(
-                    mSatelliteRepository.requestIsEnabled(Executors.newSingleThreadExecutor())
+                    mSatelliteRepository
+                            .requestIsSessionStarted(Executors.newSingleThreadExecutor())
                             .get(2000, TimeUnit.MILLISECONDS));
         } catch (ExecutionException | TimeoutException | InterruptedException e) {
             Log.e(TAG, "Error to get satellite status : " + e);
diff --git a/src/com/android/settings/network/SatelliteRepository.kt b/src/com/android/settings/network/SatelliteRepository.kt
index 565fbf3..b7c25f4 100644
--- a/src/com/android/settings/network/SatelliteRepository.kt
+++ b/src/com/android/settings/network/SatelliteRepository.kt
@@ -25,6 +25,7 @@
 import androidx.concurrent.futures.CallbackToFutureAdapter
 import com.google.common.util.concurrent.Futures.immediateFuture
 import com.google.common.util.concurrent.ListenableFuture
+import java.util.concurrent.Executor
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.asExecutor
@@ -32,7 +33,6 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.callbackFlow
 import kotlinx.coroutines.flow.flowOf
-import java.util.concurrent.Executor
 import kotlinx.coroutines.flow.flowOn
 
 /**
@@ -58,20 +58,26 @@
         }
 
         return CallbackToFutureAdapter.getFuture { completer ->
-            satelliteManager.requestIsEnabled(executor,
-                object : OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> {
-                    override fun onResult(result: Boolean) {
-                        Log.i(TAG, "Satellite modem enabled status: $result")
-                        completer.set(result)
-                    }
+            try {
+                satelliteManager.requestIsEnabled(executor,
+                    object : OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> {
+                        override fun onResult(result: Boolean) {
+                            Log.i(TAG, "Satellite modem enabled status: $result")
+                            completer.set(result)
+                        }
 
-                    override fun onError(error: SatelliteManager.SatelliteException) {
-                        super.onError(error)
-                        Log.w(TAG, "Can't get satellite modem enabled status", error)
-                        completer.set(false)
-                    }
-                })
-            "requestIsEnabled"
+                        override fun onError(error: SatelliteManager.SatelliteException) {
+                            super.onError(error)
+                            Log.w(TAG, "Can't get satellite modem enabled status", error)
+                            completer.set(false)
+                        }
+                    })
+                "requestIsEnabled"
+            } catch (e: IllegalStateException) {
+                Log.w(TAG, "IllegalStateException $e")
+                completer.set(false)
+            }
+
         }
     }
 
@@ -96,14 +102,21 @@
             val callback = object : SatelliteModemStateCallback {
                 override fun onSatelliteModemStateChanged(state: Int) {
                     val isSessionStarted = isSatelliteSessionStarted(state)
-                    Log.i(TAG, "Satellite modem state changed: state=$state"
-                            + ", isSessionStarted=$isSessionStarted")
+                    Log.i(
+                        TAG, "Satellite modem state changed: state=$state"
+                            + ", isSessionStarted=$isSessionStarted"
+                    )
                     completer.set(isSessionStarted)
                     satelliteManager.unregisterForModemStateChanged(this)
                 }
             }
 
-            val registerResult = satelliteManager.registerForModemStateChanged(executor, callback)
+            var registerResult = SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN
+            try {
+                registerResult = satelliteManager.registerForModemStateChanged(executor, callback)
+            } catch (e: IllegalStateException) {
+                Log.w(TAG, "IllegalStateException $e")
+            }
             if (registerResult != SatelliteManager.SATELLITE_RESULT_SUCCESS) {
                 Log.w(TAG, "Failed to register for satellite modem state change: $registerResult")
                 completer.set(false)
@@ -132,15 +145,21 @@
         return callbackFlow {
             val callback = SatelliteModemStateCallback { state ->
                 val isSessionStarted = isSatelliteSessionStarted(state)
-                Log.i(TAG, "Satellite modem state changed: state=$state"
-                    + ", isSessionStarted=$isSessionStarted")
+                Log.i(
+                    TAG, "Satellite modem state changed: state=$state"
+                        + ", isSessionStarted=$isSessionStarted"
+                )
                 trySend(isSessionStarted)
             }
-
-            val registerResult = satelliteManager.registerForModemStateChanged(
-                defaultDispatcher.asExecutor(),
-                callback
-            )
+            var registerResult: Int = SatelliteManager.SATELLITE_RESULT_ERROR
+            try {
+                registerResult = satelliteManager.registerForModemStateChanged(
+                    defaultDispatcher.asExecutor(),
+                    callback
+                )
+            } catch (e: IllegalStateException) {
+                Log.w(TAG, "IllegalStateException $e")
+            }
 
             if (registerResult != SatelliteManager.SATELLITE_RESULT_SUCCESS) {
                 // If the registration failed (e.g., device doesn't support satellite),
@@ -150,7 +169,13 @@
                 trySend(false)
             }
 
-            awaitClose { satelliteManager.unregisterForModemStateChanged(callback) }
+            awaitClose {
+                try {
+                    satelliteManager.unregisterForModemStateChanged(callback)
+                } catch (e: IllegalStateException) {
+                    Log.w(TAG, "IllegalStateException $e")
+                }
+            }
         }.flowOn(Dispatchers.Default)
     }
 
diff --git a/src/com/android/settings/network/telephony/MobileDataSlice.java b/src/com/android/settings/network/telephony/MobileDataSlice.java
index fd65e85..f5e734d 100644
--- a/src/com/android/settings/network/telephony/MobileDataSlice.java
+++ b/src/com/android/settings/network/telephony/MobileDataSlice.java
@@ -29,6 +29,7 @@
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.UserManager;
 import android.provider.Settings;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
@@ -79,19 +80,24 @@
 
     @Override
     public Slice getSlice() {
+        ListBuilder listBuilder = createListBuilder();
+        if (!isConfigMobileNetworksAllowed()) {
+            return listBuilder.build();
+        }
+
         final IconCompat icon = IconCompat.createWithResource(mContext,
                 R.drawable.ic_network_cell);
         final String title = mContext.getText(R.string.mobile_data_settings_title).toString();
         @ColorInt final int color = Utils.getColorAccentDefaultColor(mContext);
 
-        // Return null until we can show a disabled-action Slice, blaming Airplane mode.
+        // Return empty slice until we can show a disabled-action Slice, blaming Airplane mode.
         if (isAirplaneModeEnabled()) {
-            return null;
+            return listBuilder.build();
         }
 
-        // Return null until we can show a disabled-action Slice.
+        // Return empty slice until we can show a disabled-action Slice.
         if (!isMobileDataAvailable()) {
-            return null;
+            return listBuilder.build();
         }
 
         final CharSequence summary = getSummary();
@@ -109,11 +115,15 @@
             rowBuilder.setSubtitle(summary);
         }
 
-        final ListBuilder listBuilder = new ListBuilder(mContext, getUri(),
-                ListBuilder.INFINITY)
+        return listBuilder
                 .setAccentColor(color)
-                .addRow(rowBuilder);
-        return listBuilder.build();
+                .addRow(rowBuilder)
+                .build();
+    }
+
+    @VisibleForTesting
+    ListBuilder createListBuilder() {
+        return new ListBuilder(mContext, getUri(), ListBuilder.INFINITY);
     }
 
     @Override
@@ -211,6 +221,19 @@
         return mTelephonyManager.isDataEnabled();
     }
 
+    @VisibleForTesting
+    boolean isConfigMobileNetworksAllowed() {
+        if (mContext == null) return true;
+        UserManager userManager = mContext.getSystemService(UserManager.class);
+        if (userManager == null) return true;
+        boolean isAllowed = userManager.isAdminUser() && !userManager.hasUserRestriction(
+                UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
+        if (!isAllowed) {
+            Log.w(TAG, "The user is not allowed to configure Mobile Networks.");
+        }
+        return isAllowed;
+    }
+
     /**
      * Listener for mobile data state changes.
      *
diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt b/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt
index 85ba382..4e97d31 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt
+++ b/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt
@@ -22,6 +22,9 @@
 import com.android.settings.R
 import com.android.settings.network.SubscriptionUtil
 import com.android.settings.network.telephony.MmsMessagePreferenceController.Companion.MmsMessageSearchItem
+import com.android.settings.network.telephony.NrAdvancedCallingPreferenceController.Companion.NrAdvancedCallingSearchItem
+import com.android.settings.network.telephony.RoamingPreferenceController.Companion.RoamingSearchItem
+import com.android.settings.network.telephony.WifiCallingPreferenceController.Companion.WifiCallingSearchItem
 import com.android.settings.spa.SpaSearchLanding.BundleValue
 import com.android.settings.spa.SpaSearchLanding.SpaSearchLandingFragment
 import com.android.settings.spa.SpaSearchLanding.SpaSearchLandingKey
@@ -41,6 +44,9 @@
 
         val title: String
 
+        val keywords: String?
+            get() = null
+
         fun isAvailable(subId: Int): Boolean
     }
 
@@ -89,6 +95,7 @@
             context = context,
             spaSearchLandingKey = key,
             itemTitle = searchItem.title,
+            keywords = searchItem.keywords,
             indexableClass = MobileNetworkSettings::class.java,
             pageTitle = "$simsTitle > ${subInfo.displayName}",
         )
@@ -107,6 +114,9 @@
         fun createSearchItems(context: Context): List<MobileNetworkSettingsSearchItem> =
             listOf(
                 MmsMessageSearchItem(context),
+                NrAdvancedCallingSearchItem(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 cf47c1f..5c94e84 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.MobileNetworkSettingsSearchItem
 import com.android.settings.spa.preference.ComposePreferenceController
 import com.android.settingslib.spa.widget.preference.SwitchPreference
 import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
@@ -41,6 +42,7 @@
 ) : ComposePreferenceController(context, key) {
     private var subId: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
     private var repository: VoNrRepository? = null
+    private val searchItem = NrAdvancedCallingSearchItem(context)
 
     /** Initial this PreferenceController. */
     @JvmOverloads
@@ -50,7 +52,7 @@
     }
 
     override fun getAvailabilityStatus() =
-        if (repository?.isVoNrAvailable() == true) AVAILABLE else CONDITIONALLY_UNAVAILABLE
+        if (searchItem.isAvailable(subId)) AVAILABLE else CONDITIONALLY_UNAVAILABLE
 
     @Composable
     override fun Content() {
@@ -73,4 +75,16 @@
             }
         })
     }
+
+    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()
+        }
+    }
 }
diff --git a/src/com/android/settings/network/telephony/RoamingPreferenceController.kt b/src/com/android/settings/network/telephony/RoamingPreferenceController.kt
index 2529d41..7633677 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.MobileNetworkSettingsSearchItem
 import com.android.settings.spa.preference.ComposePreferenceController
 import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
 import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
@@ -47,6 +48,7 @@
 
     private var telephonyManager = context.getSystemService(TelephonyManager::class.java)!!
     private val carrierConfigRepository = CarrierConfigRepository(context)
+    private val roamingSearchItem = RoamingSearchItem(context)
 
     fun init(fragmentManager: FragmentManager, subId: Int) {
         this.fragmentManager = fragmentManager
@@ -54,14 +56,8 @@
         telephonyManager = telephonyManager.createForSubscriptionId(subId)
     }
 
-    override fun getAvailabilityStatus(): Int {
-        if (!SubscriptionManager.isValidSubscriptionId(subId)) return CONDITIONALLY_UNAVAILABLE
-        val isForceHomeNetwork =
-            carrierConfigRepository.getBoolean(
-                subId, CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL)
-
-        return if (isForceHomeNetwork) CONDITIONALLY_UNAVAILABLE else AVAILABLE
-    }
+    override fun getAvailabilityStatus() =
+        if (roamingSearchItem.isAvailable(subId)) AVAILABLE else CONDITIONALLY_UNAVAILABLE
 
     @Composable
     override fun Content() {
@@ -101,5 +97,17 @@
 
     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)
+
+            private val carrierConfigRepository = CarrierConfigRepository(context)
+
+            override fun isAvailable(subId: Int): Boolean =
+                SubscriptionManager.isValidSubscriptionId(subId) &&
+                    !carrierConfigRepository.getBoolean(
+                        subId, CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL)
+        }
     }
 }
diff --git a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt
index 3bb2679..7e8e58c 100644
--- a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt
+++ b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt
@@ -22,14 +22,17 @@
 import android.telephony.SubscriptionManager
 import android.telephony.TelephonyManager
 import android.telephony.ims.ImsMmTelManager
-import android.util.Log
 import androidx.lifecycle.LifecycleOwner
 import androidx.preference.Preference
 import androidx.preference.PreferenceScreen
 import com.android.settings.R
+import com.android.settings.core.BasePreferenceController
+import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem
 import com.android.settings.network.telephony.wificalling.WifiCallingRepository
 import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.withContext
 
 /**
@@ -44,20 +47,21 @@
     private val wifiCallingRepositoryFactory: (subId: Int) -> WifiCallingRepository = { subId ->
         WifiCallingRepository(context, subId)
     },
-) : TelephonyBasePreferenceController(context, key) {
+) : BasePreferenceController(context, key) {
 
+    private var subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID
     private lateinit var preference: Preference
     private lateinit var callingPreferenceCategoryController: CallingPreferenceCategoryController
 
     private val resourcesForSub by lazy {
-        SubscriptionManager.getResourcesForSubId(mContext, mSubId)
+        SubscriptionManager.getResourcesForSubId(mContext, subId)
     }
 
     fun init(
         subId: Int,
         callingPreferenceCategoryController: CallingPreferenceCategoryController,
     ): WifiCallingPreferenceController {
-        mSubId = subId
+        this.subId = subId
         this.callingPreferenceCategoryController = callingPreferenceCategoryController
         return this
     }
@@ -65,39 +69,32 @@
     /**
      * Note: Visibility also controlled by [onViewCreated].
      */
-    override fun getAvailabilityStatus(subId: Int) =
+    override fun getAvailabilityStatus() =
         if (SubscriptionManager.isValidSubscriptionId(subId)) AVAILABLE
         else CONDITIONALLY_UNAVAILABLE
 
     override fun displayPreference(screen: PreferenceScreen) {
         // Not call super here, to avoid preference.isVisible changed unexpectedly
         preference = screen.findPreference(preferenceKey)!!
-        preference.intent?.putExtra(Settings.EXTRA_SUB_ID, mSubId)
+        preference.intent?.putExtra(Settings.EXTRA_SUB_ID, subId)
     }
 
     override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
-        if(mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID){
-            Log.e(
-                this.javaClass.simpleName,
-                "mSubId is INVALID_SUBSCRIPTION_ID"
-            )
-            return
-        }
-        wifiCallingRepositoryFactory(mSubId).wifiCallingReadyFlow()
+        wifiCallingRepositoryFactory(subId).wifiCallingReadyFlow()
             .collectLatestWithLifecycle(viewLifecycleOwner) { isReady ->
                 preference.isVisible = isReady
                 callingPreferenceCategoryController.updateChildVisible(preferenceKey, isReady)
                 if (isReady) update()
             }
 
-        callStateRepository.callStateFlow(mSubId).collectLatestWithLifecycle(viewLifecycleOwner) {
+        callStateRepository.callStateFlow(subId).collectLatestWithLifecycle(viewLifecycleOwner) {
             preference.isEnabled = (it == TelephonyManager.CALL_STATE_IDLE)
         }
     }
 
     private suspend fun update() {
         val simCallManager = mContext.getSystemService(TelecomManager::class.java)
-            ?.getSimCallManagerForSubscription(mSubId)
+            ?.getSimCallManagerForSubscription(subId)
         if (simCallManager != null) {
             val intent = withContext(Dispatchers.Default) {
                 MobileNetworkUtils.buildPhoneAccountConfigureIntent(mContext, simCallManager)
@@ -116,7 +113,7 @@
     }
 
     private fun getSummaryForWfcMode(): String {
-        val resId = when (wifiCallingRepositoryFactory(mSubId).getWiFiCallingMode()) {
+        val resId = when (wifiCallingRepositoryFactory(subId).getWiFiCallingMode()) {
             ImsMmTelManager.WIFI_MODE_WIFI_ONLY ->
                 com.android.internal.R.string.wfc_mode_wifi_only_summary
 
@@ -130,4 +127,17 @@
         }
         return resourcesForSub.getString(resId)
     }
+
+    companion object {
+        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 {
+                WifiCallingRepository(context, subId).wifiCallingReadyFlow().first()
+            }
+        }
+    }
 }
diff --git a/src/com/android/settings/notification/modes/SetupInterstitialActivity.java b/src/com/android/settings/notification/modes/SetupInterstitialActivity.java
new file mode 100644
index 0000000..028e15b
--- /dev/null
+++ b/src/com/android/settings/notification/modes/SetupInterstitialActivity.java
@@ -0,0 +1,267 @@
+/*
+ * 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.notification.modes;
+
+import static android.graphics.drawable.GradientDrawable.LINEAR_GRADIENT;
+import static android.graphics.drawable.GradientDrawable.Orientation.BL_TR;
+import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID;
+
+import android.app.ActionBar;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.Outline;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.view.ViewTreeObserver;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.widget.Toolbar;
+
+import androidx.activity.EdgeToEdge;
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import androidx.fragment.app.FragmentActivity;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
+
+/**
+ * Interstitial page for modes that are disabled, but not disabled by the user. This page
+ * provides a button to enable the mode, and then goes to the mode setup page.
+ */
+public class SetupInterstitialActivity extends FragmentActivity {
+    private static final String TAG = "ModeSetupInterstitial";
+    private ZenModesBackend mBackend;
+
+    private final ViewOutlineProvider mOutlineProvider = new ViewOutlineProvider() {
+        @Override
+        public void getOutline(View view, Outline outline) {
+            // Provides a rounded rectangle outline whose width & height matches the View.
+            float cornerRadius = getResources().getDimensionPixelSize(
+                    R.dimen.zen_mode_interstitial_corner_radius);
+            outline.setRoundRect(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight(),
+                    cornerRadius);
+        }
+    };
+
+    /**
+     * Returns an intent leading to this page for the given mode and context.
+     */
+    public static @NonNull Intent getIntent(@NonNull Context context, @NonNull ZenMode mode) {
+        return new Intent(Intent.ACTION_MAIN)
+                .setClass(context, SetupInterstitialActivity.class)
+                .setPackage(context.getPackageName())
+                .setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT)
+                .putExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID, mode.getId());
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        EdgeToEdge.enable(this);
+        Utils.setupEdgeToEdge(this);
+        super.onCreate(savedInstanceState);
+        mBackend = ZenModesBackend.getInstance(this);
+        setContentView(R.layout.mode_interstitial_layout);
+
+        // Set up toolbar to only have a back button & no title
+        Toolbar toolbar = findViewById(R.id.action_bar);
+        setActionBar(toolbar);
+        ActionBar actionBar = getActionBar();
+        if (actionBar != null) {
+            actionBar.setDisplayHomeAsUpEnabled(true);
+            actionBar.setHomeButtonEnabled(true);
+            actionBar.setDisplayShowTitleEnabled(false);
+        }
+    }
+
+    @Override
+    public boolean onNavigateUp() {
+        // have the home button on the action bar go back
+        getOnBackPressedDispatcher().onBackPressed();
+        return true;
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        // See if we have mode data
+        final Intent intent = getIntent();
+        if (intent == null) {
+            Log.w(TAG, "no intent found for modes interstitial");
+            finish();
+        }
+
+        String modeId = intent.getStringExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID);
+        if (modeId == null) {
+            Log.w(TAG, "no mode id included in intent: " + intent);
+            finish();
+        }
+
+        ZenMode mode = mBackend.getMode(modeId);
+        if (mode == null) {
+            Log.w(TAG, "mode not found for mode id: " + modeId);
+            finish();
+        }
+        setTitle(mode.getName());
+
+        TextView title = findViewById(R.id.mode_name_title);
+        if (title != null) {
+            title.setText(mode.getName());
+        }
+
+        ImageView img = findViewById(R.id.image);
+        if (img != null) {
+            setImage(img, mode);
+        }
+
+        Button button = findViewById(R.id.enable_mode_button);
+        if (button != null) {
+            setupButton(button, mode);
+        }
+    }
+
+    private void setImage(@NonNull ImageView img, @NonNull ZenMode mode) {
+        img.setImageDrawable(getModeDrawable(mode));
+        img.setClipToOutline(true);
+        img.setOutlineProvider(mOutlineProvider);
+
+        FrameLayout frame = findViewById(R.id.image_frame);
+        if (frame == null) {
+            return;
+        }
+        if (img.getMeasuredWidth() == 0) {
+            // set up to resize after the global layout occurs
+            img.getViewTreeObserver().addOnGlobalLayoutListener(
+                    new ViewTreeObserver.OnGlobalLayoutListener() {
+                        @Override
+                        public void onGlobalLayout() {
+                            img.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+                            sizeImageToFrame(img, frame);
+                        }
+                    });
+        } else {
+            // measured already, resize it now
+            sizeImageToFrame(img, frame);
+        }
+    }
+
+    private Drawable getModeDrawable(@NonNull ZenMode mode) {
+        // TODO: b/332730534 - set actual images depending on mode type (asynchronously?)
+        GradientDrawable placeholder = new GradientDrawable();
+        placeholder.setSize(40, 60);  // 4x6 rectangle, slightly taller than wide
+        placeholder.setGradientType(LINEAR_GRADIENT);
+        placeholder.setOrientation(BL_TR);
+        placeholder.setColors(new int[]{Color.BLACK, Color.WHITE});
+        return placeholder;
+    }
+
+    @VisibleForTesting
+    protected void sizeImageToFrame(ImageView img, FrameLayout frame) {
+        // width of the space we have available = overall size of frame - relevant padding
+        int frameHeight =
+                frame.getMeasuredHeight() - frame.getPaddingTop() - frame.getPaddingBottom();
+        int frameWidth =
+                frame.getMeasuredWidth() - frame.getPaddingLeft() - frame.getPaddingRight();
+
+        int imgHeight = img.getDrawable().getIntrinsicHeight();
+        int imgWidth = img.getDrawable().getIntrinsicWidth();
+
+        // if any of these are 0, give up because we won't be able to do the relevant math (and
+        // we probably don't have the relevant data set up)
+        if (frameHeight == 0 || frameWidth == 0 || imgHeight == 0 || imgWidth == 0) {
+            Log.w(TAG, "image or frame has invalid size parameters");
+            return;
+        }
+        float frameHWRatio = ((float) frameHeight) / frameWidth;
+        float imgHWRatio = ((float) imgHeight) / imgWidth;
+
+        // fit horizontal dimension if the frame has a taller ratio (height/width) than the image;
+        // otherwise, fit the vertical direction
+        boolean fitHorizontal = frameHWRatio > imgHWRatio;
+
+        ViewGroup.LayoutParams layoutParams = img.getLayoutParams();
+        if (layoutParams == null) {
+            Log.w(TAG, "image has null LayoutParams");
+            return;
+        }
+        if (fitHorizontal) {
+            layoutParams.width = frameWidth;
+            float scaledHeight = imgHWRatio * frameWidth;
+            layoutParams.height = (int) scaledHeight;
+        } else {
+            layoutParams.height = frameHeight;
+            float scaledWidth = /* w/h ratio */ (1 / imgHWRatio) * frameHeight;
+            layoutParams.width = (int) scaledWidth;
+        }
+        img.setLayoutParams(layoutParams);
+    }
+
+    private void setupButton(Button button, @NonNull ZenMode mode) {
+        button.setText(getString(R.string.zen_mode_setup_button_label, mode.getName()));
+        button.setOnClickListener(enableButtonListener(mode.getId()));
+    }
+
+    @VisibleForTesting
+    View.OnClickListener enableButtonListener(String modeId) {
+        return unused -> {
+            // When clicked, we first reload mode info in case it has changed in the interim,
+            // then enable the mode and then send the user to the mode's configuration page.
+            boolean updated = enableMode(modeId);
+
+            // 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();
+            }
+        };
+    }
+
+    // Enables the given mode after first refreshing its data from the backend. Returns true if
+    // the update went through, and false if for some reason the mode wasn't found.
+    private boolean enableMode(@NonNull String modeId) {
+        if (mBackend == null) {
+            return false;
+        }
+
+        ZenMode modeToUpdate = mBackend.getMode(modeId);
+        if (modeToUpdate == null) {
+            // tell the user the mode isn't found, for some reason
+            Toast.makeText(this, R.string.zen_mode_rule_not_found_text, Toast.LENGTH_SHORT)
+                    .show();
+            return false;
+        }
+
+        modeToUpdate.getRule().setEnabled(true);
+        mBackend.updateMode(modeToUpdate);
+        return true;
+    }
+}
diff --git a/src/com/android/settings/notification/modes/ZenHelperBackend.java b/src/com/android/settings/notification/modes/ZenHelperBackend.java
index a2c3578..bf91678 100644
--- a/src/com/android/settings/notification/modes/ZenHelperBackend.java
+++ b/src/com/android/settings/notification/modes/ZenHelperBackend.java
@@ -81,12 +81,20 @@
         }
     }
 
-    @SuppressWarnings("unchecked")
+    ImmutableList<ConversationChannelWrapper> getAllConversations() {
+        return getConversations(false);
+    }
+
     ImmutableList<ConversationChannelWrapper> getImportantConversations() {
+        return getConversations(true);
+    }
+
+    @SuppressWarnings("unchecked")
+    private ImmutableList<ConversationChannelWrapper> getConversations(boolean onlyImportant) {
         try {
             ImmutableList.Builder<ConversationChannelWrapper> list = new ImmutableList.Builder<>();
             ParceledListSlice<ConversationChannelWrapper> parceledList = mInm.getConversations(
-                    /* onlyImportant= */ true);
+                    onlyImportant);
             if (parceledList != null) {
                 for (ConversationChannelWrapper conversation : parceledList.getList()) {
                     if (!conversation.getNotificationChannel().isDemoted()) {
diff --git a/src/com/android/settings/notification/modes/ZenModeFragment.java b/src/com/android/settings/notification/modes/ZenModeFragment.java
index 1b7e344..0a80977 100644
--- a/src/com/android/settings/notification/modes/ZenModeFragment.java
+++ b/src/com/android/settings/notification/modes/ZenModeFragment.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.notification.modes;
 
+import static com.android.settingslib.notification.modes.ZenMode.Status.DISABLED_BY_OTHER;
+
 import android.app.AlertDialog;
 import android.app.settings.SettingsEnums;
 import android.content.Context;
@@ -25,6 +27,7 @@
 
 import androidx.activity.ComponentActivity;
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.core.view.MenuProvider;
 
 import com.android.settings.R;
@@ -41,6 +44,7 @@
     private static final int DELETE_MODE = 2;
 
     private ModeMenuProvider mModeMenuProvider;
+    private boolean mSettingsObserverRegistered = false; // for ManualDurationPreferenceController
 
     @Override
     protected int getPreferenceScreenResId() {
@@ -82,9 +86,14 @@
     @Override
     public void onStart() {
         super.onStart();
+        ZenMode mode = getMode();
+
+        // Consider redirecting to the interstitial if the mode is disabled (but not by the user).
+        if (maybeRedirectToInterstitial(mode)) {
+            return;
+        }
 
         // Set title for the entire screen
-        ZenMode mode = getMode();
         ComponentActivity activity = getActivity();
         if (mode != null && activity != null) {
             activity.setTitle(mode.getName());
@@ -94,14 +103,27 @@
 
         // allow duration preference controller to listen for settings changes
         use(ManualDurationPreferenceController.class).registerSettingsObserver();
+        mSettingsObserverRegistered = true;
+    }
+
+    private boolean maybeRedirectToInterstitial(@Nullable ZenMode mode) {
+        if (mode == null || mode.getStatus() != DISABLED_BY_OTHER) {
+            return false;
+        }
+        // don't come back here from the interstitial
+        finish();
+        mContext.startActivity(SetupInterstitialActivity.getIntent(mContext, mode));
+        return true;
     }
 
     @Override
     public void onStop() {
-        if (getActivity() != null) {
+        if (getActivity() != null && mModeMenuProvider != null) {
             getActivity().removeMenuProvider(mModeMenuProvider);
         }
-        use(ManualDurationPreferenceController.class).unregisterSettingsObserver();
+        if (mSettingsObserverRegistered) {
+            use(ManualDurationPreferenceController.class).unregisterSettingsObserver();
+        }
         super.onStop();
     }
 
diff --git a/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceController.java
index 0d3a721..4610c35ca 100644
--- a/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceController.java
@@ -189,8 +189,7 @@
                         : CONVERSATION_SENDERS_NONE;
         ImmutableList<ConversationChannelWrapper> conversationsAllowed = ImmutableList.of();
         if (conversationSendersAllowed == CONVERSATION_SENDERS_ANYONE) {
-            // TODO: b/354658240 - Need to handle CONVERSATION_SENDERS_ANYONE?
-            return;
+            conversationsAllowed = mHelperBackend.getAllConversations();
         } else if (conversationSendersAllowed == CONVERSATION_SENDERS_IMPORTANT) {
             conversationsAllowed = mHelperBackend.getImportantConversations();
         }
@@ -223,7 +222,7 @@
                     peopleItem.conversation.getShortcutInfo(),
                     peopleItem.conversation.getPkg(),
                     peopleItem.conversation.getUid(),
-                    /* important= */ true);
+                    peopleItem.conversation.getNotificationChannel().isImportantConversation());
         } else {
             throw new IllegalArgumentException("Neither all nor contact nor conversation!");
         }
diff --git a/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceController.java b/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceController.java
index 32c6a98..ab5e2d9 100644
--- a/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceController.java
@@ -26,6 +26,8 @@
 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED;
 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_UNSET;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.content.Intent;
@@ -36,7 +38,6 @@
 import android.view.View;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceCategory;
 import androidx.preference.PreferenceScreen;
@@ -48,11 +49,13 @@
 import com.android.settingslib.notification.modes.ZenModesBackend;
 import com.android.settingslib.widget.SelectorWithWidgetPreference;
 
-import java.util.ArrayList;
+import com.google.common.collect.ImmutableSet;
+
 import java.util.HashMap;
-import java.util.List;
+import java.util.LinkedHashMap;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Common preference controller functionality for zen mode priority senders preferences for both
@@ -69,9 +72,11 @@
     static final String KEY_ANY = "senders_anyone";
     static final String KEY_CONTACTS = "senders_contacts";
     static final String KEY_STARRED = "senders_starred_contacts";
-    static final String KEY_IMPORTANT = "conversations_important";
+    static final String KEY_IMPORTANT_CONVERSATIONS = "conversations_important";
+    static final String KEY_ANY_CONVERSATIONS = "conversations_any";
     static final String KEY_NONE = "senders_none";
 
+    private int mNumAllConversations = 0;
     private int mNumImportantConversations = 0;
 
     private static final Intent ALL_CONTACTS_INTENT =
@@ -86,7 +91,8 @@
     private final ZenHelperBackend mHelperBackend;
     private final PackageManager mPackageManager;
     private PreferenceCategory mPreferenceCategory;
-    private List<SelectorWithWidgetPreference> mSelectorPreferences = new ArrayList<>();
+    private final LinkedHashMap<String, SelectorWithWidgetPreference> mOptions =
+            new LinkedHashMap<>();
 
     private final ZenModeSummaryHelper mZenModeSummaryHelper;
 
@@ -110,53 +116,92 @@
 
     @Override
     public void displayPreference(PreferenceScreen screen) {
-        mPreferenceCategory = screen.findPreference(getPreferenceKey());
+        mPreferenceCategory = checkNotNull(screen.findPreference(getPreferenceKey()));
         if (mPreferenceCategory.getPreferenceCount() == 0) {
             makeSelectorPreference(KEY_STARRED,
-                    com.android.settings.R.string.zen_mode_from_starred, mIsMessages);
+                    com.android.settings.R.string.zen_mode_from_starred, mIsMessages, true);
             makeSelectorPreference(KEY_CONTACTS,
-                    com.android.settings.R.string.zen_mode_from_contacts, mIsMessages);
+                    com.android.settings.R.string.zen_mode_from_contacts, mIsMessages, true);
             if (mIsMessages) {
-                makeSelectorPreference(KEY_IMPORTANT,
-                        com.android.settings.R.string.zen_mode_from_important_conversations, true);
+                // "Any conversations" will only be available as option if it is the current value.
+                // Because it's confusing and we don't want users setting it up that way, but apps
+                // could create such ZenPolicies and we must show that.
+                makeSelectorPreference(KEY_ANY_CONVERSATIONS,
+                        com.android.settings.R.string.zen_mode_from_all_conversations, true,
+                        /* isVisibleByDefault= */ false);
+                makeSelectorPreference(KEY_IMPORTANT_CONVERSATIONS,
+                        com.android.settings.R.string.zen_mode_from_important_conversations, true,
+                        true);
             }
             makeSelectorPreference(KEY_ANY,
-                    com.android.settings.R.string.zen_mode_from_anyone, mIsMessages);
+                    com.android.settings.R.string.zen_mode_from_anyone, mIsMessages, true);
             makeSelectorPreference(KEY_NONE,
-                    com.android.settings.R.string.zen_mode_none_messages, mIsMessages);
+                    com.android.settings.R.string.zen_mode_none_messages, mIsMessages, true);
         }
         super.displayPreference(screen);
     }
 
     @Override
     public void updateState(Preference preference, @NonNull ZenMode zenMode) {
+        final int contacts = getPrioritySenders(zenMode.getPolicy());
+        final int conversations = getPriorityConversationSenders(zenMode.getPolicy());
+
         if (mIsMessages) {
             updateChannelCounts();
-        }
-        final int currContactsSetting = getPrioritySenders(zenMode.getPolicy());
-        final int currConversationsSetting = getPriorityConversationSenders(zenMode.getPolicy());
-        for (SelectorWithWidgetPreference pref : mSelectorPreferences) {
-            // for each preference, check whether the current state matches what this state
-            // would look like if the button were checked.
-            final int[] checkedState = keyToSettingEndState(pref.getKey(), true);
-            final int checkedContactsSetting = checkedState[0];
-            final int checkedConversationsSetting = checkedState[1];
 
-            boolean match = checkedContactsSetting == currContactsSetting;
-            if (mIsMessages && checkedConversationsSetting != CONVERSATION_SENDERS_UNSET) {
-                // "CONVERSATION_SENDERS_UNSET" in checkedContactsSetting means this preference
-                // doesn't govern the priority senders setting, so the full match happens when
-                // either the priority senders setting matches or if it's CONVERSATION_SENDERS_UNSET
-                // so only the conversation setting needs to match.
-                match = (match || checkedContactsSetting == PEOPLE_TYPE_UNSET)
-                        && (checkedConversationsSetting == currConversationsSetting);
+            if (contacts == PEOPLE_TYPE_ANYONE) {
+                setSelectedOption(KEY_ANY);
+            } else if (contacts == PEOPLE_TYPE_NONE && conversations == CONVERSATION_SENDERS_NONE) {
+                setSelectedOption(KEY_NONE);
+            } else {
+                ImmutableSet.Builder<String> selectedOptions = new ImmutableSet.Builder<>();
+                if (contacts == PEOPLE_TYPE_STARRED) {
+                    selectedOptions.add(KEY_STARRED);
+                } else if (contacts == PEOPLE_TYPE_CONTACTS) {
+                    selectedOptions.add(KEY_CONTACTS);
+                }
+                if (conversations == CONVERSATION_SENDERS_IMPORTANT) {
+                    selectedOptions.add(KEY_IMPORTANT_CONVERSATIONS);
+                } else if (conversations == CONVERSATION_SENDERS_ANYONE) {
+                    selectedOptions.add(KEY_ANY_CONVERSATIONS);
+                }
+                setSelectedOptions(selectedOptions.build());
             }
-
-            pref.setChecked(match);
+        } else {
+            // Calls is easy!
+            switch (contacts) {
+                case PEOPLE_TYPE_ANYONE -> setSelectedOption(KEY_ANY);
+                case PEOPLE_TYPE_CONTACTS -> setSelectedOption(KEY_CONTACTS);
+                case PEOPLE_TYPE_STARRED -> setSelectedOption(KEY_STARRED);
+                case PEOPLE_TYPE_NONE -> setSelectedOption(KEY_NONE);
+                default -> throw new IllegalArgumentException("Unexpected PeopleType: " + contacts);
+            }
         }
+
         updateSummaries();
     }
 
+    private void setSelectedOption(String key) {
+        setSelectedOptions(ImmutableSet.of(key));
+    }
+
+    private void setSelectedOptions(Set<String> keys) {
+        if (keys.isEmpty()) {
+            throw new IllegalArgumentException("At least one option should be selected!");
+        }
+
+        for (SelectorWithWidgetPreference optionPreference : mOptions.values()) {
+            optionPreference.setChecked(keys.contains(optionPreference.getKey()));
+            if (optionPreference.isChecked()) {
+                // Ensure selected options are visible. This is to support "Any conversations"
+                // which is only shown if the policy has Conversations=Anyone (and doesn't have
+                // messages=Anyone), and then remains visible until the user exits the page
+                // (so that toggling back and forth is possible without the option disappearing).
+                optionPreference.setVisible(true);
+            }
+        }
+    }
+
     public void onResume() {
         if (mIsMessages) {
             updateChannelCounts();
@@ -165,6 +210,7 @@
     }
 
     private void updateChannelCounts() {
+        mNumAllConversations = mHelperBackend.getAllConversations().size();
         mNumImportantConversations = mHelperBackend.getImportantConversations().size();
     }
 
@@ -183,13 +229,14 @@
         return CONVERSATION_SENDERS_UNSET;
     }
 
-    private SelectorWithWidgetPreference makeSelectorPreference(String key, int titleId,
-            boolean isCheckbox) {
+    private void makeSelectorPreference(String key, int titleId,
+            boolean isCheckbox, boolean isVisibleByDefault) {
         final SelectorWithWidgetPreference pref =
                 new SelectorWithWidgetPreference(mPreferenceCategory.getContext(), isCheckbox);
         pref.setKey(key);
         pref.setTitle(titleId);
         pref.setOnClickListener(mSelectorClickListener);
+        pref.setVisible(isVisibleByDefault);
 
         View.OnClickListener widgetClickListener = getWidgetClickListener(key);
         if (widgetClickListener != null) {
@@ -197,12 +244,12 @@
         }
 
         mPreferenceCategory.addPreference(pref);
-        mSelectorPreferences.add(pref);
-        return pref;
+        mOptions.put(key, pref);
     }
 
     private View.OnClickListener getWidgetClickListener(String key) {
-        if (!KEY_CONTACTS.equals(key) && !KEY_STARRED.equals(key) && !KEY_IMPORTANT.equals(key)) {
+        if (!KEY_CONTACTS.equals(key) && !KEY_STARRED.equals(key)
+                && !KEY_ANY_CONVERSATIONS.equals(key) && !KEY_IMPORTANT_CONVERSATIONS.equals(key)) {
             return null;
         }
 
@@ -221,7 +268,8 @@
             } else if (KEY_CONTACTS.equals(key)
                     && ALL_CONTACTS_INTENT.resolveActivity(mPackageManager) != null) {
                 mContext.startActivity(ALL_CONTACTS_INTENT);
-            } else if (KEY_IMPORTANT.equals(key)) {
+            } else if (KEY_ANY_CONVERSATIONS.equals(key)
+                    || KEY_IMPORTANT_CONVERSATIONS.equals(key)) {
                 // TODO: b/332937635 - set correct metrics category
                 new SubSettingLauncher(mContext)
                         .setDestination(ConversationListSettings.class.getName())
@@ -244,7 +292,7 @@
     }
 
     void updateSummaries() {
-        for (SelectorWithWidgetPreference pref : mSelectorPreferences) {
+        for (SelectorWithWidgetPreference pref : mOptions.values()) {
             pref.setSummary(getSummary(pref.getKey()));
         }
     }
@@ -255,7 +303,7 @@
     // Returns an integer array with 2 entries. The first entry is the setting for priority senders
     // and the second entry is for priority conversation senders; if isMessages is false, then
     // no changes will ever be prescribed for conversation senders.
-    int[] keyToSettingEndState(String key, boolean checked) {
+    private int[] keyToSettingEndState(String key, boolean checked) {
         int[] endState = new int[]{ PEOPLE_TYPE_UNSET, CONVERSATION_SENDERS_UNSET };
         if (!checked) {
             // Unchecking any priority-senders-based state should reset the state to NONE.
@@ -268,11 +316,12 @@
                     endState[0] = PEOPLE_TYPE_NONE;
             }
 
-            // For messages, unchecking "priority conversations" and "any" should reset conversation
-            // state to "NONE" as well.
+            // For messages, unchecking "priority/any conversations" and "any" should reset
+            // conversation state to "NONE" as well.
             if (mIsMessages) {
                 switch (key) {
-                    case KEY_IMPORTANT:
+                    case KEY_IMPORTANT_CONVERSATIONS:
+                    case KEY_ANY_CONVERSATIONS:
                     case KEY_ANY:
                     case KEY_NONE:
                         endState[1] = CONVERSATION_SENDERS_NONE;
@@ -297,9 +346,10 @@
             // In the messages case *only*, also handle changing of conversation settings.
             if (mIsMessages) {
                 switch (key) {
-                    case KEY_IMPORTANT:
+                    case KEY_IMPORTANT_CONVERSATIONS:
                         endState[1] = CONVERSATION_SENDERS_IMPORTANT;
                         break;
+                    case KEY_ANY_CONVERSATIONS:
                     case KEY_ANY:
                         endState[1] = CONVERSATION_SENDERS_ANYONE;
                         break;
@@ -335,7 +385,7 @@
     //     the contacts setting is additionally reset to "none".
     //   - if "anyone" is previously selected, and the user clicks one of the contacts values,
     //     then the conversations setting is additionally reset to "none".
-    int[] settingsToSaveOnClick(String key, boolean checked,
+    private int[] settingsToSaveOnClick(String key, boolean checked,
             int currSendersSetting, int currConvosSetting) {
         int[] savedSettings = new int[]{ PEOPLE_TYPE_UNSET, CONVERSATION_SENDERS_UNSET };
 
@@ -360,15 +410,18 @@
             // Special-case handling for the "priority conversations" checkbox:
             // If a specific selection exists for priority senders (starred, contacts), we leave
             // it untouched. Otherwise (when the senders is set to "any"), set it to NONE.
-            if (key.equals(KEY_IMPORTANT)
+            if ((key.equals(KEY_IMPORTANT_CONVERSATIONS) || key.equals(KEY_ANY_CONVERSATIONS))
                     && currSendersSetting == PEOPLE_TYPE_ANYONE) {
                 savedSettings[0] = PEOPLE_TYPE_NONE;
             }
 
-            // Flip-side special case for clicking either "contacts" option: if a specific selection
-            // exists for priority conversations, leave it untouched; otherwise, set to none.
+            // The flip-side case for the "contacts" option is slightly different -- we only
+            // reset conversations if leaving PEOPLE_ANY by selecting a contact option, but not
+            // if switching contact options. That's because starting from Anyone, checking Contacts,
+            // and then "important conversations" also shown checked because it was there (albeit
+            // subsumed into PEOPLE_ANY) would be weird.
             if ((key.equals(KEY_STARRED) || key.equals(KEY_CONTACTS))
-                    && currConvosSetting == CONVERSATION_SENDERS_ANYONE) {
+                    && currSendersSetting == PEOPLE_TYPE_ANYONE) {
                 savedSettings[1] = CONVERSATION_SENDERS_NONE;
             }
         }
@@ -382,8 +435,10 @@
                 return mZenModeSummaryHelper.getStarredContactsSummary();
             case KEY_CONTACTS:
                 return mZenModeSummaryHelper.getContactsNumberSummary();
-            case KEY_IMPORTANT:
-                return getConversationSummary();
+            case KEY_ANY_CONVERSATIONS:
+                return getConversationSummary(mNumAllConversations);
+            case KEY_IMPORTANT_CONVERSATIONS:
+                return getConversationSummary(mNumImportantConversations);
             case KEY_ANY:
                 return mContext.getResources().getString(mIsMessages
                         ? R.string.zen_mode_all_messages_summary
@@ -394,9 +449,7 @@
         }
     }
 
-    private String getConversationSummary() {
-        final int numConversations = mNumImportantConversations;
-
+    private String getConversationSummary(int numConversations) {
         if (numConversations == CONVERSATION_SENDERS_UNSET) {
             return null;
         } else {
@@ -409,8 +462,7 @@
         }
     }
 
-    @VisibleForTesting
-    SelectorWithWidgetPreference.OnClickListener mSelectorClickListener =
+    private final SelectorWithWidgetPreference.OnClickListener mSelectorClickListener =
             new SelectorWithWidgetPreference.OnClickListener() {
                 @Override
                 public void onRadioButtonClicked(SelectorWithWidgetPreference preference) {
diff --git a/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java b/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java
index 1acef20..7a16d91 100644
--- a/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java
+++ b/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java
@@ -140,6 +140,14 @@
     }
 
     String getMessagesSettingSummary(ZenPolicy policy) {
+        if (policy.getPriorityCategoryMessages() == STATE_ALLOW
+                && policy.getPriorityMessageSenders() == PEOPLE_TYPE_ANYONE) {
+            // Messages=anyone means anyone. Even if conversation senders is specially configured,
+            // saying "Anyone and priority conversations" 1) makes no sense and 2) is incorrect
+            // because conversations WILL get through by virtue of also being messages.
+            return mContext.getString(R.string.zen_mode_from_anyone);
+        }
+
         List<String> enabledCategories = getEnabledCategories(policy,
                 category -> PRIORITY_CATEGORY_MESSAGES == category
                         || PRIORITY_CATEGORY_CONVERSATIONS == category, true);
@@ -278,10 +286,11 @@
                     continue;
                 }
 
-                // For conversations, only the "priority conversations" setting is relevant; any
-                // other setting is subsumed by the messages-specific messaging.
+                // For conversations, only the "all/priority conversations" settings are relevant;
+                // any other setting is subsumed by the messages-specific messaging.
                 if (category == PRIORITY_CATEGORY_CONVERSATIONS
                         && policy.isCategoryAllowed(PRIORITY_CATEGORY_CONVERSATIONS, false)
+                        && policy.getPriorityConversationSenders() != CONVERSATION_SENDERS_ANYONE
                         && policy.getPriorityConversationSenders()
                         != CONVERSATION_SENDERS_IMPORTANT) {
                     continue;
@@ -320,13 +329,20 @@
             } else {
                 return mContext.getString(R.string.zen_mode_from_starred);
             }
-        } else if (category == PRIORITY_CATEGORY_CONVERSATIONS
-                && policy.getPriorityConversationSenders() == CONVERSATION_SENDERS_IMPORTANT) {
-            if (isFirst) {
-                return mContext.getString(R.string.zen_mode_from_important_conversations);
-            } else {
-                return mContext.getString(
-                        R.string.zen_mode_from_important_conversations_second);
+        } else if (category == PRIORITY_CATEGORY_CONVERSATIONS) {
+            if (policy.getPriorityConversationSenders() == CONVERSATION_SENDERS_IMPORTANT) {
+                if (isFirst) {
+                    return mContext.getString(R.string.zen_mode_from_important_conversations);
+                } else {
+                    return mContext.getString(
+                            R.string.zen_mode_from_important_conversations_second);
+                }
+            } else if (policy.getPriorityConversationSenders() == CONVERSATION_SENDERS_ANYONE) {
+                if (isFirst) {
+                    return mContext.getString(R.string.zen_mode_from_all_conversations);
+                } else {
+                    return mContext.getString(R.string.zen_mode_from_all_conversations_second);
+                }
             }
         } else if (category == PRIORITY_CATEGORY_EVENTS) {
             if (isFirst) {
diff --git a/src/com/android/settings/password/PasswordUtils.java b/src/com/android/settings/password/PasswordUtils.java
index a54df94..8c8afc2 100644
--- a/src/com/android/settings/password/PasswordUtils.java
+++ b/src/com/android/settings/password/PasswordUtils.java
@@ -116,7 +116,7 @@
             final ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(
                     ViewGroup.LayoutParams.WRAP_CONTENT,
                     ViewGroup.LayoutParams.WRAP_CONTENT);
-            lp.leftMargin = layoutTitleParams.leftMargin;
+            lp.setMarginStart(layoutTitleParams.leftMargin);
             lp.topMargin = (int) context.getResources().getDimensionPixelSize(
                     R.dimen.screen_lock_options_button_margin_top);
             optButton.setPadding(0, 0, 0, 0);
diff --git a/src/com/android/settings/privatespace/PrivateProfileCreationRestrictedError.java b/src/com/android/settings/privatespace/PrivateProfileCreationRestrictedError.java
new file mode 100644
index 0000000..d2bdb8c
--- /dev/null
+++ b/src/com/android/settings/privatespace/PrivateProfileCreationRestrictedError.java
@@ -0,0 +1,99 @@
+/*
+ * 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.privatespace;
+
+import android.app.Activity;
+import android.app.settings.SettingsEnums;
+import android.os.Bundle;
+import android.text.util.Linkify;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.activity.OnBackPressedCallback;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.settings.R;
+import com.android.settings.core.InstrumentedFragment;
+
+import com.google.android.setupcompat.template.FooterBarMixin;
+import com.google.android.setupcompat.template.FooterButton;
+import com.google.android.setupdesign.GlifLayout;
+
+import java.util.regex.Pattern;
+
+public class PrivateProfileCreationRestrictedError extends InstrumentedFragment {
+    private static final String TAG = "PrivateSpaceCreationErr";
+
+    @NonNull
+    @Override
+    public View onCreateView(
+            @NonNull LayoutInflater inflater,
+            @Nullable ViewGroup container,
+            @Nullable Bundle savedInstanceState) {
+        GlifLayout rootView =
+                (GlifLayout)
+                        inflater.inflate(R.layout.privatespace_creation_error, container, false);
+        final FooterBarMixin mixin = rootView.getMixin(FooterBarMixin.class);
+        mixin.setPrimaryButton(
+                new FooterButton.Builder(getContext())
+                        .setText(R.string.private_space_exit_label)
+                        .setListener(onExit())
+                        .setButtonType(FooterButton.ButtonType.NEXT)
+                        .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
+                        .build());
+        OnBackPressedCallback callback =
+                new OnBackPressedCallback(true /* enabled by default */) {
+                    @Override
+                    public void handleOnBackPressed() {
+                        // Handle the back button event. We intentionally don't want to allow back
+                        // button to work in this screen during the setup flow.
+                    }
+                };
+        requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
+        rootView.setDescriptionText(R.string.private_space_error_description);
+        TextView textView = rootView.getDescriptionTextView();
+        Pattern pattern = Pattern.compile(getString(R.string.private_space_error_causes_text));
+        Linkify.addLinks(
+                textView,
+                pattern,
+                getString(R.string.private_space_learn_more_url));
+
+        return rootView;
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return SettingsEnums.PRIVATE_SPACE_SETUP_SPACE_CREATION_ERROR;
+    }
+
+    private View.OnClickListener onExit() {
+        return v -> {
+            Activity activity = getActivity();
+            if (activity != null) {
+                mMetricsFeatureProvider.action(
+                        getContext(), SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_CANCEL_CREATE_SPACE);
+                Log.i(TAG, "private space setup exited");
+                activity.finish();
+            }
+        };
+    }
+}
+
diff --git a/src/com/android/settings/privatespace/PrivateSpaceCreationFragment.java b/src/com/android/settings/privatespace/PrivateSpaceCreationFragment.java
index ce85d72..0bfedbd 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceCreationFragment.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceCreationFragment.java
@@ -29,6 +29,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.UserManager;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -49,6 +50,7 @@
     private static final String TAG = "PrivateSpaceCreateFrag";
     private static final int PRIVATE_SPACE_CREATE_POST_DELAY_MS = 1000;
     private static final int PRIVATE_SPACE_ACCOUNT_LOGIN_POST_DELAY_MS = 5000;
+    private static final int PRIVATE_SPACE_SETUP_NO_ERROR = 0;
     private static final Handler sHandler = new Handler(Looper.getMainLooper());
     private Runnable mRunnable =
             () -> {
@@ -122,6 +124,11 @@
             Log.i(TAG, "Private Space created");
             mMetricsFeatureProvider.action(
                     getContext(), SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_SPACE_CREATED, true);
+            if (android.multiuser.Flags.showDifferentCreationErrorForUnsupportedDevices()) {
+                mMetricsFeatureProvider.action(
+                        getContext(), SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_SPACE_ERRORS,
+                        PRIVATE_SPACE_SETUP_NO_ERROR);
+            }
             if (isConnectedToInternet()) {
                 registerReceiver();
                 sHandler.postDelayed(
@@ -132,8 +139,18 @@
             }
         } else {
             mMetricsFeatureProvider.action(
-                    getContext(), SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_SPACE_CREATED, false);
-            showPrivateSpaceErrorScreen();
+                    getContext(), SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_SPACE_CREATED,
+                    false);
+            if (android.multiuser.Flags.showDifferentCreationErrorForUnsupportedDevices()) {
+                int errorCode = PrivateSpaceMaintainer.getInstance(
+                        getActivity()).getPrivateSpaceCreateError();
+                mMetricsFeatureProvider.action(
+                        getContext(), SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_SPACE_ERRORS,
+                        errorCode);
+                showPrivateSpaceErrorScreen(errorCode);
+            } else {
+                showPrivateSpaceErrorScreen();
+            }
         }
     }
 
@@ -147,6 +164,16 @@
                 .navigate(R.id.action_create_profile_error);
     }
 
+    private void showPrivateSpaceErrorScreen(int errorCode) {
+        if (errorCode == UserManager.USER_OPERATION_ERROR_USER_RESTRICTED
+                || errorCode == UserManager.USER_OPERATION_ERROR_PRIVATE_PROFILE) {
+            NavHostFragment.findNavController(PrivateSpaceCreationFragment.this)
+                    .navigate(R.id.action_create_profile_error_restrict);
+        } else {
+            showPrivateSpaceErrorScreen();
+        }
+    }
+
     /** Returns true if device has an active internet connection, false otherwise. */
     private boolean isConnectedToInternet() {
         ConnectivityManager cm =
diff --git a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
index ec044da..dd6a4bb 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
@@ -60,6 +60,7 @@
     private final Context mContext;
     private final UserManager mUserManager;
     private final ActivityManager mActivityManager;
+    private  int mErrorCode;
     @GuardedBy("this")
     private UserHandle mUserHandle;
     private final KeyguardManager mKeyguardManager;
@@ -111,6 +112,9 @@
                         userName, USER_TYPE_PROFILE_PRIVATE, new ArraySet<>());
             } catch (Exception e) {
                 Log.e(TAG, "Error creating private space", e);
+                if (android.multiuser.Flags.showDifferentCreationErrorForUnsupportedDevices()) {
+                    mErrorCode = ((UserManager.UserOperationException) e).getUserOperationResult();
+                }
                 return false;
             }
 
@@ -312,6 +316,11 @@
         return mUserManager.canAddPrivateProfile() || doesPrivateSpaceExist();
     }
 
+    /** Returns the error code for private space creation failure*/
+    public int getPrivateSpaceCreateError() {
+        return mErrorCode;
+    }
+
     /** Returns true if private space exists and is running, otherwise returns false */
     @VisibleForTesting
     synchronized boolean isPrivateProfileRunning() {
diff --git a/src/com/android/settings/spa/search/SpaSearchRepository.kt b/src/com/android/settings/spa/search/SpaSearchRepository.kt
index 0efcb70..e5334dd 100644
--- a/src/com/android/settings/spa/search/SpaSearchRepository.kt
+++ b/src/com/android/settings/spa/search/SpaSearchRepository.kt
@@ -93,10 +93,12 @@
             itemTitle: String,
             indexableClass: Class<*>,
             pageTitle: String,
+            keywords: String? = null,
         ) =
             SearchIndexableRaw(context).apply {
                 key = spaSearchLandingKey.toByteString().toStringUtf8()
                 title = itemTitle
+                this.keywords = keywords
                 intentAction = SEARCH_LANDING_ACTION
                 intentTargetClass = SpaSearchLandingActivity::class.qualifiedName
                 packageName = context.packageName
diff --git a/src/com/android/settings/users/UserDetailsSettings.java b/src/com/android/settings/users/UserDetailsSettings.java
index 588f01a..66c278e 100644
--- a/src/com/android/settings/users/UserDetailsSettings.java
+++ b/src/com/android/settings/users/UserDetailsSettings.java
@@ -370,11 +370,18 @@
             }
             mSwitchUserPref.setOnPreferenceClickListener(this);
         }
-        if (mUserInfo.isMain() || mUserInfo.isGuest() || !UserManager.isMultipleAdminEnabled()
-                || mUserManager.hasUserRestrictionForUser(UserManager.DISALLOW_GRANT_ADMIN,
-                mUserInfo.getUserHandle()) || !mUserManager.isAdminUser()) {
-            removePreference(KEY_GRANT_ADMIN);
+        if (android.multiuser.Flags.unicornModeRefactoringForHsumReadOnly()) {
+            if (isChangingAdminStatusRestricted()) {
+                removePreference(KEY_GRANT_ADMIN);
+            }
+        } else {
+            if (mUserInfo.isMain() || mUserInfo.isGuest() || !UserManager.isMultipleAdminEnabled()
+                    || mUserManager.hasUserRestrictionForUser(UserManager.DISALLOW_GRANT_ADMIN,
+                    mUserInfo.getUserHandle()) || !mUserManager.isAdminUser()) {
+                removePreference(KEY_GRANT_ADMIN);
+            }
         }
+
         if (!mUserManager.isAdminUser()) { // non admin users can't remove users and allow calls
             removePreference(KEY_ENABLE_TELEPHONY);
             removePreference(KEY_REMOVE_USER);
@@ -552,4 +559,33 @@
         //  return true so there will be no setup prompt dialog shown to the user anymore.
         return isSecondaryUser(mUserInfo) && !mUserInfo.isInitialized();
     }
+
+    /**
+     * Determines if changing admin status is restricted.
+     *
+     * <p>Admin status change is restricted under the following conditions of current & target user.
+     *
+     * <ul>
+     *   <li>The <b>current</b> user is NOT an admin user.</li>
+     *   <li>OR multiple admin support is NOT enabled.</li>
+     *   <li>OR the <b>current</b> user has DISALLOW_GRANT_ADMIN restriction applied</li>
+     *
+     *   <li>OR the <b>target</b> user ('mUserInfo') is a main user OR a guest user.</li>
+     *   <li>OR the <b>target</b> user ('mUserInfo') has DISALLOW_GRANT_ADMIN restriction.</li>
+     * </ul>
+     *
+     * @return true if changing admin status is restricted, false otherwise
+     */
+    private boolean isChangingAdminStatusRestricted() {
+        boolean currentUserRestricted = !mUserManager.isAdminUser()
+                || !UserManager.isMultipleAdminEnabled()
+                || mUserManager.hasUserRestriction(UserManager.DISALLOW_GRANT_ADMIN);
+
+        boolean targetUserRestricted = mUserInfo.isMain()
+                || mUserInfo.isGuest()
+                || mUserManager.hasUserRestrictionForUser(UserManager.DISALLOW_GRANT_ADMIN,
+                mUserInfo.getUserHandle());
+
+        return currentUserRestricted || targetUserRestricted;
+    }
 }
diff --git a/src/com/android/settings/wifi/WepNetworksPreferenceController.kt b/src/com/android/settings/wifi/WepNetworksPreferenceController.kt
index 2ce4bcd..bad7201 100644
--- a/src/com/android/settings/wifi/WepNetworksPreferenceController.kt
+++ b/src/com/android/settings/wifi/WepNetworksPreferenceController.kt
@@ -42,6 +42,8 @@
 import kotlinx.coroutines.asExecutor
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOn
 
 /** Controller that controls whether the WEP network can be connected. */
 class WepNetworksPreferenceController(context: Context, preferenceKey: String) :
@@ -49,68 +51,74 @@
 
     var wifiManager = context.getSystemService(WifiManager::class.java)!!
 
-    override fun getAvailabilityStatus() = if (Flags.androidVWifiApi()) AVAILABLE
-    else UNSUPPORTED_ON_DEVICE
+    override fun getAvailabilityStatus() =
+        if (Flags.androidVWifiApi()) AVAILABLE else UNSUPPORTED_ON_DEVICE
 
     @Composable
     override fun Content() {
-        val checked by wepAllowedFlow.flow.collectAsStateWithLifecycle(initialValue = null)
+        val isWepSupported: Boolean? =
+            isWepSupportedFlow.collectAsStateWithLifecycle(initialValue = null).value
+        val isWepAllowed: Boolean? =
+            wepAllowedFlow.flow.collectAsStateWithLifecycle(initialValue = null).value
         var openDialog by rememberSaveable { mutableStateOf(false) }
-        val wifiInfo = wifiManager.connectionInfo
-        SwitchPreference(object : SwitchPreferenceModel {
-            override val title = stringResource(R.string.wifi_allow_wep_networks)
-            override val summary = { getSummary() }
-            override val checked = { checked }
-            override val changeable: () -> Boolean
-                get() = { carrierAllowed }
-            override val onCheckedChange: (Boolean) -> Unit = { newChecked ->
-                if (!newChecked && wifiInfo.currentSecurityType == WifiEntry.SECURITY_WEP) {
-                    openDialog = true
-                } else {
-                    wifiManager.setWepAllowed(newChecked)
-                    wepAllowedFlow.override(newChecked)
+        SwitchPreference(
+            object : SwitchPreferenceModel {
+                override val title = stringResource(R.string.wifi_allow_wep_networks)
+                override val summary = { getSummary(isWepSupported) }
+                override val checked = {
+                    if (isWepSupported == true) isWepAllowed else isWepSupported
                 }
-            }
-        })
+                override val changeable: () -> Boolean
+                    get() = { isWepSupported == true }
+
+                override val onCheckedChange: (Boolean) -> Unit = { newChecked ->
+                    val wifiInfo = wifiManager.connectionInfo
+                    if (!newChecked && wifiInfo.currentSecurityType == WifiEntry.SECURITY_WEP) {
+                        openDialog = true
+                    } else {
+                        wifiManager.setWepAllowed(newChecked)
+                        wepAllowedFlow.override(newChecked)
+                    }
+                }
+            })
         if (openDialog) {
             SettingsAlertDialogWithIcon(
                 onDismissRequest = { openDialog = false },
-                confirmButton = AlertDialogButton(
-                    stringResource(R.string.sim_action_yes)
-                ) {
-                    wifiManager.setWepAllowed(false)
-                    wepAllowedFlow.override(false)
-                    openDialog = false
-                },
+                confirmButton =
+                    AlertDialogButton(stringResource(R.string.sim_action_yes)) {
+                        wifiManager.setWepAllowed(false)
+                        wepAllowedFlow.override(false)
+                        openDialog = false
+                    },
                 dismissButton =
-                AlertDialogButton(
-                    stringResource(R.string.wifi_cancel)
-                ) { openDialog = false },
+                    AlertDialogButton(stringResource(R.string.wifi_cancel)) { openDialog = false },
                 title = stringResource(R.string.wifi_settings_wep_networks_disconnect_title),
                 text = {
                     Text(
                         stringResource(R.string.wifi_settings_wep_networks_disconnect_summary),
                         modifier = Modifier.fillMaxWidth(),
-                        textAlign = TextAlign.Center
+                        textAlign = TextAlign.Center,
                     )
                 })
         }
     }
 
-    override fun getSummary(): String = mContext.getString(
-        if (carrierAllowed) {
-            R.string.wifi_allow_wep_networks_summary
-        } else {
-            R.string.wifi_allow_wep_networks_summary_carrier_not_allow
-        }
-    )
+    private fun getSummary(isWepSupported: Boolean?): String =
+        mContext.getString(
+            when (isWepSupported) {
+                true -> R.string.wifi_allow_wep_networks_summary
+                false -> R.string.wifi_allow_wep_networks_summary_carrier_not_allow
+                null -> R.string.summary_placeholder
+            })
 
-    private val carrierAllowed: Boolean
-        get() = wifiManager.isWepSupported
+    private val isWepSupportedFlow =
+        flow { emit(wifiManager.isWepSupported) }.flowOn(Dispatchers.Default)
 
-    val wepAllowedFlow = OverridableFlow(callbackFlow {
-        wifiManager.queryWepAllowed(Dispatchers.Default.asExecutor(), ::trySend)
+    val wepAllowedFlow =
+        OverridableFlow(
+            callbackFlow {
+                wifiManager.queryWepAllowed(Dispatchers.Default.asExecutor(), ::trySend)
 
-        awaitClose { }
-    })
-}
\ No newline at end of file
+                awaitClose {}
+            })
+}
diff --git a/src/com/android/settings/wifi/WifiEnabler.java b/src/com/android/settings/wifi/WifiEnabler.java
index d1cf7d6..bbb014b 100644
--- a/src/com/android/settings/wifi/WifiEnabler.java
+++ b/src/com/android/settings/wifi/WifiEnabler.java
@@ -139,7 +139,8 @@
         // Refresh satellite mode status.
         try {
             mIsSatelliteOn.set(
-                    mSatelliteRepository.requestIsEnabled(Executors.newSingleThreadExecutor())
+                    mSatelliteRepository
+                            .requestIsSessionStarted(Executors.newSingleThreadExecutor())
                             .get(2000, TimeUnit.MILLISECONDS));
         } catch (ExecutionException | TimeoutException | InterruptedException e) {
             Log.e(TAG, "Error to get satellite status : " + e);
diff --git a/src/com/android/settings/wifi/slice/WifiSlice.java b/src/com/android/settings/wifi/slice/WifiSlice.java
index ff448a8..3bb50d3 100644
--- a/src/com/android/settings/wifi/slice/WifiSlice.java
+++ b/src/com/android/settings/wifi/slice/WifiSlice.java
@@ -431,7 +431,7 @@
         boolean isSatelliteOn = false;
         try {
             isSatelliteOn =
-                    satelliteRepository.requestIsEnabled(Executors.newSingleThreadExecutor())
+                    satelliteRepository.requestIsSessionStarted(Executors.newSingleThreadExecutor())
                             .get(2000, TimeUnit.MILLISECONDS);
         } catch (ExecutionException | TimeoutException | InterruptedException e) {
             Log.e(TAG, "Error to get satellite status : " + e);
diff --git a/tests/robotests/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceControllerTest.java
index 569109c..78f49a6 100644
--- a/tests/robotests/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceControllerTest.java
@@ -33,7 +33,6 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Resources;
-import android.os.vibrator.Flags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
 
@@ -41,7 +40,6 @@
 import androidx.preference.SwitchPreference;
 import androidx.test.core.app.ApplicationProvider;
 
-import com.android.settings.R;
 import com.android.settings.testutils.FakeFeatureFactory;
 
 import org.junit.Before;
@@ -87,7 +85,6 @@
 
     @Test
     public void getAvailabilityStatus_featureSupported_available() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
         when(mResources.getBoolean(
                 com.android.internal.R.bool.config_keyboardVibrationSettingsSupported))
                 .thenReturn(true);
@@ -97,7 +94,6 @@
 
     @Test
     public void getAvailabilityStatus_featureNotSupported_unavailable() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
         when(mResources.getBoolean(
                 com.android.internal.R.bool.config_keyboardVibrationSettingsSupported))
                 .thenReturn(false);
@@ -105,15 +101,6 @@
         assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
     }
 
-    @Test
-    public void getAvailabilityStatus_keyboardCategoryDisabled_unavailable() {
-        mSetFlagsRule.disableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
-        when(mResources.getBoolean(
-                com.android.internal.R.bool.config_keyboardVibrationSettingsSupported))
-                .thenReturn(true);
-
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
-    }
 
     @Test
     public void updateState_mainVibrateDisabled_shouldReturnFalseForCheckedAndEnabled() {
diff --git a/tests/robotests/src/com/android/settings/accessibility/RestrictedPreferenceHelperTest.java b/tests/robotests/src/com/android/settings/accessibility/RestrictedPreferenceHelperTest.java
index b4f4dc1..2a41e62 100644
--- a/tests/robotests/src/com/android/settings/accessibility/RestrictedPreferenceHelperTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/RestrictedPreferenceHelperTest.java
@@ -31,9 +31,8 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 
 import androidx.test.core.app.ApplicationProvider;
@@ -83,8 +82,6 @@
 
     @Rule
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
 
     @Test
     public void createAccessibilityServicePreferenceList_hasOneInfo_containsSameKey() {
@@ -100,7 +97,7 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(value = {android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS,
+    @EnableFlags(value = {android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS,
             android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED})
     public void createAccessibilityServicePreferenceList_ecmRestricted_prefIsEcmRestricted() {
         ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs(
@@ -116,7 +113,7 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(value = {android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS,
+    @EnableFlags(value = {android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS,
             android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED})
     public void createAccessibilityServicePreferenceList_ecmNotRestricted_prefIsNotEcmRestricted() {
         ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs();
@@ -144,6 +141,40 @@
         assertThat(preference.getKey()).isEqualTo(key);
     }
 
+    @Test
+    @EnableFlags(value = {android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS,
+            android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED})
+    @DisableFlags(Flags.FLAG_NEVER_RESTRICT_ACCESSIBILITY_ACTIVITY)
+    public void createAccessibilityActivityPreference_ecmRestricted_prefIsEcmRestricted() {
+        setMockAccessibilityShortcutInfo(mShortcutInfo);
+        ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs(PACKAGE_NAME);
+
+        final List<AccessibilityActivityPreference> preferenceList =
+                mHelper.createAccessibilityActivityPreferenceList(List.of(mShortcutInfo));
+        assertThat(preferenceList).hasSize(1);
+        final RestrictedPreference preference = preferenceList.get(0);
+
+        assertThat(preference.isDisabledByEcm()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(value = {
+            android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS,
+            android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED,
+            Flags.FLAG_NEVER_RESTRICT_ACCESSIBILITY_ACTIVITY,
+    })
+    public void createAccessibilityActivityPreference_ecmRestricted_prefIsNotEcmRestricted() {
+        setMockAccessibilityShortcutInfo(mShortcutInfo);
+        ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs(PACKAGE_NAME);
+
+        final List<AccessibilityActivityPreference> preferenceList =
+                mHelper.createAccessibilityActivityPreferenceList(List.of(mShortcutInfo));
+        assertThat(preferenceList).hasSize(1);
+        final RestrictedPreference preference = preferenceList.get(0);
+
+        assertThat(preference.isDisabledByEcm()).isFalse();
+    }
+
     private AccessibilityServiceInfo getMockAccessibilityServiceInfo(String packageName,
             String className) {
         final ApplicationInfo applicationInfo = new ApplicationInfo();
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsProfilesControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsProfilesControllerTest.java
index 2d1f4c0..219c37b 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsProfilesControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsProfilesControllerTest.java
@@ -44,6 +44,7 @@
 import com.android.settingslib.R;
 import com.android.settingslib.bluetooth.A2dpProfile;
 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.HearingAidProfile;
 import com.android.settingslib.bluetooth.LeAudioProfile;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.LocalBluetoothProfile;
@@ -90,8 +91,12 @@
     @Mock
     private CachedBluetoothDeviceManager mCachedBluetoothDeviceManager;
 
-    private @Mock A2dpProfile mA2dpProfile;
-    private @Mock LeAudioProfile mLeAudioProfile;
+    @Mock
+    private A2dpProfile mA2dpProfile;
+    @Mock
+    private LeAudioProfile mLeAudioProfile;
+    @Mock
+    private HearingAidProfile mHearingAidProfile;
 
     @Override
     public void setUp() {
@@ -399,18 +404,23 @@
         when(mProfileManager.getProfileByName(eq(mA2dpProfile.toString())))
                 .thenReturn(mA2dpProfile);
         when(mA2dpProfile.getNameResource(any()))
-                .thenReturn(com.android.settingslib.R.string.bluetooth_profile_a2dp);
+                .thenReturn(R.string.bluetooth_profile_a2dp);
         when(mA2dpProfile.getHighQualityAudioOptionLabel(any())).thenReturn(
-                mContext.getString(com.android.settingslib.R
-                        .string.bluetooth_profile_a2dp_high_quality_unknown_codec));
+                mContext.getString(R.string.bluetooth_profile_a2dp_high_quality_unknown_codec));
         when(mA2dpProfile.isProfileReady()).thenReturn(true);
         when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
 
         when(mLeAudioProfile.toString()).thenReturn("LE_AUDIO");
         when(mLeAudioProfile.getNameResource(any()))
-                .thenReturn(com.android.settingslib.R.string.bluetooth_profile_le_audio);
+                .thenReturn(R.string.bluetooth_profile_le_audio);
         when(mLeAudioProfile.isProfileReady()).thenReturn(true);
         when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
+
+        when(mHearingAidProfile.toString()).thenReturn("HearingAid");
+        when(mHearingAidProfile.getNameResource(any()))
+                .thenReturn(R.string.bluetooth_profile_hearing_aid);
+        when(mHearingAidProfile.isProfileReady()).thenReturn(true);
+        when(mProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
     }
 
     private void addA2dpProfileToDevice(boolean preferred, boolean supportsHighQualityAudio,
@@ -426,6 +436,11 @@
         mConnectableProfiles.add(mLeAudioProfile);
     }
 
+    private void addHearingAidProfileToDevice(boolean enabled) {
+        when(mHearingAidProfile.isEnabled(any())).thenReturn(enabled);
+        mConnectableProfiles.add(mHearingAidProfile);
+    }
+
     private SwitchPreferenceCompat getHighQualityAudioPref() {
         return (SwitchPreferenceCompat) mScreen.findPreference(
                 BluetoothDetailsProfilesController.HIGH_QUALITY_AUDIO_PREF_TAG);
@@ -591,4 +606,27 @@
         List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
         assertThat(switches.get(0).isVisible()).isFalse();
     }
+
+    @Test
+    public void ashaHearingAid_hideAshaToggle() {
+        setupDevice(makeDefaultDeviceConfig());
+        addHearingAidProfileToDevice(true);
+
+        showScreen(mController);
+
+        List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
+        assertThat(switches.isEmpty()).isTrue();
+    }
+    @Test
+    public void ashaHearingAidWithLeAudio_showLeAudioToggle() {
+        setupDevice(makeDefaultDeviceConfig());
+        addHearingAidProfileToDevice(false);
+        addLeAudioProfileToDevice(true);
+
+        showScreen(mController);
+
+        List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
+        assertThat(switches.getFirst().getTitle()).isEqualTo(
+                mContext.getString(mLeAudioProfile.getNameResource(mDevice)));
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragmentTest.java
index 8e4915c..7d8846d 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragmentTest.java
@@ -21,10 +21,12 @@
 import static org.mockito.Mockito.doReturn;
 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.os.Bundle;
+import android.view.View;
 
 import androidx.test.core.app.ApplicationProvider;
 
@@ -52,6 +54,7 @@
 
     @Mock private SettingsActivity mActivity;
     @Mock private SettingsMainSwitchBar mSwitchBar;
+    @Mock private View mView;
     @Mock private AudioSharingDeviceVolumeGroupController mVolumeGroupController;
     @Mock private AudioSharingCallAudioPreferenceController mCallAudioController;
     @Mock private AudioSharingPlaySoundPreferenceController mPlaySoundController;
@@ -61,6 +64,7 @@
 
     @Before
     public void setUp() {
+        when(mSwitchBar.getRootView()).thenReturn(mView);
         mFragment = new AudioSharingDashboardFragment();
     }
 
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java
index bfb4f2f..711ef6f 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java
@@ -51,6 +51,8 @@
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.FeatureFlagUtils;
 import android.util.Pair;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
 import android.widget.CompoundButton;
 
 import androidx.fragment.app.DialogFragment;
@@ -590,4 +592,26 @@
         mController.mBroadcastAssistantCallback.onSourceLost(/* broadcastId= */ 1);
         verifyNoMoreInteractions(mFeatureFactory.metricsFeatureProvider);
     }
+
+    @Test
+    public void testAccessibilityDelegate() {
+        View view = new View(mContext);
+        AccessibilityEvent event =
+                new AccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+        event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+        assertThat(
+                        mSwitchBar
+                                .getRootView()
+                                .getAccessibilityDelegate()
+                                .onRequestSendAccessibilityEvent(mSwitchBar, view, event))
+                .isTrue();
+
+        event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_ENABLED);
+        assertThat(
+                        mSwitchBar
+                                .getRootView()
+                                .getAccessibilityDelegate()
+                                .onRequestSendAccessibilityEvent(mSwitchBar, view, event))
+                .isFalse();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamConfirmDialogTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamConfirmDialogTest.java
index e00a146..9c83fa6 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamConfirmDialogTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamConfirmDialogTest.java
@@ -23,7 +23,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreferenceTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreferenceTest.java
index c8f9358..2515668 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreferenceTest.java
@@ -28,7 +28,6 @@
 import android.view.LayoutInflater;
 import android.view.View;
 
-import androidx.preference.Preference.OnPreferenceClickListener;
 import androidx.preference.PreferenceViewHolder;
 import androidx.test.core.app.ApplicationProvider;
 
@@ -94,17 +93,6 @@
     }
 
     @Test
-    public void setConnected_shouldUpdatePreferenceUI() {
-        String summary = "Connected";
-        OnPreferenceClickListener listener = mock(OnPreferenceClickListener.class);
-        mPreference.setIsConnected(true, summary, listener);
-
-        assertThat(mPreference.getSummary()).isNotNull();
-        assertThat(mPreference.getSummary().toString()).isEqualTo(summary);
-        assertThat(mPreference.getOnPreferenceClickListener()).isEqualTo(listener);
-    }
-
-    @Test
     public void setAudioStreamMetadata_shouldUpdateMetadata() {
         AudioStreamPreference p =
                 AudioStreamPreference.fromMetadata(
@@ -147,7 +135,7 @@
 
     @Test
     public void shouldHideSecondTarget_connected() {
-        mPreference.setIsConnected(true, "", null);
+        mPreference.setIsConnected(true);
         assertThat(mPreference.shouldHideSecondTarget()).isTrue();
     }
 
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandlerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandlerTest.java
index adc77a1..e44dee9 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandlerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandlerTest.java
@@ -21,7 +21,6 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -31,6 +30,7 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.text.SpannableString;
 
 import androidx.preference.Preference;
 import androidx.test.core.app.ApplicationProvider;
@@ -39,6 +39,7 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
@@ -75,7 +76,9 @@
 
         verify(mPreference, never()).setAudioStreamState(any());
         verify(mHandler, never()).performAction(any(), any(), any());
-        verify(mPreference, never()).setIsConnected(anyBoolean(), anyString(), any());
+        verify(mPreference, never()).setIsConnected(anyBoolean());
+        verify(mPreference, never()).setSummary(any());
+        verify(mPreference, never()).setOnPreferenceClickListener(any());
     }
 
     @Test
@@ -93,7 +96,9 @@
                 .setAudioStreamState(
                         AudioStreamsProgressCategoryController.AudioStreamState.SOURCE_ADDED);
         verify(mHandler).performAction(any(), any(), any());
-        verify(mPreference).setIsConnected(eq(true), eq(""), eq(null));
+        verify(mPreference).setIsConnected(eq(true));
+        verify(mPreference).setSummary(eq(""));
+        verify(mPreference).setOnPreferenceClickListener(eq(null));
     }
 
     @Test
@@ -119,7 +124,13 @@
                         AudioStreamsProgressCategoryController.AudioStreamState
                                 .ADD_SOURCE_BAD_CODE);
         verify(mHandler).performAction(any(), any(), any());
-        verify(mPreference).setIsConnected(eq(false), eq(SUMMARY), eq(listener));
+        verify(mPreference).setIsConnected(eq(false));
+        ArgumentCaptor<SpannableString> argumentCaptor =
+                ArgumentCaptor.forClass(SpannableString.class);
+        verify(mPreference).setSummary(argumentCaptor.capture());
+        assertThat(argumentCaptor.getValue()).isNotNull();
+        assertThat(argumentCaptor.getValue().toString()).isEqualTo(SUMMARY);
+        verify(mPreference).setOnPreferenceClickListener(eq(listener));
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/development/TouchpadVisualizerPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/TouchpadVisualizerPreferenceControllerTest.java
new file mode 100644
index 0000000..98c774e
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/TouchpadVisualizerPreferenceControllerTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 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.development;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+
+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;
+
+@RunWith(RobolectricTestRunner.class)
+public class TouchpadVisualizerPreferenceControllerTest {
+
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private SwitchPreference mPreference;
+
+    private Context mContext;
+
+    private TouchpadVisualizerPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mController = new TouchpadVisualizerPreferenceController(mContext);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
+        mController.displayPreference(mScreen);
+    }
+
+    @Test
+    public void updateState_touchpadVisualizerEnabled_shouldCheckedPreference() {
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.TOUCHPAD_VISUALIZER, ShowTapsPreferenceController.SETTING_VALUE_ON);
+
+        mController.updateState(mPreference);
+
+        verify(mPreference).setChecked(true);
+    }
+
+    @Test
+    public void updateState_touchpadVisualizerDisabled_shouldUncheckedPreference() {
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.TOUCHPAD_VISUALIZER,
+                ShowTapsPreferenceController.SETTING_VALUE_OFF);
+
+        mController.updateState(mPreference);
+
+        verify(mPreference).setChecked(false);
+    }
+
+    @Test
+    public void onPreferenceChange_preferenceChecked_shouldEnableTouchpadVisualizer() {
+        mController.onPreferenceChange(mPreference, true /* new value */);
+
+        final int touchpadVisualizer = Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.TOUCHPAD_VISUALIZER, -1 /* default */);
+
+        assertThat(touchpadVisualizer).isEqualTo(ShowTapsPreferenceController.SETTING_VALUE_ON);
+    }
+
+    @Test
+    public void onPreferenceChange_preferenceUnchecked_shouldDisableTouchpadVisualizer() {
+        mController.onPreferenceChange(mPreference, false /* new value */);
+
+        final int showTapsMode = Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.TOUCHPAD_VISUALIZER, -1 /* default */);
+
+        assertThat(showTapsMode).isEqualTo(ShowTapsPreferenceController.SETTING_VALUE_OFF);
+    }
+
+    @Test
+    public void onDeveloperOptionsSwitchDisabled_preferenceShouldBeEnabled() {
+        mController.onDeveloperOptionsSwitchDisabled();
+
+        final int showTapsMode = Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.TOUCHPAD_VISUALIZER, -1 /* default */);
+
+        assertThat(showTapsMode).isEqualTo(ShowTapsPreferenceController.SETTING_VALUE_OFF);
+        verify(mPreference).setEnabled(false);
+        verify(mPreference).setChecked(false);
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
index a3b35be..a694b2f 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
@@ -38,6 +38,8 @@
 import android.os.LocaleList;
 import android.os.UserHandle;
 
+import androidx.core.util.Pair;
+
 import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
 import com.android.settings.fuelgauge.batteryusage.db.BatteryEventEntity;
 import com.android.settings.fuelgauge.batteryusage.db.BatteryUsageSlotEntity;
@@ -384,8 +386,9 @@
                         /* cachedUsageConsumePower= */ 1.5);
         BatteryOptimizationModeCache optimizationModeCache =
                 new BatteryOptimizationModeCache(mContext);
-        optimizationModeCache.mBatteryOptimizeModeCacheMap.put(
-                (int) batteryDiffEntry.mUid, BatteryOptimizationMode.MODE_OPTIMIZED);
+        optimizationModeCache.mBatteryOptimizeModeCache.put(
+                (int) batteryDiffEntry.mUid,
+                Pair.create(BatteryOptimizationMode.MODE_OPTIMIZED, false));
 
         final BatteryUsageDiff batteryUsageDiff =
                 ConvertUtils.convertToBatteryUsageDiff(batteryDiffEntry, optimizationModeCache);
@@ -408,6 +411,7 @@
         assertThat(batteryUsageDiff.getKey()).isEqualTo("key");
         assertThat(batteryUsageDiff.getAppOptimizationMode())
                 .isEqualTo(BatteryOptimizationMode.MODE_OPTIMIZED);
+        assertThat(batteryUsageDiff.getIsAppOptimizationModeMutable()).isFalse();
         assertThat(batteryUsageDiff.hasPackageName()).isFalse();
         assertThat(batteryUsageDiff.hasLabel()).isFalse();
     }
diff --git a/tests/robotests/src/com/android/settings/network/SatelliteRepositoryTest.kt b/tests/robotests/src/com/android/settings/network/SatelliteRepositoryTest.kt
index 62fd10a..619d290 100644
--- a/tests/robotests/src/com/android/settings/network/SatelliteRepositoryTest.kt
+++ b/tests/robotests/src/com/android/settings/network/SatelliteRepositoryTest.kt
@@ -91,7 +91,8 @@
 
     @Test
     fun requestIsSessionStarted_resultIsTrue() = runBlocking {
-        `when`(mockSatelliteManager.registerForModemStateChanged(any(), any())
+        `when`(
+            mockSatelliteManager.registerForModemStateChanged(any(), any())
         ).thenAnswer { invocation ->
             val callback = invocation.getArgument<SatelliteModemStateCallback>(1)
             callback.onSatelliteModemStateChanged(SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED)
@@ -105,7 +106,8 @@
 
     @Test
     fun requestIsSessionStarted_resultIsFalse() = runBlocking {
-        `when`(mockSatelliteManager.registerForModemStateChanged(any(), any())
+        `when`(
+            mockSatelliteManager.registerForModemStateChanged(any(), any())
         ).thenAnswer { invocation ->
             val callback = invocation.getArgument<SatelliteModemStateCallback>(1)
             callback.onSatelliteModemStateChanged(SatelliteManager.SATELLITE_MODEM_STATE_OFF)
@@ -119,7 +121,8 @@
 
     @Test
     fun requestIsSessionStarted_registerFailed() = runBlocking {
-        `when`(mockSatelliteManager.registerForModemStateChanged(any(), any())
+        `when`(
+            mockSatelliteManager.registerForModemStateChanged(any(), any())
         ).thenAnswer {
             SatelliteManager.SATELLITE_RESULT_ERROR
         }
@@ -130,6 +133,17 @@
     }
 
     @Test
+    fun requestIsSessionStarted_phoneCrash_registerFailed() = runBlocking {
+        `when`(
+            mockSatelliteManager.registerForModemStateChanged(any(), any())
+        ).thenThrow(IllegalStateException("Telephony is null"))
+
+        val result: ListenableFuture<Boolean> = repository.requestIsSessionStarted(mockExecutor)
+        assertFalse(result.get())
+        verify(mockSatelliteManager, never()).unregisterForModemStateChanged(any())
+    }
+
+    @Test
     fun requestIsSessionStarted_nullSatelliteManager() = runBlocking {
         `when`(spyContext.getSystemService(SatelliteManager::class.java)).thenReturn(null)
 
@@ -157,6 +171,17 @@
         assertFalse(result.get())
     }
 
+    @Test
+    fun requestIsEnabled_phoneCrash_resultIsFalse() = runBlocking {
+        `when`(
+            mockSatelliteManager.requestIsEnabled(any(), any())
+        ).thenThrow(IllegalStateException("Telephony is null"))
+
+        val result: ListenableFuture<Boolean> =
+            repository.requestIsEnabled(mockExecutor)
+        assertFalse(result.get())
+    }
+
 
     @Test
     fun requestIsEnabled_exceptionFailure() = runBlocking {
@@ -232,7 +257,8 @@
 
     @Test
     fun getIsSessionStartedFlow_registerFailed() = runBlocking {
-        `when`(mockSatelliteManager.registerForModemStateChanged(any(), any())
+        `when`(
+            mockSatelliteManager.registerForModemStateChanged(any(), any())
         ).thenAnswer {
             SatelliteManager.SATELLITE_RESULT_ERROR
         }
diff --git a/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java b/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java
index 8445fe2..9cd69b4 100644
--- a/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java
+++ b/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java
@@ -19,8 +19,10 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 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;
@@ -37,6 +39,7 @@
 import androidx.slice.Slice;
 import androidx.slice.SliceMetadata;
 import androidx.slice.SliceProvider;
+import androidx.slice.builders.ListBuilder;
 import androidx.slice.core.SliceAction;
 import androidx.slice.widget.SliceLiveData;
 
@@ -68,6 +71,7 @@
 
     private Context mContext;
     private MobileDataSlice mMobileDataSlice;
+    private ListBuilder mListBuilder;
 
     @Before
     public void setUp() {
@@ -86,6 +90,8 @@
         SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
 
         mMobileDataSlice = spy(new MobileDataSlice(mContext));
+        mListBuilder = spy(mMobileDataSlice.createListBuilder());
+        doReturn(mListBuilder).when(mMobileDataSlice).createListBuilder();
     }
 
     @Test
@@ -175,25 +181,41 @@
     @Test
     public void isMobileDataAvailable_noSubscriptions_slicePrimaryActionIsEmpty() {
         when(mSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(new ArrayList<>());
-        final Slice mobileData = mMobileDataSlice.getSlice();
 
-        assertThat(mobileData).isNull();
+        Slice mobileData = mMobileDataSlice.getSlice();
+
+        assertThat(mobileData).isNotNull();
+        verify(mListBuilder, never()).addRow(any());
     }
 
     @Test
     public void isMobileDataAvailable_nullSubscriptions_slicePrimaryActionIsEmpty() {
         when(mSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(null);
-        final Slice mobileData = mMobileDataSlice.getSlice();
 
-        assertThat(mobileData).isNull();
+        Slice mobileData = mMobileDataSlice.getSlice();
+
+        assertThat(mobileData).isNotNull();
+        verify(mListBuilder, never()).addRow(any());
     }
 
     @Test
     public void airplaneModeEnabled_slicePrimaryActionIsEmpty() {
         doReturn(true).when(mMobileDataSlice).isAirplaneModeEnabled();
         doReturn(mSubscriptionInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(SUB_ID);
-        final Slice mobileData = mMobileDataSlice.getSlice();
 
-        assertThat(mobileData).isNull();
+        Slice mobileData = mMobileDataSlice.getSlice();
+
+        assertThat(mobileData).isNotNull();
+        verify(mListBuilder, never()).addRow(any());
+    }
+
+    @Test
+    public void getSlice_disallowConfigMobileNetworks_slicePrimaryActionIsEmpty() {
+        doReturn(false).when(mMobileDataSlice).isConfigMobileNetworksAllowed();
+
+        Slice mobileData = mMobileDataSlice.getSlice();
+
+        assertThat(mobileData).isNotNull();
+        verify(mListBuilder, never()).addRow(any());
     }
 }
diff --git a/tests/robotests/src/com/android/settings/notification/modes/SetupInterstitialActivityTest.java b/tests/robotests/src/com/android/settings/notification/modes/SetupInterstitialActivityTest.java
new file mode 100644
index 0000000..e73610b
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/modes/SetupInterstitialActivityTest.java
@@ -0,0 +1,248 @@
+/*
+ * 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.notification.modes;
+
+import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID;
+
+import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT;
+import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
+
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import androidx.test.core.app.ActivityScenario;
+
+import com.android.settingslib.notification.modes.TestModeBuilder;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
+
+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;
+
+@RunWith(RobolectricTestRunner.class)
+public class SetupInterstitialActivityTest {
+    private static final String MODE_ID = "modeId";
+
+    @Mock
+    private ZenModesBackend mBackend;
+
+    @Mock
+    private ImageView mImage;
+
+    @Mock
+    private Drawable mDrawable;
+
+    @Mock
+    private FrameLayout mFrame;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        // set global backend instance so that when the interstitial activity launches, it'll get
+        // this mock backend
+        ZenModesBackend.setInstance(mBackend);
+
+        when(mBackend.getMode(MODE_ID)).thenReturn(new TestModeBuilder().build());
+        when(mImage.getDrawable()).thenReturn(mDrawable);
+        when(mImage.getLayoutParams()).thenReturn(new ViewGroup.LayoutParams(0, 0));
+    }
+
+    @Test
+    public void enableButton_enablesModeAndRedirectsToModePage() {
+        ZenMode mode = new TestModeBuilder().setId(MODE_ID).setEnabled(false).build();
+        when(mBackend.getMode(MODE_ID)).thenReturn(mode);
+
+        // Set up scenario with this mode information
+        ActivityScenario<SetupInterstitialActivity> scenario =
+                ActivityScenario.launch(new Intent(Intent.ACTION_MAIN)
+                        .setClass(RuntimeEnvironment.getApplication(),
+                                SetupInterstitialActivity.class)
+                        .putExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID, MODE_ID));
+        scenario.onActivity(activity -> {
+            View.OnClickListener listener = activity.enableButtonListener(MODE_ID);
+
+            // simulate button press even though we don't actually have a button
+            listener.onClick(null);
+
+            // verify that the backend got a request to enable the mode
+            ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
+            verify(mBackend).updateMode(captor.capture());
+            ZenMode updatedMode = captor.getValue();
+            assertThat(updatedMode.getId()).isEqualTo(MODE_ID);
+            assertThat(updatedMode.isEnabled()).isTrue();
+
+            // confirm that the next activity is the mode page
+            Intent openModePageIntent = shadowOf(activity).getNextStartedActivity();
+            assertThat(openModePageIntent.getStringExtra(EXTRA_SHOW_FRAGMENT))
+                    .isEqualTo(ZenModeFragment.class.getName());
+            Bundle fragmentArgs = openModePageIntent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
+            assertThat(fragmentArgs).isNotNull();
+            assertThat(fragmentArgs.getString(EXTRA_AUTOMATIC_ZEN_RULE_ID)).isEqualTo(MODE_ID);
+        });
+        scenario.close();
+    }
+
+    @Test
+    public void setImageToFrame_sizeZero() {
+        ActivityScenario<SetupInterstitialActivity> scenario =
+                ActivityScenario.launch(new Intent(Intent.ACTION_MAIN)
+                        .setClass(RuntimeEnvironment.getApplication(),
+                                SetupInterstitialActivity.class)
+                        .putExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID, MODE_ID));
+        scenario.onActivity(activity -> {
+            // when either the image or the frame has a size 0, we do nothing
+            when(mDrawable.getIntrinsicWidth()).thenReturn(0);
+            when(mDrawable.getIntrinsicHeight()).thenReturn(25);
+            when(mFrame.getMeasuredWidth()).thenReturn(40);
+            when(mFrame.getMeasuredHeight()).thenReturn(50);
+
+            activity.sizeImageToFrame(mImage, mFrame);
+            verify(mImage, never()).setLayoutParams(any());
+        });
+        scenario.close();
+    }
+
+    @Test
+    public void setImageToFrame_imageLargerThanFrame() {
+        ActivityScenario<SetupInterstitialActivity> scenario =
+                ActivityScenario.launch(new Intent(Intent.ACTION_MAIN)
+                        .setClass(RuntimeEnvironment.getApplication(),
+                                SetupInterstitialActivity.class)
+                        .putExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID, MODE_ID));
+        scenario.onActivity(activity -> {
+            // image: 900(w)x1500(h); frame: 600(w)x500(h)
+            // image expected to be scaled down to match the height of the frame -> 300(w)x500(h)
+            when(mDrawable.getIntrinsicWidth()).thenReturn(900);
+            when(mDrawable.getIntrinsicHeight()).thenReturn(1500);
+            when(mFrame.getMeasuredWidth()).thenReturn(600);
+            when(mFrame.getMeasuredHeight()).thenReturn(500);
+
+            ArgumentCaptor<ViewGroup.LayoutParams> captor = ArgumentCaptor.forClass(
+                    ViewGroup.LayoutParams.class);
+            activity.sizeImageToFrame(mImage, mFrame);
+            verify(mImage).setLayoutParams(captor.capture());
+            ViewGroup.LayoutParams out = captor.getValue();
+            assertThat(out.width).isEqualTo(300);
+            assertThat(out.height).isEqualTo(500);
+        });
+        scenario.close();
+    }
+
+    @Test
+    public void setImageToFrame_imageSmallerThanFrame() {
+        ActivityScenario<SetupInterstitialActivity> scenario =
+                ActivityScenario.launch(new Intent(Intent.ACTION_MAIN)
+                        .setClass(RuntimeEnvironment.getApplication(),
+                                SetupInterstitialActivity.class)
+                        .putExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID, MODE_ID));
+        scenario.onActivity(activity -> {
+            // image: 300(w)x200(h); frame: 900(w)x1200(h)
+            // image expected to be scaled up to match the width of the frame -> 900(w)x600(h)
+            when(mDrawable.getIntrinsicWidth()).thenReturn(300);
+            when(mDrawable.getIntrinsicHeight()).thenReturn(200);
+            when(mFrame.getMeasuredWidth()).thenReturn(900);
+            when(mFrame.getMeasuredHeight()).thenReturn(1200);
+
+            ArgumentCaptor<ViewGroup.LayoutParams> captor = ArgumentCaptor.forClass(
+                    ViewGroup.LayoutParams.class);
+            activity.sizeImageToFrame(mImage, mFrame);
+            verify(mImage).setLayoutParams(captor.capture());
+            ViewGroup.LayoutParams out = captor.getValue();
+            assertThat(out.width).isEqualTo(900);
+            assertThat(out.height).isEqualTo(600);
+        });
+        scenario.close();
+    }
+
+    @Test
+    public void setImageToFrame_horizontalImageNarrowerThanFrame() {
+        ActivityScenario<SetupInterstitialActivity> scenario =
+                ActivityScenario.launch(new Intent(Intent.ACTION_MAIN)
+                        .setClass(RuntimeEnvironment.getApplication(),
+                                SetupInterstitialActivity.class)
+                        .putExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID, MODE_ID));
+        scenario.onActivity(activity -> {
+            // image: 600(w)x400(h); frame: 1000(w)x100(h)
+            // both image and frame are wider than tall, but frame is much narrower
+            // so should fit image to height of frame -> 150(w)x100(h)
+            when(mDrawable.getIntrinsicWidth()).thenReturn(600);
+            when(mDrawable.getIntrinsicHeight()).thenReturn(400);
+            when(mFrame.getMeasuredWidth()).thenReturn(1000);
+            when(mFrame.getMeasuredHeight()).thenReturn(100);
+
+            ArgumentCaptor<ViewGroup.LayoutParams> captor = ArgumentCaptor.forClass(
+                    ViewGroup.LayoutParams.class);
+            activity.sizeImageToFrame(mImage, mFrame);
+            verify(mImage).setLayoutParams(captor.capture());
+            ViewGroup.LayoutParams out = captor.getValue();
+            assertThat(out.width).isEqualTo(150);
+            assertThat(out.height).isEqualTo(100);
+        });
+        scenario.close();
+    }
+
+    @Test
+    public void setImageToFrame_accountsForPadding() {
+        ActivityScenario<SetupInterstitialActivity> scenario =
+                ActivityScenario.launch(new Intent(Intent.ACTION_MAIN)
+                        .setClass(RuntimeEnvironment.getApplication(),
+                                SetupInterstitialActivity.class)
+                        .putExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID, MODE_ID));
+        scenario.onActivity(activity -> {
+            // image: 200(w)x300(h); frame: 1000(w)x1000(h), 50 top/bottom padding, 100 l/r padding
+            // effective size of frame is therefore 800(w)x900(h)
+            // scale image to the height of the effective frame -> 600(w)x900(h)
+            when(mDrawable.getIntrinsicWidth()).thenReturn(200);
+            when(mDrawable.getIntrinsicHeight()).thenReturn(300);
+            when(mFrame.getMeasuredWidth()).thenReturn(1000);
+            when(mFrame.getMeasuredHeight()).thenReturn(1000);
+            when(mFrame.getPaddingTop()).thenReturn(50);
+            when(mFrame.getPaddingBottom()).thenReturn(50);
+            when(mFrame.getPaddingLeft()).thenReturn(100);
+            when(mFrame.getPaddingRight()).thenReturn(100);
+
+            ArgumentCaptor<ViewGroup.LayoutParams> captor = ArgumentCaptor.forClass(
+                    ViewGroup.LayoutParams.class);
+            activity.sizeImageToFrame(mImage, mFrame);
+            verify(mImage).setLayoutParams(captor.capture());
+            ViewGroup.LayoutParams out = captor.getValue();
+            assertThat(out.width).isEqualTo(600);
+            assertThat(out.height).isEqualTo(900);
+        });
+        scenario.close();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeFragmentTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeFragmentTest.java
new file mode 100644
index 0000000..576e32a
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeFragmentTest.java
@@ -0,0 +1,125 @@
+/*
+ * 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.notification.modes;
+
+import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.fragment.app.testing.FragmentScenario;
+import androidx.lifecycle.Lifecycle;
+
+import com.android.settingslib.notification.modes.TestModeBuilder;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
+
+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;
+
+@RunWith(RobolectricTestRunner.class)
+public class ZenModeFragmentTest {
+    private static final String MODE_ID = "modeId";
+
+    @Mock
+    private ZenModesBackend mBackend;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        // set up static instance so that the fragment will get a mock version of the backend
+        ZenModesBackend.setInstance(mBackend);
+    }
+
+    // Sets up the scenario's fragment by passing in arguments setting the provided mode ID.
+    // After running this method, users can then use scenario.onFragment(fragment -> {...}) on the
+    // returned scenario to test fragment behavior.
+    private FragmentScenario<ZenModeFragment> setUpScenarioForModeId(String modeId) {
+        Bundle args = new Bundle();
+        args.putString(EXTRA_AUTOMATIC_ZEN_RULE_ID, modeId);
+        return FragmentScenario.launch(
+                ZenModeFragment.class, /* bundle= */ args, 0, Lifecycle.State.INITIALIZED);
+    }
+
+    @Test
+    public void disabledMode_redirectsToInterstitial() {
+        // Mode is disabled, and not by the user
+        ZenMode mode = new TestModeBuilder().setId(MODE_ID).setEnabled(false, false)
+                .build();
+
+        when(mBackend.getMode(MODE_ID)).thenReturn(mode);
+
+        // actually set up fragment for testing
+        FragmentScenario scenario = setUpScenarioForModeId(MODE_ID);
+        scenario.moveToState(Lifecycle.State.STARTED);
+
+        scenario.onFragment(fragment -> {
+            // since the mode is disabled & not by the user, we should go to the next activity
+            Intent nextIntent = shadowOf(fragment.getActivity()).getNextStartedActivity();
+            assertThat(nextIntent).isNotNull();
+            assertThat(nextIntent.getComponent().getClassName()).isEqualTo(
+                    SetupInterstitialActivity.class.getCanonicalName());
+            assertThat(nextIntent.getStringExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID)).isEqualTo(MODE_ID);
+        });
+        scenario.close();
+    }
+
+    @Test
+    public void disabledMode_byUser_noRedirect() {
+        // Mode is disabled by the user
+        ZenMode mode = new TestModeBuilder().setId(MODE_ID).setEnabled(false, true)
+                .build();
+
+        when(mBackend.getMode(MODE_ID)).thenReturn(mode);
+        FragmentScenario scenario = setUpScenarioForModeId(MODE_ID);
+        scenario.moveToState(Lifecycle.State.STARTED);
+
+        scenario.onFragment(fragment -> {
+            // there shouldn't be a next started activity, because we don't redirect
+            Intent nextIntent = shadowOf(fragment.getActivity()).getNextStartedActivity();
+            assertThat(nextIntent).isNull();
+        });
+        scenario.close();
+    }
+
+    @Test
+    public void enabledMode_noRedirect() {
+        // enabled rule
+        ZenMode mode = new TestModeBuilder().setId(MODE_ID).setEnabled(true)
+                .build();
+
+        when(mBackend.getMode(MODE_ID)).thenReturn(mode);
+        FragmentScenario scenario = setUpScenarioForModeId(MODE_ID);
+        scenario.moveToState(Lifecycle.State.STARTED);
+
+        scenario.onFragment(fragment -> {
+            // there shouldn't be a next started activity, because we don't redirect
+            Intent nextIntent = shadowOf(fragment.getActivity()).getNextStartedActivity();
+            assertThat(nextIntent).isNull();
+        });
+        scenario.close();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceControllerTest.java
index 64de141..eb57094 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceControllerTest.java
@@ -19,21 +19,23 @@
 import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_ANYONE;
 import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
 import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_NONE;
-import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_UNSET;
 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE;
 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS;
 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_NONE;
 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED;
-import static android.service.notification.ZenPolicy.PEOPLE_TYPE_UNSET;
-import static android.service.notification.ZenPolicy.STATE_UNSET;
+import static android.service.notification.ZenPolicy.conversationTypeToString;
+import static android.service.notification.ZenPolicy.peopleTypeToString;
 
 import static com.android.settings.notification.modes.ZenModePrioritySendersPreferenceController.KEY_ANY;
+import static com.android.settings.notification.modes.ZenModePrioritySendersPreferenceController.KEY_ANY_CONVERSATIONS;
 import static com.android.settings.notification.modes.ZenModePrioritySendersPreferenceController.KEY_CONTACTS;
-import static com.android.settings.notification.modes.ZenModePrioritySendersPreferenceController.KEY_IMPORTANT;
+import static com.android.settings.notification.modes.ZenModePrioritySendersPreferenceController.KEY_IMPORTANT_CONVERSATIONS;
 import static com.android.settings.notification.modes.ZenModePrioritySendersPreferenceController.KEY_NONE;
 import static com.android.settings.notification.modes.ZenModePrioritySendersPreferenceController.KEY_STARRED;
 
+import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -43,11 +45,14 @@
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.service.notification.ZenPolicy;
+import android.service.notification.ZenPolicy.ConversationSenders;
+import android.service.notification.ZenPolicy.PeopleType;
 
 import androidx.preference.Preference;
 import androidx.preference.PreferenceCategory;
 import androidx.preference.PreferenceManager;
 import androidx.preference.PreferenceScreen;
+import androidx.preference.TwoStatePreference;
 
 import com.android.settingslib.notification.modes.TestModeBuilder;
 import com.android.settingslib.notification.modes.ZenMode;
@@ -66,6 +71,9 @@
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
 @RunWith(RobolectricTestRunner.class)
 @EnableFlags(Flags.FLAG_MODES_UI)
 public final class ZenModePrioritySendersPreferenceControllerTest {
@@ -109,24 +117,30 @@
         when(mHelperBackend.getAllContacts()).thenReturn(
                 ImmutableList.of(new ZenHelperBackend.Contact(1, "The only contact", null)));
         when(mHelperBackend.getAllContactsCount()).thenReturn(1);
+
         when(mHelperBackend.getImportantConversations()).thenReturn(ImmutableList.of());
+        when(mHelperBackend.getAllConversations()).thenReturn(ImmutableList.of());
     }
 
     @Test
     public void testDisplayPreferences_makeMessagesPrefs() {
         mMessagesController.displayPreference(mPreferenceScreen);
 
-        // Starred contacts, Contacts, Priority Conversations, Any, None
-        assertThat(mMessagesPrefCategory.getPreferenceCount()).isEqualTo(5);
+        // "Any Conversations" is invisible by default.
+        assertThat(getAllOptions(mMessagesPrefCategory)).containsExactly(KEY_STARRED, KEY_CONTACTS,
+                KEY_ANY_CONVERSATIONS, KEY_IMPORTANT_CONVERSATIONS, KEY_ANY, KEY_NONE).inOrder();
+        assertThat(getVisibleOptions(mMessagesPrefCategory)).containsExactly(KEY_STARRED,
+                KEY_CONTACTS, KEY_IMPORTANT_CONVERSATIONS, KEY_ANY, KEY_NONE).inOrder();
     }
 
     @Test
     public void testDisplayPreferences_makeCallsPrefs() {
         mCallsController.displayPreference(mPreferenceScreen);
 
-        assertThat(mCallsPrefCategory.getPreferenceCount()).isEqualTo(4);
-        assertThat((Comparable<?>) mCallsPrefCategory.findPreference(KEY_IMPORTANT)).isNull();
-
+        assertThat(getAllOptions(mCallsPrefCategory)).containsExactly(KEY_STARRED, KEY_CONTACTS,
+                KEY_ANY, KEY_NONE).inOrder();
+        assertThat(getVisibleOptions(mCallsPrefCategory)).containsExactly(KEY_STARRED, KEY_CONTACTS,
+                KEY_ANY, KEY_NONE).inOrder();
     }
 
     @Test
@@ -143,287 +157,1198 @@
     }
 
     @Test
-    public void testKeyToSettingEndState_messagesCheck() {
-        int[] endState;
+    public void updateState_callsAny() {
+        ZenMode zenMode = newModeWithPolicy(p -> p.allowCalls(PEOPLE_TYPE_ANYONE));
+        mCallsController.displayPreference(mPreferenceScreen);
 
-        // For KEY_NONE everything should be none.
-        endState = mMessagesController.keyToSettingEndState(KEY_NONE, true);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_NONE);
+        mCallsController.updateZenMode(mCallsPrefCategory, zenMode);
 
-        // For KEY_ANY everything should be allowed.
-        endState = mMessagesController.keyToSettingEndState(KEY_ANY, true);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_ANYONE);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_ANYONE);
-
-        // For [starred] contacts, we should set the priority senders, but not the conversations
-        endState = mMessagesController.keyToSettingEndState(KEY_STARRED, true);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_STARRED);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
-
-        endState = mMessagesController.keyToSettingEndState(KEY_CONTACTS, true);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_CONTACTS);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
-
-        // For priority conversations, we should set the conversations but not priority senders
-        endState = mMessagesController.keyToSettingEndState(KEY_IMPORTANT, true);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_UNSET);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_IMPORTANT);
+        assertThat(getCheckedOptions(mCallsPrefCategory)).containsExactly(KEY_ANY);
     }
 
     @Test
-    public void testKeyToSettingEndState_messagesUncheck() {
-        int[] endState;
+    public void updateState_callsContacts() {
+        ZenMode zenMode = newModeWithPolicy(p -> p.allowCalls(PEOPLE_TYPE_CONTACTS));
+        mCallsController.displayPreference(mPreferenceScreen);
 
-        // For KEY_NONE, "unchecking" still means "none".
-        endState = mMessagesController.keyToSettingEndState(KEY_NONE, false);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_NONE);
+        mCallsController.updateZenMode(mCallsPrefCategory, zenMode);
 
-        // For KEY_ANY unchecking resets the state to "none".
-        endState = mMessagesController.keyToSettingEndState(KEY_ANY, false);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_NONE);
-
-        // For [starred] contacts, we should unset the priority senders, but not the conversations
-        endState = mMessagesController.keyToSettingEndState(KEY_STARRED, false);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
-
-        endState = mMessagesController.keyToSettingEndState(KEY_CONTACTS, false);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
-
-        // For priority conversations, we should set the conversations but not priority senders
-        endState = mMessagesController.keyToSettingEndState(KEY_IMPORTANT, false);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_UNSET);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_NONE);
+        assertThat(getCheckedOptions(mCallsPrefCategory)).containsExactly(KEY_CONTACTS);
     }
 
     @Test
-    public void testKeyToSettingEndState_callsCheck() {
-        int[] endState;
+    public void updateState_callsStarredContacts() {
+        ZenMode zenMode = newModeWithPolicy(p -> p.allowCalls(PEOPLE_TYPE_STARRED));
+        mCallsController.displayPreference(mPreferenceScreen);
 
-        // For calls: we should never set conversations, as this is unrelated to calls.
-        // For KEY_NONE senders should be none.
-        endState = mCallsController.keyToSettingEndState(KEY_NONE, true);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+        mCallsController.updateZenMode(mCallsPrefCategory, zenMode);
 
-        // For KEY_ANY senders should be ANY.
-        endState = mCallsController.keyToSettingEndState(KEY_ANY, true);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_ANYONE);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
-
-        // For [starred] contacts, we should set the priority senders accordingly
-        endState = mCallsController.keyToSettingEndState(KEY_STARRED, true);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_STARRED);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
-
-        endState = mCallsController.keyToSettingEndState(KEY_CONTACTS, true);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_CONTACTS);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+        assertThat(getCheckedOptions(mCallsPrefCategory)).containsExactly(KEY_STARRED);
     }
 
     @Test
-    public void testKeyToSettingEndState_callsUncheck() {
-        int[] endState;
+    public void updateState_callsNone() {
+        ZenMode zenMode = newModeWithPolicy(p -> p.allowCalls(PEOPLE_TYPE_NONE));
+        mCallsController.displayPreference(mPreferenceScreen);
 
-        // A calls setup should never set conversations settings.
-        // For KEY_NONE, "unchecking" still means "none".
-        endState = mCallsController.keyToSettingEndState(KEY_NONE, false);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+        mCallsController.updateZenMode(mCallsPrefCategory, zenMode);
 
-        // For KEY_ANY unchecking resets the state to "none".
-        endState = mCallsController.keyToSettingEndState(KEY_ANY, false);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
-
-        // For [starred] contacts, we should unset the priority senders, but not the conversations
-        endState = mCallsController.keyToSettingEndState(KEY_STARRED, false);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
-
-        endState = mCallsController.keyToSettingEndState(KEY_CONTACTS, false);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+        assertThat(getCheckedOptions(mCallsPrefCategory)).containsExactly(KEY_NONE);
     }
 
     @Test
-    public void testSettingsToSaveOnClick_messagesCheck() {
-        int[] endState;
+    public void updateState_msgAnyConvAny() {
+        ZenMode zenMode = newModeWithPolicy(p ->
+                p.allowMessages(PEOPLE_TYPE_ANYONE)
+                        .allowConversations(CONVERSATION_SENDERS_ANYONE));
 
-        // For KEY_NONE everything should be none.
-        endState = mMessagesController.settingsToSaveOnClick(
-                KEY_NONE, true, PEOPLE_TYPE_ANYONE, CONVERSATION_SENDERS_ANYONE);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_NONE);
+        mMessagesController.displayPreference(mPreferenceScreen);
+        mMessagesController.updateZenMode(mMessagesPrefCategory, zenMode);
 
-        // For KEY_ANY everything should be allowed.
-        endState = mMessagesController.settingsToSaveOnClick(
-                KEY_ANY, true, PEOPLE_TYPE_NONE, CONVERSATION_SENDERS_NONE);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_ANYONE);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_ANYONE);
-
-        // For [starred] contacts, we should set the priority senders, but not the conversations
-        endState =  mMessagesController.settingsToSaveOnClick(
-                KEY_STARRED, true, PEOPLE_TYPE_NONE, CONVERSATION_SENDERS_NONE);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_STARRED);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
-
-        endState = mMessagesController.settingsToSaveOnClick(
-                KEY_CONTACTS, true, PEOPLE_TYPE_NONE, CONVERSATION_SENDERS_NONE);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_CONTACTS);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
-
-        // For priority conversations, we should set the conversations but not priority senders
-        endState = mMessagesController.settingsToSaveOnClick(
-                KEY_IMPORTANT, true, PEOPLE_TYPE_NONE, CONVERSATION_SENDERS_NONE);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_UNSET);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_IMPORTANT);
+        // Messages=ANY shows ANY checked, regardless of conversations value (all conversations are
+        // messages and will get through).
+        assertThat(getVisibleOptions(mMessagesPrefCategory)).containsExactly(
+                KEY_ANY, KEY_STARRED, KEY_CONTACTS, KEY_IMPORTANT_CONVERSATIONS, KEY_NONE);
+        assertThat(getCheckedOptions(mMessagesPrefCategory)).containsExactly(KEY_ANY);
     }
 
     @Test
-    public void testSettingsToSaveOnClick_messagesUncheck() {
-        int[] endState;
+    public void updateState_msgAnyConvImportant() {
+        ZenMode zenMode = newModeWithPolicy(p ->
+                p.allowMessages(PEOPLE_TYPE_ANYONE)
+                        .allowConversations(CONVERSATION_SENDERS_IMPORTANT));
 
-        // For KEY_NONE, "unchecking" still means "none".
-        endState = mMessagesController.settingsToSaveOnClick(
-                KEY_NONE, false, PEOPLE_TYPE_NONE, CONVERSATION_SENDERS_NONE);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_UNSET);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+        mMessagesController.displayPreference(mPreferenceScreen);
+        mMessagesController.updateZenMode(mMessagesPrefCategory, zenMode);
 
-        // For KEY_ANY unchecking resets the state to "none".
-        endState = mMessagesController.settingsToSaveOnClick(
-                KEY_ANY, false, PEOPLE_TYPE_ANYONE, CONVERSATION_SENDERS_ANYONE);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_NONE);
-
-        // For [starred] contacts, we should unset the priority senders, but not the conversations
-        endState = mMessagesController.settingsToSaveOnClick(
-                KEY_STARRED, false, PEOPLE_TYPE_STARRED, CONVERSATION_SENDERS_IMPORTANT);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
-
-        endState = mMessagesController.settingsToSaveOnClick(
-                KEY_CONTACTS, false, PEOPLE_TYPE_CONTACTS, CONVERSATION_SENDERS_IMPORTANT);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
-
-        // For priority conversations, we should set the conversations but not priority senders
-        endState = mMessagesController.settingsToSaveOnClick(
-                KEY_IMPORTANT, false, PEOPLE_TYPE_CONTACTS, CONVERSATION_SENDERS_IMPORTANT);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_UNSET);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_NONE);
+        // Messages=ANY shows ANY checked, regardless of conversations value (all conversations are
+        // messages and will get through).
+        assertThat(getVisibleOptions(mMessagesPrefCategory)).containsExactly(
+                KEY_ANY, KEY_STARRED, KEY_CONTACTS, KEY_IMPORTANT_CONVERSATIONS, KEY_NONE);
+        assertThat(getCheckedOptions(mMessagesPrefCategory)).containsExactly(KEY_ANY);
     }
 
     @Test
-    public void testSettingsToSaveOnClick_callsCheck() {
-        int[] endState;
+    public void updateState_msgAnyConvNone() {
+        ZenMode zenMode = newModeWithPolicy(p ->
+                p.allowMessages(PEOPLE_TYPE_ANYONE)
+                        .allowConversations(CONVERSATION_SENDERS_NONE));
 
-        // For calls: we should never set conversations, as this is unrelated to calls.
-        // For KEY_NONE senders should be none.
-        endState = mCallsController.settingsToSaveOnClick(
-                KEY_NONE, true, PEOPLE_TYPE_CONTACTS, CONVERSATION_SENDERS_IMPORTANT);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+        mMessagesController.displayPreference(mPreferenceScreen);
+        mMessagesController.updateZenMode(mMessagesPrefCategory, zenMode);
 
-        // For KEY_ANY senders should be ANY.
-        endState = mCallsController.settingsToSaveOnClick(
-                KEY_ANY, true, PEOPLE_TYPE_CONTACTS, CONVERSATION_SENDERS_IMPORTANT);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_ANYONE);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
-
-        // For [starred] contacts, we should set the priority senders accordingly
-        endState = mCallsController.settingsToSaveOnClick(
-                KEY_STARRED, true, PEOPLE_TYPE_CONTACTS, CONVERSATION_SENDERS_IMPORTANT);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_STARRED);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
-
-        endState = mCallsController.settingsToSaveOnClick(
-                KEY_CONTACTS, true, PEOPLE_TYPE_STARRED, CONVERSATION_SENDERS_IMPORTANT);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_CONTACTS);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+        // Messages=ANY shows ANY checked, regardless of conversations value (all conversations are
+        // messages and will get through).
+        assertThat(getVisibleOptions(mMessagesPrefCategory)).containsExactly(
+                KEY_ANY, KEY_STARRED, KEY_CONTACTS, KEY_IMPORTANT_CONVERSATIONS, KEY_NONE);
+        assertThat(getCheckedOptions(mMessagesPrefCategory)).containsExactly(KEY_ANY);
     }
 
     @Test
-    public void testSettingsToSaveOnClick_callsUncheck() {
-        int[] endState;
+    public void updateState_msgContactsConvAny() {
+        ZenMode zenMode = newModeWithPolicy(p ->
+                p.allowMessages(PEOPLE_TYPE_CONTACTS)
+                        .allowConversations(CONVERSATION_SENDERS_ANYONE));
 
-        // A calls setup should never set conversations settings.
-        // For KEY_NONE, "unchecking" still means "none".
-        endState = mCallsController.settingsToSaveOnClick(
-                KEY_NONE, false, PEOPLE_TYPE_NONE, CONVERSATION_SENDERS_NONE);
-        assertThat(endState[0]).isEqualTo(STATE_UNSET);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+        mMessagesController.displayPreference(mPreferenceScreen);
+        mMessagesController.updateZenMode(mMessagesPrefCategory, zenMode);
 
-        // For KEY_ANY unchecking resets the state to "none".
-        endState = mCallsController.settingsToSaveOnClick(
-                KEY_ANY, false, PEOPLE_TYPE_ANYONE, CONVERSATION_SENDERS_ANYONE);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
-
-        // For [starred] contacts, we should unset the priority senders, but not the conversations
-        endState = mCallsController.settingsToSaveOnClick(
-                KEY_STARRED, false, PEOPLE_TYPE_STARRED, CONVERSATION_SENDERS_IMPORTANT);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
-
-        endState = mCallsController.settingsToSaveOnClick(
-                KEY_CONTACTS, false, PEOPLE_TYPE_CONTACTS, CONVERSATION_SENDERS_IMPORTANT);
-        assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
-        assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+        // Shows a checked option for conversations=ANY which is normally unavailable.
+        assertThat(getVisibleOptions(mMessagesPrefCategory)).containsExactly(KEY_ANY,
+                KEY_STARRED, KEY_CONTACTS, KEY_ANY_CONVERSATIONS, KEY_IMPORTANT_CONVERSATIONS,
+                KEY_NONE);
+        assertThat(getCheckedOptions(mMessagesPrefCategory)).containsExactly(KEY_CONTACTS,
+                KEY_ANY_CONVERSATIONS);
     }
 
     @Test
-    public void testSettingsToSave_messages_noChange() {
-        int[] savedSettings;
+    public void updateState_msgContactsConvImportant() {
+        ZenMode zenMode = newModeWithPolicy(p ->
+                p.allowMessages(PEOPLE_TYPE_CONTACTS)
+                        .allowConversations(CONVERSATION_SENDERS_IMPORTANT));
 
-        savedSettings = mMessagesController.settingsToSaveOnClick(
-                KEY_NONE, true, PEOPLE_TYPE_NONE, CONVERSATION_SENDERS_NONE);
-        assertThat(savedSettings[0]).isEqualTo(STATE_UNSET);
-        assertThat(savedSettings[1]).isEqualTo(STATE_UNSET);
+        mMessagesController.displayPreference(mPreferenceScreen);
+        mMessagesController.updateZenMode(mMessagesPrefCategory, zenMode);
 
-        savedSettings = mMessagesController.settingsToSaveOnClick(
-                KEY_ANY, true, PEOPLE_TYPE_ANYONE, CONVERSATION_SENDERS_ANYONE);
-        assertThat(savedSettings[0]).isEqualTo(STATE_UNSET);
-        assertThat(savedSettings[1]).isEqualTo(STATE_UNSET);
-
-        savedSettings = mMessagesController.settingsToSaveOnClick(
-                KEY_STARRED, true, PEOPLE_TYPE_STARRED, CONVERSATION_SENDERS_ANYONE);
-        assertThat(savedSettings[0]).isEqualTo(STATE_UNSET);
-
-        savedSettings = mMessagesController.settingsToSaveOnClick(
-                KEY_CONTACTS, true, PEOPLE_TYPE_CONTACTS, CONVERSATION_SENDERS_ANYONE);
-        assertThat(savedSettings[0]).isEqualTo(STATE_UNSET);
-
-        savedSettings = mMessagesController.settingsToSaveOnClick(
-                KEY_IMPORTANT, true, PEOPLE_TYPE_CONTACTS, CONVERSATION_SENDERS_IMPORTANT);
-        assertThat(savedSettings[1]).isEqualTo(STATE_UNSET);
+        // Contacts and important conversations.
+        assertThat(getVisibleOptions(mMessagesPrefCategory)).containsExactly(
+                KEY_ANY, KEY_STARRED, KEY_CONTACTS, KEY_IMPORTANT_CONVERSATIONS, KEY_NONE);
+        assertThat(getCheckedOptions(mMessagesPrefCategory)).containsExactly(KEY_CONTACTS,
+                KEY_IMPORTANT_CONVERSATIONS);
     }
 
     @Test
-    public void testSettingsToSave_calls_noChange() {
-        int[] savedSettings;
+    public void updateState_msgContactsConvNone() {
+        ZenMode zenMode = newModeWithPolicy(p ->
+                p.allowMessages(PEOPLE_TYPE_CONTACTS)
+                        .allowConversations(CONVERSATION_SENDERS_NONE));
 
-        savedSettings = mMessagesController.settingsToSaveOnClick(
-                KEY_NONE, true, PEOPLE_TYPE_NONE, CONVERSATION_SENDERS_NONE);
-        assertThat(savedSettings[0]).isEqualTo(STATE_UNSET);
-        assertThat(savedSettings[1]).isEqualTo(STATE_UNSET);
+        mMessagesController.displayPreference(mPreferenceScreen);
+        mMessagesController.updateZenMode(mMessagesPrefCategory, zenMode);
 
-        savedSettings = mMessagesController.settingsToSaveOnClick(
-                KEY_ANY, true, PEOPLE_TYPE_ANYONE, CONVERSATION_SENDERS_ANYONE);
-        assertThat(savedSettings[0]).isEqualTo(STATE_UNSET);
-        assertThat(savedSettings[1]).isEqualTo(STATE_UNSET);
+        // Just contacts (will include conversations with those contacts but not explicitly chosen).
+        assertThat(getVisibleOptions(mMessagesPrefCategory)).containsExactly(
+                KEY_ANY, KEY_STARRED, KEY_CONTACTS, KEY_IMPORTANT_CONVERSATIONS, KEY_NONE);
+        assertThat(getCheckedOptions(mMessagesPrefCategory)).containsExactly(KEY_CONTACTS);
+    }
 
-        savedSettings = mMessagesController.settingsToSaveOnClick(
-                KEY_STARRED, true, PEOPLE_TYPE_STARRED, CONVERSATION_SENDERS_ANYONE);
-        assertThat(savedSettings[0]).isEqualTo(STATE_UNSET);
+    @Test
+    public void updateState_msgStarredConvAny() {
+        ZenMode zenMode = newModeWithPolicy(p ->
+                p.allowMessages(PEOPLE_TYPE_STARRED)
+                        .allowConversations(CONVERSATION_SENDERS_ANYONE));
 
-        savedSettings = mMessagesController.settingsToSaveOnClick(
-                KEY_CONTACTS, true, PEOPLE_TYPE_CONTACTS, CONVERSATION_SENDERS_ANYONE);
-        assertThat(savedSettings[0]).isEqualTo(STATE_UNSET);
+        mMessagesController.displayPreference(mPreferenceScreen);
+        mMessagesController.updateZenMode(mMessagesPrefCategory, zenMode);
+
+        // Shows a checked option for conversations=ANY which is normally unavailable.
+        assertThat(getVisibleOptions(mMessagesPrefCategory)).containsExactly(KEY_ANY,
+                KEY_STARRED, KEY_CONTACTS, KEY_ANY_CONVERSATIONS, KEY_IMPORTANT_CONVERSATIONS,
+                KEY_NONE);
+        assertThat(getCheckedOptions(mMessagesPrefCategory)).containsExactly(KEY_STARRED,
+                KEY_ANY_CONVERSATIONS);
+    }
+
+    @Test
+    public void updateState_msgStarredConvImportant() {
+        ZenMode zenMode = newModeWithPolicy(p ->
+                p.allowMessages(PEOPLE_TYPE_STARRED)
+                        .allowConversations(CONVERSATION_SENDERS_IMPORTANT));
+
+        mMessagesController.displayPreference(mPreferenceScreen);
+        mMessagesController.updateZenMode(mMessagesPrefCategory, zenMode);
+
+        // Starred contacts and important conversations.
+        assertThat(getVisibleOptions(mMessagesPrefCategory)).containsExactly(
+                KEY_ANY, KEY_STARRED, KEY_CONTACTS, KEY_IMPORTANT_CONVERSATIONS, KEY_NONE);
+        assertThat(getCheckedOptions(mMessagesPrefCategory)).containsExactly(KEY_STARRED,
+                KEY_IMPORTANT_CONVERSATIONS);
+    }
+
+    @Test
+    public void updateState_msgStarredConvNone() {
+        ZenMode zenMode = newModeWithPolicy(p ->
+                p.allowMessages(PEOPLE_TYPE_STARRED)
+                        .allowConversations(CONVERSATION_SENDERS_NONE));
+
+        mMessagesController.displayPreference(mPreferenceScreen);
+        mMessagesController.updateZenMode(mMessagesPrefCategory, zenMode);
+
+        // Just starred contacts (will include conversations with those contacts but not
+        // explicitly chosen).
+        assertThat(getVisibleOptions(mMessagesPrefCategory)).containsExactly(
+                KEY_ANY, KEY_STARRED, KEY_CONTACTS, KEY_IMPORTANT_CONVERSATIONS, KEY_NONE);
+        assertThat(getCheckedOptions(mMessagesPrefCategory)).containsExactly(KEY_STARRED);
+    }
+
+    @Test
+    public void updateState_msgNoneConvAny() {
+        ZenMode zenMode = newModeWithPolicy(p ->
+                p.allowMessages(PEOPLE_TYPE_NONE)
+                        .allowConversations(CONVERSATION_SENDERS_ANYONE));
+
+        mMessagesController.displayPreference(mPreferenceScreen);
+        mMessagesController.updateZenMode(mMessagesPrefCategory, zenMode);
+
+        // Shows a checked option for conversations=ANY which is normally unavailable.
+        // Only option checked (messages=NONE is reserved for none at all).
+        assertThat(getVisibleOptions(mMessagesPrefCategory)).containsExactly(KEY_ANY,
+                KEY_STARRED, KEY_CONTACTS, KEY_ANY_CONVERSATIONS, KEY_IMPORTANT_CONVERSATIONS,
+                KEY_NONE);
+        assertThat(getCheckedOptions(mMessagesPrefCategory)).containsExactly(
+                KEY_ANY_CONVERSATIONS);
+    }
+
+    @Test
+    public void updateState_msgNoneConvImportant() {
+        ZenMode zenMode = newModeWithPolicy(p ->
+                p.allowMessages(PEOPLE_TYPE_NONE)
+                        .allowConversations(CONVERSATION_SENDERS_IMPORTANT));
+
+        mMessagesController.displayPreference(mPreferenceScreen);
+        mMessagesController.updateZenMode(mMessagesPrefCategory, zenMode);
+
+        // Only important conversations (messages=NONE is reserved for none at all).
+        assertThat(getVisibleOptions(mMessagesPrefCategory)).containsExactly(
+                KEY_ANY, KEY_STARRED, KEY_CONTACTS, KEY_IMPORTANT_CONVERSATIONS, KEY_NONE);
+        assertThat(getCheckedOptions(mMessagesPrefCategory)).containsExactly(
+                KEY_IMPORTANT_CONVERSATIONS);
+    }
+
+    @Test
+    public void updateState_msgNoneConvNone() {
+        ZenMode zenMode = newModeWithPolicy(p ->
+                p.allowMessages(PEOPLE_TYPE_NONE)
+                        .allowConversations(CONVERSATION_SENDERS_NONE));
+
+        mMessagesController.displayPreference(mPreferenceScreen);
+        mMessagesController.updateZenMode(mMessagesPrefCategory, zenMode);
+
+        // Just starred contacts (will include conversations with those contacts but not
+        // explicitly chosen).
+        assertThat(getVisibleOptions(mMessagesPrefCategory)).containsExactly(
+                KEY_ANY, KEY_STARRED, KEY_CONTACTS, KEY_IMPORTANT_CONVERSATIONS, KEY_NONE);
+        assertThat(getCheckedOptions(mMessagesPrefCategory)).containsExactly(KEY_NONE);
+    }
+
+    // --------------------------------------------------------------------------
+    // Message checkbox tests, starting with Msg=Any, Conv=Any
+
+    @Test
+    public void checkContacts_fromMsgAnyConvAny_toMsgContactsConvNone() {
+        checkSomeContacts_fromMsgAnyConvAny_toMsgSomeContactsConvNone(KEY_CONTACTS,
+                PEOPLE_TYPE_CONTACTS);
+    }
+
+    @Test
+    public void checkStarred_fromMsgAnyConvAny_toMsgStarredConvNone() {
+        checkSomeContacts_fromMsgAnyConvAny_toMsgSomeContactsConvNone(KEY_STARRED,
+                PEOPLE_TYPE_STARRED);
+    }
+
+    private void checkSomeContacts_fromMsgAnyConvAny_toMsgSomeContactsConvNone(
+            String contactsOptionKey, @PeopleType int toMessageSenders) {
+        setUpMessagesController(p ->
+                p.allowMessages(PEOPLE_TYPE_ANYONE)
+                        .allowConversations(CONVERSATION_SENDERS_ANYONE));
+
+        // Choosing CONTACTS/STARRED will also internally switch conversations to NONE (which is
+        // fine because the user didn't see the old conv=Any, just messages=Anyone).
+        setMessagesOptionChecked(contactsOptionKey, true);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(toMessageSenders);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_NONE);
+    }
+
+    @Test
+    public void checkImportantConv_fromMsgAnyConvAny_toMsgNoneConvImportant() {
+        setUpMessagesController(p ->
+                p.allowMessages(PEOPLE_TYPE_ANYONE)
+                        .allowConversations(CONVERSATION_SENDERS_ANYONE));
+
+        setMessagesOptionChecked(KEY_IMPORTANT_CONVERSATIONS, true);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(PEOPLE_TYPE_NONE);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_IMPORTANT);
+    }
+
+    @Test
+    public void checkAnyConv_fromMsgAnyConvAny_toMsgNoneConvAny() {
+        setUpMessagesController(p ->
+                p.allowMessages(PEOPLE_TYPE_ANYONE)
+                        .allowConversations(CONVERSATION_SENDERS_ANYONE));
+
+        // Normally this option won't be visible, but it could be if the page was launched with
+        // conv=Any previously.
+        setMessagesOptionChecked(KEY_ANY_CONVERSATIONS, true);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(PEOPLE_TYPE_NONE);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_ANYONE);
+    }
+
+    @Test
+    public void uncheckAnyone_fromMsgAnyConvAny_toMsgNoneConvNone() {
+        uncheckAnyone_fromState_toMsgNoneConvNone(PEOPLE_TYPE_ANYONE,
+                CONVERSATION_SENDERS_ANYONE);
+    }
+
+    @Test
+    public void checkNone_fromMsgAnyConvAny_toMsgNoneConvNone() {
+        checkNone_fromState_toMsgNoneConvNone(PEOPLE_TYPE_ANYONE, CONVERSATION_SENDERS_ANYONE);
+    }
+
+    // --------------------------------------------------------------------------
+    // Message checkbox tests, starting with Msg=Any, Conv=Important
+
+    @Test
+    public void checkContacts_fromMsgAnyConvImportant_toMsgContactsConvNone() {
+        checkSomeContacts_fromMsgAnyConvImportant_toMsgSomeContactsConvNone(KEY_CONTACTS,
+                PEOPLE_TYPE_CONTACTS);
+    }
+
+    @Test
+    public void checkStarred_fromMsgAnyConvImportant_toMsgStarredConvNone() {
+        checkSomeContacts_fromMsgAnyConvImportant_toMsgSomeContactsConvNone(KEY_STARRED,
+                PEOPLE_TYPE_STARRED);
+    }
+
+    private void checkSomeContacts_fromMsgAnyConvImportant_toMsgSomeContactsConvNone(
+            String contactsOptionKey, @PeopleType int toMessageSenders) {
+        setUpMessagesController(p ->
+                p.allowMessages(PEOPLE_TYPE_ANYONE)
+                        .allowConversations(CONVERSATION_SENDERS_IMPORTANT));
+
+        // Choosing CONTACTS/STARRED will also internally switch conversations to NONE (which is
+        // fine because the user didn't see the old setting, just messages=Anyone).
+        setMessagesOptionChecked(contactsOptionKey, true);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(toMessageSenders);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_NONE);
+    }
+
+    @Test
+    public void checkImportantConv_fromMsgAnyConvImportant_toMsgNoneConvImportant() {
+        setUpMessagesController(p ->
+                p.allowMessages(PEOPLE_TYPE_ANYONE)
+                        .allowConversations(CONVERSATION_SENDERS_IMPORTANT));
+
+        // Although conv=IMPORTANT previously, we show it as Anyone, so selecting important
+        // conversations should switch to important conversations only.
+        setMessagesOptionChecked(KEY_IMPORTANT_CONVERSATIONS, true);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(PEOPLE_TYPE_NONE);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_IMPORTANT);
+    }
+
+    @Test
+    public void checkAnyConv_fromMsgAnyConvImportant_toMsgNoneConvAny() {
+        setUpMessagesController(p ->
+                p.allowMessages(PEOPLE_TYPE_ANYONE)
+                        .allowConversations(CONVERSATION_SENDERS_ANYONE));
+
+        // Normally this option won't be visible, but it could be if the page was launched with
+        // conv=Any previously.
+        setMessagesOptionChecked(KEY_ANY_CONVERSATIONS, true);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(PEOPLE_TYPE_NONE);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_ANYONE);
+    }
+
+    @Test
+    public void uncheckAnyone_fromMsgAnyConvImportant_toMsgNoneConvNone() {
+        uncheckAnyone_fromState_toMsgNoneConvNone(PEOPLE_TYPE_ANYONE,
+                CONVERSATION_SENDERS_IMPORTANT);
+    }
+
+    @Test
+    public void checkNone_fromMsgAnyConvImportant_toMsgNoneConvNone() {
+        checkNone_fromState_toMsgNoneConvNone(PEOPLE_TYPE_ANYONE,
+                CONVERSATION_SENDERS_IMPORTANT);
+    }
+
+    // --------------------------------------------------------------------------
+    // Message checkbox tests, starting with Msg=Any, Conv=None
+
+    @Test
+    public void checkContacts_fromMsgAnyConvNone_toMsgContactsConvNone() {
+        checkSomeContacts_fromMsgAnyConvNone_toMsgSomeContactsConvNone(KEY_CONTACTS,
+                PEOPLE_TYPE_CONTACTS);
+    }
+
+    @Test
+    public void checkStarred_fromMsgAnyConvNone_toMsgStarredConvNone() {
+        checkSomeContacts_fromMsgAnyConvNone_toMsgSomeContactsConvNone(KEY_STARRED,
+                PEOPLE_TYPE_STARRED);
+    }
+
+    private void checkSomeContacts_fromMsgAnyConvNone_toMsgSomeContactsConvNone(
+            String contactsOptionKey, @PeopleType int toMessageSenders) {
+        setUpMessagesController(p ->
+                p.allowMessages(PEOPLE_TYPE_ANYONE)
+                        .allowConversations(CONVERSATION_SENDERS_NONE));
+
+        // Choosing CONTACTS/STARRED will also internally switch conversations to NONE (which is
+        // fine because the user didn't see the old setting, just messages=Anyone).
+        setMessagesOptionChecked(contactsOptionKey, true);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(toMessageSenders);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_NONE);
+    }
+
+    @Test
+    public void checkImportantConv_fromMsgAnyConvNone_toMsgNoneConvImportant() {
+        setUpMessagesController(p ->
+                p.allowMessages(PEOPLE_TYPE_ANYONE)
+                        .allowConversations(CONVERSATION_SENDERS_NONE));
+
+        setMessagesOptionChecked(KEY_IMPORTANT_CONVERSATIONS, true);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(PEOPLE_TYPE_NONE);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_IMPORTANT);
+    }
+
+    @Test
+    public void checkAnyConv_fromMsgAnyConvNone_toMsgNoneConvAny() {
+        setUpMessagesController(p ->
+                p.allowMessages(PEOPLE_TYPE_ANYONE)
+                        .allowConversations(CONVERSATION_SENDERS_NONE));
+
+        // Normally this option won't be visible, but it could be if the page was launched with
+        // conv=Any previously.
+        setMessagesOptionChecked(KEY_ANY_CONVERSATIONS, true);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(PEOPLE_TYPE_NONE);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_ANYONE);
+    }
+
+    @Test
+    public void uncheckAnyone_fromMsgAnyConvNone_toMsgNoneConvNone() {
+        uncheckAnyone_fromState_toMsgNoneConvNone(PEOPLE_TYPE_ANYONE,
+                CONVERSATION_SENDERS_NONE);
+    }
+
+    @Test
+    public void checkNone_fromMsgAnyConvNone_toMsgNoneConvNone() {
+        checkNone_fromState_toMsgNoneConvNone(PEOPLE_TYPE_ANYONE,
+                CONVERSATION_SENDERS_NONE);
+    }
+
+    // --------------------------------------------------------------------------
+    // Message checkbox tests, starting with Msg=Contacts OR Starred, Conv=Any
+
+    @Test
+    public void switchContacts_fromMsgStarredConvAny_toMsgContactsConvAny() {
+        switchContacts_fromMsgSomeContactsConvAny_toMsgOtherContactsConvAny(PEOPLE_TYPE_STARRED,
+                KEY_CONTACTS, PEOPLE_TYPE_CONTACTS);
+    }
+
+    @Test
+    public void switchContacts_fromMsgContactsConvAny_toMsgStarredConvAny() {
+        switchContacts_fromMsgSomeContactsConvAny_toMsgOtherContactsConvAny(PEOPLE_TYPE_CONTACTS,
+                KEY_STARRED, PEOPLE_TYPE_STARRED);
+    }
+
+    private void switchContacts_fromMsgSomeContactsConvAny_toMsgOtherContactsConvAny(
+            @PeopleType int fromMessageSenders, String checkingContactsOptionKey,
+            @PeopleType int toMessageSenders) {
+        setUpMessagesController(p ->
+                p.allowMessages(fromMessageSenders)
+                        .allowConversations(CONVERSATION_SENDERS_ANYONE));
+
+        // Switching CONTACTS/STARRED or vice-versa will leave conversations untouched.
+        setMessagesOptionChecked(checkingContactsOptionKey, true);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(toMessageSenders);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_ANYONE);
+    }
+
+    @Test
+    public void uncheckStarred_fromMsgStarredConvAny_toMsgNoneConvAny() {
+        uncheckSomeContacts_fromMsgSomeContactsConvAny_toMsgNoneConvAny(PEOPLE_TYPE_STARRED,
+                KEY_STARRED);
+    }
+
+    @Test
+    public void uncheckContacts_fromMsgContactsConvAny_toMsgNoneConvAny() {
+        uncheckSomeContacts_fromMsgSomeContactsConvAny_toMsgNoneConvAny(
+                PEOPLE_TYPE_CONTACTS, KEY_CONTACTS);
+    }
+
+    private void uncheckSomeContacts_fromMsgSomeContactsConvAny_toMsgNoneConvAny(
+            @PeopleType int fromMessageSenders, String checkingContactsOptionKey) {
+        setUpMessagesController(p ->
+                p.allowMessages(fromMessageSenders)
+                        .allowConversations(CONVERSATION_SENDERS_ANYONE));
+
+        // Unchecking CONTACTS or STARRED will leave conversations untouched.
+        setMessagesOptionChecked(checkingContactsOptionKey, false);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(PEOPLE_TYPE_NONE);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_ANYONE);
+    }
+
+    @Test
+    public void checkImportantConv_fromMsgStarredConvAny_toMsgStarredConvImportant() {
+        checkImportantConv_fromMsgSomeContactsConvAny_toMsgSomeContactsConvImportant(
+                PEOPLE_TYPE_STARRED);
+    }
+
+    @Test
+    public void checkImportantConv_fromMsgContactsConvAny_toMsgContactsConvImportant() {
+        checkImportantConv_fromMsgSomeContactsConvAny_toMsgSomeContactsConvImportant(
+                PEOPLE_TYPE_CONTACTS);
+    }
+
+    private void checkImportantConv_fromMsgSomeContactsConvAny_toMsgSomeContactsConvImportant(
+            @PeopleType int fromMessageSenders) {
+        setUpMessagesController(p ->
+                p.allowMessages(fromMessageSenders)
+                        .allowConversations(CONVERSATION_SENDERS_ANYONE));
+
+        // Choosing important conversations leaves contacts untouched.
+        setMessagesOptionChecked(KEY_IMPORTANT_CONVERSATIONS, true);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(fromMessageSenders);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_IMPORTANT);
+    }
+
+    @Test
+    public void uncheckAnyConv_fromMsgStarredConvAny_toMsgStarredConvNone() {
+        uncheckAnyConv_fromMsgSomeContactsConvAny_toMsgSomeContactsConvNone(
+                PEOPLE_TYPE_STARRED);
+    }
+
+    @Test
+    public void uncheckAnyConv_fromMsgContactsConvAny_toMsgContactsConvNone() {
+        uncheckAnyConv_fromMsgSomeContactsConvAny_toMsgSomeContactsConvNone(
+                PEOPLE_TYPE_CONTACTS);
+    }
+
+    private void uncheckAnyConv_fromMsgSomeContactsConvAny_toMsgSomeContactsConvNone(
+            @PeopleType int fromMessageSenders) {
+        setUpMessagesController(p ->
+                p.allowMessages(fromMessageSenders)
+                        .allowConversations(CONVERSATION_SENDERS_ANYONE));
+
+        // Unmarking any conversation leaves contacts untouched.
+        setMessagesOptionChecked(KEY_ANY_CONVERSATIONS, false);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(fromMessageSenders);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_NONE);
+    }
+
+    @Test
+    public void checkAnyone_fromMsgStarredConvAny_toMsgAnyConvNone() {
+        checkAnyone_fromState_toMsgAnyConvAny(PEOPLE_TYPE_STARRED,
+                CONVERSATION_SENDERS_ANYONE);
+    }
+
+    @Test
+    public void checkAnyone_fromMsgContactsConvAny_toMsgAnyConvNone() {
+        checkAnyone_fromState_toMsgAnyConvAny(PEOPLE_TYPE_CONTACTS,
+                CONVERSATION_SENDERS_ANYONE);
+    }
+
+    @Test
+    public void checkNone_fromMsgStarredConvAny_toMsgNoneConvNone() {
+        checkNone_fromState_toMsgNoneConvNone(PEOPLE_TYPE_STARRED, CONVERSATION_SENDERS_ANYONE);
+    }
+
+    @Test
+    public void checkNone_fromMsgContactsConvAny_toMsgNoneConvNone() {
+        checkNone_fromState_toMsgNoneConvNone(PEOPLE_TYPE_CONTACTS, CONVERSATION_SENDERS_ANYONE);
+    }
+
+    // --------------------------------------------------------------------------
+    // Message checkbox tests, starting with Msg=Contacts OR Starred, Conv=Important
+
+    @Test
+    public void switchContacts_fromMsgStarredConvImportant_toMsgContactsConvImportant() {
+        switchContacts_fromMsgSomeContactsConvImportant_toMsgOtherContactsConvImportant(
+                PEOPLE_TYPE_CONTACTS, KEY_STARRED, PEOPLE_TYPE_STARRED);
+    }
+
+    @Test
+    public void switchContacts_fromMsgContactsConvImportant_toMsgStarredConvImportant() {
+        switchContacts_fromMsgSomeContactsConvImportant_toMsgOtherContactsConvImportant(
+                PEOPLE_TYPE_STARRED, KEY_CONTACTS, PEOPLE_TYPE_CONTACTS);
+    }
+
+    private void switchContacts_fromMsgSomeContactsConvImportant_toMsgOtherContactsConvImportant(
+            @PeopleType int fromMessageSenders, String checkingContactsOptionKey,
+            @PeopleType int toMessageSenders) {
+        setUpMessagesController(p ->
+                p.allowMessages(fromMessageSenders)
+                        .allowConversations(CONVERSATION_SENDERS_IMPORTANT));
+
+        // Switching CONTACTS/STARRED or vice-versa will leave conversations untouched.
+        setMessagesOptionChecked(checkingContactsOptionKey, true);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(toMessageSenders);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_IMPORTANT);
+    }
+
+    @Test
+    public void uncheckStarred_fromMsgStarredConvImportant_toMsgNoneConvImportant() {
+        uncheckSomeContacts_fromMsgSomeContactsConvImportant_toMsgNoneConvImportant(
+                PEOPLE_TYPE_STARRED, KEY_STARRED);
+    }
+
+    @Test
+    public void uncheckContacts_fromMsgContactsConvImportant_toMsgNoneConvImportant() {
+        uncheckSomeContacts_fromMsgSomeContactsConvImportant_toMsgNoneConvImportant(
+                PEOPLE_TYPE_CONTACTS, KEY_CONTACTS);
+    }
+
+    private void uncheckSomeContacts_fromMsgSomeContactsConvImportant_toMsgNoneConvImportant(
+            @PeopleType int fromMessageSenders, String checkingContactsOptionKey) {
+        setUpMessagesController(p ->
+                p.allowMessages(fromMessageSenders)
+                        .allowConversations(CONVERSATION_SENDERS_IMPORTANT));
+
+        // Unchecking CONTACTS or STARRED will leave conversations untouched.
+        setMessagesOptionChecked(checkingContactsOptionKey, false);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(PEOPLE_TYPE_NONE);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_IMPORTANT);
+    }
+
+    @Test
+    public void uncheckImportantConv_fromMsgStarredConvImportant_toMsgStarredConvNone() {
+        uncheckImportantConv_fromMsgSomeContactsConvImportant_toMsgSomeContactsConvNone(
+                PEOPLE_TYPE_STARRED);
+    }
+
+    @Test
+    public void uncheckImportantConv_fromMsgContactsConvImportant_toMsgContactsConvNone() {
+        uncheckImportantConv_fromMsgSomeContactsConvImportant_toMsgSomeContactsConvNone(
+                PEOPLE_TYPE_CONTACTS);
+    }
+
+    private void uncheckImportantConv_fromMsgSomeContactsConvImportant_toMsgSomeContactsConvNone(
+            @PeopleType int fromMessageSenders) {
+        setUpMessagesController(p ->
+                p.allowMessages(fromMessageSenders)
+                        .allowConversations(CONVERSATION_SENDERS_IMPORTANT));
+
+        // Deselecting important conversations leaves contacts untouched.
+        setMessagesOptionChecked(KEY_IMPORTANT_CONVERSATIONS, false);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(fromMessageSenders);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_NONE);
+    }
+
+    @Test
+    public void checkAnyConv_fromMsgStarredConvImportant_toMsgStarredConvAny() {
+        checkAnyConv_fromMsgSomeContactsConvImportant_toMsgSomeContactsConvAny(
+                PEOPLE_TYPE_STARRED);
+    }
+
+    @Test
+    public void checkAnyConv_fromMsgContactsConvImportant_toMsgContactsConvAny() {
+        checkAnyConv_fromMsgSomeContactsConvImportant_toMsgSomeContactsConvAny(
+                PEOPLE_TYPE_CONTACTS);
+    }
+
+    private void checkAnyConv_fromMsgSomeContactsConvImportant_toMsgSomeContactsConvAny(
+            @PeopleType int fromMessageSenders) {
+        setUpMessagesController(p ->
+                p.allowMessages(fromMessageSenders)
+                        .allowConversations(CONVERSATION_SENDERS_IMPORTANT));
+
+        // Selecting any conversations leaves contacts untouched.
+        setMessagesOptionChecked(KEY_ANY_CONVERSATIONS, true);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(fromMessageSenders);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_ANYONE);
+    }
+
+    @Test
+    public void checkAnyone_fromMsgStarredConvImportant_toMsgAnyConvNone() {
+        checkAnyone_fromState_toMsgAnyConvAny(PEOPLE_TYPE_STARRED,
+                CONVERSATION_SENDERS_IMPORTANT);
+    }
+
+    @Test
+    public void checkAnyone_fromMsgContactsConvImportant_toMsgAnyConvNone() {
+        checkAnyone_fromState_toMsgAnyConvAny(PEOPLE_TYPE_CONTACTS,
+                CONVERSATION_SENDERS_IMPORTANT);
+    }
+
+    @Test
+    public void checkNone_fromMsgStarredConvImportant_toMsgNoneConvNone() {
+        checkNone_fromState_toMsgNoneConvNone(PEOPLE_TYPE_STARRED,
+                CONVERSATION_SENDERS_IMPORTANT);
+    }
+
+    @Test
+    public void checkNone_fromMsgContactsConvImportant_toMsgNoneConvNone() {
+        checkNone_fromState_toMsgNoneConvNone(PEOPLE_TYPE_CONTACTS,
+                CONVERSATION_SENDERS_IMPORTANT);
+    }
+
+    // --------------------------------------------------------------------------
+    // Message checkbox tests, starting with Msg=Contacts OR Starred, Conv=None
+
+    @Test
+    public void switchContacts_fromMsgStarredConvNone_toMsgContactsConvNone() {
+        switchContacts_fromMsgSomeContactsConvNone_toMsgSomeContactsConvNone(PEOPLE_TYPE_CONTACTS,
+                KEY_STARRED, PEOPLE_TYPE_STARRED);
+    }
+
+    @Test
+    public void switchContacts_fromMsgContactsConvNone_toMsgStarredConvNone() {
+        switchContacts_fromMsgSomeContactsConvNone_toMsgSomeContactsConvNone(PEOPLE_TYPE_STARRED,
+                KEY_CONTACTS, PEOPLE_TYPE_CONTACTS);
+    }
+
+    private void switchContacts_fromMsgSomeContactsConvNone_toMsgSomeContactsConvNone(
+            @PeopleType int fromMessageSenders, String checkingContactsOptionKey,
+            @PeopleType int toMessageSenders) {
+        setUpMessagesController(p ->
+                p.allowMessages(fromMessageSenders)
+                        .allowConversations(CONVERSATION_SENDERS_NONE));
+
+        // Switching CONTACTS/STARRED or vice-versa will leave conversations untouched.
+        setMessagesOptionChecked(checkingContactsOptionKey, true);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(toMessageSenders);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_NONE);
+    }
+
+    @Test
+    public void uncheckStarred_fromMsgStarredConvNone_toMsgNoneConvNone() {
+        uncheckSomeContacts_fromMsgSomeContactsConvNone_toMsgNoneConvNone(
+                PEOPLE_TYPE_STARRED, KEY_STARRED);
+    }
+
+    @Test
+    public void uncheckContacts_fromMsgContactsConvNone_toMsgNoneConvNone() {
+        uncheckSomeContacts_fromMsgSomeContactsConvNone_toMsgNoneConvNone(
+                PEOPLE_TYPE_CONTACTS, KEY_CONTACTS);
+    }
+
+    private void uncheckSomeContacts_fromMsgSomeContactsConvNone_toMsgNoneConvNone(
+            @PeopleType int fromMessageSenders, String checkingContactsOptionKey) {
+        setUpMessagesController(p ->
+                p.allowMessages(fromMessageSenders)
+                        .allowConversations(CONVERSATION_SENDERS_NONE));
+
+        // Unchecking CONTACTS or STARRED will leave conversations untouched.
+        setMessagesOptionChecked(checkingContactsOptionKey, false);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(PEOPLE_TYPE_NONE);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_NONE);
+    }
+
+    @Test
+    public void checkImportantConv_fromMsgStarredConvNone_toMsgSomeContactsConvImportant() {
+        checkImportantConv_fromMsgSomeContactsConvNone_toMsgSomeContactsConvImportant(
+                PEOPLE_TYPE_STARRED);
+    }
+
+    @Test
+    public void checkImportantConv_fromMsgContactsConvNone_toMsgSomeContactsConvImportant() {
+        checkImportantConv_fromMsgSomeContactsConvNone_toMsgSomeContactsConvImportant(
+                PEOPLE_TYPE_CONTACTS);
+    }
+
+    private void checkImportantConv_fromMsgSomeContactsConvNone_toMsgSomeContactsConvImportant(
+            @PeopleType int fromMessageSenders) {
+        setUpMessagesController(p ->
+                p.allowMessages(fromMessageSenders)
+                        .allowConversations(CONVERSATION_SENDERS_NONE));
+
+        // Deselecting important conversations leaves contacts untouched.
+        setMessagesOptionChecked(KEY_IMPORTANT_CONVERSATIONS, true);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(fromMessageSenders);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_IMPORTANT);
+    }
+
+    @Test
+    public void checkAnyConv_fromMsgStarredConvNone_toMsgStarredConvAny() {
+        checkAnyConv_fromMsgSomeContactsConvNone_toMsgSomeContactsConvAny(PEOPLE_TYPE_STARRED);
+    }
+
+    @Test
+    public void checkAnyConv_fromMsgContactsConvNone_toMsgContactsConvAny() {
+        checkAnyConv_fromMsgSomeContactsConvNone_toMsgSomeContactsConvAny(PEOPLE_TYPE_CONTACTS);
+    }
+
+    private void checkAnyConv_fromMsgSomeContactsConvNone_toMsgSomeContactsConvAny(
+            @PeopleType int fromMessageSenders) {
+        setUpMessagesController(p ->
+                p.allowMessages(fromMessageSenders)
+                        .allowConversations(CONVERSATION_SENDERS_IMPORTANT));
+
+        // Selecting any conversations leaves contacts untouched.
+        setMessagesOptionChecked(KEY_ANY_CONVERSATIONS, true);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(fromMessageSenders);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_ANYONE);
+    }
+
+    @Test
+    public void checkAnyone_fromMsgStarredConvNone_toMsgAnyConvNone() {
+        checkAnyone_fromState_toMsgAnyConvAny(PEOPLE_TYPE_STARRED,
+                CONVERSATION_SENDERS_IMPORTANT);
+    }
+
+    @Test
+    public void checkAnyone_fromMsgContactsConvNone_toMsgAnyConvNone() {
+        checkAnyone_fromState_toMsgAnyConvAny(PEOPLE_TYPE_CONTACTS,
+                CONVERSATION_SENDERS_IMPORTANT);
+    }
+
+    @Test
+    public void checkNone_fromMsgStarredConvNone_toMsgNoneConvNone() {
+        checkNone_fromState_toMsgNoneConvNone(PEOPLE_TYPE_STARRED,
+                CONVERSATION_SENDERS_NONE);
+    }
+
+    @Test
+    public void checkNone_fromMsgContactsConvNone_toMsgNoneConvNone() {
+        checkNone_fromState_toMsgNoneConvNone(PEOPLE_TYPE_CONTACTS,
+                CONVERSATION_SENDERS_NONE);
+    }
+
+    // --------------------------------------------------------------------------
+    // Message checkbox tests, starting with Msg=None, Conv=Any
+
+    @Test
+    public void checkContacts_fromMsgNoneConvAny_toMsgContactsConvAny() {
+        checkSomeContacts_fromMsgNoneConvAny_toMsgSomeContactsConvAny(KEY_CONTACTS,
+                PEOPLE_TYPE_CONTACTS);
+    }
+
+    @Test
+    public void checkStarred_fromMsgNoneConvAny_toMsgStarredConvAny() {
+        checkSomeContacts_fromMsgNoneConvAny_toMsgSomeContactsConvAny(KEY_STARRED,
+                PEOPLE_TYPE_STARRED);
+    }
+
+    private void checkSomeContacts_fromMsgNoneConvAny_toMsgSomeContactsConvAny(
+            String contactsOptionKey, @PeopleType int toMessageSenders) {
+        setUpMessagesController(p ->
+                p.allowMessages(PEOPLE_TYPE_NONE)
+                        .allowConversations(CONVERSATION_SENDERS_ANYONE));
+
+        // Adding CONTACTS/STARRED will leave conversations untouched.
+        setMessagesOptionChecked(contactsOptionKey, true);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(toMessageSenders);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_ANYONE);
+    }
+
+    @Test
+    public void checkImportantConv_fromMsgNoneConvAny_toMsgNoneConvImportant() {
+        setUpMessagesController(p ->
+                p.allowMessages(PEOPLE_TYPE_NONE)
+                        .allowConversations(CONVERSATION_SENDERS_ANYONE));
+
+        setMessagesOptionChecked(KEY_IMPORTANT_CONVERSATIONS, true);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(PEOPLE_TYPE_NONE);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_IMPORTANT);
+    }
+
+    @Test
+    public void uncheckAnyConv_fromMsgNoneConvAny_toMsgNoneConvNone() {
+        setUpMessagesController(p ->
+                p.allowMessages(PEOPLE_TYPE_NONE)
+                        .allowConversations(CONVERSATION_SENDERS_ANYONE));
+
+        setMessagesOptionChecked(KEY_ANY_CONVERSATIONS, false);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(PEOPLE_TYPE_NONE);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_NONE);
+    }
+
+    @Test
+    public void checkAnyone_fromMsgNoneConvAny_toMsgAnyConvNone() {
+        checkAnyone_fromState_toMsgAnyConvAny(PEOPLE_TYPE_NONE,
+                CONVERSATION_SENDERS_ANYONE);
+    }
+
+    @Test
+    public void checkNone_fromMsgNoneConvAny_toMsgNoneConvNone() {
+        checkNone_fromState_toMsgNoneConvNone(PEOPLE_TYPE_NONE,
+                CONVERSATION_SENDERS_ANYONE);
+    }
+
+    // --------------------------------------------------------------------------
+    // Message checkbox tests, starting with Msg=None, Conv=Important
+
+    @Test
+    public void checkContacts_fromMsgNoneConvImportant_toMsgContactsConvImportant() {
+        checkSomeContacts_fromMsgNoneConvImportant_toMsgSomeContactsConvImportant(KEY_CONTACTS,
+                PEOPLE_TYPE_CONTACTS);
+    }
+
+    @Test
+    public void checkStarred_fromMsgNoneConvImportant_toMsgStarredConvImportant() {
+        checkSomeContacts_fromMsgNoneConvImportant_toMsgSomeContactsConvImportant(KEY_STARRED,
+                PEOPLE_TYPE_STARRED);
+    }
+
+    private void checkSomeContacts_fromMsgNoneConvImportant_toMsgSomeContactsConvImportant(
+            String contactsOptionKey, @PeopleType int toMessageSenders) {
+        setUpMessagesController(p ->
+                p.allowMessages(PEOPLE_TYPE_NONE)
+                        .allowConversations(CONVERSATION_SENDERS_IMPORTANT));
+
+        // Adding CONTACTS/STARRED will leave conversations untouched.
+        setMessagesOptionChecked(contactsOptionKey, true);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(toMessageSenders);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_IMPORTANT);
+    }
+
+    @Test
+    public void uncheckImportantConv_fromMsgNoneConvImportant_toMsgNoneConvNone() {
+        setUpMessagesController(p ->
+                p.allowMessages(PEOPLE_TYPE_NONE)
+                        .allowConversations(CONVERSATION_SENDERS_IMPORTANT));
+
+        setMessagesOptionChecked(KEY_IMPORTANT_CONVERSATIONS, false);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(PEOPLE_TYPE_NONE);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_NONE);
+    }
+
+    @Test
+    public void checkAnyConv_fromMsgNoneConvImportant_toMsgNoneConvAny() {
+        setUpMessagesController(p ->
+                p.allowMessages(PEOPLE_TYPE_NONE)
+                        .allowConversations(CONVERSATION_SENDERS_IMPORTANT));
+
+        // Normally this option won't be visible, but it could be if the page was launched with
+        // conv=Any previously.
+        setMessagesOptionChecked(KEY_ANY_CONVERSATIONS, true);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(PEOPLE_TYPE_NONE);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_ANYONE);
+    }
+
+    @Test
+    public void checkAnyone_fromMsgNoneConvImportant_toMsgAnyConvNone() {
+        checkAnyone_fromState_toMsgAnyConvAny(PEOPLE_TYPE_NONE,
+                CONVERSATION_SENDERS_IMPORTANT);
+    }
+
+    @Test
+    public void checkNone_fromMsgNoneConvImportant_toMsgNoneConvNone() {
+        checkNone_fromState_toMsgNoneConvNone(PEOPLE_TYPE_NONE,
+                CONVERSATION_SENDERS_IMPORTANT);
+    }
+
+    // --------------------------------------------------------------------------
+    // Message checkbox tests, starting with Msg=None, Conv=None
+
+    @Test
+    public void checkContacts_fromMsgNoneConvNone_toMsgContactsConvNone() {
+        checkSomeContacts_fromMsgNoneConvNone_toMsgSomeContactsConvNone(KEY_CONTACTS,
+                PEOPLE_TYPE_CONTACTS);
+    }
+
+    @Test
+    public void checkStarred_fromMsgNoneConvNone_toMsgStarredConvNone() {
+        checkSomeContacts_fromMsgNoneConvNone_toMsgSomeContactsConvNone(KEY_STARRED,
+                PEOPLE_TYPE_STARRED);
+    }
+
+    private void checkSomeContacts_fromMsgNoneConvNone_toMsgSomeContactsConvNone(
+            String contactsOptionKey, @PeopleType int toMessageSenders) {
+        setUpMessagesController(p ->
+                p.allowMessages(PEOPLE_TYPE_NONE)
+                        .allowConversations(CONVERSATION_SENDERS_NONE));
+
+        // Choosing CONTACTS/STARRED will leave conversations untouched.
+        setMessagesOptionChecked(contactsOptionKey, true);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(toMessageSenders);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_NONE);
+    }
+
+    @Test
+    public void checkImportantConv_fromMsgNoneConvNone_toMsgNoneConvImportant() {
+        setUpMessagesController(p ->
+                p.allowMessages(PEOPLE_TYPE_NONE)
+                        .allowConversations(CONVERSATION_SENDERS_NONE));
+
+        setMessagesOptionChecked(KEY_IMPORTANT_CONVERSATIONS, true);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(PEOPLE_TYPE_NONE);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_IMPORTANT);
+    }
+
+    @Test
+    public void checkAnyConv_fromMsgNoneConvNone_toMsgNoneConvAny() {
+        setUpMessagesController(p ->
+                p.allowMessages(PEOPLE_TYPE_NONE)
+                        .allowConversations(CONVERSATION_SENDERS_NONE));
+
+        // Normally this option won't be visible, but it could be if the page was launched with
+        // conv=Any previously.
+        setMessagesOptionChecked(KEY_ANY_CONVERSATIONS, true);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(PEOPLE_TYPE_NONE);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_ANYONE);
+    }
+
+    @Test
+    public void checkAnyone_fromMsgNoneConvNone_toMsgAnyConvNone() {
+        checkAnyone_fromState_toMsgAnyConvAny(PEOPLE_TYPE_NONE,
+                CONVERSATION_SENDERS_NONE);
+    }
+
+    @Test
+    public void uncheckNone_fromMsgNoneConvNone_noChanges() {
+        setUpMessagesController(p ->
+                p.allowMessages(PEOPLE_TYPE_NONE)
+                        .allowConversations(CONVERSATION_SENDERS_NONE));
+
+        setMessagesOptionChecked(KEY_NONE, false);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertThat(savedPolicy.getPriorityMessageSenders()).isEqualTo(PEOPLE_TYPE_NONE);
+        assertThat(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_NONE);
+    }
+
+    // --------------------------------------------------------------------------
+    // Message checkbox tests, common cases.
+
+    private void checkAnyone_fromState_toMsgAnyConvAny(@PeopleType int fromMsg,
+            @ConversationSenders int fromConv) {
+        setUpMessagesController(p ->
+                p.allowMessages(fromMsg).allowConversations(fromConv));
+        String context = "Trying to check Anyone; starting with Msg=" + peopleTypeToString(fromMsg)
+                + ", Conv=" + conversationTypeToString(fromConv);
+
+        // Checking ANY will always unselect everything else in the UI, no matter the initial state,
+        // but will save PEOPLE_ANY and CONVERSATIONS_ANY (which is redundant but equivalent).
+        setMessagesOptionChecked(KEY_ANY, true);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertWithMessage(context).that(savedPolicy.getPriorityMessageSenders()).isEqualTo(
+                PEOPLE_TYPE_ANYONE);
+        assertWithMessage(context).that(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_ANYONE);
+    }
+
+    private void uncheckAnyone_fromState_toMsgNoneConvNone(@PeopleType int fromMsg,
+            @ConversationSenders int fromConv) {
+        setUpMessagesController(p ->
+                p.allowMessages(fromMsg).allowConversations(fromConv));
+        String context = "Trying to uncheck Anyone; starting with Msg=" + peopleTypeToString(
+                fromMsg) + ", Conv=" + conversationTypeToString(fromConv);
+
+        // Unchecking ANYONE means NONE to both, even if Anyone was previously Msg=Any&Conv=Any or
+        // Msg=Any&Conv=Important.
+        setMessagesOptionChecked(KEY_ANY, false);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertWithMessage(context).that(savedPolicy.getPriorityMessageSenders()).isEqualTo(
+                PEOPLE_TYPE_NONE);
+        assertWithMessage(context).that(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_NONE);
+    }
+
+    private void checkNone_fromState_toMsgNoneConvNone(@PeopleType int fromMsg,
+            @ConversationSenders int fromConv) {
+        setUpMessagesController(p ->
+                p.allowMessages(fromMsg).allowConversations(fromConv));
+        String context = "Trying to check None; starting with Msg=" + peopleTypeToString(fromMsg)
+                + ", Conv=" + conversationTypeToString(fromConv);
+
+        // Checking NONE will always unselect everything else, no matter the initial state.
+        setMessagesOptionChecked(KEY_NONE, true);
+        ZenPolicy savedPolicy = getSavedPolicy();
+
+        assertWithMessage(context).that(savedPolicy.getPriorityMessageSenders()).isEqualTo(
+                PEOPLE_TYPE_NONE);
+        assertWithMessage(context).that(savedPolicy.getPriorityConversationSenders()).isEqualTo(
+                CONVERSATION_SENDERS_NONE);
+    }
+
+    private void setUpMessagesController(Consumer<ZenPolicy.Builder> policyMaker) {
+        ZenMode zenMode = newModeWithPolicy(policyMaker);
+        mMessagesController.displayPreference(mPreferenceScreen);
+        mMessagesController.updateZenMode(mMessagesPrefCategory, zenMode);
+    }
+
+    private static ZenMode newModeWithPolicy(Consumer<ZenPolicy.Builder> policyMaker) {
+        ZenPolicy.Builder policyBuilder = new ZenPolicy.Builder();
+        policyMaker.accept(policyBuilder);
+        return new TestModeBuilder().setZenPolicy(policyBuilder.build()).build();
+    }
+
+    private static ImmutableList<String> getAllOptions(PreferenceCategory category) {
+        return getOptions(category, o -> true);
+    }
+
+    private static ImmutableList<String> getVisibleOptions(PreferenceCategory category) {
+        return getOptions(category, Preference::isVisible);
+    }
+
+    private static ImmutableList<String> getCheckedOptions(PreferenceCategory category) {
+        return getOptions(category, TwoStatePreference::isChecked);
+    }
+
+    private static ImmutableList<String> getOptions(PreferenceCategory category,
+            Predicate<SelectorWithWidgetPreference> filter) {
+        ImmutableList.Builder<String> keys = new ImmutableList.Builder<>();
+        for (int i = 0; i < category.getPreferenceCount(); i++) {
+            SelectorWithWidgetPreference option =
+                    (SelectorWithWidgetPreference) category.getPreference(i);
+            if (filter.test(option)) {
+                keys.add(category.getPreference(i).getKey());
+            }
+        }
+        return keys.build();
+    }
+
+
+    private static void setOptionChecked(PreferenceCategory category, String key, boolean checked) {
+        SelectorWithWidgetPreference preference = checkNotNull(category.findPreference(key));
+        if (preference.isChecked() == checked) {
+            throw new IllegalArgumentException(
+                    "This test is trying to " + (checked ? "check" : "uncheck") + " " + key
+                            + ", but it's already " + (checked ? "checked" : "unchecked") + "!");
+        }
+        preference.performClick();
+    }
+
+    private void setMessagesOptionChecked(String key, boolean checked) {
+        setOptionChecked(mMessagesPrefCategory, key, checked);
+    }
+
+    private ZenPolicy getSavedPolicy() {
+        ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
+        verify(mBackend).updateMode(captor.capture());
+        return captor.getValue().getPolicy();
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModesListItemPreferenceTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModesListItemPreferenceTest.java
index ea45a71..1c8e96e 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModesListItemPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModesListItemPreferenceTest.java
@@ -22,7 +22,6 @@
 import android.content.Context;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
-import android.service.notification.ZenModeConfig;
 
 import com.android.settingslib.notification.modes.TestModeBuilder;
 import com.android.settingslib.notification.modes.ZenMode;
@@ -94,14 +93,10 @@
 
     @Test
     public void setZenMode_modeDisabledByApp() {
-        ZenModeConfig.ZenRule configRule = new ZenModeConfig.ZenRule();
-        configRule.enabled = false;
-        configRule.disabledOrigin = ZenModeConfig.UPDATE_ORIGIN_APP;
         ZenMode mode = new TestModeBuilder()
                 .setName("Mode disabled by app")
                 .setTriggerDescription("When the cat's away")
-                .setEnabled(false)
-                .setConfigZenRule(configRule)
+                .setEnabled(false, /* byUser= */ false)
                 .build();
 
         ZenModesListItemPreference preference = new ZenModesListItemPreference(mContext, mode);
@@ -114,14 +109,10 @@
 
     @Test
     public void setZenMode_modeDisabledByUser() {
-        ZenModeConfig.ZenRule configRule = new ZenModeConfig.ZenRule();
-        configRule.enabled = false;
-        configRule.disabledOrigin = ZenModeConfig.UPDATE_ORIGIN_USER;
         ZenMode mode = new TestModeBuilder()
                 .setName("Mode disabled by user")
                 .setTriggerDescription("When the Levee Breaks")
-                .setEnabled(false)
-                .setConfigZenRule(configRule)
+                .setEnabled(false, /* byUser= */ true)
                 .build();
 
         ZenModesListItemPreference preference = new ZenModesListItemPreference(mContext, mode);
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java
index a7257f5..948eec0 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java
@@ -21,8 +21,11 @@
 import static android.service.notification.Condition.SOURCE_UNKNOWN;
 import static android.service.notification.Condition.STATE_TRUE;
 import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_ANYONE;
+import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE;
 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_NONE;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED;
 import static android.service.notification.ZenPolicy.VISUAL_EFFECT_AMBIENT;
 import static android.service.notification.ZenPolicy.VISUAL_EFFECT_LIGHTS;
 
@@ -124,6 +127,59 @@
     }
 
     @Test
+    public void getMessagesSettingSummary_allMessages() {
+        ZenPolicy policy1 = new ZenPolicy.Builder()
+                .allowMessages(PEOPLE_TYPE_ANYONE)
+                .build();
+        ZenPolicy policy2 = new ZenPolicy.Builder()
+                .allowMessages(PEOPLE_TYPE_ANYONE)
+                .allowConversations(CONVERSATION_SENDERS_IMPORTANT)
+                .build();
+        ZenPolicy policy3 = new ZenPolicy.Builder()
+                .allowMessages(PEOPLE_TYPE_ANYONE)
+                .allowConversations(CONVERSATION_SENDERS_ANYONE)
+                .build();
+
+        assertThat(mSummaryHelper.getMessagesSettingSummary(policy1)).isEqualTo("Anyone");
+        assertThat(mSummaryHelper.getMessagesSettingSummary(policy2)).isEqualTo("Anyone");
+        assertThat(mSummaryHelper.getMessagesSettingSummary(policy3)).isEqualTo("Anyone");
+    }
+
+    @Test
+    public void getMessagesSettingSummary_noMessagesButSomeConversations() {
+        ZenPolicy policy1 = new ZenPolicy.Builder()
+                .allowMessages(PEOPLE_TYPE_NONE)
+                .allowConversations(CONVERSATION_SENDERS_IMPORTANT)
+                .build();
+        ZenPolicy policy2 = new ZenPolicy.Builder()
+                .allowMessages(PEOPLE_TYPE_NONE)
+                .allowConversations(CONVERSATION_SENDERS_ANYONE)
+                .build();
+
+        assertThat(mSummaryHelper.getMessagesSettingSummary(policy1)).isEqualTo(
+                "Priority conversations");
+        assertThat(mSummaryHelper.getMessagesSettingSummary(policy2)).isEqualTo(
+                "All conversations");
+    }
+
+    @Test
+    public void getMessagesSettingSummary_contactsAndConversations() {
+        ZenPolicy policy1 = new ZenPolicy.Builder()
+                .allowMessages(PEOPLE_TYPE_STARRED)
+                .allowConversations(CONVERSATION_SENDERS_IMPORTANT)
+                .build();
+        ZenPolicy policy2 = new ZenPolicy.Builder()
+                .allowMessages(PEOPLE_TYPE_STARRED)
+                .allowConversations(CONVERSATION_SENDERS_ANYONE)
+                .build();
+
+        assertThat(mSummaryHelper.getMessagesSettingSummary(policy1)).isEqualTo(
+                "Starred contacts and priority conversations");
+        assertThat(mSummaryHelper.getMessagesSettingSummary(policy2)).isEqualTo(
+                "Starred contacts and all conversations");
+    }
+
+    @Test
     public void getOtherSoundCategoriesSummary_single() {
         ZenMode zenMode = new TestModeBuilder()
                 .setZenPolicy(new ZenPolicy.Builder().allowAlarms(true).build())
diff --git a/tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java b/tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java
index e035274..482aa5d 100644
--- a/tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java
@@ -729,6 +729,7 @@
     public void initialize_restrictUserSelected_shouldNotShowGrantAdminPref_MultipleAdminEnabled() {
         setupSelectedUser();
         ShadowUserManager.setIsMultipleAdminEnabled(true);
+        // target user has DISALLOW_GRANT_ADMIN restriction
         mUserManager.setUserRestriction(mUserInfo.getUserHandle(),
                 UserManager.DISALLOW_GRANT_ADMIN, true);
         mFragment.initialize(mActivity, mArguments);
@@ -736,6 +737,18 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_UNICORN_MODE_REFACTORING_FOR_HSUM_READ_ONLY)
+    public void initialize_currentUserRestrict_shouldNotShowGrantAdminPref_MultipleAdminEnabled() {
+        setupSelectedUser();
+        ShadowUserManager.setIsMultipleAdminEnabled(true);
+        // current user has DISALLOW_GRANT_ADMIN restriction
+        mUserManager.setUserRestriction(mContext.getUser(),
+                UserManager.DISALLOW_GRANT_ADMIN, true);
+        mFragment.initialize(mActivity, mArguments);
+        verify(mFragment).removePreference(KEY_GRANT_ADMIN);
+    }
+
+    @Test
     public void initialize_mainUserSelected_shouldShowGrantAdminPref_MultipleAdminEnabled() {
         setupSelectedMainUser();
         ShadowUserManager.setIsMultipleAdminEnabled(true);
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProviderTest.kt
index 6a6b91b..5ced84b 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProviderTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProviderTest.kt
@@ -37,45 +37,32 @@
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.runTest
-import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.junit.MockitoJUnit
-import org.mockito.junit.MockitoRule
 import org.mockito.kotlin.any
 import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doReturn
 import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
 import org.mockito.kotlin.whenever
 
 @RunWith(AndroidJUnit4::class)
 class BackgroundInstalledAppsPageProviderTest {
-    @get:Rule
-    val composeTestRule = createComposeRule()
+    @get:Rule val composeTestRule = createComposeRule()
 
-    @get:Rule
-    val mockito: MockitoRule = MockitoJUnit.rule()
+    private val mockPackageManager = mock<PackageManager>()
 
-    private val context: Context = ApplicationProvider.getApplicationContext()
+    private val context: Context =
+        spy(ApplicationProvider.getApplicationContext()) {
+            on { packageManager } doReturn mockPackageManager
+        }
 
-    @Mock
-    private lateinit var mockContext: Context
-
-    @Mock
-    private lateinit var mockPackageManager: PackageManager
-
-    @Mock
-    private lateinit var mockBackgroundInstallControlService: IBackgroundInstallControlService
-
-    private var packageInfoFlagsCaptor = argumentCaptor<PackageManager.PackageInfoFlags>()
+    private val mockBackgroundInstallControlService = mock<IBackgroundInstallControlService>()
 
     private val fakeNavControllerWrapper = FakeNavControllerWrapper()
 
-    @Before
-    fun setup() {
-        whenever(mockContext.packageManager).thenReturn(mockPackageManager)
-    }
     @Test
     fun allAppListPageProvider_name() {
         assertThat(BackgroundInstalledAppsPageProvider.name)
@@ -108,7 +95,7 @@
 
         setInjectEntry(false)
 
-        composeTestRule.onNodeWithText("0 apps").assertIsDisplayed()
+        composeTestRule.waitUntilExists(hasText("0 apps"))
     }
 
     @Test
@@ -200,7 +187,8 @@
 
     @Test
     fun backgroundInstalledAppsWithGroupingListModel_transform() = runTest {
-        val listModel = BackgroundInstalledAppsWithGroupingListModel(mockContext)
+        val packageInfoFlagsCaptor = argumentCaptor<PackageManager.PackageInfoFlags>()
+        val listModel = BackgroundInstalledAppsWithGroupingListModel(context)
         whenever(mockPackageManager.getPackageInfoAsUser(
             eq(TEST_PACKAGE_NAME),
             packageInfoFlagsCaptor.capture(),
@@ -218,7 +206,7 @@
 
     @Test
     fun backgroundInstalledAppsWithGroupingListModel_filter() = runTest {
-        val listModel = BackgroundInstalledAppsWithGroupingListModel(mockContext)
+        val listModel = BackgroundInstalledAppsWithGroupingListModel(context)
         listModel.setBackgroundInstallControlService(mockBackgroundInstallControlService)
         whenever(mockBackgroundInstallControlService.getBackgroundInstalledPackages(
             PackageManager.MATCH_ALL.toLong(),
diff --git a/tests/spa_unit/src/com/android/settings/wifi/WepNetworksPreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/wifi/WepNetworksPreferenceControllerTest.kt
index 49e6a17..9183096 100644
--- a/tests/spa_unit/src/com/android/settings/wifi/WepNetworksPreferenceControllerTest.kt
+++ b/tests/spa_unit/src/com/android/settings/wifi/WepNetworksPreferenceControllerTest.kt
@@ -17,6 +17,7 @@
 package com.android.settings.wifi
 
 import android.content.Context
+import android.net.wifi.WifiInfo
 import android.net.wifi.WifiManager
 import androidx.compose.ui.test.assertIsOff
 import androidx.compose.ui.test.assertIsOn
@@ -30,7 +31,6 @@
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.settings.R
-import com.android.settings.dashboard.DashboardFragment
 import com.android.settings.spa.preference.ComposePreference
 import com.android.settingslib.spa.testutils.onDialogText
 import com.android.wifitrackerlib.WifiEntry
@@ -45,29 +45,31 @@
 import org.mockito.kotlin.doReturn
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.spy
+import org.mockito.kotlin.stub
 
 @RunWith(AndroidJUnit4::class)
 class WepNetworksPreferenceControllerTest {
 
-    @get:Rule
-    val composeTestRule = createComposeRule()
+    @get:Rule val composeTestRule = createComposeRule()
 
     private var wepAllowed = true
 
-    private var mockWifiInfo = mock<android.net.wifi.WifiInfo> {
-        on { it.currentSecurityType } doReturn WifiEntry.SECURITY_EAP
-        on { it.ssid } doReturn SSID
-    }
-
-    private var mockWifiManager = mock<WifiManager> {
-        on { queryWepAllowed(any(), any()) } doAnswer {
-            @Suppress("UNCHECKED_CAST")
-            val consumer = it.arguments[1] as Consumer<Boolean>
-            consumer.accept(wepAllowed)
+    private var mockWifiInfo =
+        mock<WifiInfo> {
+            on { currentSecurityType } doReturn WifiEntry.SECURITY_EAP
+            on { ssid } doReturn SSID
         }
-        on { it.isWepSupported } doReturn true
-        on { it.connectionInfo } doReturn mockWifiInfo
-    }
+
+    private var mockWifiManager =
+        mock<WifiManager> {
+            on { queryWepAllowed(any(), any()) } doAnswer
+                {
+                    @Suppress("UNCHECKED_CAST") val consumer = it.arguments[1] as Consumer<Boolean>
+                    consumer.accept(wepAllowed)
+                }
+            on { isWepSupported } doReturn true
+            on { connectionInfo } doReturn mockWifiInfo
+        }
 
     private var context: Context =
         spy(ApplicationProvider.getApplicationContext()) {
@@ -85,74 +87,101 @@
     }
 
     @Test
-    fun wepAllowedTrue_turnOn() {
+    fun isChecked_wepSupportedAndAllowed_isOn() {
+        mockWifiManager.stub { on { isWepSupported } doReturn true }
         wepAllowed = true
-        composeTestRule.setContent {
-            controller.Content()
-        }
 
-        composeTestRule.onNodeWithText(context.getString(R.string.wifi_allow_wep_networks))
+        composeTestRule.setContent { controller.Content() }
+
+        composeTestRule
+            .onNodeWithText(context.getString(R.string.wifi_allow_wep_networks))
             .assertIsOn()
     }
 
     @Test
-    fun wepAllowedFalse_turnOff() {
+    fun isChecked_wepSupportedAndNotAllowed_isOff() {
+        mockWifiManager.stub { on { isWepSupported } doReturn true }
         wepAllowed = false
-        composeTestRule.setContent {
-            controller.Content()
-        }
 
-        composeTestRule.onNodeWithText(context.getString(R.string.wifi_allow_wep_networks))
+        composeTestRule.setContent { controller.Content() }
+
+        composeTestRule
+            .onNodeWithText(context.getString(R.string.wifi_allow_wep_networks))
+            .assertIsOff()
+    }
+
+    @Test
+    fun isChecked_wepNotSupportedAndAllowed_isOff() {
+        mockWifiManager.stub { on { isWepSupported } doReturn false }
+        wepAllowed = true
+
+        composeTestRule.setContent { controller.Content() }
+
+        composeTestRule
+            .onNodeWithText(context.getString(R.string.wifi_allow_wep_networks))
+            .assertIsOff()
+    }
+
+    @Test
+    fun isChecked_wepNotSupportedAndNotAllowed_isOff() {
+        mockWifiManager.stub { on { isWepSupported } doReturn false }
+        wepAllowed = false
+
+        composeTestRule.setContent { controller.Content() }
+
+        composeTestRule
+            .onNodeWithText(context.getString(R.string.wifi_allow_wep_networks))
             .assertIsOff()
     }
 
     @Test
     fun onClick_turnOn() {
         wepAllowed = false
-        composeTestRule.setContent {
-            controller.Content()
-        }
+        composeTestRule.setContent { controller.Content() }
 
         composeTestRule.onRoot().performClick()
-        composeTestRule.onNodeWithText(context.getString(R.string.wifi_allow_wep_networks))
+        composeTestRule
+            .onNodeWithText(context.getString(R.string.wifi_allow_wep_networks))
             .assertIsOn()
     }
 
     @Test
     fun onClick_turnOff() {
         wepAllowed = true
-        composeTestRule.setContent {
-            controller.Content()
-        }
+        composeTestRule.setContent { controller.Content() }
 
         composeTestRule.onRoot().performClick()
-        composeTestRule.onNodeWithText(context.getString(R.string.wifi_allow_wep_networks))
+        composeTestRule
+            .onNodeWithText(context.getString(R.string.wifi_allow_wep_networks))
             .assertIsOff()
     }
 
     @Test
     fun whenClick_wepAllowed_openDialog() {
         wepAllowed = true
-        Mockito.`when`(mockWifiInfo.currentSecurityType).thenReturn(WifiEntry.SECURITY_WEP)
-        composeTestRule.setContent {
-            controller.Content()
+        mockWifiInfo.stub {
+            on { currentSecurityType } doReturn WifiEntry.SECURITY_WEP
         }
+        composeTestRule.setContent { controller.Content() }
 
         composeTestRule.onRoot().performClick()
-        composeTestRule.onDialogText(context.getString(R.string.wifi_disconnect_button_text))
+        composeTestRule
+            .onDialogText(context.getString(R.string.wifi_disconnect_button_text))
             .isDisplayed()
     }
 
     @Test
     fun whenClick_wepDisallowed_openDialog() {
         wepAllowed = false
-        Mockito.`when`(mockWifiInfo.currentSecurityType).thenReturn(WifiEntry.SECURITY_WEP)
-        composeTestRule.setContent {
-            controller.Content()
+        mockWifiInfo.stub {
+            on { currentSecurityType } doReturn WifiEntry.SECURITY_WEP
         }
 
+        composeTestRule.setContent { controller.Content() }
+
         composeTestRule.onRoot().performClick()
-        composeTestRule.onDialogText(context.getString(R.string.wifi_disconnect_button_text))
+        composeTestRule
+            .onDialogText(context.getString(R.string.wifi_disconnect_button_text))
             .isNotDisplayed()
     }
 
@@ -160,4 +189,4 @@
         const val TEST_KEY = "test_key"
         const val SSID = "ssid"
     }
-}
\ No newline at end of file
+}
diff --git a/tests/unit/src/com/android/settings/localepicker/TermsOfAddressCategoryControllerTest.java b/tests/unit/src/com/android/settings/localepicker/TermsOfAddressCategoryControllerTest.java
index e316b25..b025abd 100644
--- a/tests/unit/src/com/android/settings/localepicker/TermsOfAddressCategoryControllerTest.java
+++ b/tests/unit/src/com/android/settings/localepicker/TermsOfAddressCategoryControllerTest.java
@@ -17,13 +17,11 @@
 package com.android.settings.localepicker;
 
 import static com.android.settings.core.BasePreferenceController.AVAILABLE;
-import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.spy;
 
-import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.os.Looper;
 
@@ -67,14 +65,6 @@
     }
 
     @Test
-    public void getAvailabilityStatus_returnUnavailable() {
-        Locale.setDefault(Locale.forLanguageTag("fr-CA"));
-
-        assertThat(mTermsOfAddressCategoryController.getAvailabilityStatus()).isEqualTo(
-                CONDITIONALLY_UNAVAILABLE);
-    }
-
-    @Test
     public void getAvailabilityStatus_returnAvailable() {
         Locale.setDefault(Locale.forLanguageTag("fr-FR"));