Merge "[Audiosharing] Test getInstance for states" into main
diff --git a/Android.bp b/Android.bp
index cb898be..0c6d8d1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -125,6 +125,9 @@
         "telephony-common",
         "ims-common",
     ],
+    flags_packages: [
+        "android.app.flags-aconfig",
+    ],
 }
 
 platform_compat_config {
@@ -155,6 +158,9 @@
     optimize: {
         proguard_flags_files: ["proguard.flags"],
     },
+    flags_packages: [
+        "android.app.flags-aconfig",
+    ],
 }
 
 android_library_import {
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 90b42f4..8cfd9b5 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -140,7 +140,7 @@
     <uses-permission android:name="android.permission.REMAP_MODIFIER_KEYS" />
     <uses-permission android:name="android.permission.ACCESS_GPU_SERVICE" />
     <uses-permission android:name="android.permission.MANAGE_GAME_MODE" />
-    <uses-permission android:name="android.permission.RESTART_PHONE_PROCESS" />
+    <uses-permission android:name="android.permission.RESTART_TELEPHONY_PROCESS" />
     <uses-permission android:name="android.permission.MANAGE_ENHANCED_CONFIRMATION_STATES" />
     <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
     <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
@@ -1287,20 +1287,63 @@
         </activity>
 
         <activity
-            android:name="Settings$ZenModeSettingsActivity"
+            android:name="Settings$ModesSettingsActivity"
             android:label="@string/zen_mode_settings_title"
             android:icon="@drawable/ic_homepage_notification"
             android:exported="true">
-            <intent-filter android:priority="1">
+            <intent-filter android:priority="1"
+                           android:featureFlag="android.app.modes_ui">
                 <action android:name="android.settings.ZEN_MODE_SETTINGS" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
-            <intent-filter android:priority="1">
+            <intent-filter android:priority="1"
+                           android:featureFlag="android.app.modes_ui">
                 <action android:name="android.settings.ZEN_MODE_PRIORITY_SETTINGS" />
                 <category android:name="android.intent.category.BROWSABLE" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
-            <intent-filter android:priority="41">
+            <intent-filter android:priority="41"
+                           android:featureFlag="android.app.modes_ui">
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.android.settings.SHORTCUT" />
+            </intent-filter>
+            <intent-filter android:priority="10"
+                           android:featureFlag="android.app.modes_ui">
+                <action android:name="android.settings.ZEN_MODE_AUTOMATION_SETTINGS" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter android:priority="10"
+                           android:featureFlag="android.app.modes_ui">
+                <action android:name="android.settings.ACTION_CONDITION_PROVIDER_SETTINGS" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+                       android:value="com.android.settings.notification.modes.ZenModesListFragment"/>
+            <meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
+                       android:value="@string/menu_key_notifications"/>
+            <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
+                       android:value="true" />
+        </activity>
+
+        <activity
+            android:name="Settings$ZenModeSettingsActivity"
+            android:label="@string/zen_mode_settings_title"
+            android:icon="@drawable/ic_homepage_notification"
+            android:exported="true">
+            <intent-filter android:priority="1"
+                           android:featureFlag="!android.app.modes_ui">
+                <action android:name="android.settings.ZEN_MODE_SETTINGS" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter android:priority="1"
+                           android:featureFlag="!android.app.modes_ui">
+                <action android:name="android.settings.ZEN_MODE_PRIORITY_SETTINGS" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter android:priority="41"
+                           android:featureFlag="!android.app.modes_ui">
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="com.android.settings.SHORTCUT" />
             </intent-filter>
@@ -1313,6 +1356,20 @@
         </activity>
 
         <activity
+            android:name="Settings$ModeSettingsActivity"
+            android:exported="true">
+            <intent-filter android:priority="1"
+                           android:featureFlag="android.app.modes_ui">
+                <action android:name="android.settings.AUTOMATIC_ZEN_RULE_SETTINGS" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+                       android:value="com.android.settings.notification.modes.ZenModeFragment"/>
+            <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
+                       android:value="true" />
+        </activity>
+
+        <activity
             android:name=".notification.zen.ZenSuggestionActivity"
             android:label="@string/zen_mode_settings_title"
             android:icon="@drawable/ic_suggestion_dnd"
@@ -1351,11 +1408,13 @@
             android:label="@string/zen_mode_automation_settings_title"
             android:icon="@drawable/ic_notifications"
             android:exported="true">
-            <intent-filter android:priority="1">
+            <intent-filter android:priority="10"
+                           android:featureFlag="!android.app.modes_ui">
                 <action android:name="android.settings.ZEN_MODE_AUTOMATION_SETTINGS" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
-            <intent-filter android:priority="1">
+            <intent-filter android:priority="10"
+                           android:featureFlag="!android.app.modes_ui">
                 <action android:name="android.settings.ACTION_CONDITION_PROVIDER_SETTINGS" />
                 <category android:name="android.intent.category.BROWSABLE" />
                 <category android:name="android.intent.category.DEFAULT" />
@@ -2832,17 +2891,20 @@
         <!-- Note this must not be exported since it returns the password in the intent -->
         <activity android:name=".password.ConfirmLockPattern$InternalActivity"
             android:exported="false"
+            android:enableOnBackInvokedCallback="false"
             android:theme="@style/GlifTheme.Light"/>
 
         <!-- Note this must not be exported since it returns the password in the intent -->
         <activity android:name=".password.ConfirmLockPassword$InternalActivity"
             android:exported="false"
             android:windowSoftInputMode="adjustResize"
+            android:enableOnBackInvokedCallback="false"
             android:theme="@style/GlifTheme.Light"/>
 
         <activity android:name=".password.SetupChooseLockGeneric"
             android:theme="@style/GlifTheme.Light"
             android:exported="true"
+            android:enableOnBackInvokedCallback="false"
             android:label="@string/lock_settings_picker_title">
             <intent-filter android:priority="1">
                 <action android:name="com.android.settings.SETUP_LOCK_SCREEN" />
@@ -2852,16 +2914,19 @@
 
         <activity android:name=".password.SetupChooseLockGeneric$InternalActivity"
             android:exported="false"
+            android:enableOnBackInvokedCallback="false"
             android:excludeFromRecents="true" />
 
         <activity android:name=".password.ChooseLockGeneric"
             android:label="@string/lockpassword_choose_lock_generic_header"
             android:excludeFromRecents="true"
+            android:enableOnBackInvokedCallback="false"
             android:exported="false" />
 
         <activity android:name=".password.SetNewPasswordActivity"
             android:theme="@android:style/Theme.NoDisplay"
             android:exported="true"
+            android:enableOnBackInvokedCallback="false"
             android:excludeFromRecents="true" >
             <intent-filter android:priority="1">
                 <action android:name="android.app.action.SET_NEW_PASSWORD" />
@@ -2907,24 +2972,29 @@
         <activity android:name=".password.ChooseLockGeneric$InternalActivity"
             android:exported="false"
             android:label="@string/lockpassword_choose_lock_generic_header"
+            android:enableOnBackInvokedCallback="false"
             android:excludeFromRecents="true" />
 
         <activity android:name=".password.SetupChooseLockPattern"
             android:exported="false"
+            android:enableOnBackInvokedCallback="false"
             android:theme="@style/GlifTheme.Light" />
 
         <activity android:name=".password.ChooseLockPattern"
             android:exported="false"
+            android:enableOnBackInvokedCallback="false"
             android:theme="@style/GlifTheme.Light" />
 
         <activity android:name=".password.SetupChooseLockPassword"
             android:exported="false"
             android:theme="@style/GlifTheme.Light"
+            android:enableOnBackInvokedCallback="false"
             android:windowSoftInputMode="stateVisible|adjustResize" />
 
         <activity android:name=".password.ChooseLockPassword"
             android:exported="false"
             android:theme="@style/GlifTheme.Light"
+            android:enableOnBackInvokedCallback="false"
             android:windowSoftInputMode="stateVisible|adjustResize"/>
 
         <activity
diff --git a/aconfig/settings_wifi_flag_declarations.aconfig b/aconfig/settings_wifi_flag_declarations.aconfig
new file mode 100644
index 0000000..cb8007f
--- /dev/null
+++ b/aconfig/settings_wifi_flag_declarations.aconfig
@@ -0,0 +1,12 @@
+package: "com.android.settings.flags"
+container: "system_ext"
+
+# NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
+
+flag {
+    name: "enable_wifi_sharing_runtime_fragment"
+    namespace: "prism_qr"
+    description: "Use WifiFeatureProvider to get the instance of WifiDppQrCodeGeneratorFragment."
+    bug: "329012096"
+}
+
diff --git a/res/drawable/ic_do_not_disturb_on_24dp.xml b/res/drawable/ic_do_not_disturb_on_24dp.xml
deleted file mode 100644
index cace8d4..0000000
--- a/res/drawable/ic_do_not_disturb_on_24dp.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-    Copyright (C) 2018 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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24dp"
-        android:height="24dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M12,2C6.48,2 2,6.48 2,12c0,5.52 4.48,10 10,10c5.52,0 10,-4.48 10,-10C22,6.48 17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8c0,-4.41 3.59,-8 8,-8c4.41,0 8,3.59 8,8C20,16.41 16.41,20 12,20z"/>
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M7,11h10v2h-10z"/>
-</vector>
diff --git a/res/layout-land/request_manage_credentials.xml b/res/layout-land/request_manage_credentials.xml
index fbe0bd0..f6bfa0e 100644
--- a/res/layout-land/request_manage_credentials.xml
+++ b/res/layout-land/request_manage_credentials.xml
@@ -16,6 +16,7 @@
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:fitsSystemWindows="true"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="horizontal"
diff --git a/res/layout-sw600dp/request_manage_credentials.xml b/res/layout-sw600dp/request_manage_credentials.xml
index 42facd3..529edf1 100644
--- a/res/layout-sw600dp/request_manage_credentials.xml
+++ b/res/layout-sw600dp/request_manage_credentials.xml
@@ -17,6 +17,7 @@
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:fitsSystemWindows="true"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
diff --git a/res/layout/request_manage_credentials.xml b/res/layout/request_manage_credentials.xml
index ee697f7..4f6a5c4 100644
--- a/res/layout/request_manage_credentials.xml
+++ b/res/layout/request_manage_credentials.xml
@@ -19,6 +19,7 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:fitsSystemWindows="true"
     android:clipChildren="true">
 
     <RelativeLayout
diff --git a/res/layout/search_bar_unified_version.xml b/res/layout/search_bar_unified_version.xml
index a8ad6fc..dbcf266 100644
--- a/res/layout/search_bar_unified_version.xml
+++ b/res/layout/search_bar_unified_version.xml
@@ -45,6 +45,6 @@
             android:layout_height="wrap_content"
             android:paddingStart="8dp"
             android:paddingEnd="8dp"
-            android:text="@string/search_settings"/>
+            android:text="@string/homepage_search"/>
     </LinearLayout>
 </com.google.android.material.card.MaterialCardView>
diff --git a/res/layout/settings_homepage_container_v2.xml b/res/layout/settings_homepage_container_v2.xml
index 5ae5fbd..b244579 100644
--- a/res/layout/settings_homepage_container_v2.xml
+++ b/res/layout/settings_homepage_container_v2.xml
@@ -69,8 +69,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:orientation="horizontal"
-            android:paddingTop="8dp"
-            android:paddingBottom="24dp"
+            android:paddingVertical="8dp"
             android:paddingStart="?android:attr/listPreferredItemPaddingStart"
             android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
 
diff --git a/res/layout/zen_mode_type_item.xml b/res/layout/zen_mode_type_item.xml
new file mode 100644
index 0000000..841ca00
--- /dev/null
+++ b/res/layout/zen_mode_type_item.xml
@@ -0,0 +1,62 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?android:attr/selectableItemBackground"
+    android:gravity="center_vertical"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:paddingTop="8dp"
+    android:paddingBottom="8dp"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+    <ImageView
+        android:id="@+id/icon"
+        android:layout_width="24dp"
+        android:layout_height="24dp"
+        android:layout_gravity="center" />
+
+    <RelativeLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:paddingStart="16dp"
+        android:layout_weight="1">
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal"
+            android:singleLine="true"
+            android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body1"
+            android:textSize="16sp" />
+
+        <TextView
+            android:id="@+id/subtitle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignStart="@id/title"
+            android:layout_below="@id/title"
+            android:maxLines="2"
+            android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Subhead"
+            android:textColor="?android:attr/textColorSecondary"
+            android:textSize="14sp" />
+
+    </RelativeLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f92fd2a..ed9341d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -7927,6 +7927,31 @@
     <!-- Zen Modes: Summary for the Do not Disturb option and associated settings page. [CHAR LIMIT=240]-->
     <string name="zen_mode_settings_summary">Only get notified by important people and apps</string>
 
+    <!-- Zen Modes: Option to add an automatic schedule for a mode. [CHAR_LIMIT=40] -->
+    <string name="zen_mode_select_schedule">Select activation type</string>
+
+    <!-- Priority Modes: Option to choose a time-based schedule for a mode. [CHAR_LIMIT=40] -->
+    <string name="zen_mode_select_schedule_time">Time</string>
+    <!-- Priority Modes: Example text for the option to choose a time-based schedule for a mode. [CHAR_LIMIT=60] -->
+    <string name="zen_mode_select_schedule_time_example">Ex. \"9:30 – 5:00 PM\"</string>
+
+    <!-- Priority Modes: Option to choose a calendar-events-based schedule for a mode. [CHAR_LIMIT=40] -->
+    <string name="zen_mode_select_schedule_calendar">Calendar</string>
+    <!-- Priority Modes: Example text for the option to choose a calendar-events-based schedule for a mode. [CHAR_LIMIT=60] -->
+    <string name="zen_mode_select_schedule_calendar_example">Ex. \"Personal calendar\"</string>
+
+    <!-- Priority Modes: Short text that indicates that a mode is currently on (active). [CHAR_LIMIT=10] -->
+    <string name="zen_mode_active_text">ON</string>
+
+    <!-- Priority Modes: Format string for the "current state + trigger description summary for rules in the list. [CHAR_LIMIT=10] -->
+    <string name="zen_mode_format_status_and_trigger" translatable="false"><xliff:g id="current_status" example="ON">%1$s</xliff:g> • <xliff:g id="trigger_description" example="Mon-Fri, 23:00-7:00">%2$s</xliff:g></string>
+
+    <!-- Priority Modes: Call to action for a mode that is disabled and needs to be configured. [CHAR_LIMIT=40] -->
+    <string name="zen_mode_disabled_tap_to_set_up">Tap to set up</string>
+
+    <!-- Priority Modes: Indicates that a mode is disabled by the user. [CHAR_LIMIT=40] -->
+    <string name="zen_mode_disabled_by_user">Paused</string>
+
     <!-- Subtitle for the Do not Disturb slice. [CHAR LIMIT=50]-->
     <string name="zen_mode_slice_subtitle">Limit interruptions</string>
 
@@ -7942,9 +7967,15 @@
     <!--  Do not disturb: Title for dialog that allows users to delete DND rules/schedules[CHAR LIMIT=40] -->
     <string name="zen_mode_delete_automatic_rules">Delete schedules</string>
 
-    <!--  Do not disturb: Delete text button presented in a dialog to confirm the user would like to delete the selected DND rules. [CHAR LIMIT=30] -->
+    <!-- Do not disturb: Delete text button presented in a dialog to confirm the user would like to delete the selected DND rules. [CHAR LIMIT=30] -->
     <string name="zen_mode_schedule_delete">Delete</string>
 
+    <!-- Do not disturb: Menu option for deleting a mode on its configuration page [CHAR LIMIT=40] -->
+    <string name="zen_mode_menu_delete_mode">Delete mode</string>
+
+    <!-- Do not disturb: Confirmation dialog asking the user whether they would like to delete the named mode [CHAR LIMIT: 40] -->
+    <string name="zen_mode_delete_mode_confirmation">Delete \"<xliff:g id="mode" example="My Schedule">%1$s</xliff:g>\" mode?</string>
+
     <!--  Do not disturb: Edit label for button that allows user to edit the dnd schedule name. [CHAR LIMIT=30] -->
     <string name="zen_mode_rule_name_edit">Edit</string>
 
@@ -8003,7 +8034,7 @@
     <string name="zen_mode_visual_signals_settings_subtitle">Allow visual signals</string>
 
     <!-- Do not disturb: mode page section title [CHAR LIMIT=80] -->
-    <string name="mode_interruption_filter_title">Notifications that can reach you</string>
+    <string name="mode_interruption_filter_title">Stay focused</string>
     <!-- Do not disturb: mode page section title [CHAR LIMIT=80] -->
     <string name="mode_device_effects_title">Additional actions</string>
 
@@ -8047,7 +8078,10 @@
         other {{effect_1}, {effect_2}, and # more}
         }
     </string>
-
+    <!-- Modes: setting for whether the mode should filter (silence/hide) notifications/volume streams -->
+    <string name="mode_notification_filter_title">Filter interruptions</string>
+    <!-- Modes: subtext when a mode is not filtering (silence/hide) notifications/volume streams -->
+    <string name="mode_no_notification_filter">No interruptions are filtered</string>
 
     <!-- Do not disturb: restrict notifications settings title [CHAR LIMIT=80] -->
     <string name="zen_mode_restrict_notifications_title">Display options for filtered
@@ -13588,4 +13622,7 @@
 
     <!-- url for learning more about bluetooth audio sharing -->
     <string name="help_url_audio_sharing" translatable="false"></string>
+
+    <!-- Text for Search bar of Settings home screen [CHAR LIMIT=34] -->
+    <string name="homepage_search">Search Settings</string>
 </resources>
diff --git a/res/xml/mobile_network_settings.xml b/res/xml/mobile_network_settings.xml
index eb9f442..51cbbe6 100644
--- a/res/xml/mobile_network_settings.xml
+++ b/res/xml/mobile_network_settings.xml
@@ -85,13 +85,9 @@
             android:summary="@string/auto_data_switch_summary"
             settings:controller="com.android.settings.network.telephony.AutoDataSwitchPreferenceController"/>
 
-        <com.android.settingslib.RestrictedSwitchPreference
+        <com.android.settings.spa.preference.ComposePreference
             android:key="button_roaming_key"
             android:title="@string/roaming"
-            android:persistent="false"
-            android:summaryOn="@string/roaming_enable"
-            android:summaryOff="@string/roaming_disable"
-            settings:userRestriction="no_data_roaming"
             settings:controller="com.android.settings.network.telephony.RoamingPreferenceController"/>
 
         <Preference
diff --git a/res/xml/modes_rule_settings.xml b/res/xml/modes_rule_settings.xml
index cf090be..0df9f80 100644
--- a/res/xml/modes_rule_settings.xml
+++ b/res/xml/modes_rule_settings.xml
@@ -35,13 +35,18 @@
     <PreferenceCategory
             android:title="@string/mode_interruption_filter_title"
             android:key="modes_filters">
+
+        <SwitchPreferenceCompat
+            android:key="allow_filtering"
+            android:title="@string/mode_notification_filter_title"/>
+
         <Preference
                 android:key="zen_mode_people"
                 android:title="@string/zen_category_people"/>
 
         <Preference
             android:key="zen_mode_apps"
-            android:title="@string/zen_category_apps" />
+            android:title="@string/zen_category_apps"/>
 
         <Preference
                 android:key="zen_other_settings"
diff --git a/src/com/android/settings/ResetNetworkRequest.java b/src/com/android/settings/ResetNetworkRequest.java
index 7632ea0..8df67e7 100644
--- a/src/com/android/settings/ResetNetworkRequest.java
+++ b/src/com/android/settings/ResetNetworkRequest.java
@@ -271,12 +271,12 @@
             builder.resetIms(mSubscriptionIdToResetIms);
         }
         // Reset phone process and RILD may impact above components, keep them at the end
-        if ((mResetOptions & RESET_PHONE_PROCESS) != 0) {
-            builder.restartPhoneProcess();
-        }
         if ((mResetOptions & RESET_RILD) != 0) {
             builder.restartRild();
         }
+        if ((mResetOptions & RESET_PHONE_PROCESS) != 0) {
+            builder.restartPhoneProcess();
+        }
         return builder;
     }
 }
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 3367bf1..e3bb1a1 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -18,6 +18,8 @@
 
 import static android.provider.Settings.ACTION_PRIVACY_SETTINGS;
 
+import android.annotation.FlaggedApi;
+import android.app.Flags;
 import android.content.ActivityNotFoundException;
 import android.content.Intent;
 import android.os.Bundle;
@@ -317,11 +319,13 @@
     public static class PrintSettingsActivity extends SettingsActivity { /* empty */ }
     public static class PrintJobSettingsActivity extends SettingsActivity { /* empty */ }
     public static class ZenModeSettingsActivity extends SettingsActivity { /* empty */ }
-    public static class ZenModeBehaviorSettingsActivity extends SettingsActivity { /* empty */ }
-    public static class ZenModeBlockedEffectsSettingsActivity extends SettingsActivity { /* empty */ }
     public static class ZenModeAutomationSettingsActivity extends SettingsActivity { /* empty */ }
     public static class ZenModeScheduleRuleSettingsActivity extends SettingsActivity { /* empty */ }
     public static class ZenModeEventRuleSettingsActivity extends SettingsActivity { /* empty */ }
+    @FlaggedApi(Flags.FLAG_MODES_UI)
+    public static class ModeSettingsActivity extends SettingsActivity { /* empty */ }
+    @FlaggedApi(Flags.FLAG_MODES_UI)
+    public static class ModesSettingsActivity extends SettingsActivity { /* empty */ }
     public static class SoundSettingsActivity extends SettingsActivity { /* empty */ }
     public static class ConfigureNotificationSettingsActivity extends SettingsActivity { /* empty */ }
     public static class ConversationListSettingsActivity extends SettingsActivity { /* empty */ }
diff --git a/src/com/android/settings/accessibility/AccessibilityQuickSettingsPrimarySwitchPreferenceController.java b/src/com/android/settings/accessibility/AccessibilityQuickSettingsPrimarySwitchPreferenceController.java
index e82cd96..8a3f22d 100644
--- a/src/com/android/settings/accessibility/AccessibilityQuickSettingsPrimarySwitchPreferenceController.java
+++ b/src/com/android/settings/accessibility/AccessibilityQuickSettingsPrimarySwitchPreferenceController.java
@@ -21,6 +21,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 
+import androidx.annotation.Nullable;
 import androidx.preference.PreferenceScreen;
 
 import com.android.settings.R;
@@ -42,6 +43,7 @@
     private boolean mNeedsQSTooltipReshow = false;
 
     /** Returns the accessibility tile component name. */
+    @Nullable
     abstract ComponentName getTileComponentName();
 
     /** Returns the accessibility tile tooltip content. */
diff --git a/src/com/android/settings/accessibility/ReduceBrightColorsPreferenceController.java b/src/com/android/settings/accessibility/ReduceBrightColorsPreferenceController.java
index 3ca089c..e7f59f4 100644
--- a/src/com/android/settings/accessibility/ReduceBrightColorsPreferenceController.java
+++ b/src/com/android/settings/accessibility/ReduceBrightColorsPreferenceController.java
@@ -29,6 +29,7 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 
+import androidx.annotation.Nullable;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
 
@@ -125,9 +126,14 @@
         mContext.getContentResolver().unregisterContentObserver(mSettingsContentObserver);
     }
 
+    @Nullable
     @Override
     protected ComponentName getTileComponentName() {
-        return REDUCE_BRIGHT_COLORS_TILE_SERVICE_COMPONENT_NAME;
+        // TODO: When clean up the feature flag, change the parent class from
+        // AccessibilityQuickSettingsPrimarySwitchPreferenceController to
+        // TogglePreferenceController
+        return android.view.accessibility.Flags.a11yQsShortcut()
+                ? null : REDUCE_BRIGHT_COLORS_TILE_SERVICE_COMPONENT_NAME;
     }
 
     @Override
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioController.java b/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioController.java
index 30e86fe..4ff7136 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioController.java
@@ -19,13 +19,16 @@
 import static android.media.Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
 
 import android.app.settings.SettingsEnums;
+import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.media.AudioDeviceAttributes;
 import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
 import android.media.Spatializer;
 import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceCategory;
@@ -37,9 +40,14 @@
 import com.android.settings.R;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothProfile;
 import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.flags.Flags;
 import com.android.settingslib.utils.ThreadUtils;
 
+import com.google.common.collect.ImmutableSet;
+
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -53,22 +61,27 @@
     private static final String KEY_SPATIAL_AUDIO = "spatial_audio";
     private static final String KEY_HEAD_TRACKING = "head_tracking";
 
+    private final AudioManager mAudioManager;
     private final Spatializer mSpatializer;
 
     @VisibleForTesting
     PreferenceCategory mProfilesContainer;
-    @VisibleForTesting
-    AudioDeviceAttributes mAudioDevice = null;
+    @VisibleForTesting @Nullable AudioDeviceAttributes mAudioDevice = null;
 
     AtomicBoolean mHasHeadTracker = new AtomicBoolean(false);
     AtomicBoolean mInitialRefresh = new AtomicBoolean(true);
 
+    public static final Set<Integer> SA_PROFILES =
+            ImmutableSet.of(
+                    BluetoothProfile.A2DP, BluetoothProfile.LE_AUDIO, BluetoothProfile.HEARING_AID);
+
     public BluetoothDetailsSpatialAudioController(
             Context context,
             PreferenceFragmentCompat fragment,
             CachedBluetoothDevice device,
             Lifecycle lifecycle) {
         super(context, fragment, device, lifecycle);
+        mAudioManager = context.getSystemService(AudioManager.class);
         mSpatializer = FeatureFactory.getFeatureFactory().getBluetoothFeatureProvider()
                 .getSpatializer(context);
     }
@@ -142,8 +155,12 @@
 
     @Override
     protected void refresh() {
-        if (mAudioDevice == null) {
-            getAvailableDevice();
+        if (Flags.enableDeterminingSpatialAudioAttributesByProfile()) {
+            getAvailableDeviceByProfileState();
+        } else {
+            if (mAudioDevice == null) {
+                getAvailableDevice();
+            }
         }
         ThreadUtils.postOnBackgroundThread(
                 () -> {
@@ -274,6 +291,77 @@
                 + ", type : " + (mAudioDevice == null ? "no type" : mAudioDevice.getType()));
     }
 
+    private void getAvailableDeviceByProfileState() {
+        Log.i(
+                TAG,
+                "getAvailableDevice() mCachedDevice: "
+                        + mCachedDevice
+                        + " profiles: "
+                        + mCachedDevice.getProfiles());
+
+        AudioDeviceAttributes saDevice = null;
+        for (LocalBluetoothProfile profile : mCachedDevice.getProfiles()) {
+            // pick first enabled profile that is compatible with spatial audio
+            if (SA_PROFILES.contains(profile.getProfileId())
+                    && profile.isEnabled(mCachedDevice.getDevice())) {
+                switch (profile.getProfileId()) {
+                    case BluetoothProfile.A2DP:
+                        saDevice =
+                                new AudioDeviceAttributes(
+                                        AudioDeviceAttributes.ROLE_OUTPUT,
+                                        AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
+                                        mCachedDevice.getAddress());
+                        break;
+                    case BluetoothProfile.LE_AUDIO:
+                        if (mAudioManager.getBluetoothAudioDeviceCategory(
+                                mCachedDevice.getAddress())
+                                == AudioManager.AUDIO_DEVICE_CATEGORY_SPEAKER) {
+                            saDevice =
+                                    new AudioDeviceAttributes(
+                                            AudioDeviceAttributes.ROLE_OUTPUT,
+                                            AudioDeviceInfo.TYPE_BLE_SPEAKER,
+                                            mCachedDevice.getAddress());
+                        } else {
+                            saDevice =
+                                    new AudioDeviceAttributes(
+                                            AudioDeviceAttributes.ROLE_OUTPUT,
+                                            AudioDeviceInfo.TYPE_BLE_HEADSET,
+                                            mCachedDevice.getAddress());
+                        }
+
+                        break;
+                    case BluetoothProfile.HEARING_AID:
+                        saDevice =
+                                new AudioDeviceAttributes(
+                                        AudioDeviceAttributes.ROLE_OUTPUT,
+                                        AudioDeviceInfo.TYPE_HEARING_AID,
+                                        mCachedDevice.getAddress());
+                        break;
+                    default:
+                        Log.i(
+                                TAG,
+                                "unrecognized profile for spatial audio: "
+                                        + profile.getProfileId());
+                        break;
+                }
+                break;
+            }
+        }
+        mAudioDevice = null;
+        if (saDevice != null && mSpatializer.isAvailableForDevice(saDevice)) {
+            mAudioDevice = saDevice;
+        }
+
+        Log.d(
+                TAG,
+                "getAvailableDevice() device : "
+                        + mCachedDevice.getDevice().getAnonymizedAddress()
+                        + ", is available : "
+                        + (mAudioDevice != null)
+                        + ", type : "
+                        + (mAudioDevice == null ? "no type" : mAudioDevice.getType()));
+    }
+
     @VisibleForTesting
     void setAvailableDevice(AudioDeviceAttributes audioDevice) {
         mAudioDevice = audioDevice;
diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index 1c14712..11c05f3 100644
--- a/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -162,6 +162,8 @@
 import com.android.settings.notification.app.ChannelNotificationSettings;
 import com.android.settings.notification.app.ConversationListSettings;
 import com.android.settings.notification.history.NotificationStation;
+import com.android.settings.notification.modes.ZenModeFragment;
+import com.android.settings.notification.modes.ZenModesListFragment;
 import com.android.settings.notification.zen.ZenAccessSettings;
 import com.android.settings.notification.zen.ZenModeAutomationSettings;
 import com.android.settings.notification.zen.ZenModeBlockedEffectsSettings;
@@ -396,6 +398,8 @@
             CellularSecuritySettingsFragment.class.getName(),
             AccessibilityHearingAidsFragment.class.getName(),
             HearingDevicePairingFragment.class.getName(),
+            ZenModesListFragment.class.getName(),
+            ZenModeFragment.class.getName()
     };
 
     public static final String[] SETTINGS_FOR_RESTRICTED = {
diff --git a/src/com/android/settings/datausage/lib/BillingCycleRepository.kt b/src/com/android/settings/datausage/lib/BillingCycleRepository.kt
index d324c75..59c853d 100644
--- a/src/com/android/settings/datausage/lib/BillingCycleRepository.kt
+++ b/src/com/android/settings/datausage/lib/BillingCycleRepository.kt
@@ -21,7 +21,7 @@
 import android.os.ServiceManager
 import android.util.Log
 import androidx.annotation.OpenForTesting
-import com.android.settings.network.telephony.TelephonyRepository
+import com.android.settings.network.telephony.MobileDataRepository
 import com.android.settingslib.spaprivileged.framework.common.userManager
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.Flow
@@ -36,13 +36,13 @@
         INetworkManagementService.Stub.asInterface(
             ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)
         ),
-    private val telephonyRepository: TelephonyRepository = TelephonyRepository(context),
+    private val mobileDataRepository: MobileDataRepository = MobileDataRepository(context),
 ) {
     private val userManager = context.userManager
 
     fun isModifiableFlow(subId: Int): Flow<Boolean> =
-        telephonyRepository.isDataEnabledFlow(subId).map { isDataEnabled ->
-            isDataEnabled && isBandwidthControlEnabled() && userManager.isAdminUser
+        mobileDataRepository.isMobileDataEnabledFlow(subId).map { mobileDataEnabled ->
+            mobileDataEnabled && isBandwidthControlEnabled() && userManager.isAdminUser
         }.conflate().flowOn(Dispatchers.Default)
 
     open fun isBandwidthControlEnabled(): Boolean = try {
diff --git a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogRepository.kt b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogRepository.kt
index 5ed6993..760f8b6 100644
--- a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogRepository.kt
+++ b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogRepository.kt
@@ -23,10 +23,10 @@
 import androidx.lifecycle.LifecycleOwner
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
+import com.android.settings.network.telephony.CarrierConfigRepository
 import com.android.settings.network.telephony.SimSlotRepository
 import com.android.settings.network.telephony.ims.ImsMmTelRepository
 import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl
-import com.android.settings.network.telephony.safeGetConfig
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
@@ -39,7 +39,9 @@
 import kotlinx.coroutines.launch
 
 @OptIn(ExperimentalCoroutinesApi::class)
-class SimStatusDialogRepository @JvmOverloads constructor(
+class SimStatusDialogRepository
+@JvmOverloads
+constructor(
     private val context: Context,
     private val simSlotRepository: SimSlotRepository = SimSlotRepository(context),
     private val signalStrengthRepository: SignalStrengthRepository =
@@ -48,7 +50,7 @@
         ImsMmTelRepositoryImpl(context, subId)
     },
 ) {
-    private val carrierConfigManager = context.getSystemService(CarrierConfigManager::class.java)!!
+    private val carrierConfigRepository = CarrierConfigRepository(context)
 
     data class SimStatusDialogInfo(
         val signalStrength: String? = null,
@@ -73,7 +75,8 @@
     }
 
     private fun simStatusDialogInfoBySlotFlow(simSlotIndex: Int): Flow<SimStatusDialogInfo> =
-        simSlotRepository.subIdInSimSlotFlow(simSlotIndex)
+        simSlotRepository
+            .subIdInSimSlotFlow(simSlotIndex)
             .flatMapLatest { subId ->
                 if (SubscriptionManager.isValidSubscriptionId(subId)) {
                     simStatusDialogInfoFlow(subId)
@@ -99,22 +102,16 @@
         }
 
     private fun showUpFlow(subId: Int) = flow {
-        val config = carrierConfigManager.safeGetConfig(
-            keys = listOf(
-                CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL,
-                CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL,
-            ),
-            subId = subId,
-        )
-        val visibility = SimStatusDialogVisibility(
-            signalStrengthShowUp = config.getBoolean(
-                CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL,
-                true,  // by default we show the signal strength in sim status
-            ),
-            imsRegisteredShowUp = config.getBoolean(
-                CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL
-            ),
-        )
+        val visibility =
+            carrierConfigRepository.transformConfig(subId) {
+                SimStatusDialogVisibility(
+                    signalStrengthShowUp =
+                        getBoolean(
+                            CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL),
+                    imsRegisteredShowUp =
+                        getBoolean(CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL),
+                )
+            }
         emit(visibility)
     }
 }
diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
index 0132273..e7b9a42 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
@@ -23,11 +23,13 @@
 import android.util.ArrayMap;
 import android.util.SparseIntArray;
 
+import com.android.settings.fuelgauge.batteryusage.BatteryDiffData;
 import com.android.settings.fuelgauge.batteryusage.DetectRequestSourceType;
 import com.android.settings.fuelgauge.batteryusage.PowerAnomalyEventList;
 import com.android.settingslib.fuelgauge.Estimate;
 
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /** Feature Provider used in power usage */
@@ -157,4 +159,8 @@
 
     /** Whether the device is under the battery defender mode */
     boolean isBatteryDefend(BatteryInfo info);
+
+    /** Collect and process battery reattribute data if needed. */
+    boolean processBatteryReattributeData(
+            Context context, Map<Long, BatteryDiffData> batteryDiffDataMap);
 }
diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
index 1675ce6..267c0a3 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
@@ -28,12 +28,14 @@
 import android.util.SparseIntArray;
 
 import com.android.internal.util.ArrayUtils;
+import com.android.settings.fuelgauge.batteryusage.BatteryDiffData;
 import com.android.settings.fuelgauge.batteryusage.DetectRequestSourceType;
 import com.android.settings.fuelgauge.batteryusage.PowerAnomalyEventList;
 import com.android.settingslib.fuelgauge.Estimate;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /** Implementation of {@code PowerUsageFeatureProvider} */
@@ -245,4 +247,10 @@
     public boolean isBatteryDefend(BatteryInfo info) {
         return info.isBatteryDefender && !isExtraDefend();
     }
+
+    @Override
+    public boolean processBatteryReattributeData(
+            Context context, Map<Long, BatteryDiffData> batteryDiffDataMap) {
+        return false;
+    }
 }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java
index 7ea7203..b5d5099 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java
@@ -77,11 +77,13 @@
         processAndSortEntries(mSystemEntries);
     }
 
-    long getStartTimestamp() {
+    /** Gets the start timestamp. */
+    public long getStartTimestamp() {
         return mStartTimestamp;
     }
 
-    long getEndTimestamp() {
+    /** Gets the end timestamp. */
+    public long getEndTimestamp() {
         return mEndTimestamp;
     }
 
@@ -97,7 +99,8 @@
         return mScreenOnTime;
     }
 
-    List<BatteryDiffEntry> getAppDiffEntryList() {
+    /** Gets the {@link BatteryDiffEntry} list for apps. */
+    public List<BatteryDiffEntry> getAppDiffEntryList() {
         return mAppEntries;
     }
 
@@ -296,8 +299,7 @@
      * Sets total consume power, and adjusts the percentages to ensure the total round percentage
      * could be 100%, and then sorts entries based on the sorting key.
      */
-    @VisibleForTesting
-    static void processAndSortEntries(final List<BatteryDiffEntry> batteryDiffEntries) {
+    public static void processAndSortEntries(final List<BatteryDiffEntry> batteryDiffEntries) {
         if (batteryDiffEntries.isEmpty()) {
             return;
         }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java
index 0836912..7ef4615 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java
@@ -128,6 +128,9 @@
                             final PowerUsageFeatureProvider featureProvider =
                                     FeatureFactory.getFeatureFactory()
                                             .getPowerUsageFeatureProvider();
+                            // Collect and process battery reattribute data.
+                            featureProvider.processBatteryReattributeData(
+                                    context, batteryDiffDataMap);
                             DatabaseUtils.sendBatteryUsageSlotData(
                                     context,
                                     ConvertUtils.convertToBatteryUsageSlotList(
diff --git a/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProvider.java b/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProvider.java
index baae109..7e759ee 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProvider.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProvider.java
@@ -55,12 +55,12 @@
         }
         writer.println("dump BatteryUsage and AppUsage states:");
         LogUtils.dumpAppOptimizationModeEventHist(context, writer);
+        LogUtils.dumpBatteryReattributeDatabaseHist(context, writer);
         LogUtils.dumpBatteryUsageDatabaseHist(context, writer);
         LogUtils.dumpAppUsageDatabaseHist(context, writer);
         LogUtils.dumpBatteryUsageSlotDatabaseHist(context, writer);
         LogUtils.dumpBatteryEventDatabaseHist(context, writer);
         LogUtils.dumpBatteryStateDatabaseHist(context, writer);
-        LogUtils.dumpBatteryReattributeDatabaseHist(context, writer);
     }
 
     @Override
diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeEntity.java b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeEntity.java
index 5367849..0ee2260 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeEntity.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeEntity.java
@@ -58,12 +58,16 @@
     @NonNull
     @Override
     public String toString() {
+        final BatteryReattribute batteryReattribute =
+                ConvertUtils.decodeBatteryReattribute(reattributeData);
         final StringBuilder builder = new StringBuilder()
                 .append("\nBatteryReattributeEntity{")
                 .append("\n\t" + utcToLocalTimeForLogging(timestampStart))
                 .append("\n\t" + utcToLocalTimeForLogging(timestampEnd))
-                .append("\n\t" + ConvertUtils.decodeBatteryReattribute(reattributeData))
-                .append("\n}");
-        return builder.toString();
+                .append("\n\t" + batteryReattribute);
+        if (batteryReattribute != null) {
+            builder.append("\n\t" + batteryReattribute.getReattributeDataMap());
+        }
+        return builder.append("\n}").toString();
     }
 }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDatabase.java b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDatabase.java
index 9a4f164..c3aea08 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDatabase.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDatabase.java
@@ -33,10 +33,10 @@
             BatteryUsageSlotEntity.class,
             BatteryReattributeEntity.class
         },
-        version = 1)
+        version = 3)
 public abstract class BatteryStateDatabase extends RoomDatabase {
     private static final String TAG = "BatteryStateDatabase";
-    private static final String DB_FILE_NAME = "battery-usage-db-v10";
+    private static final String DB_FILE_NAME = "battery-usage-db-v11";
 
     private static BatteryStateDatabase sBatteryStateDatabase;
 
diff --git a/src/com/android/settings/homepage/contextualcards/conditional/DndConditionCardController.java b/src/com/android/settings/homepage/contextualcards/conditional/DndConditionCardController.java
index e69a336..6362068 100644
--- a/src/com/android/settings/homepage/contextualcards/conditional/DndConditionCardController.java
+++ b/src/com/android/settings/homepage/contextualcards/conditional/DndConditionCardController.java
@@ -105,7 +105,8 @@
                         + mAppContext.getText(R.string.condition_zen_title))
                 .setTitleText(mAppContext.getText(R.string.condition_zen_title).toString())
                 .setSummaryText(getSummary())
-                .setIconDrawable(mAppContext.getDrawable(R.drawable.ic_do_not_disturb_on_24dp))
+                .setIconDrawable(mAppContext.getDrawable(
+                        com.android.settingslib.R.drawable.ic_do_not_disturb_on_24dp))
                 .setViewType(ConditionContextualCardRenderer.VIEW_TYPE_HALF_WIDTH)
                 .build();
     }
diff --git a/src/com/android/settings/network/MobileDataEnabledFlow.kt b/src/com/android/settings/network/MobileDataEnabledFlow.kt
deleted file mode 100644
index 1f995a9..0000000
--- a/src/com/android/settings/network/MobileDataEnabledFlow.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.network
-
-import android.content.Context
-import android.provider.Settings
-import android.telephony.SubscriptionManager
-import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalChangeFlow
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.merge
-
-/**
- * Flow for mobile data enabled changed event.
- *
- * Note: This flow can only notify enabled status changes, cannot provide the latest status.
- */
-fun Context.mobileDataEnabledFlow(subId: Int, sendInitialValue: Boolean = true): Flow<Unit> {
-    val flow = settingsGlobalChangeFlow(Settings.Global.MOBILE_DATA, sendInitialValue)
-    return when (subId) {
-        SubscriptionManager.INVALID_SUBSCRIPTION_ID -> flow
-        else -> {
-            val subIdFlow = settingsGlobalChangeFlow(
-                name = Settings.Global.MOBILE_DATA + subId,
-                sendInitialValue = false,
-            )
-            merge(flow, subIdFlow)
-        }
-    }
-}
diff --git a/src/com/android/settings/network/MobileNetworkRepository.java b/src/com/android/settings/network/MobileNetworkRepository.java
index 8ee5389..ce6f884 100644
--- a/src/com/android/settings/network/MobileNetworkRepository.java
+++ b/src/com/android/settings/network/MobileNetworkRepository.java
@@ -49,7 +49,6 @@
 import com.android.settingslib.mobile.dataservice.MobileNetworkInfoEntity;
 import com.android.settingslib.mobile.dataservice.SubscriptionInfoDao;
 import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
-import com.android.settingslib.mobile.dataservice.UiccInfoDao;
 import com.android.settingslib.mobile.dataservice.UiccInfoEntity;
 
 import java.util.ArrayList;
@@ -81,12 +80,9 @@
     private SubscriptionManager mSubscriptionManager;
     private MobileNetworkDatabase mMobileNetworkDatabase;
     private SubscriptionInfoDao mSubscriptionInfoDao;
-    private UiccInfoDao mUiccInfoDao;
     private MobileNetworkInfoDao mMobileNetworkInfoDao;
     private List<SubscriptionInfoEntity> mAvailableSubInfoEntityList = new ArrayList<>();
     private List<SubscriptionInfoEntity> mActiveSubInfoEntityList = new ArrayList<>();
-    private List<UiccInfoEntity> mUiccInfoEntityList = new ArrayList<>();
-    private List<MobileNetworkInfoEntity> mMobileNetworkInfoEntityList = new ArrayList<>();
     private Context mContext;
     private AirplaneModeObserver mAirplaneModeObserver;
     private DataRoamingObserver mDataRoamingObserver;
@@ -124,7 +120,6 @@
         mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_MOBILE_NETWORK_DB_CREATED);
         mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
         mSubscriptionInfoDao = mMobileNetworkDatabase.mSubscriptionInfoDao();
-        mUiccInfoDao = mMobileNetworkDatabase.mUiccInfoDao();
         mMobileNetworkInfoDao = mMobileNetworkDatabase.mMobileNetworkInfoDao();
         mAirplaneModeObserver = new AirplaneModeObserver(new Handler(Looper.getMainLooper()));
         mDataRoamingObserver = new DataRoamingObserver(new Handler(Looper.getMainLooper()));
@@ -338,22 +333,6 @@
                 lifecycleOwner, this::onAllMobileNetworkInfoChanged);
     }
 
-    public List<SubscriptionInfoEntity> getAvailableSubInfoEntityList() {
-        return mAvailableSubInfoEntityList;
-    }
-
-    public List<SubscriptionInfoEntity> getActiveSubscriptionInfoList() {
-        return mActiveSubInfoEntityList;
-    }
-
-    public List<UiccInfoEntity> getUiccInfoEntityList() {
-        return mUiccInfoEntityList;
-    }
-
-    public List<MobileNetworkInfoEntity> getMobileNetworkInfoEntityList() {
-        return mMobileNetworkInfoEntityList;
-    }
-
     public SubscriptionInfoEntity getSubInfoById(String subId) {
         return mSubscriptionInfoDao.querySubInfoById(subId);
     }
@@ -464,7 +443,6 @@
     }
 
     private void onAllUiccInfoChanged(List<UiccInfoEntity> uiccInfoEntityList) {
-        mUiccInfoEntityList = new ArrayList<>(uiccInfoEntityList);
         for (MobileNetworkCallback callback : sCallbacks) {
             callback.onAllUiccInfoChanged(uiccInfoEntityList);
         }
@@ -474,7 +452,6 @@
 
     private void onAllMobileNetworkInfoChanged(
             List<MobileNetworkInfoEntity> mobileNetworkInfoEntityList) {
-        mMobileNetworkInfoEntityList = new ArrayList<>(mobileNetworkInfoEntityList);
         for (MobileNetworkCallback callback : sCallbacks) {
             callback.onAllMobileNetworkInfoChanged(mobileNetworkInfoEntityList);
         }
@@ -515,8 +492,6 @@
         mMobileNetworkDatabase.deleteSubInfoBySubId(subId);
         mMobileNetworkDatabase.deleteUiccInfoBySubId(subId);
         mMobileNetworkDatabase.deleteMobileNetworkInfoBySubId(subId);
-        mUiccInfoEntityList.removeIf(info -> info.subId.equals(subId));
-        mMobileNetworkInfoEntityList.removeIf(info -> info.subId.equals(subId));
         int id = Integer.parseInt(subId);
         removerRegisterBySubId(id);
         mSubscriptionInfoMap.remove(id);
@@ -741,7 +716,6 @@
     }
 
     private class PhoneCallStateTelephonyCallback extends TelephonyCallback implements
-            TelephonyCallback.CallStateListener,
             TelephonyCallback.UserMobileDataStateListener {
 
         private int mSubId;
@@ -751,13 +725,6 @@
         }
 
         @Override
-        public void onCallStateChanged(int state) {
-            for (MobileNetworkCallback callback : sCallbacks) {
-                callback.onCallStateChanged(state);
-            }
-        }
-
-        @Override
         public void onUserMobileDataStateChanged(boolean enabled) {
             Log.d(TAG, "onUserMobileDataStateChanged enabled " + enabled + " on SUB " + mSubId);
             sExecutor.execute(() -> {
@@ -793,9 +760,6 @@
          */
         default void onDataRoamingChanged(int subId, boolean enabled) {
         }
-
-        default void onCallStateChanged(int state) {
-        }
     }
 
     public void dump(IndentingPrintWriter printwriter) {
@@ -803,8 +767,6 @@
         printwriter.increaseIndent();
         printwriter.println(" availableSubInfoEntityList= " + mAvailableSubInfoEntityList);
         printwriter.println(" activeSubInfoEntityList=" + mActiveSubInfoEntityList);
-        printwriter.println(" mobileNetworkInfoEntityList= " + mMobileNetworkInfoEntityList);
-        printwriter.println(" uiccInfoEntityList= " + mUiccInfoEntityList);
         printwriter.println(" CacheSubscriptionInfoEntityMap= " + sCacheSubscriptionInfoEntityMap);
         printwriter.println(" SubscriptionInfoMap= " + mSubscriptionInfoMap);
         printwriter.flush();
diff --git a/src/com/android/settings/network/ResetNetworkOperationBuilder.java b/src/com/android/settings/network/ResetNetworkOperationBuilder.java
index 6f36074..47c06d4 100644
--- a/src/com/android/settings/network/ResetNetworkOperationBuilder.java
+++ b/src/com/android/settings/network/ResetNetworkOperationBuilder.java
@@ -18,6 +18,7 @@
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothManager;
+import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.net.ConnectivityManager;
@@ -28,11 +29,14 @@
 import android.net.wifi.p2p.WifiP2pManager;
 import android.os.Looper;
 import android.os.RecoverySystem;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.R;
 import com.android.settings.ResetNetworkRequest;
@@ -257,15 +261,15 @@
      */
     public ResetNetworkOperationBuilder restartPhoneProcess() {
         Runnable runnable = () -> {
-            try {
-                mContext.getContentResolver().call(
-                        getResetTelephonyContentProviderAuthority(),
-                        METHOD_RESTART_PHONE_PROCESS,
-                        /* arg= */ null,
-                        /* extras= */ null);
-                Log.i(TAG, "Phone process was restarted.");
-            } catch (IllegalArgumentException iae) {
-                Log.w(TAG, "Fail to restart phone process: " + iae);
+            // Unstable content provider can avoid us getting killed together with phone process
+            try (ContentProviderClient client = getUnstableTelephonyContentProviderClient()) {
+                if (client != null) {
+                    client.call(METHOD_RESTART_PHONE_PROCESS, /* arg= */ null, /* extra= */ null);
+                    Log.i(TAG, "Phone process was restarted.");
+                }
+            } catch (RemoteException re) {
+                // It's normal to throw RE since phone process immediately dies
+                Log.i(TAG, "Phone process has been restarted: " + re);
             }
         };
         mResetSequence.add(runnable);
@@ -279,15 +283,13 @@
      */
     public ResetNetworkOperationBuilder restartRild() {
         Runnable runnable = () -> {
-            try {
-                mContext.getContentResolver().call(
-                        getResetTelephonyContentProviderAuthority(),
-                        METHOD_RESTART_RILD,
-                        /* arg= */ null,
-                        /* extras= */ null);
-                Log.i(TAG, "RILD was restarted.");
-            } catch (IllegalArgumentException iae) {
-                Log.w(TAG, "Fail to restart RILD: " + iae);
+            try (ContentProviderClient client = getUnstableTelephonyContentProviderClient()) {
+                if (client != null) {
+                    client.call(METHOD_RESTART_RILD, /* arg= */ null, /* extra= */ null);
+                    Log.i(TAG, "RILD was restarted.");
+                }
+            } catch (RemoteException re) {
+                Log.w(TAG, "Fail to restart RILD: " + re);
             }
         };
         mResetSequence.add(runnable);
@@ -322,9 +324,18 @@
      * @return the authority of the telephony content provider that support methods
      * resetPhoneProcess and resetRild.
      */
-    @VisibleForTesting
-    String getResetTelephonyContentProviderAuthority() {
+    private String getResetTelephonyContentProviderAuthority() {
         return mContext.getResources().getString(
                 R.string.reset_telephony_stack_content_provider_authority);
     }
+
+    /**
+     * @return the unstable content provider to avoid us getting killed with phone process
+     */
+    @Nullable
+    @VisibleForTesting
+    public ContentProviderClient getUnstableTelephonyContentProviderClient() {
+        return mContext.getContentResolver().acquireUnstableContentProviderClient(
+                getResetTelephonyContentProviderAuthority());
+    }
 }
diff --git a/src/com/android/settings/network/SimOnboardingService.kt b/src/com/android/settings/network/SimOnboardingService.kt
index f6c6065..ea0b5ac 100644
--- a/src/com/android/settings/network/SimOnboardingService.kt
+++ b/src/com/android/settings/network/SimOnboardingService.kt
@@ -27,9 +27,8 @@
 import android.telephony.UiccSlotInfo
 import android.util.Log
 import com.android.settings.network.SimOnboardingActivity.Companion.CallbackType
-import com.android.settings.network.telephony.TelephonyRepository
+import com.android.settings.network.telephony.MobileDataRepository
 import com.android.settings.sim.SimActivationNotifier
-import com.android.settings.spa.network.setAutomaticData
 import com.android.settings.spa.network.setDefaultData
 import com.android.settings.spa.network.setDefaultSms
 import com.android.settings.spa.network.setDefaultVoice
@@ -366,7 +365,7 @@
                     wifiPickerTrackerHelper,
                     targetPrimarySimMobileData
                 )
-                TelephonyRepository(context).setAutomaticData(
+                MobileDataRepository(context).setAutoDataSwitch(
                     targetNonDds,
                     targetPrimarySimAutoDataSwitch.value
                 )
diff --git a/src/com/android/settings/network/apn/ApnEditCarrierEnabled.kt b/src/com/android/settings/network/apn/ApnEditCarrierEnabled.kt
new file mode 100644
index 0000000..bd58da8
--- /dev/null
+++ b/src/com/android/settings/network/apn/ApnEditCarrierEnabled.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.apn
+
+import android.provider.Telephony
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.booleanResource
+import androidx.compose.ui.res.stringResource
+import com.android.settings.R
+import com.android.settingslib.spa.widget.preference.SwitchPreference
+import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
+
+@Composable
+fun ApnEditCarrierEnabled(apnData: ApnData, onCarrierEnabledChanged: (Boolean) -> Unit) {
+    SwitchPreference(
+        object : SwitchPreferenceModel {
+            override val title = stringResource(R.string.carrier_enabled)
+            val allowEdit = booleanResource(R.bool.config_allow_edit_carrier_enabled)
+            override val changeable = {
+                allowEdit && apnData.isFieldEnabled(Telephony.Carriers.CARRIER_ENABLED)
+            }
+            override val checked = { apnData.carrierEnabled }
+            override val onCheckedChange = onCarrierEnabledChanged
+        }
+    )
+}
diff --git a/src/com/android/settings/network/apn/ApnEditPageProvider.kt b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
index 099e2fa..5442082 100644
--- a/src/com/android/settings/network/apn/ApnEditPageProvider.kt
+++ b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
@@ -235,19 +235,7 @@
                 enabled = apnData.isFieldEnabled(Telephony.Carriers.ROAMING_PROTOCOL),
             ) { apnData = apnData.copy(apnRoaming = it) }
             ApnNetworkTypeCheckBox(apnData) { apnData = apnData.copy(networkType = it) }
-            SwitchPreference(
-                object : SwitchPreferenceModel {
-                    override val title = stringResource(R.string.carrier_enabled)
-                    override val changeable = {
-                        apnData.apnEnableEnabled &&
-                            apnData.isFieldEnabled(Telephony.Carriers.CARRIER_ENABLED)
-                    }
-                    override val checked = { apnData.apnEnable }
-                    override val onCheckedChange = { newChecked: Boolean ->
-                        apnData = apnData.copy(apnEnable = newChecked)
-                    }
-                }
-            )
+            ApnEditCarrierEnabled(apnData) { apnData = apnData.copy(carrierEnabled = it) }
         }
     }
 }
\ No newline at end of file
diff --git a/src/com/android/settings/network/apn/ApnPreference.java b/src/com/android/settings/network/apn/ApnPreference.java
index 879fcb6..55258c1 100644
--- a/src/com/android/settings/network/apn/ApnPreference.java
+++ b/src/com/android/settings/network/apn/ApnPreference.java
@@ -85,10 +85,11 @@
         final RelativeLayout textArea = (RelativeLayout) view.findViewById(R.id.text_layout);
         textArea.setOnClickListener(this);
 
+        final View radioButtonFrame = view.itemView.requireViewById(R.id.apn_radio_button_frame);
         final RadioButton rb = view.itemView.requireViewById(R.id.apn_radiobutton);
         mRadioButton = rb;
         if (mDefaultSelectable) {
-            view.itemView.requireViewById(R.id.apn_radio_button_frame).setOnClickListener((v) -> {
+            radioButtonFrame.setOnClickListener((v) -> {
                 rb.performClick();
             });
             rb.setOnCheckedChangeListener(this);
@@ -96,9 +97,9 @@
             mProtectFromCheckedChange = true;
             rb.setChecked(mIsChecked);
             mProtectFromCheckedChange = false;
-            rb.setVisibility(View.VISIBLE);
+            radioButtonFrame.setVisibility(View.VISIBLE);
         } else {
-            rb.setVisibility(View.GONE);
+            radioButtonFrame.setVisibility(View.GONE);
         }
     }
 
diff --git a/src/com/android/settings/network/apn/ApnRepository.kt b/src/com/android/settings/network/apn/ApnRepository.kt
index 2d41976..8433715 100644
--- a/src/com/android/settings/network/apn/ApnRepository.kt
+++ b/src/com/android/settings/network/apn/ApnRepository.kt
@@ -90,7 +90,7 @@
                 apnRoaming = context.convertProtocol2Options(
                     cursor.getString(Telephony.Carriers.ROAMING_PROTOCOL)
                 ),
-                apnEnable = cursor.getInt(Telephony.Carriers.CARRIER_ENABLED) == 1,
+                carrierEnabled = cursor.getInt(Telephony.Carriers.CARRIER_ENABLED) == 1,
                 networkType = cursor.getLong(Telephony.Carriers.NETWORK_TYPE_BITMASK),
                 edited = cursor.getInt(Telephony.Carriers.EDITED_STATUS),
                 userEditable = cursor.getInt(Telephony.Carriers.USER_EDITABLE),
diff --git a/src/com/android/settings/network/apn/ApnStatus.kt b/src/com/android/settings/network/apn/ApnStatus.kt
index dc50452..6492d39 100644
--- a/src/com/android/settings/network/apn/ApnStatus.kt
+++ b/src/com/android/settings/network/apn/ApnStatus.kt
@@ -44,11 +44,10 @@
     val apnType: String = "",
     val apnProtocol: Int = -1,
     val apnRoaming: Int = -1,
-    val apnEnable: Boolean = true,
+    val carrierEnabled: Boolean = true,
     val networkType: Long = 0,
     val edited: Int = Telephony.Carriers.USER_EDITED,
     val userEditable: Int = 1,
-    val apnEnableEnabled: Boolean = true,
     val newApn: Boolean = false,
     val subId: Int = -1,
     val validEnabled: Boolean = false,
@@ -72,7 +71,7 @@
         Telephony.Carriers.NETWORK_TYPE_BITMASK to networkType,
         // Copy network type into lingering network type.
         Telephony.Carriers.LINGERING_NETWORK_TYPE_BITMASK to networkType,
-        Telephony.Carriers.CARRIER_ENABLED to apnEnable,
+        Telephony.Carriers.CARRIER_ENABLED to carrierEnabled,
         Telephony.Carriers.EDITED_STATUS to Telephony.Carriers.USER_EDITED,
     )
 
@@ -134,10 +133,6 @@
         )
     }
 
-    apnDataInit = apnDataInit.copy(
-        apnEnableEnabled =
-        context.resources.getBoolean(R.bool.config_allow_edit_carrier_enabled)
-    )
     // TODO: mIsCarrierIdApn
     return disableInit(apnDataInit)
 }
diff --git a/src/com/android/settings/network/telephony/CarrierConfigManagerExt.kt b/src/com/android/settings/network/telephony/CarrierConfigManagerExt.kt
index 05b4c07..5408ab0 100644
--- a/src/com/android/settings/network/telephony/CarrierConfigManagerExt.kt
+++ b/src/com/android/settings/network/telephony/CarrierConfigManagerExt.kt
@@ -24,6 +24,7 @@
 /**
  * Gets the configuration values of the specified config keys applied.
  */
+@Deprecated("Use CarrierConfigRepository instead")
 fun CarrierConfigManager.safeGetConfig(
     keys: List<String>,
     subId: Int = SubscriptionManager.getDefaultSubscriptionId(),
diff --git a/src/com/android/settings/network/telephony/CarrierConfigRepository.kt b/src/com/android/settings/network/telephony/CarrierConfigRepository.kt
new file mode 100644
index 0000000..3ec529d
--- /dev/null
+++ b/src/com/android/settings/network/telephony/CarrierConfigRepository.kt
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.telephony
+
+import android.content.Context
+import android.os.PersistableBundle
+import android.telephony.CarrierConfigManager
+import android.telephony.SubscriptionManager
+import android.util.Log
+import androidx.annotation.VisibleForTesting
+import java.util.concurrent.ConcurrentHashMap
+import kotlinx.atomicfu.atomic
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.asExecutor
+
+class CarrierConfigRepository(private val context: Context) {
+
+    private val carrierConfigManager: CarrierConfigManager? =
+        context.getSystemService(CarrierConfigManager::class.java)
+
+    private enum class KeyType {
+        BOOLEAN,
+        INT,
+        STRING
+    }
+
+    interface CarrierConfigAccessor {
+        fun getBoolean(key: String): Boolean
+
+        fun getInt(key: String): Int
+
+        fun getString(key: String): String?
+    }
+
+    private class Accessor(private val cache: ConfigCache) : CarrierConfigAccessor {
+        private val keysToRetrieve = mutableMapOf<String, KeyType>()
+
+        override fun getBoolean(key: String): Boolean {
+            check(key.endsWith("_bool")) { "Boolean key should ends with _bool" }
+            val value = cache[key]
+            return if (value == null) {
+                keysToRetrieve += key to KeyType.BOOLEAN
+                DefaultConfig.getBoolean(key)
+            } else {
+                check(value is BooleanConfigValue) { "Boolean value type wrong" }
+                value.value
+            }
+        }
+
+        override fun getInt(key: String): Int {
+            check(key.endsWith("_int")) { "Int key should ends with _int" }
+            val value = cache[key]
+            return if (value == null) {
+                keysToRetrieve += key to KeyType.INT
+                DefaultConfig.getInt(key)
+            } else {
+                check(value is IntConfigValue) { "Int value type wrong" }
+                value.value
+            }
+        }
+
+        override fun getString(key: String): String? {
+            check(key.endsWith("_string")) { "String key should ends with _string" }
+            val value = cache[key]
+            return if (value == null) {
+                keysToRetrieve += key to KeyType.STRING
+                DefaultConfig.getString(key)
+            } else {
+                check(value is StringConfigValue) { "String value type wrong" }
+                value.value
+            }
+        }
+
+        fun getKeysToRetrieve(): Map<String, KeyType> = keysToRetrieve
+    }
+
+    /**
+     * Gets the configuration values for the given [subId].
+     *
+     * Configuration values could be accessed in [block]. Note: [block] could be called multiple
+     * times, so it should be pure function without side effort.
+     */
+    fun <T> transformConfig(subId: Int, block: CarrierConfigAccessor.() -> T): T {
+        val perSubCache = getPerSubCache(subId)
+        val accessor = Accessor(perSubCache)
+        val result = accessor.block()
+        val keysToRetrieve = accessor.getKeysToRetrieve()
+        // If all keys found in the first pass, no need to collect again
+        if (keysToRetrieve.isEmpty()) return result
+
+        perSubCache.update(subId, keysToRetrieve)
+
+        return accessor.block()
+    }
+
+    /** Gets the configuration boolean for the given [subId] and [key]. */
+    fun getBoolean(subId: Int, key: String): Boolean = transformConfig(subId) { getBoolean(key) }
+
+    /** Gets the configuration int for the given [subId] and [key]. */
+    fun getInt(subId: Int, key: String): Int = transformConfig(subId) { getInt(key) }
+
+    /** Gets the configuration string for the given [subId] and [key]. */
+    fun getString(subId: Int, key: String): String? = transformConfig(subId) { getString(key) }
+
+    private fun ConfigCache.update(subId: Int, keysToRetrieve: Map<String, KeyType>) {
+        val config = safeGetConfig(subId, keysToRetrieve.keys) ?: return
+        for ((key, type) in keysToRetrieve) {
+            when (type) {
+                KeyType.BOOLEAN -> this[key] = BooleanConfigValue(config.getBoolean(key))
+                KeyType.INT -> this[key] = IntConfigValue(config.getInt(key))
+                KeyType.STRING -> this[key] = StringConfigValue(config.getString(key))
+            }
+        }
+    }
+
+    /** Gets the configuration values of the specified config keys applied. */
+    private fun safeGetConfig(subId: Int, keys: Collection<String>): PersistableBundle? {
+        if (carrierConfigManager == null || !SubscriptionManager.isValidSubscriptionId(subId)) {
+            return null
+        }
+        tryRegisterListener(context)
+        return try {
+            carrierConfigManager.getConfigForSubId(subId, *keys.toTypedArray())
+        } catch (e: Exception) {
+            Log.e(TAG, "safeGetConfig: exception", e)
+            // The CarrierConfigLoader (the service implemented the CarrierConfigManager) hasn't
+            // been initialized yet. This may occurs during very early phase of phone booting up
+            // or when Phone process has been restarted.
+            // Settings should not assume Carrier config loader (and any other system services
+            // as well) are always available. If not available, use default value instead.
+            null
+        }
+    }
+
+    companion object {
+        private const val TAG = "CarrierConfigRepository"
+
+        private val DefaultConfig = CarrierConfigManager.getDefaultConfig()
+
+        /** Cache of config values for each subscription. */
+        private val Cache = ConcurrentHashMap<Int, ConfigCache>()
+
+        private fun getPerSubCache(subId: Int) =
+            Cache.computeIfAbsent(subId) { ConcurrentHashMap() }
+
+        /** To make sure the registerCarrierConfigChangeListener is only called once. */
+        private val ListenerRegistered = atomic(false)
+
+        private fun tryRegisterListener(context: Context) {
+            if (ListenerRegistered.compareAndSet(expect = false, update = true)) {
+                val carrierConfigManager =
+                    context.applicationContext.getSystemService(CarrierConfigManager::class.java)
+                if (carrierConfigManager != null) {
+                    carrierConfigManager.registerCarrierConfigChangeListener()
+                } else {
+                    ListenerRegistered.getAndSet(false)
+                }
+            }
+        }
+
+        private fun CarrierConfigManager.registerCarrierConfigChangeListener() {
+            val executor = Dispatchers.Default.asExecutor()
+            registerCarrierConfigChangeListener(executor) { _, subId, _, _ ->
+                Log.d(TAG, "[$subId] onCarrierConfigChanged")
+                Cache.remove(subId)
+            }
+        }
+
+        @VisibleForTesting
+        fun resetForTest() {
+            Cache.clear()
+            ListenerRegistered.getAndSet(false)
+        }
+
+        @VisibleForTesting
+        fun setBooleanForTest(subId: Int, key: String, value: Boolean) {
+            check(key.endsWith("_bool")) { "Boolean key should ends with _bool" }
+            getPerSubCache(subId)[key] = BooleanConfigValue(value)
+        }
+
+        @VisibleForTesting
+        fun setIntForTest(subId: Int, key: String, value: Int) {
+            check(key.endsWith("_int")) { "Int key should ends with _int" }
+            getPerSubCache(subId)[key] = IntConfigValue(value)
+        }
+
+        @VisibleForTesting
+        fun setStringForTest(subId: Int, key: String, value: String) {
+            check(key.endsWith("_string")) { "String key should ends with _string" }
+            getPerSubCache(subId)[key] = StringConfigValue(value)
+        }
+    }
+}
+
+private sealed interface ConfigValue
+
+private data class BooleanConfigValue(val value: Boolean) : ConfigValue
+
+private data class IntConfigValue(val value: Int) : ConfigValue
+
+private data class StringConfigValue(val value: String?) : ConfigValue
+
+private typealias ConfigCache = ConcurrentHashMap<String, ConfigValue>
diff --git a/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt b/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt
index e14d5f8..445597f 100644
--- a/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt
+++ b/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt
@@ -22,7 +22,6 @@
 import android.telephony.data.ApnSetting
 import androidx.lifecycle.LifecycleOwner
 import androidx.preference.PreferenceScreen
-import com.android.settings.network.mobileDataEnabledFlow
 import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
 import kotlinx.coroutines.flow.combine
 
@@ -71,7 +70,7 @@
 
     override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
         combine(
-            mContext.mobileDataEnabledFlow(mSubId),
+            MobileDataRepository(mContext).mobileDataEnabledChangedFlow(mSubId),
             mContext.subscriptionsChangedFlow(), // Capture isMobileDataPolicyEnabled() changes
         ) { _, _ -> }.collectLatestWithLifecycle(viewLifecycleOwner) {
             preferenceScreen?.let { super.displayPreference(it) }
diff --git a/src/com/android/settings/network/telephony/MobileDataRepository.kt b/src/com/android/settings/network/telephony/MobileDataRepository.kt
new file mode 100644
index 0000000..5a0dff7
--- /dev/null
+++ b/src/com/android/settings/network/telephony/MobileDataRepository.kt
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.telephony
+
+import android.content.Context
+import android.provider.Settings
+import android.telephony.SubscriptionManager
+import android.telephony.TelephonyManager
+import android.telephony.TelephonyManager.MobileDataPolicy
+import android.util.Log
+import com.android.settings.wifi.WifiPickerTrackerHelper
+import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalChangeFlow
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.catch
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onEach
+
+class MobileDataRepository(
+    private val context: Context,
+    private val subscriptionsChangedFlow: Flow<Unit> = context.subscriptionsChangedFlow(),
+) {
+    fun isMobileDataPolicyEnabledFlow(subId: Int, @MobileDataPolicy policy: Int): Flow<Boolean> {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false)
+        val telephonyManager = context.telephonyManager(subId)
+        return subscriptionsChangedFlow
+            .map { telephonyManager.isMobileDataPolicyEnabled(policy) }
+            .distinctUntilChanged()
+            .conflate()
+            .onEach { Log.d(TAG, "[$subId] isMobileDataPolicyEnabled($policy): $it") }
+            .flowOn(Dispatchers.Default)
+    }
+
+    fun setMobileDataPolicyEnabled(subId: Int, @MobileDataPolicy policy: Int, enabled: Boolean) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) return
+        Log.d(TAG, "[$subId] setMobileDataPolicyEnabled($policy): $enabled")
+        context.telephonyManager(subId).setMobileDataPolicyEnabled(policy, enabled)
+    }
+
+    fun setAutoDataSwitch(subId: Int, newEnabled: Boolean) {
+        setMobileDataPolicyEnabled(
+            subId = subId,
+            policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
+            enabled = newEnabled,
+        )
+    }
+
+    /**
+     * Flow for mobile data enabled changed event.
+     *
+     * Note: This flow can only notify enabled status changes, cannot provide the latest status.
+     */
+    fun mobileDataEnabledChangedFlow(subId: Int, sendInitialValue: Boolean = true): Flow<Unit> =
+        mobileSettingsGlobalChangedFlow(Settings.Global.MOBILE_DATA, subId, sendInitialValue)
+
+    private fun mobileSettingsGlobalChangedFlow(
+        name: String,
+        subId: Int,
+        sendInitialValue: Boolean = true,
+    ): Flow<Unit> {
+        val flow = context.settingsGlobalChangeFlow(name, sendInitialValue)
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) return flow
+        val subIdFlow =
+            context.settingsGlobalChangeFlow(name = name + subId, sendInitialValue = false)
+        return merge(flow, subIdFlow)
+    }
+
+    fun isMobileDataEnabledFlow(subId: Int): Flow<Boolean> {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false)
+        val telephonyManager = context.telephonyManager(subId)
+        return mobileDataEnabledChangedFlow(subId)
+            .map {
+                telephonyManager.isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER)
+            }
+            .catch { e ->
+                Log.w(TAG, "[$subId] isMobileDataEnabledFlow: exception", e)
+                emit(false)
+            }
+            .distinctUntilChanged()
+            .conflate()
+            .onEach { Log.d(TAG, "[$subId] isMobileDataEnabledFlow: $it") }
+            .flowOn(Dispatchers.Default)
+    }
+
+    fun setMobileDataEnabled(
+        subId: Int,
+        enabled: Boolean,
+        wifiPickerTrackerHelper: WifiPickerTrackerHelper? = null,
+    ) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) return
+
+        Log.d(TAG, "setMobileDataEnabled: $enabled")
+        MobileNetworkUtils.setMobileDataEnabled(
+            context, subId, enabled, /* disableOtherSubscriptions= */ true)
+
+        if (wifiPickerTrackerHelper != null &&
+            !wifiPickerTrackerHelper.isCarrierNetworkProvisionEnabled(subId)) {
+            wifiPickerTrackerHelper.setCarrierNetworkEnabled(enabled)
+        }
+    }
+
+    /** Creates an instance of a cold Flow for whether data roaming is enabled of given [subId]. */
+    fun isDataRoamingEnabledFlow(subId: Int): Flow<Boolean> {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false)
+        val telephonyManager = context.telephonyManager(subId)
+        return mobileSettingsGlobalChangedFlow(Settings.Global.DATA_ROAMING, subId)
+            .map { telephonyManager.isDataRoamingEnabled }
+            .distinctUntilChanged()
+            .conflate()
+            .onEach { Log.d(TAG, "[$subId] isDataRoamingEnabledFlow: $it") }
+            .flowOn(Dispatchers.Default)
+    }
+
+    private companion object {
+        private const val TAG = "MobileDataRepository"
+    }
+}
diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
index 8b927a9..d70ef25 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
@@ -20,11 +20,14 @@
 
 import android.app.Activity;
 import android.app.settings.SettingsEnums;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.os.Bundle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -79,7 +82,6 @@
     @VisibleForTesting
     static final String KEY_CLICKED_PREF = "key_clicked_pref";
 
-    private static final String KEY_ROAMING_PREF = "button_roaming_key";
     private static final String KEY_CALLS_PREF = "calls_preference";
     private static final String KEY_SMS_PREF = "sms_preference";
     private static final String KEY_MOBILE_DATA_PREF = "mobile_data_enable";
@@ -107,6 +109,15 @@
     private SubscriptionInfoEntity mSubscriptionInfoEntity;
     private MobileNetworkInfoEntity mMobileNetworkInfoEntity;
 
+    private BroadcastReceiver mBrocastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent.getAction().equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
+                redrawPreferenceControllers();
+            }
+        }
+    };
+
     public MobileNetworkSettings() {
         super(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
     }
@@ -178,8 +189,6 @@
 
         return Arrays.asList(
                 new DataUsageSummaryPreferenceController(context, mSubId),
-                new RoamingPreferenceController(context, KEY_ROAMING_PREF, getSettingsLifecycle(),
-                        this, mSubId),
                 new CallsDefaultSubscriptionController(context, KEY_CALLS_PREF,
                         getSettingsLifecycle(), this),
                 new SmsDefaultSubscriptionController(context, KEY_SMS_PREF, getSettingsLifecycle(),
@@ -263,8 +272,7 @@
         final RoamingPreferenceController roamingPreferenceController =
                 use(RoamingPreferenceController.class);
         if (roamingPreferenceController != null) {
-            roamingPreferenceController.init(getFragmentManager(), mSubId,
-                    mMobileNetworkInfoEntity);
+            roamingPreferenceController.init(getParentFragmentManager(), mSubId);
         }
         final SatelliteSettingPreferenceController satelliteSettingPreferenceController = use(
                 SatelliteSettingPreferenceController.class);
@@ -355,6 +363,10 @@
         mMobileNetworkRepository.updateEntity();
         // TODO: remove log after fixing b/182326102
         Log.d(LOG_TAG, "onResume() subId=" + mSubId);
+
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+        getContext().registerReceiver(mBrocastReceiver, intentFilter, Context.RECEIVER_EXPORTED);
     }
 
     private void onSubscriptionDetailChanged() {
@@ -374,6 +386,7 @@
     @Override
     public void onPause() {
         mMobileNetworkRepository.removeRegister(this);
+        getContext().unregisterReceiver(mBrocastReceiver);
         super.onPause();
     }
 
diff --git a/src/com/android/settings/network/telephony/RoamingPreferenceController.java b/src/com/android/settings/network/telephony/RoamingPreferenceController.java
deleted file mode 100644
index bf02308..0000000
--- a/src/com/android/settings/network/telephony/RoamingPreferenceController.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.network.telephony;
-
-import static androidx.lifecycle.Lifecycle.Event.ON_START;
-import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
-
-import android.content.Context;
-import android.os.PersistableBundle;
-import android.telephony.CarrierConfigManager;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.util.Log;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.fragment.app.FragmentManager;
-import androidx.lifecycle.LifecycleObserver;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.lifecycle.OnLifecycleEvent;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
-
-import com.android.settings.network.MobileNetworkRepository;
-import com.android.settingslib.RestrictedSwitchPreference;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-import com.android.settingslib.mobile.dataservice.MobileNetworkInfoEntity;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Preference controller for "Roaming"
- */
-public class RoamingPreferenceController extends TelephonyTogglePreferenceController implements
-        LifecycleObserver, MobileNetworkRepository.MobileNetworkCallback {
-    private static final String TAG = "RoamingController";
-    private static final String DIALOG_TAG = "MobileDataDialog";
-
-    private RestrictedSwitchPreference mSwitchPreference;
-    private TelephonyManager mTelephonyManager;
-    private CarrierConfigManager mCarrierConfigManager;
-    protected MobileNetworkRepository mMobileNetworkRepository;
-    protected LifecycleOwner mLifecycleOwner;
-    private List<MobileNetworkInfoEntity> mMobileNetworkInfoEntityList = new ArrayList<>();
-
-    @VisibleForTesting
-    FragmentManager mFragmentManager;
-    MobileNetworkInfoEntity mMobileNetworkInfoEntity;
-
-    public RoamingPreferenceController(Context context, String key, Lifecycle lifecycle,
-            LifecycleOwner lifecycleOwner, int subId) {
-        this(context, key);
-        mSubId = subId;
-        mLifecycleOwner = lifecycleOwner;
-        if (lifecycle != null) {
-            lifecycle.addObserver(this);
-        }
-    }
-
-    public RoamingPreferenceController(Context context, String key) {
-        super(context, key);
-        mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class);
-        mMobileNetworkRepository = MobileNetworkRepository.getInstance(context);
-    }
-
-    @Override
-    public int getAvailabilityStatus() {
-        final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId);
-        if (carrierConfig != null && carrierConfig.getBoolean(
-                CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL)) {
-            return CONDITIONALLY_UNAVAILABLE;
-        }
-        return AVAILABLE;
-    }
-
-    @OnLifecycleEvent(ON_START)
-    public void onStart() {
-        mMobileNetworkRepository.addRegister(mLifecycleOwner, this, mSubId);
-        mMobileNetworkRepository.updateEntity();
-    }
-
-    @OnLifecycleEvent(ON_STOP)
-    public void onStop() {
-        mMobileNetworkRepository.removeRegister(this);
-    }
-
-    @Override
-    public void displayPreference(PreferenceScreen screen) {
-        super.displayPreference(screen);
-        mSwitchPreference = screen.findPreference(getPreferenceKey());
-    }
-
-    @Override
-    public int getAvailabilityStatus(int subId) {
-        return mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
-                ? AVAILABLE
-                : AVAILABLE_UNSEARCHABLE;
-    }
-
-    @Override
-    public boolean setChecked(boolean isChecked) {
-        if (isDialogNeeded()) {
-            showDialog();
-        } else {
-            // Update data directly if we don't need dialog
-            mTelephonyManager.setDataRoamingEnabled(isChecked);
-            return true;
-        }
-
-        return false;
-    }
-
-    @Override
-    public void updateState(Preference preference) {
-        super.updateState(preference);
-        mSwitchPreference = (RestrictedSwitchPreference) preference;
-        update();
-    }
-
-    private void update() {
-        if (mSwitchPreference == null) {
-            return;
-        }
-        if (!mSwitchPreference.isDisabledByAdmin()) {
-            mSwitchPreference.setEnabled(mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID);
-            mSwitchPreference.setChecked(isChecked());
-        }
-    }
-
-    @VisibleForTesting
-    boolean isDialogNeeded() {
-        final boolean isRoamingEnabled = mMobileNetworkInfoEntity == null ? false
-                : mMobileNetworkInfoEntity.isDataRoamingEnabled;
-        final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(
-                mSubId);
-        // Need dialog if we need to turn on roaming and the roaming charge indication is allowed
-        if (!isRoamingEnabled && (carrierConfig == null || !carrierConfig.getBoolean(
-                CarrierConfigManager.KEY_DISABLE_CHARGE_INDICATION_BOOL))) {
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public boolean isChecked() {
-        return mMobileNetworkInfoEntity == null ? false
-                : mMobileNetworkInfoEntity.isDataRoamingEnabled;
-    }
-
-    public void init(FragmentManager fragmentManager, int subId, MobileNetworkInfoEntity entity) {
-        mFragmentManager = fragmentManager;
-        mSubId = subId;
-        mMobileNetworkInfoEntity = entity;
-        mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
-        if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-            return;
-        }
-        final TelephonyManager telephonyManager = mTelephonyManager
-                .createForSubscriptionId(mSubId);
-        if (telephonyManager == null) {
-            Log.w(TAG, "fail to init in sub" + mSubId);
-            mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-            return;
-        }
-        mTelephonyManager = telephonyManager;
-    }
-
-    private void showDialog() {
-        final RoamingDialogFragment dialogFragment = RoamingDialogFragment.newInstance(mSubId);
-
-        dialogFragment.show(mFragmentManager, DIALOG_TAG);
-    }
-
-    @VisibleForTesting
-    public void setMobileNetworkInfoEntity(MobileNetworkInfoEntity mobileNetworkInfoEntity) {
-        mMobileNetworkInfoEntity = mobileNetworkInfoEntity;
-    }
-
-    @Override
-    public void onAllMobileNetworkInfoChanged(
-            List<MobileNetworkInfoEntity> mobileNetworkInfoEntityList) {
-        mMobileNetworkInfoEntityList = mobileNetworkInfoEntityList;
-        mMobileNetworkInfoEntityList.forEach(entity -> {
-            if (Integer.parseInt(entity.subId) == mSubId) {
-                mMobileNetworkInfoEntity = entity;
-                update();
-                refreshSummary(mSwitchPreference);
-                return;
-            }
-        });
-    }
-
-    @Override
-    public void onDataRoamingChanged(int subId, boolean enabled) {
-        if (subId != mSubId) {
-            Log.d(TAG, "onDataRoamingChanged - wrong subId : " + subId + " / " + enabled);
-            return;
-        }
-        update();
-    }
-}
diff --git a/src/com/android/settings/network/telephony/RoamingPreferenceController.kt b/src/com/android/settings/network/telephony/RoamingPreferenceController.kt
new file mode 100644
index 0000000..2529d41
--- /dev/null
+++ b/src/com/android/settings/network/telephony/RoamingPreferenceController.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.telephony
+
+import android.content.Context
+import android.os.UserManager
+import android.telephony.CarrierConfigManager
+import android.telephony.SubscriptionManager
+import android.telephony.TelephonyManager
+import androidx.annotation.VisibleForTesting
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.res.stringResource
+import androidx.fragment.app.FragmentManager
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.settings.R
+import com.android.settings.spa.preference.ComposePreferenceController
+import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
+import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
+import com.android.settingslib.spaprivileged.template.preference.RestrictedSwitchPreference
+
+/** Preference controller for "Roaming" */
+class RoamingPreferenceController
+@JvmOverloads
+constructor(
+    context: Context,
+    key: String,
+    private val mobileDataRepository: MobileDataRepository = MobileDataRepository(context),
+) : ComposePreferenceController(context, key) {
+    @VisibleForTesting var fragmentManager: FragmentManager? = null
+    private var subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID
+
+    private var telephonyManager = context.getSystemService(TelephonyManager::class.java)!!
+    private val carrierConfigRepository = CarrierConfigRepository(context)
+
+    fun init(fragmentManager: FragmentManager, subId: Int) {
+        this.fragmentManager = fragmentManager
+        this.subId = subId
+        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
+    }
+
+    @Composable
+    override fun Content() {
+        val summary = stringResource(R.string.roaming_enable)
+        val isDataRoamingEnabled by
+            remember { mobileDataRepository.isDataRoamingEnabledFlow(subId) }
+                .collectAsStateWithLifecycle(null)
+        RestrictedSwitchPreference(
+            model =
+                object : SwitchPreferenceModel {
+                    override val title = stringResource(R.string.roaming)
+                    override val summary = { summary }
+                    override val checked = { isDataRoamingEnabled }
+                    override val onCheckedChange: (Boolean) -> Unit = { newChecked ->
+                        if (newChecked && isDialogNeeded()) {
+                            showDialog()
+                        } else {
+                            // Update data directly if we don't need dialog
+                            telephonyManager.isDataRoamingEnabled = newChecked
+                        }
+                    }
+                },
+            restrictions = Restrictions(keys = listOf(UserManager.DISALLOW_DATA_ROAMING)),
+        )
+    }
+
+    @VisibleForTesting
+    fun isDialogNeeded(): Boolean {
+        // Need dialog if we need to turn on roaming and the roaming charge indication is allowed
+        return !carrierConfigRepository.getBoolean(
+            subId, CarrierConfigManager.KEY_DISABLE_CHARGE_INDICATION_BOOL)
+    }
+
+    private fun showDialog() {
+        fragmentManager?.let { RoamingDialogFragment.newInstance(subId).show(it, DIALOG_TAG) }
+    }
+
+    companion object {
+        private const val DIALOG_TAG = "MobileDataDialog"
+    }
+}
diff --git a/src/com/android/settings/network/telephony/TelephonyRepository.kt b/src/com/android/settings/network/telephony/TelephonyRepository.kt
index 7c334ee..3317c71 100644
--- a/src/com/android/settings/network/telephony/TelephonyRepository.kt
+++ b/src/com/android/settings/network/telephony/TelephonyRepository.kt
@@ -17,98 +17,16 @@
 package com.android.settings.network.telephony
 
 import android.content.Context
-import android.telephony.SubscriptionManager
 import android.telephony.TelephonyCallback
 import android.telephony.TelephonyManager
-import android.util.Log
-import com.android.settings.network.mobileDataEnabledFlow
-import com.android.settings.wifi.WifiPickerTrackerHelper
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.asExecutor
 import kotlinx.coroutines.channels.ProducerScope
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.flow.catch
 import kotlinx.coroutines.flow.conflate
-import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onEach
-
-class TelephonyRepository(
-    private val context: Context,
-    private val subscriptionsChangedFlow: Flow<Unit> = context.subscriptionsChangedFlow(),
-) {
-    fun isMobileDataPolicyEnabledFlow(
-        subId: Int,
-        @TelephonyManager.MobileDataPolicy policy: Int,
-    ): Flow<Boolean> {
-        if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false)
-
-        val telephonyManager = context.telephonyManager(subId)
-
-        return subscriptionsChangedFlow.map {
-            telephonyManager.isMobileDataPolicyEnabled(policy)
-                .also { Log.d(TAG, "[$subId] isMobileDataPolicyEnabled($policy): $it") }
-        }.conflate().flowOn(Dispatchers.Default)
-    }
-
-    fun setMobileDataPolicyEnabled(
-        subId: Int,
-        @TelephonyManager.MobileDataPolicy policy: Int,
-        enabled: Boolean,
-    ) {
-        if (!SubscriptionManager.isValidSubscriptionId(subId)) return
-
-        val telephonyManager = context.telephonyManager(subId)
-        Log.d(TAG, "[$subId] setMobileDataPolicyEnabled($policy): $enabled")
-        telephonyManager.setMobileDataPolicyEnabled(policy, enabled)
-    }
-
-    fun isDataEnabledFlow(subId: Int): Flow<Boolean> {
-        if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false)
-
-        return context.mobileDataEnabledFlow(subId)
-            .map {
-                val telephonyManager = context.telephonyManager(subId)
-                telephonyManager.isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER)
-            }
-            .catch {
-                Log.w(TAG, "[$subId] isDataEnabledFlow: exception", it)
-                emit(false)
-            }
-            .onEach { Log.d(TAG, "[$subId] isDataEnabledFlow: isDataEnabled() = $it") }
-            .conflate()
-            .flowOn(Dispatchers.Default)
-    }
-
-    fun setMobileData(
-        subId: Int,
-        enabled: Boolean,
-        wifiPickerTrackerHelper: WifiPickerTrackerHelper? = null
-    ) {
-        if (!SubscriptionManager.isValidSubscriptionId(subId)) return
-
-        Log.d(TAG, "setMobileData: $enabled")
-        MobileNetworkUtils.setMobileDataEnabled(
-            context,
-            subId,
-            enabled /* enabled */,
-            true /* disableOtherSubscriptions */
-        )
-
-        if (wifiPickerTrackerHelper != null
-            && !wifiPickerTrackerHelper.isCarrierNetworkProvisionEnabled(subId)
-        ) {
-            wifiPickerTrackerHelper.setCarrierNetworkEnabled(enabled)
-        }
-    }
-
-    private companion object {
-        private const val TAG = "TelephonyRepository"
-    }
-}
 
 /** Creates an instance of a cold Flow for Telephony callback of given [subId]. */
 fun <T> Context.telephonyCallbackFlow(
diff --git a/src/com/android/settings/network/telephony/wificalling/CrossSimCallingViewModel.kt b/src/com/android/settings/network/telephony/wificalling/CrossSimCallingViewModel.kt
index 5dcac1e..fb0bd82 100644
--- a/src/com/android/settings/network/telephony/wificalling/CrossSimCallingViewModel.kt
+++ b/src/com/android/settings/network/telephony/wificalling/CrossSimCallingViewModel.kt
@@ -24,7 +24,7 @@
 import androidx.lifecycle.AndroidViewModel
 import androidx.lifecycle.viewModelScope
 import com.android.settings.R
-import com.android.settings.network.mobileDataEnabledFlow
+import com.android.settings.network.telephony.MobileDataRepository
 import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl
 import com.android.settings.network.telephony.requireSubscriptionManager
 import com.android.settings.network.telephony.safeGetConfig
@@ -54,6 +54,7 @@
     private val scope = viewModelScope + Dispatchers.Default
     private val metricsFeatureProvider = featureFactory.metricsFeatureProvider
     private val updateChannel = Channel<Unit>()
+    private val mobileDataRepository = MobileDataRepository(application)
 
     init {
         val resources = application.resources
@@ -81,7 +82,7 @@
     }
 
     private fun List<Int>.anyMobileDataEnableChangedFlow() = map { subId ->
-        application.mobileDataEnabledFlow(subId = subId, sendInitialValue = false)
+        mobileDataRepository.mobileDataEnabledChangedFlow(subId = subId, sendInitialValue = false)
     }.merge()
 
     private suspend fun updateCrossSimCalling(activeSubIds: List<Int>, newEnabled: Boolean) {
diff --git a/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt b/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt
index a5d4ba8..b5cdeda 100644
--- a/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt
+++ b/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt
@@ -21,14 +21,16 @@
 import android.telephony.CarrierConfigManager
 import android.telephony.CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL
 import android.telephony.SubscriptionManager
-import android.telephony.TelephonyManager
 import android.telephony.ims.ImsMmTelManager.WiFiCallingMode
 import android.telephony.ims.feature.MmTelFeature
 import android.telephony.ims.stub.ImsRegistrationImplBase
+import androidx.lifecycle.LifecycleOwner
 import com.android.settings.network.telephony.ims.ImsMmTelRepository
 import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl
 import com.android.settings.network.telephony.ims.imsFeatureProvisionedFlow
 import com.android.settings.network.telephony.subscriptionsChangedFlow
+import com.android.settings.network.telephony.telephonyManager
+import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
@@ -38,13 +40,19 @@
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.withContext
 
-class WifiCallingRepository(
+interface IWifiCallingRepository {
+    /** TODO: Move this to UI layer, when UI layer migrated to Kotlin. */
+    fun collectIsWifiCallingReadyFlow(lifecycleOwner: LifecycleOwner, action: (Boolean) -> Unit)
+}
+
+class WifiCallingRepository
+@JvmOverloads
+constructor(
     private val context: Context,
     private val subId: Int,
     private val imsMmTelRepository: ImsMmTelRepository = ImsMmTelRepositoryImpl(context, subId)
-) {
-    private val telephonyManager = context.getSystemService(TelephonyManager::class.java)!!
-        .createForSubscriptionId(subId)
+) : IWifiCallingRepository {
+    private val telephonyManager = context.telephonyManager(subId)
 
     private val carrierConfigManager = context.getSystemService(CarrierConfigManager::class.java)!!
 
@@ -59,6 +67,14 @@
             .getConfigForSubId(subId, KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL)
             .getBoolean(KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL)
 
+    /** TODO: Move this to UI layer, when UI layer migrated to Kotlin. */
+    override fun collectIsWifiCallingReadyFlow(
+        lifecycleOwner: LifecycleOwner,
+        action: (Boolean) -> Unit,
+    ) {
+        wifiCallingReadyFlow().collectLatestWithLifecycle(lifecycleOwner, action = action)
+    }
+
     @OptIn(ExperimentalCoroutinesApi::class)
     fun wifiCallingReadyFlow(): Flow<Boolean> {
         if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false)
diff --git a/src/com/android/settings/notification/modes/AbstractZenModePreferenceController.java b/src/com/android/settings/notification/modes/AbstractZenModePreferenceController.java
index aebc4eb..9f819d1 100644
--- a/src/com/android/settings/notification/modes/AbstractZenModePreferenceController.java
+++ b/src/com/android/settings/notification/modes/AbstractZenModePreferenceController.java
@@ -26,6 +26,8 @@
 import androidx.preference.Preference;
 
 import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 
 import com.google.common.base.Preconditions;
 
diff --git a/src/com/android/settings/notification/modes/IconLoader.java b/src/com/android/settings/notification/modes/IconLoader.java
deleted file mode 100644
index c590285..0000000
--- a/src/com/android/settings/notification/modes/IconLoader.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * 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 com.google.common.util.concurrent.Futures.immediateFuture;
-
-import static java.util.Objects.requireNonNull;
-
-import android.annotation.Nullable;
-import android.app.AutomaticZenRule;
-import android.content.Context;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.InsetDrawable;
-import android.service.notification.SystemZenRules;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.LruCache;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
-import androidx.appcompat.content.res.AppCompatResources;
-
-import com.google.common.util.concurrent.FluentFuture;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.common.util.concurrent.MoreExecutors;
-
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-class IconLoader {
-
-    private static final String TAG = "ZenIconLoader";
-
-    private static final Drawable MISSING = new ColorDrawable();
-
-    @Nullable // Until first usage
-    private static IconLoader sInstance;
-
-    private final LruCache<String, Drawable> mCache;
-    private final ListeningExecutorService mBackgroundExecutor;
-
-    static IconLoader getInstance() {
-        if (sInstance == null) {
-            sInstance = new IconLoader();
-        }
-        return sInstance;
-    }
-
-    private IconLoader() {
-        this(Executors.newFixedThreadPool(4));
-    }
-
-    @VisibleForTesting
-    IconLoader(ExecutorService backgroundExecutor) {
-        mCache = new LruCache<>(50);
-        mBackgroundExecutor =
-                MoreExecutors.listeningDecorator(backgroundExecutor);
-    }
-
-    @NonNull
-    ListenableFuture<Drawable> getIcon(Context context, @NonNull AutomaticZenRule rule) {
-        if (rule.getIconResId() == 0) {
-            return Futures.immediateFuture(getFallbackIcon(context, rule.getType()));
-        }
-
-        return FluentFuture.from(loadIcon(context, rule.getPackageName(), rule.getIconResId()))
-                .transform(icon ->
-                        icon != null ? icon : getFallbackIcon(context, rule.getType()),
-                        MoreExecutors.directExecutor());
-    }
-
-    @NonNull
-    private ListenableFuture</* @Nullable */ Drawable> loadIcon(Context context, String pkg,
-            int iconResId) {
-        String cacheKey = pkg + ":" + iconResId;
-        synchronized (mCache) {
-            Drawable cachedValue = mCache.get(cacheKey);
-            if (cachedValue != null) {
-                return immediateFuture(cachedValue != MISSING ? cachedValue : null);
-            }
-        }
-
-        return FluentFuture.from(mBackgroundExecutor.submit(() -> {
-            if (TextUtils.isEmpty(pkg) || SystemZenRules.PACKAGE_ANDROID.equals(pkg)) {
-                return context.getDrawable(iconResId);
-            } else {
-                Context appContext = context.createPackageContext(pkg, 0);
-                Drawable appDrawable = AppCompatResources.getDrawable(appContext, iconResId);
-                return getMonochromeIconIfPresent(appDrawable);
-            }
-        })).catching(Exception.class, ex -> {
-            // If we cannot resolve the icon, then store MISSING in the cache below, so
-            // we don't try again.
-            Log.e(TAG, "Error while loading icon " + cacheKey, ex);
-            return null;
-        }, MoreExecutors.directExecutor()).transform(drawable -> {
-            synchronized (mCache) {
-                mCache.put(cacheKey, drawable != null ? drawable : MISSING);
-            }
-            return drawable;
-        }, MoreExecutors.directExecutor());
-    }
-
-    private static Drawable getFallbackIcon(Context context, int ruleType) {
-        int iconResIdFromType = switch (ruleType) {
-            case AutomaticZenRule.TYPE_UNKNOWN ->
-                    com.android.internal.R.drawable.ic_zen_mode_type_unknown;
-            case AutomaticZenRule.TYPE_OTHER ->
-                    com.android.internal.R.drawable.ic_zen_mode_type_other;
-            case AutomaticZenRule.TYPE_SCHEDULE_TIME ->
-                    com.android.internal.R.drawable.ic_zen_mode_type_schedule_time;
-            case AutomaticZenRule.TYPE_SCHEDULE_CALENDAR ->
-                    com.android.internal.R.drawable.ic_zen_mode_type_schedule_calendar;
-            case AutomaticZenRule.TYPE_BEDTIME ->
-                    com.android.internal.R.drawable.ic_zen_mode_type_bedtime;
-            case AutomaticZenRule.TYPE_DRIVING ->
-                    com.android.internal.R.drawable.ic_zen_mode_type_driving;
-            case AutomaticZenRule.TYPE_IMMERSIVE ->
-                    com.android.internal.R.drawable.ic_zen_mode_type_immersive;
-            case AutomaticZenRule.TYPE_THEATER ->
-                    com.android.internal.R.drawable.ic_zen_mode_type_theater;
-            case AutomaticZenRule.TYPE_MANAGED ->
-                    com.android.internal.R.drawable.ic_zen_mode_type_managed;
-            default ->
-                    com.android.internal.R.drawable.ic_zen_mode_type_unknown;
-        };
-        return requireNonNull(context.getDrawable(iconResIdFromType));
-    }
-
-    private static Drawable getMonochromeIconIfPresent(Drawable icon) {
-        // For created rules, the app should've provided a monochrome Drawable. However, implicit
-        // rules have the app's icon, which is not -- but might have a monochrome layer. Thus
-        // we choose it, if present.
-        if (icon instanceof AdaptiveIconDrawable adaptiveIcon) {
-            if (adaptiveIcon.getMonochrome() != null) {
-                // Wrap with negative inset => scale icon (inspired from BaseIconFactory)
-                return new InsetDrawable(adaptiveIcon.getMonochrome(),
-                        -2.0f * AdaptiveIconDrawable.getExtraInsetFraction());
-            }
-        }
-        return icon;
-    }
-}
diff --git a/src/com/android/settings/notification/modes/IconUtil.java b/src/com/android/settings/notification/modes/IconUtil.java
index c6ecaa0..1e653bf 100644
--- a/src/com/android/settings/notification/modes/IconUtil.java
+++ b/src/com/android/settings/notification/modes/IconUtil.java
@@ -24,6 +24,7 @@
 import android.graphics.drawable.ShapeDrawable;
 import android.graphics.drawable.shapes.OvalShape;
 
+import androidx.annotation.AttrRes;
 import androidx.annotation.DrawableRes;
 import androidx.annotation.NonNull;
 
@@ -32,10 +33,18 @@
 
 class IconUtil {
 
-    static Drawable applyTint(@NonNull Context context, @NonNull Drawable icon) {
+    static Drawable applyNormalTint(@NonNull Context context, @NonNull Drawable icon) {
+        return applyTint(context, icon, android.R.attr.colorControlNormal);
+    }
+
+    static Drawable applyAccentTint(@NonNull Context context, @NonNull Drawable icon) {
+        return applyTint(context, icon, android.R.attr.colorAccent);
+    }
+
+    private static Drawable applyTint(@NonNull Context context, @NonNull Drawable icon,
+            @AttrRes int colorAttr) {
         icon = icon.mutate();
-        icon.setTintList(
-                Utils.getColorAttr(context, android.R.attr.colorControlNormal));
+        icon.setTintList(Utils.getColorAttr(context, colorAttr));
         return icon;
     }
 
diff --git a/src/com/android/settings/notification/modes/InterruptionFilterPreferenceController.java b/src/com/android/settings/notification/modes/InterruptionFilterPreferenceController.java
new file mode 100644
index 0000000..8bdeea4
--- /dev/null
+++ b/src/com/android/settings/notification/modes/InterruptionFilterPreferenceController.java
@@ -0,0 +1,64 @@
+/*
+ * 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.app.NotificationManager.INTERRUPTION_FILTER_ALL;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.preference.Preference;
+import androidx.preference.TwoStatePreference;
+
+import com.android.settings.R;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
+
+class InterruptionFilterPreferenceController extends AbstractZenModePreferenceController
+        implements Preference.OnPreferenceChangeListener {
+
+    public InterruptionFilterPreferenceController(Context context, String key,
+            ZenModesBackend backend) {
+        super(context, key, backend);
+    }
+
+    @Override
+    public boolean isAvailable(ZenMode zenMode) {
+        return !zenMode.isManualDnd();
+    }
+
+    @Override
+    public void updateState(Preference preference, @NonNull ZenMode zenMode) {
+        boolean filteringNotifications = zenMode.getRule().getInterruptionFilter()
+                != INTERRUPTION_FILTER_ALL;
+        ((TwoStatePreference) preference).setChecked(filteringNotifications);
+        preference.setSummary(filteringNotifications ? "" :
+                mContext.getResources().getString(R.string.mode_no_notification_filter));
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        final boolean filterNotifications = ((Boolean) newValue);
+        return saveMode(zenMode -> {
+            zenMode.getRule().setInterruptionFilter(filterNotifications
+                    ? INTERRUPTION_FILTER_PRIORITY
+                    : INTERRUPTION_FILTER_ALL);
+            return zenMode;
+        });
+    }
+}
diff --git a/src/com/android/settings/notification/modes/ZenMode.java b/src/com/android/settings/notification/modes/ZenMode.java
deleted file mode 100644
index cbe915b..0000000
--- a/src/com/android/settings/notification/modes/ZenMode.java
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * 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.app.NotificationManager.INTERRUPTION_FILTER_ALL;
-import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
-
-import static java.util.Objects.requireNonNull;
-
-import android.annotation.SuppressLint;
-import android.app.AutomaticZenRule;
-import android.app.NotificationManager;
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.service.notification.ZenDeviceEffects;
-import android.service.notification.ZenPolicy;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.settings.R;
-
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-
-import java.util.Objects;
-
-/**
- * Represents either an {@link AutomaticZenRule} or the manual DND rule in a unified way.
- *
- * <p>It also adapts other rule features that we don't want to expose in the UI, such as
- * interruption filters other than {@code PRIORITY}, rules without specific icons, etc.
- */
-class ZenMode {
-
-    private static final String TAG = "ZenMode";
-
-    /**
-     * Additional value for the {@code @ZenPolicy.ChannelType} enumeration that indicates that all
-     * channels can bypass DND when this policy is active.
-     *
-     * <p>This value shouldn't be used on "real" ZenPolicy objects sent to or returned from
-     * {@link android.app.NotificationManager}; it's a way of representing rules with interruption
-     * filter = {@link NotificationManager#INTERRUPTION_FILTER_ALL} in the UI.
-     */
-    public static final int CHANNEL_POLICY_ALL = -1;
-
-    static final String MANUAL_DND_MODE_ID = "manual_dnd";
-
-    @SuppressLint("WrongConstant")
-    private static final ZenPolicy POLICY_INTERRUPTION_FILTER_ALL =
-            new ZenPolicy.Builder()
-                    .allowChannels(CHANNEL_POLICY_ALL)
-                    .allowAllSounds()
-                    .showAllVisualEffects()
-                    .build();
-
-    // Must match com.android.server.notification.ZenModeHelper#applyCustomPolicy.
-    private static final ZenPolicy POLICY_INTERRUPTION_FILTER_ALARMS =
-            new ZenPolicy.Builder()
-                    .disallowAllSounds()
-                    .allowAlarms(true)
-                    .allowMedia(true)
-                    .allowPriorityChannels(false)
-                    .build();
-
-    // Must match com.android.server.notification.ZenModeHelper#applyCustomPolicy.
-    private static final ZenPolicy POLICY_INTERRUPTION_FILTER_NONE =
-            new ZenPolicy.Builder()
-                    .disallowAllSounds()
-                    .hideAllVisualEffects()
-                    .allowPriorityChannels(false)
-                    .build();
-
-    private final String mId;
-    private AutomaticZenRule mRule;
-    private final boolean mIsActive;
-    private final boolean mIsManualDnd;
-
-    ZenMode(String id, AutomaticZenRule rule, boolean isActive) {
-        this(id, rule, isActive, false);
-    }
-
-    private ZenMode(String id, AutomaticZenRule rule, boolean isActive, boolean isManualDnd) {
-        mId = id;
-        mRule = rule;
-        mIsActive = isActive;
-        mIsManualDnd = isManualDnd;
-    }
-
-    static ZenMode manualDndMode(AutomaticZenRule manualRule, boolean isActive) {
-        return new ZenMode(MANUAL_DND_MODE_ID, manualRule, isActive, true);
-    }
-
-    @NonNull
-    public String getId() {
-        return mId;
-    }
-
-    @NonNull
-    public AutomaticZenRule getRule() {
-        return mRule;
-    }
-
-    @NonNull
-    public ListenableFuture<Drawable> getIcon(@NonNull Context context,
-            @NonNull IconLoader iconLoader) {
-        if (mIsManualDnd) {
-            return Futures.immediateFuture(requireNonNull(
-                    context.getDrawable(R.drawable.ic_do_not_disturb_on_24dp)));
-        }
-
-        return iconLoader.getIcon(context, mRule);
-    }
-
-    @NonNull
-    public ZenPolicy getPolicy() {
-        switch (mRule.getInterruptionFilter()) {
-            case INTERRUPTION_FILTER_PRIORITY:
-                return requireNonNull(mRule.getZenPolicy());
-
-            case NotificationManager.INTERRUPTION_FILTER_ALL:
-                return POLICY_INTERRUPTION_FILTER_ALL;
-
-            case NotificationManager.INTERRUPTION_FILTER_ALARMS:
-                return POLICY_INTERRUPTION_FILTER_ALARMS;
-
-            case NotificationManager.INTERRUPTION_FILTER_NONE:
-                return POLICY_INTERRUPTION_FILTER_NONE;
-
-            case NotificationManager.INTERRUPTION_FILTER_UNKNOWN:
-            default:
-                Log.wtf(TAG, "Rule " + mId + " with unexpected interruptionFilter "
-                        + mRule.getInterruptionFilter());
-                return requireNonNull(mRule.getZenPolicy());
-        }
-    }
-
-    /**
-     * Updates the {@link ZenPolicy} of the associated {@link AutomaticZenRule} based on the
-     * supplied policy. In some cases this involves conversions, so that the following call
-     * to {@link #getPolicy} might return a different policy from the one supplied here.
-     */
-    @SuppressLint("WrongConstant")
-    public void setPolicy(@NonNull ZenPolicy policy) {
-        ZenPolicy currentPolicy = getPolicy();
-        if (currentPolicy.equals(policy)) {
-            return;
-        }
-
-        // A policy with CHANNEL_POLICY_ALL is only a UI representation of the
-        // INTERRUPTION_FILTER_ALL filter. Thus, switching to or away to this value only updates
-        // the filter, discarding the rest of the supplied policy.
-        if (policy.getAllowedChannels() == CHANNEL_POLICY_ALL
-                && currentPolicy.getAllowedChannels() != CHANNEL_POLICY_ALL) {
-            if (mIsManualDnd) {
-                throw new IllegalArgumentException("Manual DND cannot have CHANNEL_POLICY_ALL");
-            }
-            mRule.setInterruptionFilter(INTERRUPTION_FILTER_ALL);
-            // Preserve the existing policy, e.g. if the user goes PRIORITY -> ALL -> PRIORITY that
-            // shouldn't discard all other policy customizations. The existing policy will be a
-            // synthetic one if the rule originally had filter NONE or ALARMS_ONLY and that's fine.
-            if (mRule.getZenPolicy() == null) {
-                mRule.setZenPolicy(currentPolicy);
-            }
-            return;
-        } else if (policy.getAllowedChannels() != CHANNEL_POLICY_ALL
-                && currentPolicy.getAllowedChannels() == CHANNEL_POLICY_ALL) {
-            mRule.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY);
-            // Go back to whatever policy the rule had before, unless the rule never had one, in
-            // which case we use the supplied policy (which we know has a valid allowedChannels).
-            if (mRule.getZenPolicy() == null) {
-                mRule.setZenPolicy(policy);
-            }
-            return;
-        }
-
-        // If policy is customized from any of the "special" ones, make the rule PRIORITY.
-        if (mRule.getInterruptionFilter() != INTERRUPTION_FILTER_PRIORITY) {
-            mRule.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY);
-        }
-        mRule.setZenPolicy(policy);
-    }
-
-    @NonNull
-    public ZenDeviceEffects getDeviceEffects() {
-        return mRule.getDeviceEffects() != null
-                ? mRule.getDeviceEffects()
-                : new ZenDeviceEffects.Builder().build();
-    }
-
-    public boolean canEditName() {
-        return !isManualDnd();
-    }
-
-    public boolean canEditIcon() {
-        return !isManualDnd();
-    }
-
-    public boolean canBeDeleted() {
-        return !mIsManualDnd;
-    }
-
-    public boolean isManualDnd() {
-        return mIsManualDnd;
-    }
-
-    public boolean isActive() {
-        return mIsActive;
-    }
-
-    @Override
-    public boolean equals(@Nullable Object obj) {
-        return obj instanceof ZenMode other
-                && mId.equals(other.mId)
-                && mRule.equals(other.mRule)
-                && mIsActive == other.mIsActive;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mId, mRule, mIsActive);
-    }
-
-    @Override
-    public String toString() {
-        return mId + "(" + (mIsActive ? "active" : "inactive") + ") -> " + mRule;
-    }
-}
diff --git a/src/com/android/settings/notification/modes/ZenModeActionsPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeActionsPreferenceController.java
index 8585234..914683f 100644
--- a/src/com/android/settings/notification/modes/ZenModeActionsPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeActionsPreferenceController.java
@@ -16,7 +16,7 @@
 
 package com.android.settings.notification.modes;
 
-import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
+import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID;
 
 import android.content.Context;
 import android.os.Bundle;
@@ -27,6 +27,8 @@
 
 import com.android.settings.R;
 import com.android.settings.core.SubSettingLauncher;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 import com.android.settingslib.widget.ActionButtonsPreference;
 
 class ZenModeActionsPreferenceController extends AbstractZenModePreferenceController {
@@ -50,7 +52,7 @@
         buttonsPreference.setButton2Enabled(zenMode.canEditIcon());
         buttonsPreference.setButton2OnClickListener(v -> {
             Bundle bundle = new Bundle();
-            bundle.putString(MODE_ID, zenMode.getId());
+            bundle.putString(EXTRA_AUTOMATIC_ZEN_RULE_ID, zenMode.getId());
             new SubSettingLauncher(mContext)
                     .setDestination(ZenModeIconPickerFragment.class.getName())
                     // TODO: b/332937635 - Update metrics category
diff --git a/src/com/android/settings/notification/modes/ZenModeAppsFragment.java b/src/com/android/settings/notification/modes/ZenModeAppsFragment.java
index 73329a2..19035dd 100644
--- a/src/com/android/settings/notification/modes/ZenModeAppsFragment.java
+++ b/src/com/android/settings/notification/modes/ZenModeAppsFragment.java
@@ -37,10 +37,6 @@
                 context, ZenModeAppsPreferenceController.KEY_PRIORITY, mBackend));
         controllers.add(new ZenModeAppsPreferenceController(
                 context, ZenModeAppsPreferenceController.KEY_NONE, mBackend));
-        // TODO: b/308819928 - The manual DND mode cannot have the ALL type;
-        // unify the controllers into one and only create a preference if isManualDnd is false.
-        controllers.add(new ZenModeAppsPreferenceController(
-                context, ZenModeAppsPreferenceController.KEY_ALL, mBackend));
         return controllers;
     }
 
diff --git a/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java
index 691c92e..f62dfdd 100644
--- a/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java
@@ -16,7 +16,8 @@
 
 package com.android.settings.notification.modes;
 
-import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
+import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID;
 
 import android.content.Context;
 import android.os.Bundle;
@@ -30,6 +31,8 @@
 
 import com.android.settings.core.SubSettingLauncher;
 import com.android.settingslib.applications.ApplicationsState;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -45,10 +48,12 @@
     private static final String TAG = "ZenModeAppsLinkPreferenceController";
 
     private final ZenModeSummaryHelper mSummaryHelper;
+    private final ApplicationsState mApplicationsState;
     private ApplicationsState.Session mAppSession;
     private final ZenHelperBackend mHelperBackend;
     private ZenMode mZenMode;
     private Preference mPreference;
+    private final Fragment mHost;
 
     ZenModeAppsLinkPreferenceController(Context context, String key, Fragment host,
             ApplicationsState applicationsState, ZenModesBackend backend,
@@ -56,15 +61,19 @@
         super(context, key, backend);
         mSummaryHelper = new ZenModeSummaryHelper(mContext, helperBackend);
         mHelperBackend = helperBackend;
-        if (applicationsState != null && host != null) {
-            mAppSession = applicationsState.newSession(mAppSessionCallbacks, host.getLifecycle());
-        }
+        mApplicationsState = applicationsState;
+        mHost = host;
+    }
+
+    @Override
+    public boolean isAvailable(ZenMode zenMode) {
+        return zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL;
     }
 
     @Override
     public void updateState(Preference preference, @NonNull ZenMode zenMode) {
         Bundle bundle = new Bundle();
-        bundle.putString(MODE_ID, zenMode.getId());
+        bundle.putString(EXTRA_AUTOMATIC_ZEN_RULE_ID, zenMode.getId());
         // TODO(b/332937635): Update metrics category
         preference.setIntent(new SubSettingLauncher(mContext)
                 .setDestination(ZenModeAppsFragment.class.getName())
@@ -73,6 +82,9 @@
                 .toIntent());
         mZenMode = zenMode;
         mPreference = preference;
+        if (mApplicationsState != null && mHost != null) {
+            mAppSession = mApplicationsState.newSession(mAppSessionCallbacks, mHost.getLifecycle());
+        }
         triggerUpdateAppsBypassingDndSummaryText();
     }
 
@@ -118,11 +130,13 @@
         return appsBypassingDnd;
     }
 
-    @VisibleForTesting final ApplicationsState.Callbacks mAppSessionCallbacks =
+    @VisibleForTesting
+    final ApplicationsState.Callbacks mAppSessionCallbacks =
             new ApplicationsState.Callbacks() {
 
                 @Override
-                public void onRunningStateChanged(boolean running) { }
+                public void onRunningStateChanged(boolean running) {
+                }
 
                 @Override
                 public void onPackageListChanged() {
@@ -135,16 +149,20 @@
                 }
 
                 @Override
-                public void onPackageIconChanged() { }
+                public void onPackageIconChanged() {
+                }
 
                 @Override
-                public void onPackageSizeChanged(String packageName) { }
+                public void onPackageSizeChanged(String packageName) {
+                }
 
                 @Override
-                public void onAllSizesComputed() { }
+                public void onAllSizesComputed() {
+                }
 
                 @Override
-                public void onLauncherInfoChanged() { }
+                public void onLauncherInfoChanged() {
+                }
 
                 @Override
                 public void onLoadEntriesCompleted() {
diff --git a/src/com/android/settings/notification/modes/ZenModeAppsPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeAppsPreferenceController.java
index 704bce0..1d807ed 100644
--- a/src/com/android/settings/notification/modes/ZenModeAppsPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeAppsPreferenceController.java
@@ -16,7 +16,7 @@
 
 package com.android.settings.notification.modes;
 
-import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
+import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID;
 
 import android.app.settings.SettingsEnums;
 import android.content.Context;
@@ -31,6 +31,8 @@
 import androidx.preference.TwoStatePreference;
 
 import com.android.settings.core.SubSettingLauncher;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 import com.android.settingslib.widget.SelectorWithWidgetPreference;
 
 public class ZenModeAppsPreferenceController extends
@@ -38,11 +40,9 @@
 
     static final String KEY_PRIORITY = "zen_mode_apps_priority";
     static final String KEY_NONE = "zen_mode_apps_none";
-    static final String KEY_ALL = "zen_mode_apps_all";
 
     String mModeId;
 
-
     public ZenModeAppsPreferenceController(@NonNull Context context,
             @NonNull String key, @Nullable ZenModesBackend backend) {
         super(context, key, backend);
@@ -79,13 +79,6 @@
                         == ZenPolicy.CHANNEL_POLICY_NONE;
                 pref.setChecked(policy_none);
                 break;
-            case KEY_ALL:
-                // A UI-only setting; the underlying policy never actually has this value,
-                // but ZenMode acts as though it does for the sake of UI consistency.
-                boolean policy_all = zenMode.getPolicy().getAllowedChannels()
-                        == ZenMode.CHANNEL_POLICY_ALL;
-                pref.setChecked(policy_all);
-                break;
         }
     }
 
@@ -96,8 +89,6 @@
                 return savePolicy(p -> p.allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY));
             case KEY_NONE:
                 return savePolicy(p -> p.allowChannels(ZenPolicy.CHANNEL_POLICY_NONE));
-            case KEY_ALL:
-                return savePolicy(p -> p.allowChannels(ZenMode.CHANNEL_POLICY_ALL));
         }
         return true;
     }
@@ -114,7 +105,7 @@
     private void launchPrioritySettings() {
         Bundle bundle = new Bundle();
         if (mModeId != null) {
-            bundle.putString(MODE_ID, mModeId);
+            bundle.putString(EXTRA_AUTOMATIC_ZEN_RULE_ID, mModeId);
         }
         // TODO(b/332937635): Update metrics category
         new SubSettingLauncher(mContext)
diff --git a/src/com/android/settings/notification/modes/ZenModeButtonPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeButtonPreferenceController.java
index 1846dfc..4a99b33 100644
--- a/src/com/android/settings/notification/modes/ZenModeButtonPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeButtonPreferenceController.java
@@ -23,9 +23,11 @@
 import androidx.preference.Preference;
 
 import com.android.settings.R;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 import com.android.settingslib.widget.LayoutPreference;
 
-public class ZenModeButtonPreferenceController extends AbstractZenModePreferenceController {
+class ZenModeButtonPreferenceController extends AbstractZenModePreferenceController {
 
     private Button mZenButton;
 
diff --git a/src/com/android/settings/notification/modes/ZenModeCallsLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeCallsLinkPreferenceController.java
index 8d27d4c..e5c1e48 100644
--- a/src/com/android/settings/notification/modes/ZenModeCallsLinkPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeCallsLinkPreferenceController.java
@@ -16,7 +16,7 @@
 
 package com.android.settings.notification.modes;
 
-import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
+import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID;
 
 import android.content.Context;
 import android.os.Bundle;
@@ -25,6 +25,8 @@
 import androidx.preference.Preference;
 
 import com.android.settings.core.SubSettingLauncher;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 
 class ZenModeCallsLinkPreferenceController extends AbstractZenModePreferenceController  {
 
@@ -39,7 +41,7 @@
     @Override
     public void updateState(Preference preference, @NonNull ZenMode zenMode) {
         Bundle bundle = new Bundle();
-        bundle.putString(MODE_ID, zenMode.getId());
+        bundle.putString(EXTRA_AUTOMATIC_ZEN_RULE_ID, zenMode.getId());
         // TODO(b/332937635): Update metrics category
         preference.setIntent(new SubSettingLauncher(mContext)
                 .setDestination(ZenModeCallsFragment.class.getName())
diff --git a/src/com/android/settings/notification/modes/ZenModeDisplayEffectPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeDisplayEffectPreferenceController.java
index bca7b55..b0d3952 100644
--- a/src/com/android/settings/notification/modes/ZenModeDisplayEffectPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeDisplayEffectPreferenceController.java
@@ -23,7 +23,10 @@
 import androidx.preference.Preference;
 import androidx.preference.TwoStatePreference;
 
-public class ZenModeDisplayEffectPreferenceController extends AbstractZenModePreferenceController
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
+
+class ZenModeDisplayEffectPreferenceController extends AbstractZenModePreferenceController
         implements Preference.OnPreferenceChangeListener {
 
     public ZenModeDisplayEffectPreferenceController(Context context, String key,
@@ -34,24 +37,20 @@
     @Override
     public void updateState(Preference preference, @NonNull ZenMode zenMode) {
         TwoStatePreference pref = (TwoStatePreference) preference;
-        ZenDeviceEffects effects =  zenMode.getRule().getDeviceEffects();
-        if (effects == null) {
-            pref.setChecked(false);
-        } else {
-            switch (getPreferenceKey()) {
-                case "effect_greyscale":
-                    pref.setChecked(effects.shouldDisplayGrayscale());
-                    break;
-                case "effect_aod":
-                    pref.setChecked(effects.shouldSuppressAmbientDisplay());
-                    break;
-                case "effect_wallpaper":
-                    pref.setChecked(effects.shouldDimWallpaper());
-                    break;
-                case "effect_dark_theme":
-                    pref.setChecked(effects.shouldUseNightMode());
-                    break;
-            }
+        ZenDeviceEffects effects =  zenMode.getDeviceEffects();
+        switch (getPreferenceKey()) {
+            case "effect_greyscale":
+                pref.setChecked(effects.shouldDisplayGrayscale());
+                break;
+            case "effect_aod":
+                pref.setChecked(effects.shouldSuppressAmbientDisplay());
+                break;
+            case "effect_wallpaper":
+                pref.setChecked(effects.shouldDimWallpaper());
+                break;
+            case "effect_dark_theme":
+                pref.setChecked(effects.shouldUseNightMode());
+                break;
         }
     }
 
diff --git a/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceController.java
index 712c78a..d3559f1 100644
--- a/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceController.java
@@ -16,7 +16,7 @@
 
 package com.android.settings.notification.modes;
 
-import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
+import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID;
 
 import android.content.Context;
 import android.os.Bundle;
@@ -25,6 +25,8 @@
 import androidx.preference.Preference;
 
 import com.android.settings.core.SubSettingLauncher;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 
 class ZenModeDisplayLinkPreferenceController extends AbstractZenModePreferenceController  {
 
@@ -39,7 +41,7 @@
     @Override
     void updateState(Preference preference, @NonNull ZenMode zenMode) {
         Bundle bundle = new Bundle();
-        bundle.putString(MODE_ID, zenMode.getId());
+        bundle.putString(EXTRA_AUTOMATIC_ZEN_RULE_ID, zenMode.getId());
         // TODO(b/332937635): Update metrics category
         preference.setIntent(new SubSettingLauncher(mContext)
                 .setDestination(ZenModeDisplayFragment.class.getName())
diff --git a/src/com/android/settings/notification/modes/ZenModeExitAtAlarmPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeExitAtAlarmPreferenceController.java
index 8517af1..326bc97 100644
--- a/src/com/android/settings/notification/modes/ZenModeExitAtAlarmPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeExitAtAlarmPreferenceController.java
@@ -23,6 +23,9 @@
 import androidx.preference.Preference;
 import androidx.preference.TwoStatePreference;
 
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
+
 /**
  * Preference controller controlling whether a time schedule-based mode ends at the next alarm.
  */
diff --git a/src/com/android/settings/notification/modes/ZenModeFragment.java b/src/com/android/settings/notification/modes/ZenModeFragment.java
index 6bda5e1..5897c4d 100644
--- a/src/com/android/settings/notification/modes/ZenModeFragment.java
+++ b/src/com/android/settings/notification/modes/ZenModeFragment.java
@@ -16,20 +16,28 @@
 
 package com.android.settings.notification.modes;
 
+import android.app.AlertDialog;
 import android.app.Application;
 import android.app.AutomaticZenRule;
 import android.app.settings.SettingsEnums;
 import android.content.Context;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
 
 import com.android.settings.R;
 import com.android.settingslib.applications.ApplicationsState;
 import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.notification.modes.ZenMode;
 
 import java.util.ArrayList;
 import java.util.List;
 
 public class ZenModeFragment extends ZenModeFragmentBase {
 
+    // for mode deletion menu
+    private static final int DELETE_MODE = 1;
+
     @Override
     protected int getPreferenceScreenResId() {
         return R.xml.modes_rule_settings;
@@ -52,7 +60,9 @@
         prefControllers.add(new ZenModeDisplayLinkPreferenceController(
                 context, "mode_display_settings", mBackend, mHelperBackend));
         prefControllers.add(new ZenModeSetTriggerLinkPreferenceController(context,
-                "zen_automatic_trigger_category", mBackend));
+                "zen_automatic_trigger_category", this, mBackend));
+        prefControllers.add(new InterruptionFilterPreferenceController(
+                context, "allow_filtering", mBackend));
         return prefControllers;
     }
 
@@ -74,4 +84,43 @@
         // TODO: b/332937635 - make this the correct metrics category
         return SettingsEnums.NOTIFICATION_ZEN_MODE_AUTOMATION;
     }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        menu.add(Menu.NONE, DELETE_MODE, Menu.NONE, R.string.zen_mode_menu_delete_mode);
+        super.onCreateOptionsMenu(menu, inflater);
+    }
+
+    @Override
+    protected boolean onOptionsItemSelected(MenuItem item, ZenMode zenMode) {
+        switch (item.getItemId()) {
+            case DELETE_MODE:
+                new AlertDialog.Builder(mContext)
+                        .setTitle(mContext.getString(R.string.zen_mode_delete_mode_confirmation,
+                                zenMode.getRule().getName()))
+                        .setPositiveButton(R.string.zen_mode_schedule_delete,
+                                (dialog, which) -> {
+                                    // start finishing before calling removeMode() so that we don't
+                                    // try to update this activity with a nonexistent mode when the
+                                    // zen mode config is updated
+                                    finish();
+                                    mBackend.removeMode(zenMode);
+                                })
+                        .setNegativeButton(R.string.cancel, null)
+                        .show();
+                return true;
+            default:
+                return super.onOptionsItemSelected(item);
+        }
+    }
+
+    @Override
+    protected void updateZenModeState() {
+        // Because this fragment may be asked to finish by the delete menu but not be done doing
+        // so yet, ignore any attempts to update info in that case.
+        if (getActivity() != null && getActivity().isFinishing()) {
+            return;
+        }
+        super.updateZenModeState();
+    }
 }
diff --git a/src/com/android/settings/notification/modes/ZenModeFragmentBase.java b/src/com/android/settings/notification/modes/ZenModeFragmentBase.java
index e086524..d08f7ea 100644
--- a/src/com/android/settings/notification/modes/ZenModeFragmentBase.java
+++ b/src/com/android/settings/notification/modes/ZenModeFragmentBase.java
@@ -16,10 +16,13 @@
 
 package com.android.settings.notification.modes;
 
+import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID;
+
 import android.app.AutomaticZenRule;
 import android.content.Context;
 import android.os.Bundle;
 import android.util.Log;
+import android.view.MenuItem;
 import android.widget.Toast;
 
 import androidx.annotation.NonNull;
@@ -29,6 +32,7 @@
 
 import com.android.settings.R;
 import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.notification.modes.ZenMode;
 
 import java.util.List;
 
@@ -37,7 +41,6 @@
  */
 abstract class ZenModeFragmentBase extends ZenModesFragmentBase {
     static final String TAG = "ZenModeSettings";
-    static final String MODE_ID = "MODE_ID";
 
     @Nullable  // only until reloadMode() is called
     private ZenMode mZenMode;
@@ -46,17 +49,21 @@
     public void onAttach(@NonNull Context context) {
         super.onAttach(context);
 
-        // TODO: b/322373473 - Update if modes page ends up using a different method of passing id
+        String id = null;
+        if (getActivity() != null && getActivity().getIntent() != null) {
+            id = getActivity().getIntent().getStringExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID);
+        }
         Bundle bundle = getArguments();
-        if (bundle != null && bundle.containsKey(MODE_ID)) {
-            String id = bundle.getString(MODE_ID);
-            if (!reloadMode(id)) {
-                Log.e(TAG, "Mode id " + id + " not found");
-                toastAndFinish();
-                return;
-            }
-        } else {
-            Log.e(TAG, "Mode id required to set mode config settings");
+        if (id == null && bundle != null && bundle.containsKey(EXTRA_AUTOMATIC_ZEN_RULE_ID)) {
+            id = bundle.getString(EXTRA_AUTOMATIC_ZEN_RULE_ID);
+        }
+        if (id == null) {
+            Log.d(TAG, "No id provided");
+            toastAndFinish();
+            return;
+        }
+        if (!reloadMode(id)) {
+            Log.d(TAG, "Mode id " + id + " not found");
             toastAndFinish();
             return;
         }
@@ -108,6 +115,18 @@
         updateControllers();
     }
 
+    @Override
+    public final boolean onOptionsItemSelected(MenuItem item) {
+        if (mZenMode != null) {
+            return onOptionsItemSelected(item, mZenMode);
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    protected boolean onOptionsItemSelected(MenuItem item, @NonNull ZenMode zenMode) {
+        return true;
+    }
+
     private void updateControllers() {
         if (getPreferenceControllers() == null || mZenMode == null) {
             return;
@@ -120,10 +139,6 @@
         }
         for (List<AbstractPreferenceController> list : getPreferenceControllers()) {
             for (AbstractPreferenceController controller : list) {
-                if (!controller.isAvailable()) {
-                    continue;
-                }
-
                 try {
                     // Find preference associated with controller
                     final String key = controller.getPreferenceKey();
@@ -137,6 +152,7 @@
                                 String.format("Cannot find preference with key %s in Controller %s",
                                         key, controller.getClass().getSimpleName()));
                     }
+                    controller.displayPreference(screen);
                 } catch (ClassCastException e) {
                     // Skip any controllers that aren't AbstractZenModePreferenceController.
                     Log.d(TAG, "Could not cast: " + controller.getClass().getSimpleName());
diff --git a/src/com/android/settings/notification/modes/ZenModeHeaderController.java b/src/com/android/settings/notification/modes/ZenModeHeaderController.java
index d8f0a67..1845ee8 100644
--- a/src/com/android/settings/notification/modes/ZenModeHeaderController.java
+++ b/src/com/android/settings/notification/modes/ZenModeHeaderController.java
@@ -25,6 +25,9 @@
 import com.android.settings.R;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.widget.EntityHeaderController;
+import com.android.settingslib.notification.modes.ZenIconLoader;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 import com.android.settingslib.widget.LayoutPreference;
 
 class ZenModeHeaderController extends AbstractZenModePreferenceController {
@@ -62,8 +65,8 @@
         }
 
         FutureUtil.whenDone(
-                zenMode.getIcon(mContext, IconLoader.getInstance()),
-                icon -> mHeaderController.setIcon(IconUtil.applyTint(mContext, icon))
+                zenMode.getIcon(mContext, ZenIconLoader.getInstance()),
+                icon -> mHeaderController.setIcon(IconUtil.applyNormalTint(mContext, icon))
                         .done(/* rebindActions= */ false),
                 mContext.getMainExecutor());
     }
diff --git a/src/com/android/settings/notification/modes/ZenModeIconPickerIconPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeIconPickerIconPreferenceController.java
index 9eaaa97..d1d53af 100644
--- a/src/com/android/settings/notification/modes/ZenModeIconPickerIconPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeIconPickerIconPreferenceController.java
@@ -25,6 +25,9 @@
 import com.android.settings.R;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.widget.EntityHeaderController;
+import com.android.settingslib.notification.modes.ZenIconLoader;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 import com.android.settingslib.widget.LayoutPreference;
 
 class ZenModeIconPickerIconPreferenceController extends AbstractZenModePreferenceController {
@@ -51,8 +54,8 @@
         }
 
         FutureUtil.whenDone(
-                zenMode.getIcon(mContext, IconLoader.getInstance()),
-                icon -> mHeaderController.setIcon(IconUtil.applyTint(mContext, icon))
+                zenMode.getIcon(mContext, ZenIconLoader.getInstance()),
+                icon -> mHeaderController.setIcon(IconUtil.applyNormalTint(mContext, icon))
                         .done(/* rebindActions= */ false),
                 mContext.getMainExecutor());
     }
diff --git a/src/com/android/settings/notification/modes/ZenModeIconPickerListPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeIconPickerListPreferenceController.java
index fc991dc..85ceafe 100644
--- a/src/com/android/settings/notification/modes/ZenModeIconPickerListPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeIconPickerListPreferenceController.java
@@ -33,6 +33,8 @@
 
 import com.android.settings.R;
 import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 import com.android.settingslib.widget.LayoutPreference;
 
 import com.google.common.collect.ImmutableList;
diff --git a/src/com/android/settings/notification/modes/ZenModeMessagesLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeMessagesLinkPreferenceController.java
index 6e563c4..9b7c8a1 100644
--- a/src/com/android/settings/notification/modes/ZenModeMessagesLinkPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeMessagesLinkPreferenceController.java
@@ -16,7 +16,7 @@
 
 package com.android.settings.notification.modes;
 
-import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
+import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID;
 
 import android.content.Context;
 import android.os.Bundle;
@@ -25,6 +25,8 @@
 import androidx.preference.Preference;
 
 import com.android.settings.core.SubSettingLauncher;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 
 class ZenModeMessagesLinkPreferenceController extends AbstractZenModePreferenceController {
     private final ZenModeSummaryHelper mSummaryHelper;
@@ -38,7 +40,7 @@
     @Override
     public void updateState(Preference preference, @NonNull ZenMode zenMode) {
         Bundle bundle = new Bundle();
-        bundle.putString(MODE_ID, zenMode.getId());
+        bundle.putString(EXTRA_AUTOMATIC_ZEN_RULE_ID, zenMode.getId());
         // TODO(b/332937635): Update metrics category
         preference.setIntent(new SubSettingLauncher(mContext)
                 .setDestination(ZenModeMessagesFragment.class.getName())
diff --git a/src/com/android/settings/notification/modes/ZenModeNotifVisLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeNotifVisLinkPreferenceController.java
index 15da96e..a2d9411 100644
--- a/src/com/android/settings/notification/modes/ZenModeNotifVisLinkPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeNotifVisLinkPreferenceController.java
@@ -16,7 +16,8 @@
 
 package com.android.settings.notification.modes;
 
-import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
+import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID;
 
 import android.content.Context;
 import android.os.Bundle;
@@ -25,6 +26,8 @@
 import androidx.preference.Preference;
 
 import com.android.settings.core.SubSettingLauncher;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 
 class ZenModeNotifVisLinkPreferenceController extends AbstractZenModePreferenceController  {
 
@@ -37,9 +40,14 @@
     }
 
     @Override
+    public boolean isAvailable(ZenMode zenMode) {
+        return zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL;
+    }
+
+    @Override
     public void updateState(Preference preference, @NonNull ZenMode zenMode) {
         Bundle bundle = new Bundle();
-        bundle.putString(MODE_ID, zenMode.getId());
+        bundle.putString(EXTRA_AUTOMATIC_ZEN_RULE_ID, zenMode.getId());
         // TODO(b/332937635): Update metrics category
         preference.setIntent(new SubSettingLauncher(mContext)
                 .setDestination(ZenModeNotifVisFragment.class.getName())
diff --git a/src/com/android/settings/notification/modes/ZenModeNotifVisPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeNotifVisPreferenceController.java
index f918b25..3d9f713 100644
--- a/src/com/android/settings/notification/modes/ZenModeNotifVisPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeNotifVisPreferenceController.java
@@ -21,19 +21,21 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
-import androidx.preference.CheckBoxPreference;
 import androidx.preference.Preference;
 import androidx.preference.TwoStatePreference;
 
-import com.android.settings.widget.DisabledCheckBoxPreference;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 
 public class ZenModeNotifVisPreferenceController extends AbstractZenModePreferenceController
         implements Preference.OnPreferenceChangeListener {
 
-    @VisibleForTesting protected @ZenPolicy.VisualEffect int mEffect;
+    @VisibleForTesting
+    protected @ZenPolicy.VisualEffect int mEffect;
 
     // if any of these effects are suppressed, this effect must be too
-    @VisibleForTesting protected @ZenPolicy.VisualEffect int[] mParentSuppressedEffects;
+    @VisibleForTesting
+    protected @ZenPolicy.VisualEffect int[] mParentSuppressedEffects;
 
     public ZenModeNotifVisPreferenceController(Context context, String key,
             @ZenPolicy.VisualEffect int visualEffect,
diff --git a/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceController.java
index 89b719e..99625eb 100644
--- a/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceController.java
@@ -16,7 +16,8 @@
 
 package com.android.settings.notification.modes;
 
-import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
+import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID;
 
 import android.content.Context;
 import android.os.Bundle;
@@ -25,6 +26,8 @@
 import androidx.preference.Preference;
 
 import com.android.settings.core.SubSettingLauncher;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 
 /**
  * Preference with a link and summary about what other sounds can break through the mode
@@ -40,9 +43,14 @@
     }
 
     @Override
+    public boolean isAvailable(ZenMode zenMode) {
+        return zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL;
+    }
+
+    @Override
     public void updateState(Preference preference, @NonNull ZenMode zenMode) {
         Bundle bundle = new Bundle();
-        bundle.putString(MODE_ID, zenMode.getId());
+        bundle.putString(EXTRA_AUTOMATIC_ZEN_RULE_ID, zenMode.getId());
         preference.setIntent(new SubSettingLauncher(mContext)
                 .setDestination(ZenModeOtherFragment.class.getName())
                 .setSourceMetricsCategory(0)
diff --git a/src/com/android/settings/notification/modes/ZenModeOtherPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeOtherPreferenceController.java
index a770164e..ad5fa6a 100644
--- a/src/com/android/settings/notification/modes/ZenModeOtherPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeOtherPreferenceController.java
@@ -28,6 +28,9 @@
 import androidx.preference.Preference;
 import androidx.preference.TwoStatePreference;
 
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
+
 class ZenModeOtherPreferenceController extends AbstractZenModePreferenceController
         implements Preference.OnPreferenceChangeListener {
 
diff --git a/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceController.java
index 271ca72..1613a01 100644
--- a/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceController.java
@@ -16,7 +16,8 @@
 
 package com.android.settings.notification.modes;
 
-import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
+import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID;
 
 import android.content.Context;
 import android.os.Bundle;
@@ -25,6 +26,8 @@
 import androidx.preference.Preference;
 
 import com.android.settings.core.SubSettingLauncher;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 
 /**
  * Preference with a link and summary about what calls and messages can break through the mode
@@ -40,9 +43,14 @@
     }
 
     @Override
+    public boolean isAvailable(ZenMode zenMode) {
+        return zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL;
+    }
+
+    @Override
     public void updateState(Preference preference, @NonNull ZenMode zenMode) {
         Bundle bundle = new Bundle();
-        bundle.putString(MODE_ID, zenMode.getId());
+        bundle.putString(EXTRA_AUTOMATIC_ZEN_RULE_ID, zenMode.getId());
         // TODO(b/332937635): Update metrics category
         preference.setIntent(new SubSettingLauncher(mContext)
                 .setDestination(ZenModePeopleFragment.class.getName())
diff --git a/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceController.java b/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceController.java
index 31a8a0d..0f9323d 100644
--- a/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceController.java
@@ -46,6 +46,8 @@
 import com.android.settings.R;
 import com.android.settings.core.SubSettingLauncher;
 import com.android.settings.notification.app.ConversationListSettings;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 import com.android.settingslib.widget.SelectorWithWidgetPreference;
 
 import java.util.ArrayList;
diff --git a/src/com/android/settings/notification/modes/ZenModeRepeatCallersPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeRepeatCallersPreferenceController.java
index 7569051..ae62e35 100644
--- a/src/com/android/settings/notification/modes/ZenModeRepeatCallersPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeRepeatCallersPreferenceController.java
@@ -26,6 +26,8 @@
 import androidx.preference.TwoStatePreference;
 
 import com.android.settings.R;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 
 class ZenModeRepeatCallersPreferenceController extends AbstractZenModePreferenceController
         implements Preference.OnPreferenceChangeListener {
diff --git a/src/com/android/settings/notification/modes/ZenModeScheduleChooserDialog.java b/src/com/android/settings/notification/modes/ZenModeScheduleChooserDialog.java
new file mode 100644
index 0000000..14264b7
--- /dev/null
+++ b/src/com/android/settings/notification/modes/ZenModeScheduleChooserDialog.java
@@ -0,0 +1,149 @@
+/*
+ * 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 com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+import android.service.notification.ZenModeConfig;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.appcompat.app.AlertDialog;
+
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.dashboard.DashboardFragment;
+
+import com.google.common.collect.ImmutableList;
+
+public class ZenModeScheduleChooserDialog extends InstrumentedDialogFragment {
+
+    private static final String TAG = "ZenModeScheduleChooserDialog";
+
+    static final int OPTION_TIME = 0;
+    static final int OPTION_CALENDAR = 1;
+
+    private record ScheduleOption(@StringRes int nameResId, @StringRes int exampleResId,
+                                  @DrawableRes int iconResId) {}
+
+    private static final ImmutableList<ScheduleOption> SCHEDULE_OPTIONS = ImmutableList.of(
+            new ScheduleOption(R.string.zen_mode_select_schedule_time,
+                    R.string.zen_mode_select_schedule_time_example,
+                    com.android.internal.R.drawable.ic_zen_mode_type_schedule_time),
+            new ScheduleOption(R.string.zen_mode_select_schedule_calendar,
+                    R.string.zen_mode_select_schedule_calendar_example,
+                    com.android.internal.R.drawable.ic_zen_mode_type_schedule_calendar));
+
+    private OnScheduleOptionListener mOptionListener;
+
+    interface OnScheduleOptionListener {
+        void onScheduleSelected(Uri conditionId);
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        // TODO: b/332937635 - Update metrics category
+        return 0;
+    }
+
+    static void show(DashboardFragment parent, OnScheduleOptionListener optionListener) {
+        ZenModeScheduleChooserDialog dialog = new ZenModeScheduleChooserDialog();
+        dialog.mOptionListener = optionListener;
+        dialog.setTargetFragment(parent, 0);
+        dialog.show(parent.getParentFragmentManager(), TAG);
+    }
+
+    @NonNull
+    @Override
+    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+        checkState(getContext() != null);
+        return new AlertDialog.Builder(getContext())
+                .setTitle(R.string.zen_mode_choose_rule_type)
+                .setAdapter(new OptionsAdapter(getContext()),
+                        (dialog, which) -> onScheduleTypeSelected(which))
+                .setNegativeButton(R.string.cancel, null)
+                .create();
+    }
+
+    private static class OptionsAdapter extends ArrayAdapter<ScheduleOption> {
+
+        private final LayoutInflater mInflater;
+
+        OptionsAdapter(@NonNull Context context) {
+            super(context, R.layout.zen_mode_type_item, SCHEDULE_OPTIONS);
+            mInflater = LayoutInflater.from(context);
+        }
+
+        @NonNull
+        @Override
+        public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
+            if (convertView == null) {
+                convertView = mInflater.inflate(R.layout.zen_mode_type_item, parent, false);
+            }
+            // No need for holder pattern since we have only 2 items.
+            ImageView imageView = checkNotNull(convertView.findViewById(R.id.icon));
+            TextView title = checkNotNull(convertView.findViewById(R.id.title));
+            TextView subtitle = checkNotNull(convertView.findViewById(R.id.subtitle));
+
+            ScheduleOption option = checkNotNull(getItem(position));
+            imageView.setImageResource(option.iconResId());
+            title.setText(option.nameResId());
+            subtitle.setText(option.exampleResId());
+
+            return convertView;
+        }
+    }
+
+    private void onScheduleTypeSelected(int whichOption) {
+        Uri conditionId = switch (whichOption) {
+            case OPTION_TIME -> getDefaultScheduleTimeCondition();
+            case OPTION_CALENDAR -> getDefaultScheduleCalendarCondition();
+            default -> ZenModeConfig.toCustomManualConditionId();
+        };
+
+        mOptionListener.onScheduleSelected(conditionId);
+    }
+
+    private static Uri getDefaultScheduleTimeCondition() {
+        ZenModeConfig.ScheduleInfo schedule = new ZenModeConfig.ScheduleInfo();
+        schedule.days = ZenModeConfig.ALL_DAYS;
+        schedule.startHour = 9;
+        schedule.startMinute = 30;
+        schedule.endHour = 17;
+        return ZenModeConfig.toScheduleConditionId(schedule);
+    }
+
+    private static Uri getDefaultScheduleCalendarCondition() {
+        ZenModeConfig.EventInfo eventInfo = new ZenModeConfig.EventInfo();
+        eventInfo.calendarId = null; // All calendars of the current user.
+        eventInfo.reply = ZenModeConfig.EventInfo.REPLY_ANY_EXCEPT_NO;
+        return ZenModeConfig.toEventConditionId(eventInfo);
+    }
+}
diff --git a/src/com/android/settings/notification/modes/ZenModeSetCalendarPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeSetCalendarPreferenceController.java
index 2841309..4f45c5c8 100644
--- a/src/com/android/settings/notification/modes/ZenModeSetCalendarPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeSetCalendarPreferenceController.java
@@ -16,14 +16,12 @@
 
 package com.android.settings.notification.modes;
 
-import android.app.Flags;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.CalendarContract;
-import android.service.notification.SystemZenRules;
 import android.service.notification.ZenModeConfig;
 
 import androidx.annotation.NonNull;
@@ -33,6 +31,8 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.R;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -42,7 +42,7 @@
 import java.util.Objects;
 import java.util.function.Function;
 
-public class ZenModeSetCalendarPreferenceController extends AbstractZenModePreferenceController {
+class ZenModeSetCalendarPreferenceController extends AbstractZenModePreferenceController {
     @VisibleForTesting
     protected static final String KEY_CALENDAR = "calendar";
     @VisibleForTesting
@@ -122,11 +122,7 @@
     @VisibleForTesting
     protected Function<ZenMode, ZenMode> updateEventMode(ZenModeConfig.EventInfo event) {
         return (zenMode) -> {
-            zenMode.getRule().setConditionId(ZenModeConfig.toEventConditionId(event));
-            if (Flags.modesApi() && Flags.modesUi()) {
-                zenMode.getRule().setTriggerDescription(
-                        SystemZenRules.getTriggerDescriptionForScheduleEvent(mContext, event));
-            }
+            zenMode.setCustomModeConditionId(mContext, ZenModeConfig.toEventConditionId(event));
             return zenMode;
         };
     }
diff --git a/src/com/android/settings/notification/modes/ZenModeSetSchedulePreferenceController.java b/src/com/android/settings/notification/modes/ZenModeSetSchedulePreferenceController.java
index a6008cc..878a508 100644
--- a/src/com/android/settings/notification/modes/ZenModeSetSchedulePreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeSetSchedulePreferenceController.java
@@ -16,9 +16,7 @@
 
 package com.android.settings.notification.modes;
 
-import android.app.Flags;
 import android.content.Context;
-import android.service.notification.SystemZenRules;
 import android.service.notification.ZenModeConfig;
 import android.text.format.DateFormat;
 import android.util.ArraySet;
@@ -33,6 +31,8 @@
 import androidx.preference.Preference;
 
 import com.android.settings.R;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 import com.android.settingslib.widget.LayoutPreference;
 
 import java.text.SimpleDateFormat;
@@ -116,16 +116,13 @@
     @VisibleForTesting
     protected Function<ZenMode, ZenMode> updateScheduleMode(ZenModeConfig.ScheduleInfo schedule) {
         return (zenMode) -> {
-            zenMode.getRule().setConditionId(ZenModeConfig.toScheduleConditionId(schedule));
-            if (Flags.modesApi() && Flags.modesUi()) {
-                zenMode.getRule().setTriggerDescription(
-                        SystemZenRules.getTriggerDescriptionForScheduleTime(mContext, schedule));
-            }
+            zenMode.setCustomModeConditionId(mContext,
+                    ZenModeConfig.toScheduleConditionId(schedule));
             return zenMode;
         };
     }
 
-    private ZenModeTimePickerFragment.TimeSetter mStartSetter = (hour, minute) -> {
+    private final ZenModeTimePickerFragment.TimeSetter mStartSetter = (hour, minute) -> {
         if (!isValidTime(hour, minute)) {
             return;
         }
@@ -137,7 +134,7 @@
         saveMode(updateScheduleMode(mSchedule));
     };
 
-    private ZenModeTimePickerFragment.TimeSetter mEndSetter = (hour, minute) -> {
+    private final ZenModeTimePickerFragment.TimeSetter mEndSetter = (hour, minute) -> {
         if (!isValidTime(hour, minute)) {
             return;
         }
diff --git a/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceController.java
index 14d5d59..1c96fee 100644
--- a/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceController.java
@@ -13,15 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.settings.notification.modes;
 
 import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR;
 import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME;
 
-import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
-
 import android.content.Context;
-import android.os.Bundle;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
@@ -29,8 +27,10 @@
 import androidx.preference.PreferenceCategory;
 
 import com.android.settings.R;
-import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.dashboard.DashboardFragment;
 import com.android.settingslib.PrimarySwitchPreference;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 
 /**
  * Preference controller for the link to an individual mode's configuration page.
@@ -39,9 +39,13 @@
     @VisibleForTesting
     protected static final String AUTOMATIC_TRIGGER_PREF_KEY = "zen_automatic_trigger_settings";
 
+    private final DashboardFragment mFragment;
+
     ZenModeSetTriggerLinkPreferenceController(Context context, String key,
+            DashboardFragment fragment,
             ZenModesBackend backend) {
         super(context, key, backend);
+        mFragment = fragment;
     }
 
     @Override
@@ -54,47 +58,52 @@
         // This controller is expected to govern a preference category so that it controls the
         // availability of the entire preference category if the mode doesn't have a way to
         // automatically trigger (such as manual DND).
-        Preference switchPref = ((PreferenceCategory) preference).findPreference(
+        PrimarySwitchPreference switchPref = ((PreferenceCategory) preference).findPreference(
                 AUTOMATIC_TRIGGER_PREF_KEY);
         if (switchPref == null) {
             return;
         }
-        ((PrimarySwitchPreference) switchPref).setChecked(zenMode.getRule().isEnabled());
+        switchPref.setChecked(zenMode.getRule().isEnabled());
         switchPref.setOnPreferenceChangeListener(mSwitchChangeListener);
+        switchPref.setSummary(zenMode.getRule().getTriggerDescription());
+        switchPref.setIcon(null);
+        switchPref.setOnPreferenceClickListener(null);
+        switchPref.setIntent(null);
 
-        Bundle bundle = new Bundle();
-        bundle.putString(MODE_ID, zenMode.getId());
-
-        // TODO: b/341961712 - direct preference to app-owned intent if available
-        switch (zenMode.getRule().getType()) {
-            case TYPE_SCHEDULE_TIME:
-                switchPref.setTitle(R.string.zen_mode_set_schedule_link);
-                switchPref.setSummary(zenMode.getRule().getTriggerDescription());
-                switchPref.setIntent(new SubSettingLauncher(mContext)
-                        .setDestination(ZenModeSetScheduleFragment.class.getName())
-                        // TODO: b/332937635 - set correct metrics category
-                        .setSourceMetricsCategory(0)
-                        .setArguments(bundle)
-                        .toIntent());
-                break;
-            case TYPE_SCHEDULE_CALENDAR:
-                switchPref.setTitle(R.string.zen_mode_set_calendar_link);
-                switchPref.setSummary(zenMode.getRule().getTriggerDescription());
-                switchPref.setIntent(new SubSettingLauncher(mContext)
-                        .setDestination(ZenModeSetCalendarFragment.class.getName())
-                        // TODO: b/332937635 - set correct metrics category
-                        .setSourceMetricsCategory(0)
-                        .setArguments(bundle)
-                        .toIntent());
-                break;
-            default:
-                // TODO: b/342156843 - change this to allow adding a trigger condition for system
-                //                     rules that don't yet have a type selected
-                switchPref.setTitle("not implemented");
+        if (zenMode.isSystemOwned() && zenMode.getType() == TYPE_SCHEDULE_TIME) {
+            switchPref.setTitle(R.string.zen_mode_set_schedule_link);
+            // TODO: b/332937635 - set correct metrics category
+            switchPref.setIntent(ZenSubSettingLauncher.forModeFragment(mContext,
+                    ZenModeSetScheduleFragment.class, zenMode.getId(), 0).toIntent());
+        } else if (zenMode.isSystemOwned() && zenMode.getType() == TYPE_SCHEDULE_CALENDAR) {
+            switchPref.setTitle(R.string.zen_mode_set_calendar_link);
+            switchPref.setIcon(null);
+            // TODO: b/332937635 - set correct metrics category
+            switchPref.setIntent(ZenSubSettingLauncher.forModeFragment(mContext,
+                    ZenModeSetCalendarFragment.class, zenMode.getId(), 0).toIntent());
+        } else if (zenMode.isSystemOwned()) {
+            switchPref.setTitle(R.string.zen_mode_select_schedule);
+            switchPref.setIcon(R.drawable.ic_add_24dp);
+            switchPref.setSummary("");
+            // TODO: b/342156843 - Hide the switch (needs support in SettingsLib).
+            switchPref.setOnPreferenceClickListener(clickedPreference -> {
+                ZenModeScheduleChooserDialog.show(mFragment, mOnScheduleOptionListener);
+                return true;
+            });
+        } else {
+            // TODO: b/341961712 - direct preference to app-owned intent if available
+            switchPref.setTitle("not implemented");
         }
     }
 
     @VisibleForTesting
+    final ZenModeScheduleChooserDialog.OnScheduleOptionListener mOnScheduleOptionListener =
+            conditionId -> saveMode(mode -> {
+                mode.setCustomModeConditionId(mContext, conditionId);
+                return mode;
+            });
+
+    @VisibleForTesting
     protected Preference.OnPreferenceChangeListener mSwitchChangeListener = (p, newValue) -> {
         final boolean newEnabled = (Boolean) newValue;
         return saveMode((zenMode) -> {
@@ -103,5 +112,6 @@
             }
             return zenMode;
         });
+        // TODO: b/342156843 - Do we want to jump to the corresponding schedule editing screen?
     };
 }
diff --git a/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java b/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java
index bf0bac9..48a4c36 100644
--- a/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java
+++ b/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java
@@ -15,6 +15,7 @@
  */
 package com.android.settings.notification.modes;
 
+import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
 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;
@@ -47,6 +48,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.settings.R;
+import com.android.settingslib.notification.modes.ZenMode;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -187,11 +189,12 @@
     String getDisplayEffectsSummary(ZenMode zenMode) {
         boolean isFirst = true;
         List<String> enabledEffects = new ArrayList<>();
-        if (!zenMode.getPolicy().shouldShowAllVisualEffects()) {
+        if (!zenMode.getPolicy().shouldShowAllVisualEffects()
+                && zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL) {
             enabledEffects.add(getBlockedEffectsSummary(zenMode));
             isFirst = false;
         }
-        ZenDeviceEffects currEffects =  zenMode.getRule().getDeviceEffects();
+        ZenDeviceEffects currEffects = zenMode.getRule().getDeviceEffects();
         if (currEffects != null) {
             if (currEffects.shouldDisplayGrayscale()) {
                 if (isFirst) {
@@ -411,8 +414,6 @@
             return formatAppsList(appsBypassing);
         } else if (zenMode.getPolicy().getAllowedChannels() == ZenPolicy.CHANNEL_POLICY_NONE) {
             return mContext.getResources().getString(R.string.zen_mode_apps_none_apps);
-        } else if (zenMode.getPolicy().getAllowedChannels() == ZenMode.CHANNEL_POLICY_ALL) {
-            return mContext.getResources().getString(R.string.zen_mode_apps_all_apps);
         }
         return "";
     }
diff --git a/src/com/android/settings/notification/modes/ZenModesBackend.java b/src/com/android/settings/notification/modes/ZenModesBackend.java
deleted file mode 100644
index b58e310..0000000
--- a/src/com/android/settings/notification/modes/ZenModesBackend.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * 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 android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.AutomaticZenRule;
-import android.app.NotificationManager;
-import android.content.Context;
-import android.net.Uri;
-import android.provider.Settings;
-import android.service.notification.Condition;
-import android.service.notification.SystemZenRules;
-import android.service.notification.ZenModeConfig;
-import android.util.Log;
-
-import com.android.settings.R;
-
-import java.time.Duration;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Class used for Settings-NMS interactions related to Mode management.
- *
- * <p>This class converts {@link AutomaticZenRule} instances, as well as the manual zen mode,
- * into the unified {@link ZenMode} format.
- */
-class ZenModesBackend {
-
-    private static final String TAG = "ZenModeBackend";
-
-    @Nullable // Until first usage
-    private static ZenModesBackend sInstance;
-
-    private final NotificationManager mNotificationManager;
-
-    private final Context mContext;
-
-    static ZenModesBackend getInstance(Context context) {
-        if (sInstance == null) {
-            sInstance = new ZenModesBackend(context.getApplicationContext());
-        }
-        return sInstance;
-    }
-
-    ZenModesBackend(Context context) {
-        mContext = context;
-        mNotificationManager = context.getSystemService(NotificationManager.class);
-    }
-
-    List<ZenMode> getModes() {
-        ArrayList<ZenMode> modes = new ArrayList<>();
-        ZenModeConfig currentConfig = mNotificationManager.getZenModeConfig();
-        modes.add(getManualDndMode(currentConfig));
-
-        Map<String, AutomaticZenRule> zenRules = mNotificationManager.getAutomaticZenRules();
-        for (Map.Entry<String, AutomaticZenRule> zenRuleEntry : zenRules.entrySet()) {
-            String ruleId = zenRuleEntry.getKey();
-            modes.add(new ZenMode(ruleId, zenRuleEntry.getValue(),
-                    isRuleActive(ruleId, currentConfig)));
-        }
-
-        modes.sort((l, r) -> {
-            if (l.isManualDnd()) {
-                return -1;
-            } else if (r.isManualDnd()) {
-                return 1;
-            }
-            return l.getRule().getName().compareTo(r.getRule().getName());
-        });
-
-        return modes;
-    }
-
-    @Nullable
-    ZenMode getMode(String id) {
-        ZenModeConfig currentConfig = mNotificationManager.getZenModeConfig();
-        if (ZenMode.MANUAL_DND_MODE_ID.equals(id)) {
-            return getManualDndMode(currentConfig);
-        } else {
-            AutomaticZenRule rule = mNotificationManager.getAutomaticZenRule(id);
-            if (rule == null) {
-                return null;
-            }
-            return new ZenMode(id, rule, isRuleActive(id, currentConfig));
-        }
-    }
-
-    private ZenMode getManualDndMode(ZenModeConfig config) {
-        ZenModeConfig.ZenRule manualRule = config.manualRule;
-        // TODO: b/333682392 - Replace with final strings for name & trigger description
-        AutomaticZenRule manualDndRule = new AutomaticZenRule.Builder(
-                mContext.getString(R.string.zen_mode_settings_title), manualRule.conditionId)
-                .setType(manualRule.type)
-                .setZenPolicy(manualRule.zenPolicy)
-                .setDeviceEffects(manualRule.zenDeviceEffects)
-                .setManualInvocationAllowed(manualRule.allowManualInvocation)
-                .setConfigurationActivity(null) // No further settings
-                .setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY)
-                .build();
-
-        return ZenMode.manualDndMode(manualDndRule, config != null && config.isManualActive());
-    }
-
-    private static boolean isRuleActive(String id, ZenModeConfig config) {
-        if (config == null) {
-            // shouldn't happen if the config is coming from NM, but be safe
-            return false;
-        }
-        ZenModeConfig.ZenRule configRule = config.automaticRules.get(id);
-        return configRule != null && configRule.isAutomaticActive();
-    }
-
-    void updateMode(ZenMode mode) {
-        if (mode.isManualDnd()) {
-            try {
-                NotificationManager.Policy dndPolicy =
-                        new ZenModeConfig().toNotificationPolicy(mode.getPolicy());
-                mNotificationManager.setNotificationPolicy(dndPolicy, /* fromUser= */ true);
-
-                mNotificationManager.setManualZenRuleDeviceEffects(
-                        mode.getRule().getDeviceEffects());
-            } catch (Exception e) {
-                Log.w(TAG, "Error updating manual mode", e);
-            }
-        } else {
-            mNotificationManager.updateAutomaticZenRule(mode.getId(), mode.getRule(),
-                    /* fromUser= */ true);
-        }
-    }
-
-    void activateMode(ZenMode mode, @Nullable Duration forDuration) {
-        if (mode.isManualDnd()) {
-            Uri durationConditionId = null;
-            if (forDuration != null) {
-                durationConditionId = ZenModeConfig.toTimeCondition(mContext,
-                        (int) forDuration.toMinutes(), ActivityManager.getCurrentUser(), true).id;
-            }
-            mNotificationManager.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
-                    durationConditionId, TAG, /* fromUser= */ true);
-
-        } else {
-            if (forDuration != null) {
-                throw new IllegalArgumentException(
-                        "Only the manual DND mode can be activated for a specific duration");
-            }
-            mNotificationManager.setAutomaticZenRuleState(mode.getId(),
-                    new Condition(mode.getRule().getConditionId(), "", Condition.STATE_TRUE,
-                            Condition.SOURCE_USER_ACTION));
-        }
-    }
-
-    void deactivateMode(ZenMode mode) {
-        if (mode.isManualDnd()) {
-            // When calling with fromUser=true this will not snooze other modes.
-            mNotificationManager.setZenMode(Settings.Global.ZEN_MODE_OFF, null, TAG,
-                    /* fromUser= */ true);
-        } else {
-            // TODO: b/333527800 - This should (potentially) snooze the rule if it was active.
-            mNotificationManager.setAutomaticZenRuleState(mode.getId(),
-                    new Condition(mode.getRule().getConditionId(), "", Condition.STATE_FALSE,
-                            Condition.SOURCE_USER_ACTION));
-        }
-    }
-
-    void removeMode(ZenMode mode) {
-        if (!mode.canBeDeleted()) {
-            throw new IllegalArgumentException("Mode " + mode + " cannot be deleted!");
-        }
-        mNotificationManager.removeAutomaticZenRule(mode.getId(), /* fromUser= */ true);
-    }
-
-    /**
-     * Creates a new custom mode with the provided {@code name}. The mode will be "manual" (i.e.
-     * not have a schedule), this can be later updated by the user in the mode settings page.
-     *
-     * @return the created mode. Only {@code null} if creation failed due to an internal error
-     */
-    @Nullable
-    ZenMode addCustomMode(String name) {
-        ZenModeConfig.ScheduleInfo schedule = new ZenModeConfig.ScheduleInfo();
-        schedule.days = ZenModeConfig.ALL_DAYS;
-        schedule.startHour = 22;
-        schedule.endHour = 7;
-
-        // TODO: b/326442408 - Create as "manual" (i.e. no trigger) instead of schedule-time.
-        AutomaticZenRule rule = new AutomaticZenRule.Builder(name,
-                ZenModeConfig.toScheduleConditionId(schedule))
-                .setPackage(ZenModeConfig.getScheduleConditionProvider().getPackageName())
-                .setType(AutomaticZenRule.TYPE_SCHEDULE_CALENDAR)
-                .setOwner(ZenModeConfig.getScheduleConditionProvider())
-                .setTriggerDescription(SystemZenRules.getTriggerDescriptionForScheduleTime(
-                        mContext, schedule))
-                .setManualInvocationAllowed(true)
-                .build();
-
-        String ruleId = mNotificationManager.addAutomaticZenRule(rule);
-        return getMode(ruleId);
-    }
-}
diff --git a/src/com/android/settings/notification/modes/ZenModesFragmentBase.java b/src/com/android/settings/notification/modes/ZenModesFragmentBase.java
index d99593a..e1156fe 100644
--- a/src/com/android/settings/notification/modes/ZenModesFragmentBase.java
+++ b/src/com/android/settings/notification/modes/ZenModesFragmentBase.java
@@ -27,6 +27,7 @@
 import android.util.Log;
 
 import com.android.settings.dashboard.RestrictedDashboardFragment;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 
 /**
  * Base class for all Settings pages controlling Modes behavior.
diff --git a/src/com/android/settings/notification/modes/ZenModesListAddModePreferenceController.java b/src/com/android/settings/notification/modes/ZenModesListAddModePreferenceController.java
index c229fb1..ba74b93 100644
--- a/src/com/android/settings/notification/modes/ZenModesListAddModePreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModesListAddModePreferenceController.java
@@ -22,6 +22,8 @@
 
 import com.android.settings.utils.ZenServiceListing;
 import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 
 import java.util.Random;
 
diff --git a/src/com/android/settings/notification/modes/ZenModesListFragment.java b/src/com/android/settings/notification/modes/ZenModesListFragment.java
index 80678f6..77107f8 100644
--- a/src/com/android/settings/notification/modes/ZenModesListFragment.java
+++ b/src/com/android/settings/notification/modes/ZenModesListFragment.java
@@ -29,6 +29,7 @@
 import com.android.settings.utils.ManagedServiceSettings;
 import com.android.settings.utils.ZenServiceListing;
 import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 import com.android.settingslib.search.SearchIndexable;
 
 import com.google.common.collect.ImmutableList;
diff --git a/src/com/android/settings/notification/modes/ZenModesListItemPreference.java b/src/com/android/settings/notification/modes/ZenModesListItemPreference.java
index 7ecfb3a..1bc6e55 100644
--- a/src/com/android/settings/notification/modes/ZenModesListItemPreference.java
+++ b/src/com/android/settings/notification/modes/ZenModesListItemPreference.java
@@ -16,16 +16,31 @@
 package com.android.settings.notification.modes;
 
 import android.content.Context;
+import android.widget.TextView;
 
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.settings.R;
 import com.android.settingslib.RestrictedPreference;
+import com.android.settingslib.Utils;
+import com.android.settingslib.notification.modes.ZenIconLoader;
+import com.android.settingslib.notification.modes.ZenMode;
+
+import com.google.common.base.Strings;
 
 /**
  * Preference representing a single mode item on the modes aggregator page. Clicking on this
  * preference leads to an individual mode's configuration page.
  */
 class ZenModesListItemPreference extends RestrictedPreference {
-    final Context mContext;
-    ZenMode mZenMode;
+
+    private final Context mContext;
+    private ZenMode mZenMode;
+
+    private TextView mTitleView;
+    private TextView mSummaryView;
 
     ZenModesListItemPreference(Context context, ZenMode zenMode) {
         super(context);
@@ -35,19 +50,65 @@
     }
 
     @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+        if (holder.findViewById(android.R.id.title) instanceof TextView titleView) {
+            mTitleView = titleView;
+        }
+        if (holder.findViewById(android.R.id.summary) instanceof TextView summaryView) {
+            mSummaryView = summaryView;
+        }
+        updateTextColor(mZenMode);
+    }
+
+    @Override
     public void onClick() {
         ZenSubSettingLauncher.forMode(mContext, mZenMode.getId()).launch();
     }
 
     public void setZenMode(ZenMode zenMode) {
         mZenMode = zenMode;
-        setTitle(mZenMode.getRule().getName());
-        setSummary(mZenMode.getRule().getTriggerDescription());
-        setIconSize(ICON_SIZE_SMALL);
+        setTitle(mZenMode.getName());
+        CharSequence statusText = switch (mZenMode.getStatus()) {
+            case ENABLED_AND_ACTIVE ->
+                    Strings.isNullOrEmpty(mZenMode.getTriggerDescription())
+                            ? mContext.getString(R.string.zen_mode_active_text)
+                            : mContext.getString(
+                                    R.string.zen_mode_format_status_and_trigger,
+                                    mContext.getString(R.string.zen_mode_active_text),
+                                    mZenMode.getRule().getTriggerDescription());
+            case ENABLED -> mZenMode.getRule().getTriggerDescription();
+            case DISABLED_BY_USER -> mContext.getString(R.string.zen_mode_disabled_by_user);
+            case DISABLED_BY_OTHER -> mContext.getString(R.string.zen_mode_disabled_tap_to_set_up);
+        };
+        setSummary(statusText);
 
+        setIconSize(ICON_SIZE_SMALL);
         FutureUtil.whenDone(
-                mZenMode.getIcon(mContext, IconLoader.getInstance()),
-                icon -> setIcon(IconUtil.applyTint(mContext, icon)),
+                mZenMode.getIcon(mContext, ZenIconLoader.getInstance()),
+                icon -> setIcon(
+                        zenMode.isActive()
+                                ? IconUtil.applyAccentTint(mContext, icon)
+                                : IconUtil.applyNormalTint(mContext, icon)),
                 mContext.getMainExecutor());
+
+        updateTextColor(zenMode);
+    }
+
+    private void updateTextColor(@Nullable ZenMode zenMode) {
+        boolean isActive = zenMode != null && zenMode.isActive();
+        if (mTitleView != null) {
+            mTitleView.setTextColor(Utils.getColorAttr(mContext,
+                    isActive ? android.R.attr.colorAccent : android.R.attr.textColorPrimary));
+        }
+        if (mSummaryView != null) {
+            mSummaryView.setTextColor(Utils.getColorAttr(mContext,
+                    isActive ? android.R.attr.colorAccent : android.R.attr.textColorSecondary));
+        }
+    }
+
+    @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+    ZenMode getZenMode() {
+        return mZenMode;
     }
 }
diff --git a/src/com/android/settings/notification/modes/ZenModesListPreferenceController.java b/src/com/android/settings/notification/modes/ZenModesListPreferenceController.java
index 5dcd9eb..fb07078 100644
--- a/src/com/android/settings/notification/modes/ZenModesListPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModesListPreferenceController.java
@@ -27,6 +27,8 @@
 
 import com.android.settings.R;
 import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 import com.android.settingslib.search.SearchIndexableRaw;
 
 import java.util.HashMap;
diff --git a/src/com/android/settings/notification/modes/ZenSubSettingLauncher.java b/src/com/android/settings/notification/modes/ZenSubSettingLauncher.java
index 11f3492..529f7fa 100644
--- a/src/com/android/settings/notification/modes/ZenSubSettingLauncher.java
+++ b/src/com/android/settings/notification/modes/ZenSubSettingLauncher.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.notification.modes;
 
+import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID;
+
 import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.os.Bundle;
@@ -29,11 +31,11 @@
                 SettingsEnums.NOTIFICATION_ZEN_MODE_AUTOMATION);
     }
 
-    private static SubSettingLauncher forModeFragment(Context context,
+    static SubSettingLauncher forModeFragment(Context context,
             Class<? extends ZenModeFragmentBase> fragmentClass, String modeId,
             int sourceMetricsCategory) {
         Bundle bundle = new Bundle();
-        bundle.putString(ZenModeFragmentBase.MODE_ID, modeId);
+        bundle.putString(EXTRA_AUTOMATIC_ZEN_RULE_ID, modeId);
 
         return new SubSettingLauncher(context)
                 .setDestination(fragmentClass.getName())
diff --git a/src/com/android/settings/notification/zen/ZenModeBackend.java b/src/com/android/settings/notification/zen/ZenModeBackend.java
index 426f52d..de641c5 100644
--- a/src/com/android/settings/notification/zen/ZenModeBackend.java
+++ b/src/com/android/settings/notification/zen/ZenModeBackend.java
@@ -116,7 +116,7 @@
                 ActivityManager.getCurrentUser(), true).id;
         if (android.app.Flags.modesApi()) {
             mNotificationManager.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
-                        conditionId, TAG, /* fromUser= */ true);
+                    conditionId, TAG, /* fromUser= */ true);
         } else {
             mNotificationManager.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
                     conditionId, TAG);
@@ -241,12 +241,14 @@
         }
 
         savePolicy(getNewDefaultPriorityCategories(allowSenders, category),
-            priorityCallSenders, priorityMessagesSenders, mPolicy.suppressedVisualEffects,
+                priorityCallSenders, priorityMessagesSenders, mPolicy.suppressedVisualEffects,
                 mPolicy.priorityConversationSenders);
 
-        if (ZenModeSettingsBase.DEBUG) Log.d(TAG, "onPrefChange allow" +
-                stringCategory + "=" + allowSenders + " allow" + stringCategory + "From="
-                + ZenModeConfig.sourceToString(allowSendersFrom));
+        if (ZenModeSettingsBase.DEBUG) {
+            Log.d(TAG, "onPrefChange allow"
+                    + stringCategory + "=" + allowSenders + " allow" + stringCategory + "From="
+                    + ZenModeConfig.sourceToString(allowSendersFrom));
+        }
     }
 
     protected void saveConversationSenders(int val) {
@@ -280,7 +282,7 @@
         switch (contactType) {
             case ZenPolicy.PEOPLE_TYPE_ANYONE:
                 return ZEN_MODE_FROM_ANYONE;
-            case  ZenPolicy.PEOPLE_TYPE_CONTACTS:
+            case ZenPolicy.PEOPLE_TYPE_CONTACTS:
                 return ZEN_MODE_FROM_CONTACTS;
             case ZenPolicy.PEOPLE_TYPE_STARRED:
                 return ZEN_MODE_FROM_STARRED;
@@ -308,7 +310,7 @@
         switch (setting) {
             case ZenPolicy.PEOPLE_TYPE_ANYONE:
                 return NotificationManager.Policy.PRIORITY_SENDERS_ANY;
-            case  ZenPolicy.PEOPLE_TYPE_CONTACTS:
+            case ZenPolicy.PEOPLE_TYPE_CONTACTS:
                 return NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS;
             case ZenPolicy.PEOPLE_TYPE_STARRED:
                 return NotificationManager.Policy.PRIORITY_SENDERS_STARRED;
@@ -321,7 +323,7 @@
     protected int getAlarmsTotalSilencePeopleSummary(int category) {
         if (category == NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) {
             return R.string.zen_mode_none_messages;
-        } else if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CALLS){
+        } else if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CALLS) {
             return R.string.zen_mode_none_calls;
         } else if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS) {
             return R.string.zen_mode_from_no_conversations;
@@ -470,8 +472,8 @@
         if (cursor != null && cursor.moveToFirst()) {
             do {
                 String contact = cursor.getString(0);
-                starredContacts.add(contact != null ? contact :
-                        mContext.getString(R.string.zen_mode_starred_contacts_empty_name));
+                int emptyNameId = R.string.zen_mode_starred_contacts_empty_name;
+                starredContacts.add(contact != null ? contact : mContext.getString(emptyNameId));
 
             } while (cursor.moveToNext());
         }
diff --git a/src/com/android/settings/notification/zen/ZenModeSliceBuilder.java b/src/com/android/settings/notification/zen/ZenModeSliceBuilder.java
index 4f6f058..d16b1e4 100644
--- a/src/com/android/settings/notification/zen/ZenModeSliceBuilder.java
+++ b/src/com/android/settings/notification/zen/ZenModeSliceBuilder.java
@@ -132,8 +132,8 @@
         final Uri contentUri = new Uri.Builder().appendPath(ZEN_MODE_SLICE_KEY).build();
         final String screenTitle = context.getText(R.string.zen_mode_settings_title).toString();
         return SliceBuilderUtils.buildSearchResultPageIntent(context,
-                ZenModeSettings.class.getName(), ZEN_MODE_SLICE_KEY, screenTitle,
-                SettingsEnums.NOTIFICATION_ZEN_MODE, R.string.menu_key_notifications)
+                        ZenModeSettings.class.getName(), ZEN_MODE_SLICE_KEY, screenTitle,
+                        SettingsEnums.NOTIFICATION_ZEN_MODE, R.string.menu_key_notifications)
                 .setClassName(context.getPackageName(), SubSettings.class.getName())
                 .setData(contentUri);
     }
diff --git a/src/com/android/settings/print/PrintSettingsPageProvider.kt b/src/com/android/settings/print/PrintSettingsPageProvider.kt
index aac0a5d..f28f0bc 100644
--- a/src/com/android/settings/print/PrintSettingsPageProvider.kt
+++ b/src/com/android/settings/print/PrintSettingsPageProvider.kt
@@ -17,16 +17,32 @@
 package com.android.settings.print
 
 import android.app.settings.SettingsEnums
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
 import android.os.Bundle
+import android.provider.Settings
 import androidx.annotation.VisibleForTesting
 import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Add
+import androidx.compose.material.icons.outlined.Print
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.alpha
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
 import androidx.core.os.bundleOf
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.settings.R
@@ -36,13 +52,18 @@
 import com.android.settings.print.PrintSettingsFragment.EXTRA_SERVICE_COMPONENT_NAME
 import com.android.settings.print.PrintSettingsFragment.EXTRA_TITLE
 import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.compose.rememberContext
 import com.android.settingslib.spa.framework.compose.rememberDrawablePainter
 import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsOpacity
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spa.widget.scaffold.RegularScaffold
 import com.android.settingslib.spa.widget.ui.Category
+import com.android.settingslib.spa.widget.ui.SettingsIcon
+import com.android.settingslib.spaprivileged.settingsprovider.settingsSecureStringFlow
 import com.android.settingslib.spaprivileged.template.common.UserProfilePager
+import kotlinx.coroutines.flow.Flow
 
 object PrintSettingsPageProvider : SettingsPageProvider {
     override val name = "PrintSettings"
@@ -52,51 +73,101 @@
         RegularScaffold(title = stringResource(R.string.print_settings)) {
             val context = LocalContext.current
             val printRepository = remember(context) { PrintRepository(context) }
-            UserProfilePager {
-                PrintServices(printRepository)
-            }
+            UserProfilePager { PrintServices(printRepository) }
         }
     }
 
     @Composable
     private fun PrintServices(printRepository: PrintRepository) {
-        val printServiceDisplayInfos by remember {
-            printRepository.printServiceDisplayInfosFlow()
-        }.collectAsStateWithLifecycle(initialValue = emptyList())
-        Category(title = stringResource(R.string.print_settings_title)) {
-            for (printServiceDisplayInfo in printServiceDisplayInfos) {
-                PrintService(printServiceDisplayInfo)
+        val printServiceDisplayInfos by
+            remember { printRepository.printServiceDisplayInfosFlow() }
+                .collectAsStateWithLifecycle(initialValue = emptyList())
+        if (printServiceDisplayInfos.isEmpty()) {
+            NoServicesInstalled()
+        } else {
+            Category(title = stringResource(R.string.print_settings_title)) {
+                for (printServiceDisplayInfo in printServiceDisplayInfos) {
+                    PrintService(printServiceDisplayInfo)
+                }
             }
         }
+        AddPrintService()
+    }
+
+    @Composable
+    private fun NoServicesInstalled() {
+        Column(
+            modifier = Modifier.fillMaxSize().padding(SettingsDimension.itemPaddingAround),
+            horizontalAlignment = Alignment.CenterHorizontally,
+        ) {
+            Icon(
+                imageVector = Icons.Outlined.Print,
+                contentDescription = null,
+                modifier =
+                    Modifier.size(110.dp)
+                        .padding(SettingsDimension.itemPaddingAround)
+                        .alpha(SettingsOpacity.SurfaceTone),
+            )
+            Text(
+                text = stringResource(R.string.print_no_services_installed),
+                style = MaterialTheme.typography.titleLarge,
+            )
+        }
     }
 
     @VisibleForTesting
     @Composable
     fun PrintService(displayInfo: PrintServiceDisplayInfo) {
         val context = LocalContext.current
-        Preference(model = object : PreferenceModel {
-            override val title = displayInfo.title
-            override val summary = { displayInfo.summary }
-            override val icon: @Composable () -> Unit = {
-                Image(
-                    painter = rememberDrawablePainter(displayInfo.icon),
-                    contentDescription = null,
-                    modifier = Modifier.size(SettingsDimension.appIconItemSize),
-                )
-            }
-            override val onClick = {
-                SubSettingLauncher(context).apply {
-                    setDestination(PrintServiceSettingsFragment::class.qualifiedName)
-                    setArguments(
-                        bundleOf(
-                            EXTRA_CHECKED to displayInfo.isEnabled,
-                            EXTRA_TITLE to displayInfo.title,
-                            EXTRA_SERVICE_COMPONENT_NAME to displayInfo.componentName
-                        )
+        Preference(
+            object : PreferenceModel {
+                override val title = displayInfo.title
+                override val summary = { displayInfo.summary }
+                override val icon: @Composable () -> Unit = {
+                    Image(
+                        painter = rememberDrawablePainter(displayInfo.icon),
+                        contentDescription = null,
+                        modifier = Modifier.size(SettingsDimension.appIconItemSize),
                     )
-                    setSourceMetricsCategory(SettingsEnums.PRINT_SETTINGS)
-                }.launch()
+                }
+                override val onClick = { launchPrintServiceSettings(context, displayInfo) }
             }
-        })
+        )
+    }
+
+    private fun launchPrintServiceSettings(context: Context, displayInfo: PrintServiceDisplayInfo) {
+        SubSettingLauncher(context)
+            .apply {
+                setDestination(PrintServiceSettingsFragment::class.qualifiedName)
+                setArguments(
+                    bundleOf(
+                        EXTRA_CHECKED to displayInfo.isEnabled,
+                        EXTRA_TITLE to displayInfo.title,
+                        EXTRA_SERVICE_COMPONENT_NAME to displayInfo.componentName
+                    )
+                )
+                setSourceMetricsCategory(SettingsEnums.PRINT_SETTINGS)
+            }
+            .launch()
+    }
+
+    @Composable
+    fun AddPrintService(
+        searchUriFlow: Flow<String> = rememberContext { context ->
+            context.settingsSecureStringFlow(Settings.Secure.PRINT_SERVICE_SEARCH_URI)
+        },
+    ) {
+        val context = LocalContext.current
+        val searchUri by searchUriFlow.collectAsStateWithLifecycle("")
+        if (searchUri.isEmpty()) return
+        Preference(
+            object : PreferenceModel {
+                override val title = stringResource(R.string.print_menu_item_add_service)
+                override val icon = @Composable { SettingsIcon(imageVector = Icons.Outlined.Add) }
+                override val onClick = {
+                    context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(searchUri)))
+                }
+            }
+        )
     }
 }
diff --git a/src/com/android/settings/spa/network/AutomaticDataSwitchingPreference.kt b/src/com/android/settings/spa/network/AutomaticDataSwitchingPreference.kt
index e79be4a..e7cc18f 100644
--- a/src/com/android/settings/spa/network/AutomaticDataSwitchingPreference.kt
+++ b/src/com/android/settings/spa/network/AutomaticDataSwitchingPreference.kt
@@ -16,13 +16,11 @@
 
 package com.android.settings.spa.network
 
-import android.telephony.TelephonyManager
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.res.stringResource
 import androidx.lifecycle.viewmodel.compose.viewModel
 import com.android.settings.R
-import com.android.settings.network.telephony.TelephonyRepository
 import com.android.settings.network.telephony.wificalling.CrossSimCallingViewModel
 import com.android.settingslib.spa.widget.preference.SwitchPreference
 import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
@@ -51,11 +49,3 @@
         }
     )
 }
-
-fun TelephonyRepository.setAutomaticData(subId: Int, newEnabled: Boolean) {
-    setMobileDataPolicyEnabled(
-        subId = subId,
-        policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
-        enabled = newEnabled,
-    )
-}
diff --git a/src/com/android/settings/spa/network/MobileDataSwitchingPreference.kt b/src/com/android/settings/spa/network/MobileDataSwitchingPreference.kt
index 0d40bca..4b95d44 100644
--- a/src/com/android/settings/spa/network/MobileDataSwitchingPreference.kt
+++ b/src/com/android/settings/spa/network/MobileDataSwitchingPreference.kt
@@ -16,12 +16,10 @@
 
 package com.android.settings.spa.network
 
-import android.telephony.TelephonyManager
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.res.stringResource
 import com.android.settings.R
-import com.android.settings.network.telephony.TelephonyRepository
 import com.android.settingslib.spa.widget.preference.SwitchPreference
 import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
 import kotlinx.coroutines.Dispatchers
diff --git a/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt b/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt
index 4b9fcf4..05a8f6a 100644
--- a/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt
+++ b/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt
@@ -48,13 +48,14 @@
 import com.android.settings.R
 import com.android.settings.network.SubscriptionInfoListViewModel
 import com.android.settings.network.telephony.DataSubscriptionRepository
-import com.android.settings.network.telephony.TelephonyRepository
+import com.android.settings.network.telephony.MobileDataRepository
 import com.android.settings.spa.network.PrimarySimRepository.PrimarySimInfo
 import com.android.settings.wifi.WifiPickerTrackerHelper
 import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
 import com.android.settingslib.spa.framework.common.SettingsPageProvider
 import com.android.settingslib.spa.framework.common.createSettingsPage
 import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.compose.rememberContext
 import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
@@ -202,21 +203,18 @@
 ) {
     val context = LocalContext.current
     val localLifecycleOwner = LocalLifecycleOwner.current
-    val wifiPickerTrackerHelper = getWifiPickerTrackerHelper(context, localLifecycleOwner)
-
-    val subscriptionManager: SubscriptionManager? =
-            context.getSystemService(SubscriptionManager::class.java)
+    val mobileDataRepository = rememberContext(::MobileDataRepository)
 
     Category(title = stringResource(id = R.string.mobile_data_settings_title)) {
         val isAutoDataEnabled by remember(nonDds.intValue) {
-            TelephonyRepository(context).isMobileDataPolicyEnabledFlow(
+            mobileDataRepository.isMobileDataPolicyEnabledFlow(
                 subId = nonDds.intValue,
                 policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH
             )
         }.collectAsStateWithLifecycle(initialValue = null)
 
         val mobileDataStateChanged by remember(mobileDataSelectedId.intValue) {
-            TelephonyRepository(context).isDataEnabledFlow(mobileDataSelectedId.intValue)
+            mobileDataRepository.isMobileDataEnabledFlow(mobileDataSelectedId.intValue)
         }.collectAsStateWithLifecycle(initialValue = false)
         val coroutineScope = rememberCoroutineScope()
 
@@ -226,8 +224,8 @@
                 coroutineScope.launch {
                     setMobileData(
                         context,
-                        subscriptionManager,
-                        wifiPickerTrackerHelper,
+                        context.getSystemService(SubscriptionManager::class.java),
+                        getWifiPickerTrackerHelper(context, localLifecycleOwner),
                         mobileDataSelectedId.intValue,
                         newEnabled
                     )
@@ -238,7 +236,7 @@
             AutomaticDataSwitchingPreference(
                 isAutoDataEnabled = { isAutoDataEnabled },
                 setAutoDataEnabled = { newEnabled ->
-                    TelephonyRepository(context).setAutomaticData(nonDds.intValue, newEnabled)
+                    mobileDataRepository.setAutoDataSwitch(nonDds.intValue, newEnabled)
                 },
             )
         }
@@ -426,6 +424,6 @@
             Log.d(NetworkCellularGroupProvider.fileName, "setDefaultData: [$targetSubId]")
             subscriptionManager?.setDefaultDataSubId(targetSubId)
         }
-        TelephonyRepository(context)
-            .setMobileData(targetSubId, enabled, wifiPickerTrackerHelper)
+        MobileDataRepository(context)
+            .setMobileDataEnabled(targetSubId, enabled, wifiPickerTrackerHelper)
     }
\ No newline at end of file
diff --git a/src/com/android/settings/users/UserDetailsSettings.java b/src/com/android/settings/users/UserDetailsSettings.java
index b48c717..71dd43f 100644
--- a/src/com/android/settings/users/UserDetailsSettings.java
+++ b/src/com/android/settings/users/UserDetailsSettings.java
@@ -363,13 +363,12 @@
         }
         if (mUserInfo.isMain() || mUserInfo.isGuest() || !UserManager.isMultipleAdminEnabled()
                 || mUserManager.hasUserRestrictionForUser(UserManager.DISALLOW_GRANT_ADMIN,
-                mUserInfo.getUserHandle())) {
+                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);
-            removePreference(KEY_GRANT_ADMIN);
             removePreference(KEY_APP_AND_CONTENT_ACCESS);
             removePreference(KEY_APP_COPYING);
         } else {
diff --git a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
index 82537d4..e5581d3 100644
--- a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
+++ b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
@@ -33,7 +33,6 @@
 import android.telephony.TelephonyManager;
 import android.telephony.ims.ImsManager;
 import android.telephony.ims.ImsMmTelManager;
-import android.telephony.ims.ProvisioningManager;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -42,12 +41,14 @@
 import android.widget.CompoundButton;
 import android.widget.CompoundButton.OnCheckedChangeListener;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.appcompat.app.AlertDialog;
+import androidx.lifecycle.LifecycleOwner;
 import androidx.preference.Preference;
 import androidx.preference.Preference.OnPreferenceClickListener;
 import androidx.preference.PreferenceScreen;
 
-import com.android.ims.ImsConfig;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.flags.Flags;
@@ -57,8 +58,12 @@
 import com.android.settings.Utils;
 import com.android.settings.core.SubSettingLauncher;
 import com.android.settings.network.ims.WifiCallingQueryImsState;
+import com.android.settings.network.telephony.wificalling.IWifiCallingRepository;
+import com.android.settings.network.telephony.wificalling.WifiCallingRepository;
 import com.android.settings.widget.SettingsMainSwitchPreference;
 
+import kotlin.Unit;
+
 import java.util.List;
 
 /**
@@ -103,7 +108,6 @@
 
     private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     private ImsMmTelManager mImsMmTelManager;
-    private ProvisioningManager mProvisioningManager;
     private TelephonyManager mTelephonyManager;
 
     private PhoneTelephonyCallback mTelephonyCallback;
@@ -188,19 +192,6 @@
                 return true;
             };
 
-    private final ProvisioningManager.Callback mProvisioningCallback =
-            new ProvisioningManager.Callback() {
-                @Override
-                public void onProvisioningIntChanged(int item, int value) {
-                    if (item == ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED
-                            || item == ImsConfig.ConfigConstants.VLT_SETTING_ENABLED) {
-                        // The provisioning policy might have changed. Update the body to make sure
-                        // this change takes effect if needed.
-                        updateBody();
-                    }
-                }
-            };
-
     @VisibleForTesting
     void showAlert(Intent intent) {
         final Context context = getActivity();
@@ -264,14 +255,6 @@
     }
 
     @VisibleForTesting
-    ProvisioningManager getImsProvisioningManager() {
-        if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
-            return null;
-        }
-        return ProvisioningManager.createForSubscriptionId(mSubId);
-    }
-
-    @VisibleForTesting
     ImsMmTelManager getImsMmTelManager() {
         if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
             return null;
@@ -294,7 +277,6 @@
                     FRAGMENT_BUNDLE_SUBID, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
         }
 
-        mProvisioningManager = getImsProvisioningManager();
         mImsMmTelManager = getImsMmTelManager();
 
         mSwitchBar = (SettingsMainSwitchPreference) findPreference(SWITCH_BAR);
@@ -336,19 +318,33 @@
         return view;
     }
 
+    @Override
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        getWifiCallingRepository().collectIsWifiCallingReadyFlow(
+                getLifecycleOwner(), (isWifiWifiCallingReadyFlow) -> {
+                    if (!isWifiWifiCallingReadyFlow) {
+                        // This screen is not allowed to be shown due to provisioning policy and
+                        // should therefore be closed.
+                        finish();
+                    }
+                    return Unit.INSTANCE;
+                });
+    }
+
     @VisibleForTesting
-    boolean isWfcProvisionedOnDevice() {
-        return queryImsState(mSubId).isWifiCallingProvisioned();
+    @NonNull
+    IWifiCallingRepository getWifiCallingRepository() {
+        return new WifiCallingRepository(requireContext(), mSubId);
+    }
+
+    @VisibleForTesting
+    @NonNull
+    LifecycleOwner getLifecycleOwner() {
+        return getViewLifecycleOwner();
     }
 
     private void updateBody() {
-        if (!isWfcProvisionedOnDevice()) {
-            // This screen is not allowed to be shown due to provisioning policy and should
-            // therefore be closed.
-            finish();
-            return;
-        }
-
         final CarrierConfigManager configManager = (CarrierConfigManager)
                 getSystemService(Context.CARRIER_CONFIG_SERVICE);
         boolean isWifiOnlySupported = true;
@@ -448,8 +444,6 @@
         if (intent.getBooleanExtra(Phone.EXTRA_KEY_ALERT_SHOW, false)) {
             showAlert(intent);
         }
-        // Register callback for provisioning changes.
-        registerProvisioningChangedCallback();
     }
 
     @Override
@@ -462,8 +456,6 @@
             mSwitchBar.removeOnSwitchChangeListener(this);
         }
         context.unregisterReceiver(mIntentReceiver);
-        // Remove callback for provisioning changes.
-        unregisterProvisioningChangedCallback();
     }
 
     /**
@@ -699,27 +691,6 @@
         return SubscriptionManager.getResourcesForSubId(getContext(), mSubId);
     }
 
-    @VisibleForTesting
-    void registerProvisioningChangedCallback() {
-        if (mProvisioningManager == null) {
-            return;
-        }
-        try {
-            mProvisioningManager.registerProvisioningChangedCallback(getContext().getMainExecutor(),
-                    mProvisioningCallback);
-        } catch (Exception ex) {
-            Log.w(TAG, "onResume: Unable to register callback for provisioning changes.");
-        }
-    }
-
-    @VisibleForTesting
-    void unregisterProvisioningChangedCallback() {
-        if (mProvisioningManager == null) {
-            return;
-        }
-        mProvisioningManager.unregisterProvisioningChangedCallback(mProvisioningCallback);
-    }
-
     /**
      * Determine whether to override roaming Wi-Fi calling preference when device is connected to
      * non-terrestrial network.
diff --git a/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java b/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java
index a7527d7..c7ad9ca 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java
@@ -35,6 +35,8 @@
 import androidx.fragment.app.FragmentTransaction;
 
 import com.android.settings.R;
+import com.android.settings.flags.Flags;
+import com.android.settings.overlay.FeatureFactory;
 
 import java.util.List;
 
@@ -236,7 +238,12 @@
                         WifiDppUtils.TAG_FRAGMENT_QR_CODE_GENERATOR);
 
         if (fragment == null) {
-            fragment = new WifiDppQrCodeGeneratorFragment();
+            if (Flags.enableWifiSharingRuntimeFragment()) {
+                fragment = FeatureFactory.getFeatureFactory().getWifiFeatureProvider()
+                    .getWifiDppQrCodeGeneratorFragment();
+            } else {
+                fragment = new WifiDppQrCodeGeneratorFragment();
+            }
         } else {
             if (fragment.isVisible()) {
                 return;
diff --git a/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java b/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java
index 3d437e2..1213b0d 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java
@@ -56,7 +56,7 @@
     private static final String TAG = "WifiDppQrCodeGeneratorFragment";
 
     private ImageView mQrCodeView;
-    private String mQrCode;
+    protected String mQrCode;
 
     private static final String CHIP_LABEL_METADATA_KEY = "android.service.chooser.chip_label";
     private static final String CHIP_ICON_METADATA_KEY = "android.service.chooser.chip_icon";
@@ -258,7 +258,7 @@
         return button;
     }
 
-    private void setQrCode() {
+    protected void setQrCode() {
         try {
             final int qrcodeSize = getContext().getResources().getDimensionPixelSize(
                     R.dimen.qrcode_size);
diff --git a/src/com/android/settings/wifi/factory/WifiFeatureProvider.java b/src/com/android/settings/wifi/factory/WifiFeatureProvider.java
index 5ab899a..e5bf81a 100644
--- a/src/com/android/settings/wifi/factory/WifiFeatureProvider.java
+++ b/src/com/android/settings/wifi/factory/WifiFeatureProvider.java
@@ -27,6 +27,7 @@
 import androidx.lifecycle.ViewModelStoreOwner;
 
 import com.android.settings.wifi.details.WifiNetworkDetailsViewModel;
+import com.android.settings.wifi.dpp.WifiDppQrCodeGeneratorFragment;
 import com.android.settings.wifi.repository.SharedConnectivityRepository;
 import com.android.settings.wifi.repository.WifiHotspotRepository;
 import com.android.settings.wifi.tether.WifiHotspotSecurityViewModel;
@@ -147,6 +148,15 @@
     }
 
     /**
+     * Gets an instance of WifiDppQrCodeGeneratorFragment
+     */
+    public WifiDppQrCodeGeneratorFragment getWifiDppQrCodeGeneratorFragment() {
+        WifiDppQrCodeGeneratorFragment fragment = new WifiDppQrCodeGeneratorFragment();
+        verboseLog(TAG, "getWifiDppQrCodeGeneratorFragment():" + fragment);
+        return fragment;
+    }
+
+    /**
      * Send a {@link Log#VERBOSE} log message.
      *
      * @param tag Used to identify the source of a log message.  It usually identifies
diff --git a/tests/Enable16KbTests/Android.bp b/tests/Enable16KbTests/Android.bp
index 781ea8f..fa05d33 100644
--- a/tests/Enable16KbTests/Android.bp
+++ b/tests/Enable16KbTests/Android.bp
@@ -33,7 +33,6 @@
     ],
     platform_apis: true,
     certificate: "platform",
-    test_suites: ["device-tests"],
     libs: [
         "android.test.runner",
         "android.test.base",
@@ -57,6 +56,6 @@
     data: [
         ":test_16kb_app",
     ],
-    test_suites: ["device-tests"],
+    test_suites: ["general-tests"],
     test_config: "AndroidTest.xml",
 }
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioControllerTest.java
index d9a917b..24528ae 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioControllerTest.java
@@ -21,26 +21,35 @@
 
 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.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.settings.SettingsEnums;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
 import android.media.AudioDeviceAttributes;
 import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
 import android.media.Spatializer;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 
 import androidx.preference.PreferenceCategory;
 import androidx.preference.TwoStatePreference;
 
 import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settingslib.bluetooth.A2dpProfile;
+import com.android.settingslib.bluetooth.HearingAidProfile;
+import com.android.settingslib.bluetooth.LeAudioProfile;
 import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.flags.Flags;
 
 import com.google.common.collect.ImmutableList;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -54,7 +63,8 @@
 
 @RunWith(RobolectricTestRunner.class)
 public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetailsControllerTestBase {
-
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
     private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C";
     private static final String KEY_SPATIAL_AUDIO = "spatial_audio";
     private static final String KEY_HEAD_TRACKING = "head_tracking";
@@ -64,6 +74,9 @@
     @Mock private Lifecycle mSpatialAudioLifecycle;
     @Mock private PreferenceCategory mProfilesContainer;
     @Mock private BluetoothDevice mBluetoothDevice;
+    @Mock private A2dpProfile mA2dpProfile;
+    @Mock private LeAudioProfile mLeAudioProfile;
+    @Mock private HearingAidProfile mHearingAidProfile;
 
     private AudioDeviceAttributes mAvailableDevice;
 
@@ -83,6 +96,12 @@
         when(mAudioManager.getSpatializer()).thenReturn(mSpatializer);
         when(mCachedDevice.getAddress()).thenReturn(MAC_ADDRESS);
         when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice);
+        when(mCachedDevice.getProfiles())
+                .thenReturn(List.of(mA2dpProfile, mLeAudioProfile, mHearingAidProfile));
+        when(mA2dpProfile.isEnabled(mBluetoothDevice)).thenReturn(true);
+        when(mA2dpProfile.getProfileId()).thenReturn(BluetoothProfile.A2DP);
+        when(mLeAudioProfile.getProfileId()).thenReturn(BluetoothProfile.LE_AUDIO);
+        when(mHearingAidProfile.getProfileId()).thenReturn(BluetoothProfile.HEARING_AID);
         when(mBluetoothDevice.getAnonymizedAddress()).thenReturn(MAC_ADDRESS);
         when(mFeatureFactory.getBluetoothFeatureProvider().getSpatializer(mContext))
                 .thenReturn(mSpatializer);
@@ -273,6 +292,52 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DETERMINING_SPATIAL_AUDIO_ATTRIBUTES_BY_PROFILE)
+    public void refresh_leAudioProfileEnabledForHeadset_useLeAudioHeadsetAttributes() {
+        when(mLeAudioProfile.isEnabled(mBluetoothDevice)).thenReturn(true);
+        when(mA2dpProfile.isEnabled(mBluetoothDevice)).thenReturn(false);
+        when(mHearingAidProfile.isEnabled(mBluetoothDevice)).thenReturn(false);
+        when(mAudioManager.getBluetoothAudioDeviceCategory(MAC_ADDRESS))
+                .thenReturn(AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES);
+        when(mSpatializer.isAvailableForDevice(any())).thenReturn(true);
+
+        mController.refresh();
+        ShadowLooper.idleMainLooper();
+
+        assertThat(mController.mAudioDevice.getType()).isEqualTo(AudioDeviceInfo.TYPE_BLE_HEADSET);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DETERMINING_SPATIAL_AUDIO_ATTRIBUTES_BY_PROFILE)
+    public void refresh_leAudioProfileEnabledForSpeaker_useLeAudioSpeakerAttributes() {
+        when(mLeAudioProfile.isEnabled(mBluetoothDevice)).thenReturn(true);
+        when(mA2dpProfile.isEnabled(mBluetoothDevice)).thenReturn(false);
+        when(mHearingAidProfile.isEnabled(mBluetoothDevice)).thenReturn(false);
+        when(mAudioManager.getBluetoothAudioDeviceCategory(MAC_ADDRESS))
+                .thenReturn(AudioManager.AUDIO_DEVICE_CATEGORY_SPEAKER);
+        when(mSpatializer.isAvailableForDevice(any())).thenReturn(true);
+
+        mController.refresh();
+        ShadowLooper.idleMainLooper();
+
+        assertThat(mController.mAudioDevice.getType()).isEqualTo(AudioDeviceInfo.TYPE_BLE_SPEAKER);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DETERMINING_SPATIAL_AUDIO_ATTRIBUTES_BY_PROFILE)
+    public void refresh_hearingAidProfileEnabled_useHearingAidAttributes() {
+        when(mLeAudioProfile.isEnabled(mBluetoothDevice)).thenReturn(false);
+        when(mA2dpProfile.isEnabled(mBluetoothDevice)).thenReturn(false);
+        when(mHearingAidProfile.isEnabled(mBluetoothDevice)).thenReturn(true);
+        when(mSpatializer.isAvailableForDevice(any())).thenReturn(true);
+
+        mController.refresh();
+        ShadowLooper.idleMainLooper();
+
+        assertThat(mController.mAudioDevice.getType()).isEqualTo(AudioDeviceInfo.TYPE_HEARING_AID);
+    }
+
+    @Test
     public void turnedOnSpatialAudio_invokesAddCompatibleAudioDevice() {
         mController.setAvailableDevice(mAvailableDevice);
         mSpatialAudioPref.setChecked(true);
diff --git a/tests/robotests/src/com/android/settings/notification/modes/IconLoaderTest.java b/tests/robotests/src/com/android/settings/notification/modes/IconLoaderTest.java
deleted file mode 100644
index 7d4a367..0000000
--- a/tests/robotests/src/com/android/settings/notification/modes/IconLoaderTest.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.AutomaticZenRule;
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.service.notification.ZenPolicy;
-
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.MoreExecutors;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-@RunWith(RobolectricTestRunner.class)
-public class IconLoaderTest {
-
-    private Context mContext;
-    private IconLoader mLoader;
-
-    @Before
-    public void setUp() {
-        mContext = RuntimeEnvironment.application;
-        mLoader = new IconLoader(MoreExecutors.newDirectExecutorService());
-    }
-
-    @Test
-    public void getIcon_systemOwnedRuleWithIcon_loads() throws Exception {
-        AutomaticZenRule systemRule = newRuleBuilder()
-                .setPackage("android")
-                .setIconResId(android.R.drawable.ic_media_play)
-                .build();
-
-        ListenableFuture<Drawable> loadFuture = mLoader.getIcon(mContext, systemRule);
-        assertThat(loadFuture.isDone()).isTrue();
-        assertThat(loadFuture.get()).isNotNull();
-    }
-
-    @Test
-    public void getIcon_ruleWithoutSpecificIcon_loadsFallback() throws Exception {
-        AutomaticZenRule rule = newRuleBuilder()
-                .setType(AutomaticZenRule.TYPE_DRIVING)
-                .setPackage("com.blah")
-                .build();
-
-        ListenableFuture<Drawable> loadFuture = mLoader.getIcon(mContext, rule);
-        assertThat(loadFuture.isDone()).isTrue();
-        assertThat(loadFuture.get()).isNotNull();
-    }
-
-    @Test
-    public void getIcon_ruleWithAppIconWithLoadFailure_loadsFallback() throws Exception {
-        AutomaticZenRule rule = newRuleBuilder()
-                .setType(AutomaticZenRule.TYPE_DRIVING)
-                .setPackage("com.blah")
-                .setIconResId(-123456)
-                .build();
-
-        ListenableFuture<Drawable> loadFuture = mLoader.getIcon(mContext, rule);
-        assertThat(loadFuture.get()).isNotNull();
-    }
-
-    private static AutomaticZenRule.Builder newRuleBuilder() {
-        return new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                .setZenPolicy(new ZenPolicy.Builder().build());
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/notification/modes/InterruptionFilterPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/InterruptionFilterPreferenceControllerTest.java
new file mode 100644
index 0000000..ff25322
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/modes/InterruptionFilterPreferenceControllerTest.java
@@ -0,0 +1,130 @@
+/*
+ * 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.app.NotificationManager.INTERRUPTION_FILTER_ALL;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
+import static android.service.notification.ZenPolicy.STATE_DISALLOW;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.app.Flags;
+import android.content.Context;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.service.notification.ZenPolicy;
+
+import androidx.preference.TwoStatePreference;
+
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+@EnableFlags(Flags.FLAG_MODES_UI)
+public final class InterruptionFilterPreferenceControllerTest {
+
+    private InterruptionFilterPreferenceController mController;
+
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+    private Context mContext;
+    @Mock private ZenModesBackend mBackend;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = RuntimeEnvironment.application;
+        mController = new InterruptionFilterPreferenceController(mContext, "something",  mBackend);
+    }
+
+    @Test
+    public void testUpdateState_all() {
+        TwoStatePreference preference = mock(TwoStatePreference.class);
+        ZenMode zenMode = new TestModeBuilder()
+                .setInterruptionFilter(INTERRUPTION_FILTER_ALL)
+                .build();
+        mController.updateZenMode(preference, zenMode);
+
+        verify(preference).setChecked(false);
+    }
+
+    @Test
+    public void testOnPreferenceChange_fromAll() {
+        TwoStatePreference preference = mock(TwoStatePreference.class);
+        ZenMode zenMode = new TestModeBuilder()
+                .setInterruptionFilter(INTERRUPTION_FILTER_ALL)
+                .build();
+
+        mController.updateZenMode(preference, zenMode);
+
+        mController.onPreferenceChange(preference, true);
+
+        ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
+        verify(mBackend).updateMode(captor.capture());
+        assertThat(captor.getValue().getPolicy().getPriorityCategoryAlarms())
+                .isEqualTo(STATE_DISALLOW);
+        assertThat(captor.getValue().getRule().getInterruptionFilter())
+                .isEqualTo(INTERRUPTION_FILTER_PRIORITY);
+    }
+
+    @Test
+    public void testUpdateState_priority() {
+        TwoStatePreference preference = mock(TwoStatePreference.class);
+        ZenMode zenMode = new TestModeBuilder()
+                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+                .setZenPolicy(new ZenPolicy.Builder().allowAlarms(true).build())
+                .build();
+        mController.updateZenMode(preference, zenMode);
+
+        verify(preference).setChecked(true);
+    }
+
+    @Test
+    public void testOnPreferenceChange_fromPriority() {
+        TwoStatePreference preference = mock(TwoStatePreference.class);
+        ZenMode zenMode = new TestModeBuilder()
+                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+                .setZenPolicy(new ZenPolicy.Builder().allowAlarms(false).build())
+                .build();
+
+        mController.updateZenMode(preference, zenMode);
+
+        mController.onPreferenceChange(preference, false);
+
+        ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
+        verify(mBackend).updateMode(captor.capture());
+        assertThat(captor.getValue().getPolicy().getPriorityCategoryAlarms())
+                .isEqualTo(STATE_DISALLOW);
+        assertThat(captor.getValue().getRule().getInterruptionFilter())
+                .isEqualTo(INTERRUPTION_FILTER_ALL);
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/modes/TestModeBuilder.java b/tests/robotests/src/com/android/settings/notification/modes/TestModeBuilder.java
new file mode 100644
index 0000000..fa12b30
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/modes/TestModeBuilder.java
@@ -0,0 +1,149 @@
+/*
+ * 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 android.app.AutomaticZenRule;
+import android.app.NotificationManager;
+import android.net.Uri;
+import android.service.notification.Condition;
+import android.service.notification.ZenDeviceEffects;
+import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenPolicy;
+
+import androidx.annotation.Nullable;
+
+import com.android.settingslib.notification.modes.ZenMode;
+
+import java.util.Random;
+
+class TestModeBuilder {
+
+    private String mId;
+    private AutomaticZenRule mRule;
+    private ZenModeConfig.ZenRule mConfigZenRule;
+
+    public static final ZenMode EXAMPLE = new TestModeBuilder().build();
+
+    TestModeBuilder() {
+        // Reasonable defaults
+        int id = new Random().nextInt(1000);
+        mId = "rule_" + id;
+        mRule = new AutomaticZenRule.Builder("Test Rule #" + id, Uri.parse("rule://" + id))
+                .setPackage("some_package")
+                .setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY)
+                .setZenPolicy(new ZenPolicy.Builder().disallowAllSounds().build())
+                .build();
+        mConfigZenRule = new ZenModeConfig.ZenRule();
+        mConfigZenRule.enabled = true;
+        mConfigZenRule.pkg = "some_package";
+    }
+
+    TestModeBuilder setId(String id) {
+        mId = id;
+        return this;
+    }
+
+    TestModeBuilder setAzr(AutomaticZenRule rule) {
+        mRule = rule;
+        mConfigZenRule.pkg = rule.getPackageName();
+        mConfigZenRule.conditionId = rule.getConditionId();
+        mConfigZenRule.enabled = rule.isEnabled();
+        return this;
+    }
+
+    TestModeBuilder setConfigZenRule(ZenModeConfig.ZenRule configZenRule) {
+        mConfigZenRule = configZenRule;
+        return this;
+    }
+
+    public TestModeBuilder setName(String name) {
+        mRule.setName(name);
+        mConfigZenRule.name = name;
+        return this;
+    }
+
+    public TestModeBuilder setPackage(String pkg) {
+        mRule.setPackageName(pkg);
+        mConfigZenRule.pkg = pkg;
+        return this;
+    }
+
+    TestModeBuilder setConditionId(Uri conditionId) {
+        mRule.setConditionId(conditionId);
+        mConfigZenRule.conditionId = conditionId;
+        return this;
+    }
+
+    TestModeBuilder setType(@AutomaticZenRule.Type int type) {
+        mRule.setType(type);
+        mConfigZenRule.type = type;
+        return this;
+    }
+
+    TestModeBuilder setInterruptionFilter(
+            @NotificationManager.InterruptionFilter int interruptionFilter) {
+        mRule.setInterruptionFilter(interruptionFilter);
+        mConfigZenRule.zenMode = NotificationManager.zenModeFromInterruptionFilter(
+                interruptionFilter, NotificationManager.INTERRUPTION_FILTER_PRIORITY);
+        return this;
+    }
+
+    TestModeBuilder setZenPolicy(@Nullable ZenPolicy policy) {
+        mRule.setZenPolicy(policy);
+        mConfigZenRule.zenPolicy = policy;
+        return this;
+    }
+
+    TestModeBuilder setDeviceEffects(@Nullable ZenDeviceEffects deviceEffects) {
+        mRule.setDeviceEffects(deviceEffects);
+        mConfigZenRule.zenDeviceEffects = deviceEffects;
+        return this;
+    }
+
+    public TestModeBuilder setEnabled(boolean enabled) {
+        mRule.setEnabled(enabled);
+        mConfigZenRule.enabled = enabled;
+        return this;
+    }
+
+    TestModeBuilder setManualInvocationAllowed(boolean allowed) {
+        mRule.setManualInvocationAllowed(allowed);
+        mConfigZenRule.allowManualInvocation = allowed;
+        return this;
+    }
+
+    public TestModeBuilder setTriggerDescription(@Nullable String triggerDescription) {
+        mRule.setTriggerDescription(triggerDescription);
+        mConfigZenRule.triggerDescription = triggerDescription;
+        return this;
+    }
+
+    TestModeBuilder setActive(boolean active) {
+        if (active) {
+            mConfigZenRule.enabled = true;
+            mConfigZenRule.condition = new Condition(mRule.getConditionId(), "...",
+                    Condition.STATE_TRUE);
+        } else {
+            mConfigZenRule.condition = null;
+        }
+        return this;
+    }
+
+    ZenMode build() {
+        return new ZenMode(mId, mRule, mConfigZenRule);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java
index 8205f3a..83f8de0 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java
@@ -17,23 +17,21 @@
 package com.android.settings.notification.modes;
 
 import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
-
-import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
+import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.app.AutomaticZenRule;
 import android.app.Flags;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
-import android.net.Uri;
 import android.os.Bundle;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
@@ -45,6 +43,8 @@
 import com.android.settings.SettingsActivity;
 import com.android.settingslib.applications.ApplicationsState;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 import com.android.settingslib.widget.SelectorWithWidgetPreference;
 
 import org.junit.Before;
@@ -101,14 +101,13 @@
     }
 
     private ZenMode createPriorityChannelsZenMode() {
-        return new ZenMode("id", new AutomaticZenRule.Builder("Bedtime",
-                Uri.parse("bed"))
-                .setType(AutomaticZenRule.TYPE_BEDTIME)
+        return new TestModeBuilder()
+                .setId("id")
                 .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                 .setZenPolicy(new ZenPolicy.Builder()
                         .allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY)
                         .build())
-                .build(), true);
+                .build();
     }
 
     @Test
@@ -137,7 +136,7 @@
         Bundle bundle = launcherIntent.getBundleExtra(
                 SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS);
         assertThat(bundle).isNotNull();
-        assertThat(bundle.getString(MODE_ID)).isEqualTo("id");
+        assertThat(bundle.getString(EXTRA_AUTOMATIC_ZEN_RULE_ID)).isEqualTo("id");
     }
 
     @Test
@@ -180,13 +179,30 @@
 
     @Test
     public void testOnPackageListChangedTriggersRebuild() {
-        mController.mAppSessionCallbacks.onPackageListChanged();
+        SelectorWithWidgetPreference preference = mock(SelectorWithWidgetPreference.class);
+        // Create a zen mode that allows priority channels to breakthrough.
+        ZenMode zenMode = createPriorityChannelsZenMode();
+        mController.updateState(preference, zenMode);
         verify(mSession).rebuild(any(), any(), eq(false));
+
+        mController.mAppSessionCallbacks.onPackageListChanged();
+        verify(mSession, times(2)).rebuild(any(), any(), eq(false));
     }
 
     @Test
     public void testOnLoadEntriesCompletedTriggersRebuild() {
-        mController.mAppSessionCallbacks.onLoadEntriesCompleted();
+        SelectorWithWidgetPreference preference = mock(SelectorWithWidgetPreference.class);
+        // Create a zen mode that allows priority channels to breakthrough.
+        ZenMode zenMode = createPriorityChannelsZenMode();
+        mController.updateState(preference, zenMode);
         verify(mSession).rebuild(any(), any(), eq(false));
+
+        mController.mAppSessionCallbacks.onLoadEntriesCompleted();
+        verify(mSession, times(2)).rebuild(any(), any(), eq(false));
+    }
+
+    @Test
+    public void testNoCrashIfAppsReadyBeforeRuleAvailable() {
+        mController.mAppSessionCallbacks.onLoadEntriesCompleted();
     }
 }
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsPreferenceControllerTest.java
index 750453d..c96dbb6 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsPreferenceControllerTest.java
@@ -16,11 +16,8 @@
 
 package com.android.settings.notification.modes;
 
-import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
-import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
 import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
 
-import static com.android.settings.notification.modes.ZenModeAppsPreferenceController.KEY_ALL;
 import static com.android.settings.notification.modes.ZenModeAppsPreferenceController.KEY_NONE;
 import static com.android.settings.notification.modes.ZenModeAppsPreferenceController.KEY_PRIORITY;
 
@@ -29,10 +26,8 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
-import android.app.AutomaticZenRule;
 import android.app.Flags;
 import android.content.Context;
-import android.net.Uri;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.service.notification.ZenPolicy;
@@ -42,6 +37,8 @@
 import androidx.preference.PreferenceScreen;
 import androidx.preference.TwoStatePreference;
 
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 import com.android.settingslib.widget.SelectorWithWidgetPreference;
 
 import org.junit.Before;
@@ -62,11 +59,9 @@
     @Mock
     private ZenModesBackend mBackend;
     private ZenModeAppsPreferenceController mPriorityController;
-    private ZenModeAppsPreferenceController mAllController;
     private ZenModeAppsPreferenceController mNoneController;
 
     private SelectorWithWidgetPreference mPriorityPref;
-    private SelectorWithWidgetPreference mAllPref;
     private SelectorWithWidgetPreference mNonePref;
     private PreferenceCategory mPrefCategory;
     private PreferenceScreen mPreferenceScreen;
@@ -81,10 +76,8 @@
 
         mPriorityController = new ZenModeAppsPreferenceController(mContext, KEY_PRIORITY, mBackend);
         mNoneController = new ZenModeAppsPreferenceController(mContext, KEY_NONE, mBackend);
-        mAllController =  new ZenModeAppsPreferenceController(mContext, KEY_ALL, mBackend);
 
         mPriorityPref = makePreference(KEY_PRIORITY, mPriorityController);
-        mAllPref = makePreference(KEY_ALL, mAllController);
         mNonePref = makePreference(KEY_NONE, mNoneController);
 
         mPrefCategory = new PreferenceCategory(mContext);
@@ -95,10 +88,8 @@
 
         mPreferenceScreen.addPreference(mPrefCategory);
         mPrefCategory.addPreference(mPriorityPref);
-        mPrefCategory.addPreference(mAllPref);
         mPrefCategory.addPreference(mNonePref);
 
-        mAllController.displayPreference(mPreferenceScreen);
         mPriorityController.displayPreference(mPreferenceScreen);
         mNoneController.displayPreference(mPreferenceScreen);
     }
@@ -112,45 +103,14 @@
     }
 
     @Test
-    public void testUpdateState_All() {
-        TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setZenPolicy(new ZenPolicy.Builder()
-                                .allowChannels(ZenMode.CHANNEL_POLICY_ALL)
-                                .build())
-                        .build(), true);
-        mAllController.updateZenMode(preference, zenMode);
-
-        verify(preference).setChecked(true);
-    }
-
-    @Test
-    public void testUpdateState_All_Unchecked() {
-        TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setZenPolicy(new ZenPolicy.Builder()
-                                .allowChannels(ZenPolicy.CHANNEL_POLICY_NONE)
-                                .build())
-                        .build(), true);
-        mAllController.updateZenMode(preference, zenMode);
-
-        verify(preference).setChecked(false);
-    }
-
-    @Test
     public void testUpdateState_None() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setZenPolicy(new ZenPolicy.Builder()
-                                .allowChannels(ZenPolicy.CHANNEL_POLICY_NONE)
-                                .build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder()
+                        .allowChannels(ZenPolicy.CHANNEL_POLICY_NONE)
+                        .build())
+                .build();
+
         mNoneController.updateZenMode(preference, zenMode);
 
         verify(preference).setChecked(true);
@@ -159,13 +119,12 @@
     @Test
     public void testUpdateState_None_Unchecked() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setZenPolicy(new ZenPolicy.Builder()
-                                .allowChannels(ZenMode.CHANNEL_POLICY_ALL)
-                                .build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder()
+                        .allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY)
+                        .build())
+                .build();
+
         mNoneController.updateZenMode(preference, zenMode);
 
         verify(preference).setChecked(false);
@@ -174,13 +133,12 @@
     @Test
     public void testUpdateState_Priority() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setZenPolicy(new ZenPolicy.Builder()
-                                .allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY)
-                                .build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder()
+                        .allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY)
+                        .build())
+                .build();
+
         mPriorityController.updateZenMode(preference, zenMode);
 
         verify(preference).setChecked(true);
@@ -189,99 +147,32 @@
     @Test
     public void testUpdateState_Priority_Unchecked() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setZenPolicy(new ZenPolicy.Builder()
-                                .allowChannels(ZenPolicy.CHANNEL_POLICY_NONE)
-                                .build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder()
+                        .allowChannels(ZenPolicy.CHANNEL_POLICY_NONE)
+                        .build())
+                .build();
+
         mPriorityController.updateZenMode(preference, zenMode);
 
         verify(preference).setChecked(false);
     }
 
     @Test
-    public void testOnPreferenceChange_All() {
-        TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_NONE)
-                        .setZenPolicy(new ZenPolicy.Builder()
-                                .allowChannels(ZenMode.CHANNEL_POLICY_ALL)
-                                .build())
-                        .build(), true);
-
-        mAllController.updateZenMode(preference, zenMode);
-        mAllController.onPreferenceChange(preference, true);
-        ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
-        verify(mBackend).updateMode(captor.capture());
-
-        assertThat(captor.getValue().getPolicy().getAllowedChannels())
-                .isEqualTo(ZenMode.CHANNEL_POLICY_ALL);
-    }
-
-    @Test
-    public void testPreferenceClick_passesCorrectCheckedState_All() {
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setZenPolicy(new ZenPolicy.Builder()
-                                .allowChannels(ZenPolicy.CHANNEL_POLICY_NONE)
-                                .build())
-                        .build(), true);
-
-
-        mAllController.updateZenMode(mAllPref, zenMode);
-        mNoneController.updateZenMode(mNonePref, zenMode);
-        mPriorityController.updateZenMode(mPriorityPref, zenMode);
-
-        // MPME is checked; ALL and PRIORITY are unchecked.
-        assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE))
-                .isChecked());
-        assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL))
-                .isChecked());
-        assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY))
-                .isChecked());
-
-        mPrefCategory.findPreference(KEY_ALL).performClick();
-
-        ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
-        verify(mBackend).updateMode(captor.capture());
-        // Checks the policy value for ALL is set.
-        // The important part is that the interruption filter is propagated to the backend.
-        assertThat(captor.getValue().getRule().getInterruptionFilter())
-                .isEqualTo(INTERRUPTION_FILTER_ALL);
-        // ALL is now checked; others are unchecked.
-        assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL))
-                .isChecked());
-        assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE))
-                .isChecked());
-        assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY))
-                .isChecked());
-    }
-
-    @Test
     public void testPreferenceClick_passesCorrectCheckedState_None() {
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setZenPolicy(new ZenPolicy.Builder()
-                                .allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY)
-                                .build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder()
+                        .allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY)
+                        .build())
+                .build();
 
-        mAllController.updateZenMode(mAllPref, zenMode);
         mNoneController.updateZenMode(mNonePref, zenMode);
         mPriorityController.updateZenMode(mPriorityPref, zenMode);
 
-        assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL))
-                .isChecked());
-        assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE))
-                .isChecked());
-        assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY))
-                .isChecked());
+        assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE))
+                .isChecked()).isFalse();
+        assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY))
+                .isChecked()).isTrue();
 
         // Click on NONE
         mPrefCategory.findPreference(KEY_NONE).performClick();
@@ -293,35 +184,31 @@
         // See AbstractZenModePreferenceController.
         assertThat(captor.getValue().getRule().getInterruptionFilter())
                 .isEqualTo(INTERRUPTION_FILTER_PRIORITY);
-        // NONE is now checked; others are unchecked.
+
+        // After screen is refreshed, NONE is now checked; others are unchecked.
+        mNoneController.updateZenMode(mNonePref, captor.getValue());
+        mPriorityController.updateZenMode(mPriorityPref, captor.getValue());
         assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE))
-                .isChecked());
-        assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL))
-                .isChecked());
-        assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY))
-                .isChecked());
+                .isChecked()).isTrue();
+        assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY))
+                .isChecked()).isFalse();
     }
 
     @Test
     public void testPreferenceClick_passesCorrectCheckedState_Priority() {
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setZenPolicy(new ZenPolicy.Builder()
-                                .allowChannels(ZenPolicy.CHANNEL_POLICY_NONE)
-                                .build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder()
+                        .allowChannels(ZenPolicy.CHANNEL_POLICY_NONE)
+                        .build())
+                .build();
 
-        mAllController.updateZenMode(mAllPref, zenMode);
         mNoneController.updateZenMode(mNonePref, zenMode);
         mPriorityController.updateZenMode(mPriorityPref, zenMode);
 
         assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE))
-                .isChecked());
-        assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL))
-                .isChecked());
-        assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY))
-                .isChecked());
+                .isChecked()).isTrue();
+        assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY))
+                .isChecked()).isFalse();
 
         // Click on PRIORITY
         mPrefCategory.findPreference(KEY_PRIORITY).performClick();
@@ -331,13 +218,13 @@
         // Checks the policy value for PRIORITY is propagated to the backend.
         assertThat(captor.getValue().getRule().getInterruptionFilter())
                 .isEqualTo(INTERRUPTION_FILTER_PRIORITY);
-        // PRIORITY is now checked; others are unchecked.
-        assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY))
-                .isChecked());
-        assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL))
-                .isChecked());
-        assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE))
-                .isChecked());
-    }
 
+        // After screen is refreshed, PRIORITY is now checked; others are unchecked.
+        mNoneController.updateZenMode(mNonePref, captor.getValue());
+        mPriorityController.updateZenMode(mPriorityPref, captor.getValue());
+        assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY))
+                .isChecked()).isTrue();
+        assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE))
+                .isChecked()).isFalse();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeButtonPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeButtonPreferenceControllerTest.java
index bda3843..625f231 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeButtonPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeButtonPreferenceControllerTest.java
@@ -16,8 +16,6 @@
 
 package com.android.settings.notification.modes;
 
-import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -25,15 +23,14 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.app.AutomaticZenRule;
 import android.app.Flags;
 import android.content.Context;
-import android.net.Uri;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
-import android.service.notification.ZenPolicy;
 import android.widget.Button;
 
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 import com.android.settingslib.widget.LayoutPreference;
 
 import org.junit.Before;
@@ -71,43 +68,34 @@
 
     @Test
     public void isAvailable_notIfAppOptsOut() {
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                .setType(AutomaticZenRule.TYPE_DRIVING)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
+        ZenMode zenMode = new TestModeBuilder()
                 .setManualInvocationAllowed(false)
-                .setEnabled(true)
-                .build(), false);
+                .build();
         mController.setZenMode(zenMode);
         assertThat(mController.isAvailable()).isFalse();
     }
 
     @Test
     public void isAvailable_notIfModeDisabled() {
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
-                        .setManualInvocationAllowed(true)
-                        .setEnabled(false)
-                        .build(), false);
+        ZenMode zenMode = new TestModeBuilder()
+                .setManualInvocationAllowed(true)
+                .setEnabled(false)
+                .build();
+
         mController.setZenMode(zenMode);
+
         assertThat(mController.isAvailable()).isFalse();
     }
 
     @Test
     public void isAvailable_appOptedIn_modeEnabled() {
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
-                        .setManualInvocationAllowed(true)
-                        .setEnabled(true)
-                        .build(), false);
+        ZenMode zenMode = new TestModeBuilder()
+                .setManualInvocationAllowed(true)
+                .setEnabled(true)
+                .build();
+
         mController.setZenMode(zenMode);
+
         assertThat(mController.isAvailable()).isTrue();
     }
 
@@ -116,15 +104,13 @@
         Button button = new Button(mContext);
         LayoutPreference pref = mock(LayoutPreference.class);
         when(pref.findViewById(anyInt())).thenReturn(button);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
-                        .setManualInvocationAllowed(true)
-                        .setEnabled(true)
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setManualInvocationAllowed(true)
+                .setActive(true)
+                .build();
+
         mController.updateZenMode(pref, zenMode);
+
         assertThat(button.getText().toString()).contains("off");
         assertThat(button.hasOnClickListeners()).isTrue();
     }
@@ -134,15 +120,13 @@
         Button button = new Button(mContext);
         LayoutPreference pref = mock(LayoutPreference.class);
         when(pref.findViewById(anyInt())).thenReturn(button);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
-                        .setManualInvocationAllowed(true)
-                        .setEnabled(true)
-                        .build(), false);
+        ZenMode zenMode = new TestModeBuilder()
+                .setManualInvocationAllowed(true)
+                .setActive(false)
+                .build();
+
         mController.updateZenMode(pref, zenMode);
+
         assertThat(button.getText().toString()).contains("on");
         assertThat(button.hasOnClickListeners()).isTrue();
     }
@@ -152,14 +136,11 @@
         Button button = new Button(mContext);
         LayoutPreference pref = mock(LayoutPreference.class);
         when(pref.findViewById(anyInt())).thenReturn(button);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
-                        .setManualInvocationAllowed(true)
-                        .setEnabled(true)
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setManualInvocationAllowed(true)
+                .setActive(true)
+                .build();
+
         mController.updateZenMode(pref, zenMode);
 
         button.callOnClick();
@@ -171,14 +152,11 @@
         Button button = new Button(mContext);
         LayoutPreference pref = mock(LayoutPreference.class);
         when(pref.findViewById(anyInt())).thenReturn(button);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
-                        .setManualInvocationAllowed(true)
-                        .setEnabled(true)
-                        .build(), false);
+        ZenMode zenMode = new TestModeBuilder()
+                .setManualInvocationAllowed(true)
+                .setActive(false)
+                .build();
+
         mController.updateZenMode(pref, zenMode);
 
         button.callOnClick();
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeCallsLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeCallsLinkPreferenceControllerTest.java
index 94c2d8a..058b2d7 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeCallsLinkPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeCallsLinkPreferenceControllerTest.java
@@ -16,22 +16,19 @@
 
 package com.android.settings.notification.modes;
 
-import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
-
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
-import android.app.AutomaticZenRule;
 import android.app.Flags;
 import android.content.Context;
-import android.net.Uri;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
-import android.service.notification.ZenPolicy;
 
 import androidx.preference.Preference;
 
+import com.android.settingslib.notification.modes.ZenModesBackend;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -67,13 +64,7 @@
     @EnableFlags(Flags.FLAG_MODES_UI)
     public void testHasSummary() {
         Preference pref = mock(Preference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                .setType(AutomaticZenRule.TYPE_DRIVING)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
-                .build(), true);
-        mController.updateZenMode(pref, zenMode);
+        mController.updateZenMode(pref, TestModeBuilder.EXAMPLE);
         verify(pref).setSummary(any());
     }
 }
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayEffectPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayEffectPreferenceControllerTest.java
index 1a62b75..a735cd9 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayEffectPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayEffectPreferenceControllerTest.java
@@ -16,22 +16,22 @@
 
 package com.android.settings.notification.modes;
 
-import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
-import static android.service.notification.ZenPolicy.STATE_ALLOW;
-import static android.service.notification.ZenPolicy.STATE_UNSET;
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
-import android.app.AutomaticZenRule;
 import android.app.Flags;
 import android.content.Context;
-import android.net.Uri;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.service.notification.ZenDeviceEffects;
-import android.service.notification.ZenPolicy;
+
 import androidx.preference.TwoStatePreference;
+
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -62,15 +62,11 @@
     @Test
     public void testUpdateState_grayscale() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowAlarms(true).build())
-                        .setDeviceEffects(new ZenDeviceEffects.Builder()
-                                .setShouldDisplayGrayscale(true)
-                                .build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setDeviceEffects(new ZenDeviceEffects.Builder()
+                        .setShouldDisplayGrayscale(true)
+                        .build())
+                .build();
 
         ZenModeDisplayEffectPreferenceController controller =
                 new ZenModeDisplayEffectPreferenceController(
@@ -84,15 +80,11 @@
     @Test
     public void testOnPreferenceChange_grayscale() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowAlarms(false).build())
-                        .setDeviceEffects(new ZenDeviceEffects.Builder()
-                                .setShouldDisplayGrayscale(true)
-                                .build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setDeviceEffects(new ZenDeviceEffects.Builder()
+                        .setShouldDisplayGrayscale(true)
+                        .build())
+                .build();
 
         ZenModeDisplayEffectPreferenceController controller =
                 new ZenModeDisplayEffectPreferenceController(mContext, "effect_greyscale", mBackend);
@@ -103,22 +95,18 @@
 
         ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
         verify(mBackend).updateMode(captor.capture());
-        assertThat(captor.getValue().getRule().getDeviceEffects().shouldDisplayGrayscale())
+        assertThat(captor.getValue().getDeviceEffects().shouldDisplayGrayscale())
                 .isFalse();
     }
 
     @Test
     public void testUpdateState_aod() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowMedia(true).build())
-                        .setDeviceEffects(new ZenDeviceEffects.Builder()
-                                .setShouldSuppressAmbientDisplay(true)
-                                .build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setDeviceEffects(new ZenDeviceEffects.Builder()
+                        .setShouldSuppressAmbientDisplay(true)
+                        .build())
+                .build();
 
         ZenModeDisplayEffectPreferenceController controller =
                 new ZenModeDisplayEffectPreferenceController(mContext, "effect_aod", mBackend);
@@ -131,15 +119,11 @@
     @Test
     public void testOnPreferenceChange_aod() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowMedia(false).build())
-                        .setDeviceEffects(new ZenDeviceEffects.Builder()
-                                .setShouldSuppressAmbientDisplay(true)
-                                .build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setDeviceEffects(new ZenDeviceEffects.Builder()
+                        .setShouldSuppressAmbientDisplay(true)
+                        .build())
+                .build();
 
         ZenModeDisplayEffectPreferenceController controller =
                 new ZenModeDisplayEffectPreferenceController(mContext, "effect_aod", mBackend);
@@ -150,22 +134,18 @@
 
         ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
         verify(mBackend).updateMode(captor.capture());
-        assertThat(captor.getValue().getRule().getDeviceEffects().shouldSuppressAmbientDisplay())
+        assertThat(captor.getValue().getDeviceEffects().shouldSuppressAmbientDisplay())
                 .isFalse();
     }
 
     @Test
     public void testUpdateState_wallpaper() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowSystem(true).build())
-                        .setDeviceEffects(new ZenDeviceEffects.Builder()
-                                .setShouldDimWallpaper(true)
-                                .build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setDeviceEffects(new ZenDeviceEffects.Builder()
+                        .setShouldDimWallpaper(true)
+                        .build())
+                .build();
 
         ZenModeDisplayEffectPreferenceController controller =
                 new ZenModeDisplayEffectPreferenceController(
@@ -179,15 +159,11 @@
     @Test
     public void testOnPreferenceChange_wallpaper() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowSystem(false).build())
-                        .setDeviceEffects(new ZenDeviceEffects.Builder()
-                                .setShouldDimWallpaper(true)
-                                .build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setDeviceEffects(new ZenDeviceEffects.Builder()
+                        .setShouldDimWallpaper(true)
+                        .build())
+                .build();
 
         ZenModeDisplayEffectPreferenceController controller =
                 new ZenModeDisplayEffectPreferenceController(
@@ -199,21 +175,17 @@
 
         ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
         verify(mBackend).updateMode(captor.capture());
-        assertThat(captor.getValue().getRule().getDeviceEffects().shouldDimWallpaper()).isFalse();
+        assertThat(captor.getValue().getDeviceEffects().shouldDimWallpaper()).isFalse();
     }
 
     @Test
     public void testUpdateState_darkTheme() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowReminders(true).build())
-                        .setDeviceEffects(new ZenDeviceEffects.Builder()
-                                .setShouldUseNightMode(true)
-                                .build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setDeviceEffects(new ZenDeviceEffects.Builder()
+                        .setShouldUseNightMode(true)
+                        .build())
+                .build();
 
         ZenModeDisplayEffectPreferenceController controller =
                 new ZenModeDisplayEffectPreferenceController(mContext, "effect_dark_theme",
@@ -227,15 +199,11 @@
     @Test
     public void testOnPreferenceChange_darkTheme() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowReminders(false).build())
-                        .setDeviceEffects(new ZenDeviceEffects.Builder()
-                                .setShouldUseNightMode(true)
-                                .build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setDeviceEffects(new ZenDeviceEffects.Builder()
+                        .setShouldUseNightMode(true)
+                        .build())
+                .build();
 
         ZenModeDisplayEffectPreferenceController controller =
                 new ZenModeDisplayEffectPreferenceController(mContext, "effect_dark_theme",
@@ -247,6 +215,6 @@
 
         ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
         verify(mBackend).updateMode(captor.capture());
-        assertThat(captor.getValue().getRule().getDeviceEffects().shouldUseNightMode()).isFalse();
+        assertThat(captor.getValue().getDeviceEffects().shouldUseNightMode()).isFalse();
     }
 }
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceControllerTest.java
index 62aa046..3ccfb9f 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceControllerTest.java
@@ -16,22 +16,19 @@
 
 package com.android.settings.notification.modes;
 
-import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
-
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
-import android.app.AutomaticZenRule;
 import android.app.Flags;
 import android.content.Context;
-import android.net.Uri;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
-import android.service.notification.ZenPolicy;
 
 import androidx.preference.Preference;
 
+import com.android.settingslib.notification.modes.ZenModesBackend;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -67,13 +64,7 @@
     @EnableFlags(Flags.FLAG_MODES_UI)
     public void testHasSummary() {
         Preference pref = mock(Preference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                .setType(AutomaticZenRule.TYPE_DRIVING)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
-                .build(), true);
-        mController.updateZenMode(pref, zenMode);
+        mController.updateZenMode(pref, TestModeBuilder.EXAMPLE);
         verify(pref).setSummary(any());
     }
 }
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeExitAtAlarmPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeExitAtAlarmPreferenceControllerTest.java
index c1c4d61..03c75fb 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeExitAtAlarmPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeExitAtAlarmPreferenceControllerTest.java
@@ -21,13 +21,15 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
-import android.app.AutomaticZenRule;
 import android.content.Context;
 import android.service.notification.ZenModeConfig;
 
 import androidx.preference.TwoStatePreference;
 import androidx.test.core.app.ApplicationProvider;
 
+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;
@@ -65,10 +67,9 @@
         scheduleInfo.endHour = 2;
         scheduleInfo.exitAtAlarm = false;
 
-        ZenMode mode = new ZenMode("id",
-                new AutomaticZenRule.Builder("name",
-                        ZenModeConfig.toScheduleConditionId(scheduleInfo)).build(),
-                true);  // is active
+        ZenMode mode = new TestModeBuilder()
+                .setConditionId(ZenModeConfig.toScheduleConditionId(scheduleInfo))
+                .build();
 
         // need to call updateZenMode for the first call
         mPrefController.updateZenMode(preference, mode);
@@ -94,10 +95,9 @@
         scheduleInfo.endHour = 2;
         scheduleInfo.exitAtAlarm = true;
 
-        ZenMode mode = new ZenMode("id",
-                new AutomaticZenRule.Builder("name",
-                        ZenModeConfig.toScheduleConditionId(scheduleInfo)).build(),
-                true);  // is active
+        ZenMode mode = new TestModeBuilder()
+                .setConditionId(ZenModeConfig.toScheduleConditionId(scheduleInfo))
+                .build();
         mPrefController.updateZenMode(preference, mode);
 
         // turn off exit at alarm
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeIconPickerListPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeIconPickerListPreferenceControllerTest.java
index ba9a6b8..5db7e92 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeIconPickerListPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeIconPickerListPreferenceControllerTest.java
@@ -23,9 +23,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.app.AutomaticZenRule;
 import android.content.Context;
-import android.net.Uri;
 
 import androidx.annotation.NonNull;
 import androidx.preference.PreferenceScreen;
@@ -33,6 +31,8 @@
 
 import com.android.settings.R;
 import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 import com.android.settingslib.widget.LayoutPreference;
 
 import com.google.common.collect.ImmutableList;
@@ -47,10 +47,7 @@
 @RunWith(RobolectricTestRunner.class)
 public class ZenModeIconPickerListPreferenceControllerTest {
 
-    private static final ZenMode ZEN_MODE = new ZenMode(
-            "mode_id",
-            new AutomaticZenRule.Builder("mode name", Uri.parse("mode")).build(),
-            /* isActive= */ false);
+    private static final ZenMode ZEN_MODE = TestModeBuilder.EXAMPLE;
 
     private ZenModesBackend mBackend;
     private ZenModeIconPickerListPreferenceController mController;
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeMessagesLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeMessagesLinkPreferenceControllerTest.java
index 9400f83..288359a 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeMessagesLinkPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeMessagesLinkPreferenceControllerTest.java
@@ -16,22 +16,19 @@
 
 package com.android.settings.notification.modes;
 
-import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
-
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
-import android.app.AutomaticZenRule;
 import android.app.Flags;
 import android.content.Context;
-import android.net.Uri;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
-import android.service.notification.ZenPolicy;
 
 import androidx.preference.Preference;
 
+import com.android.settingslib.notification.modes.ZenModesBackend;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -67,13 +64,7 @@
     @EnableFlags(Flags.FLAG_MODES_UI)
     public void testHasSummary() {
         Preference pref = mock(Preference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                .setType(AutomaticZenRule.TYPE_DRIVING)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
-                .build(), true);
-        mController.updateZenMode(pref, zenMode);
+        mController.updateZenMode(pref, TestModeBuilder.EXAMPLE);
         verify(pref).setSummary(any());
     }
 }
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeNotifVisLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeNotifVisLinkPreferenceControllerTest.java
index 00a9fbe..ee7340b 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeNotifVisLinkPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeNotifVisLinkPreferenceControllerTest.java
@@ -16,22 +16,19 @@
 
 package com.android.settings.notification.modes;
 
-import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
-
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
-import android.app.AutomaticZenRule;
 import android.app.Flags;
 import android.content.Context;
-import android.net.Uri;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
-import android.service.notification.ZenPolicy;
 
 import androidx.preference.Preference;
 
+import com.android.settingslib.notification.modes.ZenModesBackend;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -67,13 +64,7 @@
     @EnableFlags(Flags.FLAG_MODES_UI)
     public void testHasSummary() {
         Preference pref = mock(Preference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                .setType(AutomaticZenRule.TYPE_DRIVING)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
-                .build(), true);
-        mController.updateZenMode(pref, zenMode);
+        mController.updateZenMode(pref, TestModeBuilder.EXAMPLE);
         verify(pref).setSummary(any());
     }
 }
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeNotifVisPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeNotifVisPreferenceControllerTest.java
index 54edaf4..b23d946 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeNotifVisPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeNotifVisPreferenceControllerTest.java
@@ -16,7 +16,6 @@
 
 package com.android.settings.notification.modes;
 
-import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
 import static android.service.notification.ZenPolicy.STATE_ALLOW;
 import static android.service.notification.ZenPolicy.STATE_DISALLOW;
 import static android.service.notification.ZenPolicy.VISUAL_EFFECT_LIGHTS;
@@ -32,17 +31,18 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.app.AutomaticZenRule;
 import android.app.Flags;
 import android.content.Context;
 import android.content.res.Resources;
-import android.net.Uri;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.service.notification.ZenPolicy;
 
 import androidx.preference.TwoStatePreference;
 
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -96,15 +96,12 @@
     @Test
     public void updateState_notChecked() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder()
-                                .allowAlarms(true)
-                                .showAllVisualEffects()
-                                .build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder()
+                        .allowAlarms(true)
+                        .showAllVisualEffects()
+                        .build())
+                .build();
 
         mController.updateZenMode(preference, zenMode);
 
@@ -115,15 +112,12 @@
     @Test
     public void updateState_checked() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder()
-                                .allowAlarms(true)
-                                .showVisualEffect(VISUAL_EFFECT_PEEK, false)
-                                .build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder()
+                        .allowAlarms(true)
+                        .showVisualEffect(VISUAL_EFFECT_PEEK, false)
+                        .build())
+                .build();
 
         mController.updateZenMode(preference, zenMode);
 
@@ -138,16 +132,13 @@
                 "zen_effect_status", VISUAL_EFFECT_STATUS_BAR,
                 new int[]{VISUAL_EFFECT_NOTIFICATION_LIST}, mBackend);
 
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder()
-                                .allowAlarms(true)
-                                .showVisualEffect(VISUAL_EFFECT_NOTIFICATION_LIST, false)
-                                .showVisualEffect(VISUAL_EFFECT_STATUS_BAR, true)
-                                .build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder()
+                        .allowAlarms(true)
+                        .showVisualEffect(VISUAL_EFFECT_NOTIFICATION_LIST, false)
+                        .showVisualEffect(VISUAL_EFFECT_STATUS_BAR, true)
+                        .build())
+                .build();
 
         mController.updateZenMode(preference, zenMode);
 
@@ -168,15 +159,12 @@
                 "zen_effect_status", VISUAL_EFFECT_STATUS_BAR,
                 new int[]{VISUAL_EFFECT_NOTIFICATION_LIST}, mBackend);
 
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder()
-                                .allowAlarms(true)
-                                .showAllVisualEffects()
-                                .build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder()
+                        .allowAlarms(true)
+                        .showAllVisualEffects()
+                        .build())
+                .build();
 
         mController.updateZenMode(preference, zenMode);
 
@@ -188,15 +176,12 @@
     @Test
     public void onPreferenceChanged_checkedFalse() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder()
-                                .allowAlarms(true)
-                                .hideAllVisualEffects()
-                                .build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder()
+                        .allowAlarms(true)
+                        .hideAllVisualEffects()
+                        .build())
+                .build();
 
         mController.updateZenMode(preference, zenMode);
 
@@ -213,15 +198,12 @@
     @Test
     public void onPreferenceChanged_checkedTrue() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder()
-                                .allowAlarms(true)
-                                .showAllVisualEffects()
-                                .build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder()
+                        .allowAlarms(true)
+                        .showAllVisualEffects()
+                        .build())
+                .build();
 
         mController.updateZenMode(preference, zenMode);
 
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceControllerTest.java
index 699762e..c4d03fe 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceControllerTest.java
@@ -16,22 +16,19 @@
 
 package com.android.settings.notification.modes;
 
-import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
-
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
-import android.app.AutomaticZenRule;
 import android.app.Flags;
 import android.content.Context;
-import android.net.Uri;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
-import android.service.notification.ZenPolicy;
 
 import androidx.preference.Preference;
 
+import com.android.settingslib.notification.modes.ZenModesBackend;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -68,13 +65,7 @@
     @EnableFlags(Flags.FLAG_MODES_UI)
     public void testHasSummary() {
         Preference pref = mock(Preference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                .setType(AutomaticZenRule.TYPE_DRIVING)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
-                .build(), true);
-        mController.updateZenMode(pref, zenMode);
+        mController.updateZenMode(pref, TestModeBuilder.EXAMPLE);
         verify(pref).setSummary(any());
     }
 }
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeOtherPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeOtherPreferenceControllerTest.java
index 4a4a6e4..c69a8a0 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeOtherPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeOtherPreferenceControllerTest.java
@@ -16,7 +16,6 @@
 
 package com.android.settings.notification.modes;
 
-import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
 import static android.service.notification.ZenPolicy.STATE_ALLOW;
 import static android.service.notification.ZenPolicy.STATE_UNSET;
 
@@ -25,16 +24,17 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
-import android.app.AutomaticZenRule;
 import android.app.Flags;
 import android.content.Context;
-import android.net.Uri;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.service.notification.ZenPolicy;
 
 import androidx.preference.TwoStatePreference;
 
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -65,12 +65,9 @@
     @Test
     public void testUpdateState_alarms() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowAlarms(true).build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder().allowAlarms(true).build())
+                .build();
 
         ZenModeOtherPreferenceController controller =
                 new ZenModeOtherPreferenceController(mContext, "modes_category_alarm", mBackend);
@@ -83,12 +80,9 @@
     @Test
     public void testOnPreferenceChange_alarms() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowAlarms(false).build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder().allowAlarms(false).build())
+                .build();
 
         ZenModeOtherPreferenceController controller =
                 new ZenModeOtherPreferenceController(mContext, "modes_category_alarm", mBackend);
@@ -108,12 +102,9 @@
     @Test
     public void testUpdateState_media() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowMedia(true).build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder().allowMedia(true).build())
+                .build();
 
         ZenModeOtherPreferenceController controller =
                 new ZenModeOtherPreferenceController(mContext, "modes_category_media", mBackend);
@@ -126,12 +117,9 @@
     @Test
     public void testOnPreferenceChange_media() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowMedia(false).build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder().allowMedia(false).build())
+                .build();
 
         ZenModeOtherPreferenceController controller =
                 new ZenModeOtherPreferenceController(mContext, "modes_category_media", mBackend);
@@ -151,12 +139,9 @@
     @Test
     public void testUpdateState_system() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowSystem(true).build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder().allowSystem(true).build())
+                .build();
 
         ZenModeOtherPreferenceController controller =
                 new ZenModeOtherPreferenceController(mContext, "modes_category_system", mBackend);
@@ -169,12 +154,9 @@
     @Test
     public void testOnPreferenceChange_system() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowSystem(false).build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder().allowSystem(false).build())
+                .build();
 
         ZenModeOtherPreferenceController controller =
                 new ZenModeOtherPreferenceController(mContext, "modes_category_system", mBackend);
@@ -194,12 +176,9 @@
     @Test
     public void testUpdateState_reminders() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowReminders(true).build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder().allowReminders(true).build())
+                .build();
 
         ZenModeOtherPreferenceController controller =
                 new ZenModeOtherPreferenceController(mContext, "modes_category_reminders",
@@ -213,12 +192,9 @@
     @Test
     public void testOnPreferenceChange_reminders() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowReminders(false).build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder().allowReminders(false).build())
+                .build();
 
         ZenModeOtherPreferenceController controller =
                 new ZenModeOtherPreferenceController(mContext, "modes_category_reminders",
@@ -239,12 +215,9 @@
     @Test
     public void testUpdateState_events() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowEvents(true).build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder().allowEvents(true).build())
+                .build();
 
         ZenModeOtherPreferenceController controller =
                 new ZenModeOtherPreferenceController(mContext, "modes_category_events", mBackend);
@@ -257,12 +230,9 @@
     @Test
     public void testOnPreferenceChange_events() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowEvents(false).build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder().allowEvents(false).build())
+                .build();
 
         ZenModeOtherPreferenceController controller =
                 new ZenModeOtherPreferenceController(mContext, "modes_category_events", mBackend);
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceControllerTest.java
index a331318..6591b72 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceControllerTest.java
@@ -16,22 +16,19 @@
 
 package com.android.settings.notification.modes;
 
-import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
-
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
-import android.app.AutomaticZenRule;
 import android.app.Flags;
 import android.content.Context;
-import android.net.Uri;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
-import android.service.notification.ZenPolicy;
 
 import androidx.preference.Preference;
 
+import com.android.settingslib.notification.modes.ZenModesBackend;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -69,13 +66,7 @@
     @EnableFlags(Flags.FLAG_MODES_UI)
     public void testHasSummary() {
         Preference pref = mock(Preference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                .setType(AutomaticZenRule.TYPE_DRIVING)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
-                .build(), true);
-        mController.updateZenMode(pref, zenMode);
+        mController.updateZenMode(pref, TestModeBuilder.EXAMPLE);
         verify(pref).setSummary(any());
     }
 }
\ No newline at end of file
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 709af43..04df27e 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceControllerTest.java
@@ -16,7 +16,6 @@
 
 package com.android.settings.notification.modes;
 
-import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
 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;
@@ -40,11 +39,9 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.app.AutomaticZenRule;
 import android.app.Flags;
 import android.content.Context;
 import android.database.Cursor;
-import android.net.Uri;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.service.notification.ZenPolicy;
@@ -54,6 +51,8 @@
 import androidx.preference.PreferenceManager;
 import androidx.preference.PreferenceScreen;
 
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 import com.android.settingslib.widget.SelectorWithWidgetPreference;
 
 import org.junit.Before;
@@ -439,20 +438,17 @@
 
     @Test
     public void testPreferenceClick_passesCorrectCheckedState_startingUnchecked_messages() {
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder()
-                                .disallowAllSounds()
-                                .build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder()
+                        .disallowAllSounds()
+                        .build())
+                .build();
 
         mMessagesController.displayPreference(mPreferenceScreen);
         mMessagesController.updateZenMode(mMessagesPrefCategory, zenMode);
 
         assertThat(((SelectorWithWidgetPreference) mMessagesPrefCategory.findPreference(KEY_NONE))
-                .isChecked());
+                .isChecked()).isTrue();
 
         mMessagesPrefCategory.findPreference(KEY_STARRED).performClick();
 
@@ -464,14 +460,11 @@
 
     @Test
     public void testPreferenceClick_passesCorrectCheckedState_startingChecked_messages() {
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder()
-                                .allowAllSounds()
-                                .build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder()
+                        .allowAllSounds()
+                        .build())
+                .build();
 
         mMessagesController.displayPreference(mPreferenceScreen);
         mMessagesController.updateZenMode(mMessagesPrefCategory, zenMode);
@@ -490,14 +483,11 @@
 
     @Test
     public void testPreferenceClick_passesCorrectCheckedState_startingUnchecked_calls() {
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder()
-                                .disallowAllSounds()
-                                .build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder()
+                        .disallowAllSounds()
+                        .build())
+                .build();
 
         mCallsController.displayPreference(mPreferenceScreen);
         mCallsController.updateZenMode(mCallsPrefCategory, zenMode);
@@ -515,14 +505,11 @@
 
     @Test
     public void testPreferenceClick_passesCorrectCheckedState_startingChecked_calls() {
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder()
-                                .disallowAllSounds()
-                                .build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder()
+                        .disallowAllSounds()
+                        .build())
+                .build();
 
         mCallsController.displayPreference(mPreferenceScreen);
         mCallsController.updateZenMode(mCallsPrefCategory, zenMode);
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeRepeatCallersPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeRepeatCallersPreferenceControllerTest.java
index 7bbb042..c1b99e5 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeRepeatCallersPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeRepeatCallersPreferenceControllerTest.java
@@ -16,24 +16,27 @@
 
 package com.android.settings.notification.modes;
 
-import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE;
 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED;
-import static android.service.notification.ZenPolicy.STATE_ALLOW;
 import static android.service.notification.ZenPolicy.STATE_DISALLOW;
 import static android.service.notification.ZenPolicy.STATE_UNSET;
+
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
-import android.app.AutomaticZenRule;
 import android.app.Flags;
 import android.content.Context;
-import android.net.Uri;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.service.notification.ZenPolicy;
+
 import androidx.preference.TwoStatePreference;
+
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -64,14 +67,11 @@
     @Test
     public void testUpdateState_allCalls() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder()
-                                .allowCalls(PEOPLE_TYPE_ANYONE)
-                                .build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder()
+                        .allowCalls(PEOPLE_TYPE_ANYONE)
+                        .build())
+                .build();
 
         ZenModeRepeatCallersPreferenceController controller =
                 new ZenModeRepeatCallersPreferenceController(mContext, "repeat", mBackend, 1);
@@ -85,15 +85,12 @@
     @Test
     public void testUpdateState_someCalls() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder()
-                                .allowCalls(PEOPLE_TYPE_STARRED)
-                                .allowRepeatCallers(true)
-                                .build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder()
+                        .allowCalls(PEOPLE_TYPE_STARRED)
+                        .allowRepeatCallers(true)
+                        .build())
+                .build();
 
         ZenModeRepeatCallersPreferenceController controller =
                 new ZenModeRepeatCallersPreferenceController(mContext, "repeat", mBackend, 1);
@@ -107,12 +104,9 @@
     @Test
     public void testOnPreferenceChange() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(true).build())
-                        .build(), true);
+        ZenMode zenMode = new TestModeBuilder()
+                .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(true).build())
+                .build();
 
         ZenModeRepeatCallersPreferenceController controller =
                 new ZenModeRepeatCallersPreferenceController(mContext, "repeat", mBackend, 1);
@@ -130,4 +124,4 @@
         assertThat(captor.getValue().getPolicy().getPriorityCallSenders())
                 .isEqualTo(STATE_UNSET);
     }
-}
\ No newline at end of file
+}
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetCalendarPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetCalendarPreferenceControllerTest.java
index 6b24fa2..cc6a497 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetCalendarPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetCalendarPreferenceControllerTest.java
@@ -28,18 +28,20 @@
 
 import static org.mockito.Mockito.when;
 
-import android.app.AutomaticZenRule;
 import android.app.Flags;
 import android.content.Context;
-import android.net.Uri;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
+import android.service.notification.SystemZenRules;
 import android.service.notification.ZenModeConfig;
 
 import androidx.preference.DropDownPreference;
 import androidx.preference.PreferenceCategory;
 import androidx.test.core.app.ApplicationProvider;
 
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -84,9 +86,9 @@
     @Test
     @EnableFlags({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
     public void updateEventMode_updatesConditionAndTriggerDescription() {
-        ZenMode mode = new ZenMode("id",
-                new AutomaticZenRule.Builder("name", Uri.parse("condition")).build(),
-                true);  // is active
+        ZenMode mode = new TestModeBuilder()
+                .setPackage(SystemZenRules.PACKAGE_ANDROID)
+                .build();
 
         // Explicitly update preference controller with mode info first, which will also call
         // updateState()
@@ -99,6 +101,7 @@
         // apply event mode updater to existing mode
         ZenMode out = mPrefController.updateEventMode(eventInfo).apply(mode);
 
+        assertThat(out.getRule().getOwner()).isEqualTo(ZenModeConfig.getEventConditionProvider());
         assertThat(out.getRule().getConditionId()).isEqualTo(
                 ZenModeConfig.toEventConditionId(eventInfo));
         assertThat(out.getRule().getTriggerDescription()).isEqualTo("My events");
@@ -111,10 +114,9 @@
         eventInfo.calName = "Definitely A Calendar";
         eventInfo.reply = REPLY_YES;
 
-        ZenMode mode = new ZenMode("id",
-                new AutomaticZenRule.Builder("name",
-                        ZenModeConfig.toEventConditionId(eventInfo)).build(),
-                true);  // is active
+        ZenMode mode = new TestModeBuilder()
+                .setConditionId(ZenModeConfig.toEventConditionId(eventInfo))
+                .build();
         mPrefController.updateZenMode(mPrefCategory, mode);
 
         // We should see mCalendar, mReply have their values set
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetSchedulePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetSchedulePreferenceControllerTest.java
index 7cf327c..7dbc802 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetSchedulePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetSchedulePreferenceControllerTest.java
@@ -23,12 +23,11 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.app.AutomaticZenRule;
 import android.app.Flags;
 import android.content.Context;
-import android.net.Uri;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
+import android.service.notification.SystemZenRules;
 import android.service.notification.ZenModeConfig;
 import android.view.ViewGroup;
 import android.widget.ToggleButton;
@@ -37,6 +36,8 @@
 import androidx.test.core.app.ApplicationProvider;
 
 import com.android.settings.R;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -80,9 +81,9 @@
     @Test
     @EnableFlags({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
     public void updateScheduleRule_updatesConditionAndTriggerDescription() {
-        ZenMode mode = new ZenMode("id",
-                new AutomaticZenRule.Builder("name", Uri.parse("condition")).build(),
-                true);  // is active
+        ZenMode mode = new TestModeBuilder()
+                .setPackage(SystemZenRules.PACKAGE_ANDROID)
+                .build();
 
         ZenModeConfig.ScheduleInfo scheduleInfo = new ZenModeConfig.ScheduleInfo();
         scheduleInfo.days = new int[] { Calendar.MONDAY };
@@ -93,6 +94,8 @@
         assertThat(out.getRule().getConditionId())
                 .isEqualTo(ZenModeConfig.toScheduleConditionId(scheduleInfo));
         assertThat(out.getRule().getTriggerDescription()).isNotEmpty();
+        assertThat(out.getRule().getOwner()).isEqualTo(
+                ZenModeConfig.getScheduleConditionProvider());
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceControllerTest.java
index 91de4ea..31959e5 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceControllerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.notification.modes;
 
+import static android.app.AutomaticZenRule.TYPE_OTHER;
 import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR;
 import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME;
 import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
@@ -31,19 +32,21 @@
 import android.app.AutomaticZenRule;
 import android.app.Flags;
 import android.content.Context;
-import android.content.Intent;
 import android.net.Uri;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
+import android.service.notification.SystemZenRules;
 import android.service.notification.ZenModeConfig;
-import android.service.notification.ZenPolicy;
 
 import androidx.preference.PreferenceCategory;
 import androidx.test.core.app.ApplicationProvider;
 
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
+import com.android.settings.dashboard.DashboardFragment;
 import com.android.settingslib.PrimarySwitchPreference;
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -57,6 +60,7 @@
 import java.util.Calendar;
 
 @RunWith(RobolectricTestRunner.class)
+@EnableFlags(Flags.FLAG_MODES_UI)
 public class ZenModeSetTriggerLinkPreferenceControllerTest {
     @Rule
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
@@ -65,10 +69,13 @@
     private ZenModesBackend mBackend;
     private Context mContext;
 
+    private PrimarySwitchPreference mPreference;
+
     @Mock
     private PreferenceCategory mPrefCategory;
     @Mock
-    private PrimarySwitchPreference mPreference;
+    private DashboardFragment mFragment;
+
     private ZenModeSetTriggerLinkPreferenceController mPrefController;
 
     @Before
@@ -77,12 +84,12 @@
         mContext = ApplicationProvider.getApplicationContext();
 
         mPrefController = new ZenModeSetTriggerLinkPreferenceController(mContext,
-                "zen_automatic_trigger_category", mBackend);
+                "zen_automatic_trigger_category", mFragment, mBackend);
+        mPreference = new PrimarySwitchPreference(mContext);
         when(mPrefCategory.findPreference(AUTOMATIC_TRIGGER_PREF_KEY)).thenReturn(mPreference);
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_UI)
     public void testIsAvailable() {
         // should not be available for manual DND
         ZenMode manualMode = ZenMode.manualDndMode(new AutomaticZenRule.Builder("Do Not Disturb",
@@ -94,46 +101,27 @@
         assertThat(mPrefController.isAvailable()).isFalse();
 
         // should be available for other modes
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowAlarms(true).build())
-                        .setEnabled(false)
-                        .build(), false);
-        mPrefController.updateZenMode(mPrefCategory, zenMode);
+        mPrefController.updateZenMode(mPrefCategory, TestModeBuilder.EXAMPLE);
         assertThat(mPrefController.isAvailable()).isTrue();
     }
 
     @Test
     public void testUpdateState() {
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowAlarms(true).build())
-                        .setEnabled(false)
-                        .build(), false);
+        ZenMode zenMode = new TestModeBuilder().setEnabled(false).build();
 
         // Update preference controller with a zen mode that is not enabled
         mPrefController.updateZenMode(mPrefCategory, zenMode);
-        verify(mPreference).setChecked(false);
+        assertThat(mPreference.getCheckedState()).isFalse();
 
         // Now with the rule enabled
         zenMode.getRule().setEnabled(true);
         mPrefController.updateZenMode(mPrefCategory, zenMode);
-        verify(mPreference).setChecked(true);
+        assertThat(mPreference.getCheckedState()).isTrue();
     }
 
     @Test
     public void testOnPreferenceChange() {
-        ZenMode zenMode = new ZenMode("id",
-                new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                        .setType(AutomaticZenRule.TYPE_DRIVING)
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowAlarms(true).build())
-                        .setEnabled(false)
-                        .build(), false);
+        ZenMode zenMode = new TestModeBuilder().setEnabled(false).build();
 
         // start with disabled rule
         mPrefController.updateZenMode(mPrefCategory, zenMode);
@@ -152,23 +140,25 @@
         ZenModeConfig.EventInfo eventInfo = new ZenModeConfig.EventInfo();
         eventInfo.calendarId = 1L;
         eventInfo.calName = "My events";
-        ZenMode mode = new ZenMode("id", new AutomaticZenRule.Builder("name",
-                ZenModeConfig.toEventConditionId(eventInfo))
+        ZenMode mode = new TestModeBuilder()
+                .setPackage(SystemZenRules.PACKAGE_ANDROID)
+                .setConditionId(ZenModeConfig.toEventConditionId(eventInfo))
                 .setType(TYPE_SCHEDULE_CALENDAR)
                 .setTriggerDescription("My events")
-                .build(),
-                true);  // is active
+                .build();
         mPrefController.updateZenMode(mPrefCategory, mode);
 
-        verify(mPreference).setTitle(R.string.zen_mode_set_calendar_link);
-        verify(mPreference).setSummary(mode.getRule().getTriggerDescription());
+        assertThat(mPreference.getTitle()).isNotNull();
+        assertThat(mPreference.getTitle().toString()).isEqualTo(
+                mContext.getString(R.string.zen_mode_set_calendar_link));
+        assertThat(mPreference.getSummary()).isNotNull();
+        assertThat(mPreference.getSummary().toString()).isEqualTo(
+                mode.getRule().getTriggerDescription());
+        assertThat(mPreference.getIcon()).isNull();
 
-        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
-        verify(mPreference).setIntent(captor.capture());
         // Destination as written into the intent by SubSettingLauncher
-        assertThat(
-                captor.getValue().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)).isEqualTo(
-                ZenModeSetCalendarFragment.class.getName());
+        assertThat(mPreference.getIntent().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
+                .isEqualTo(ZenModeSetCalendarFragment.class.getName());
     }
 
     @Test
@@ -177,22 +167,74 @@
         scheduleInfo.days = new int[] { Calendar.MONDAY, Calendar.TUESDAY, Calendar.THURSDAY };
         scheduleInfo.startHour = 1;
         scheduleInfo.endHour = 15;
-        ZenMode mode = new ZenMode("id", new AutomaticZenRule.Builder("name",
-                ZenModeConfig.toScheduleConditionId(scheduleInfo))
+        ZenMode mode = new TestModeBuilder()
+                .setConditionId(ZenModeConfig.toScheduleConditionId(scheduleInfo))
+                .setPackage(SystemZenRules.PACKAGE_ANDROID)
                 .setType(TYPE_SCHEDULE_TIME)
                 .setTriggerDescription("some schedule")
-                .build(),
-                true);  // is active
+                .build();
         mPrefController.updateZenMode(mPrefCategory, mode);
 
-        verify(mPreference).setTitle(R.string.zen_mode_set_schedule_link);
-        verify(mPreference).setSummary(mode.getRule().getTriggerDescription());
+        assertThat(mPreference.getTitle()).isNotNull();
+        assertThat(mPreference.getTitle().toString()).isEqualTo(
+                mContext.getString(R.string.zen_mode_set_schedule_link));
+        assertThat(mPreference.getSummary()).isNotNull();
+        assertThat(mPreference.getSummary().toString()).isEqualTo(
+                mode.getRule().getTriggerDescription());
+        assertThat(mPreference.getIcon()).isNull();
 
-        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
-        verify(mPreference).setIntent(captor.capture());
         // Destination as written into the intent by SubSettingLauncher
-        assertThat(
-                captor.getValue().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)).isEqualTo(
-                ZenModeSetScheduleFragment.class.getName());
+        assertThat(mPreference.getIntent().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
+                .isEqualTo(ZenModeSetScheduleFragment.class.getName());
+    }
+
+    @Test
+    public void testRuleLink_manual() {
+        ZenMode mode = new TestModeBuilder()
+                .setConditionId(ZenModeConfig.toCustomManualConditionId())
+                .setPackage(SystemZenRules.PACKAGE_ANDROID)
+                .setType(TYPE_OTHER)
+                .setTriggerDescription("Will not be shown")
+                .build();
+        mPrefController.updateZenMode(mPrefCategory, mode);
+
+        assertThat(mPreference.getTitle()).isNotNull();
+        assertThat(mPreference.getTitle().toString()).isEqualTo(
+                mContext.getString(R.string.zen_mode_select_schedule));
+        assertThat(mPreference.getIcon()).isNotNull();
+        assertThat(mPreference.getSummary()).isNotNull();
+        assertThat(mPreference.getSummary().toString()).isEqualTo("");
+
+        // Set up a click listener to open the dialog.
+        assertThat(mPreference.getOnPreferenceClickListener()).isNotNull();
+    }
+
+    @Test
+    public void onScheduleChosen_updatesMode() {
+        ZenMode originalMode = new TestModeBuilder()
+                .setConditionId(ZenModeConfig.toCustomManualConditionId())
+                .setPackage(SystemZenRules.PACKAGE_ANDROID)
+                .setType(TYPE_OTHER)
+                .setTriggerDescription("")
+                .build();
+        mPrefController.updateZenMode(mPrefCategory, originalMode);
+
+        ZenModeConfig.ScheduleInfo scheduleInfo = new ZenModeConfig.ScheduleInfo();
+        scheduleInfo.days = new int[] { Calendar.MONDAY };
+        scheduleInfo.startHour = 12;
+        scheduleInfo.endHour = 15;
+        Uri scheduleUri = ZenModeConfig.toScheduleConditionId(scheduleInfo);
+
+        mPrefController.mOnScheduleOptionListener.onScheduleSelected(scheduleUri);
+
+        // verify the backend got asked to update the mode to be schedule-based.
+        ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
+        verify(mBackend).updateMode(captor.capture());
+        ZenMode updatedMode = captor.getValue();
+        assertThat(updatedMode.getType()).isEqualTo(TYPE_SCHEDULE_TIME);
+        assertThat(updatedMode.getRule().getConditionId()).isEqualTo(scheduleUri);
+        assertThat(updatedMode.getRule().getTriggerDescription()).isNotEmpty();
+        assertThat(updatedMode.getRule().getOwner()).isEqualTo(
+                ZenModeConfig.getScheduleConditionProvider());
     }
 }
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTest.java
deleted file mode 100644
index 0528621..0000000
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeTest.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * 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.app.NotificationManager.INTERRUPTION_FILTER_ALARMS;
-import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
-import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
-import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.AutomaticZenRule;
-import android.net.Uri;
-import android.service.notification.ZenPolicy;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-@RunWith(RobolectricTestRunner.class)
-public class ZenModeTest {
-
-    private static final ZenPolicy ZEN_POLICY = new ZenPolicy.Builder().allowAllSounds().build();
-
-    private static final AutomaticZenRule ZEN_RULE =
-            new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                    .setType(AutomaticZenRule.TYPE_DRIVING)
-                    .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                    .setZenPolicy(ZEN_POLICY)
-                    .build();
-
-    @Test
-    public void testBasicMethods() {
-        ZenMode zenMode = new ZenMode("id", ZEN_RULE, true);
-
-        assertThat(zenMode.getId()).isEqualTo("id");
-        assertThat(zenMode.getRule()).isEqualTo(ZEN_RULE);
-        assertThat(zenMode.isManualDnd()).isFalse();
-        assertThat(zenMode.canBeDeleted()).isTrue();
-        assertThat(zenMode.isActive()).isTrue();
-
-        ZenMode manualMode = ZenMode.manualDndMode(ZEN_RULE, false);
-        assertThat(manualMode.getId()).isEqualTo(ZenMode.MANUAL_DND_MODE_ID);
-        assertThat(manualMode.isManualDnd()).isTrue();
-        assertThat(manualMode.canBeDeleted()).isFalse();
-        assertThat(manualMode.isActive()).isFalse();
-    }
-
-    @Test
-    public void getPolicy_interruptionFilterPriority_returnsZenPolicy() {
-        ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                .setZenPolicy(ZEN_POLICY)
-                .build(), false);
-
-        assertThat(zenMode.getPolicy()).isEqualTo(ZEN_POLICY);
-    }
-
-    @Test
-    public void getPolicy_interruptionFilterAll_returnsPolicyAllowingAll() {
-        ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
-                .setInterruptionFilter(INTERRUPTION_FILTER_ALL)
-                .setZenPolicy(ZEN_POLICY) // should be ignored
-                .build(), false);
-
-        assertThat(zenMode.getPolicy()).isEqualTo(
-                new ZenPolicy.Builder().allowChannels(ZenMode.CHANNEL_POLICY_ALL)
-                        .allowAllSounds().showAllVisualEffects().build());
-    }
-
-    @Test
-    public void getPolicy_interruptionFilterAlarms_returnsPolicyAllowingAlarms() {
-        ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
-                .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
-                .setZenPolicy(ZEN_POLICY) // should be ignored
-                .build(), false);
-
-        assertThat(zenMode.getPolicy()).isEqualTo(
-                new ZenPolicy.Builder()
-                        .disallowAllSounds()
-                        .allowAlarms(true)
-                        .allowMedia(true)
-                        .allowPriorityChannels(false)
-                        .build());
-    }
-
-    @Test
-    public void getPolicy_interruptionFilterNone_returnsPolicyAllowingNothing() {
-        ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
-                .setInterruptionFilter(INTERRUPTION_FILTER_NONE)
-                .setZenPolicy(ZEN_POLICY) // should be ignored
-                .build(), false);
-
-        assertThat(zenMode.getPolicy()).isEqualTo(
-                new ZenPolicy.Builder()
-                        .disallowAllSounds()
-                        .hideAllVisualEffects()
-                        .allowPriorityChannels(false)
-                        .build());
-    }
-
-    @Test
-    public void setPolicy_setsInterruptionFilterPriority() {
-        ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
-                .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
-                .build(), false);
-
-        zenMode.setPolicy(ZEN_POLICY);
-
-        assertThat(zenMode.getRule().getInterruptionFilter()).isEqualTo(
-                INTERRUPTION_FILTER_PRIORITY);
-        assertThat(zenMode.getPolicy()).isEqualTo(ZEN_POLICY);
-        assertThat(zenMode.getRule().getZenPolicy()).isEqualTo(ZEN_POLICY);
-    }
-
-    @Test
-    public void setPolicy_withAllChannelsAllowed_setsInterruptionFilterAll() {
-        ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
-                .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
-                .setZenPolicy(ZEN_POLICY)
-                .build(), false);
-
-        zenMode.setPolicy(
-                new ZenPolicy.Builder().allowChannels(ZenMode.CHANNEL_POLICY_ALL).build());
-
-        assertThat(zenMode.getRule().getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALL);
-        assertThat(zenMode.getPolicy()).isEqualTo(
-                new ZenPolicy.Builder().allowChannels(ZenMode.CHANNEL_POLICY_ALL)
-                        .allowAllSounds().showAllVisualEffects().build());
-    }
-
-    @Test
-    public void setPolicy_priorityToAllChannelsAndBack_restoresOldPolicy() {
-        ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                .setZenPolicy(ZEN_POLICY)
-                .build(), false);
-
-        zenMode.setPolicy(
-                new ZenPolicy.Builder().allowChannels(ZenMode.CHANNEL_POLICY_ALL).build());
-        assertThat(zenMode.getRule().getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALL);
-        assertThat(zenMode.getPolicy()).isEqualTo(
-                new ZenPolicy.Builder().allowChannels(ZenMode.CHANNEL_POLICY_ALL)
-                        .allowAllSounds().showAllVisualEffects().build());
-
-        zenMode.setPolicy(
-                new ZenPolicy.Builder().allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY).build());
-
-        assertThat(zenMode.getRule().getInterruptionFilter()).isEqualTo(
-                INTERRUPTION_FILTER_PRIORITY);
-        assertThat(zenMode.getPolicy()).isEqualTo(ZEN_POLICY);
-        assertThat(zenMode.getRule().getZenPolicy()).isEqualTo(ZEN_POLICY);
-    }
-
-    @Test
-    public void setPolicy_alarmsOnlyToAllChannelsAndBack_restoresPolicySimilarToAlarmsOnly() {
-        ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
-                .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
-                .build(), false);
-
-        zenMode.setPolicy(
-                new ZenPolicy.Builder().allowChannels(ZenMode.CHANNEL_POLICY_ALL).build());
-        assertThat(zenMode.getRule().getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALL);
-        assertThat(zenMode.getPolicy()).isEqualTo(
-                new ZenPolicy.Builder().allowChannels(ZenMode.CHANNEL_POLICY_ALL)
-                        .allowAllSounds().showAllVisualEffects().build());
-
-        zenMode.setPolicy(
-                new ZenPolicy.Builder().allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY).build());
-
-        // We don't go back to ALARMS, but the policy must be the one the user was seeing before.
-        ZenPolicy alarmsOnlyLikePolicy = new ZenPolicy.Builder().disallowAllSounds()
-                .allowAlarms(true).allowMedia(true).allowPriorityChannels(false)
-                .build();
-        assertThat(zenMode.getRule().getInterruptionFilter()).isEqualTo(
-                INTERRUPTION_FILTER_PRIORITY);
-        assertThat(zenMode.getPolicy()).isEqualTo(alarmsOnlyLikePolicy);
-        assertThat(zenMode.getRule().getZenPolicy()).isEqualTo(alarmsOnlyLikePolicy);
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModesBackendTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModesBackendTest.java
deleted file mode 100644
index 9483683..0000000
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModesBackendTest.java
+++ /dev/null
@@ -1,363 +0,0 @@
-/*
- * 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.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
-import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-import static android.provider.Settings.Global.ZEN_MODE_OFF;
-import static android.service.notification.Condition.SOURCE_UNKNOWN;
-import static android.service.notification.Condition.STATE_FALSE;
-import static android.service.notification.Condition.STATE_TRUE;
-import static android.service.notification.ZenPolicy.STATE_ALLOW;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertThrows;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.AutomaticZenRule;
-import android.app.Flags;
-import android.app.NotificationManager;
-import android.app.NotificationManager.Policy;
-import android.content.Context;
-import android.net.Uri;
-import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
-import android.provider.Settings;
-import android.service.notification.Condition;
-import android.service.notification.ZenAdapters;
-import android.service.notification.ZenDeviceEffects;
-import android.service.notification.ZenModeConfig;
-import android.service.notification.ZenPolicy;
-
-import com.android.settings.R;
-
-import com.google.common.collect.ImmutableMap;
-
-import org.junit.Before;
-import org.junit.Rule;
-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;
-import org.robolectric.shadows.ShadowApplication;
-
-import java.time.Duration;
-import java.util.List;
-
-@RunWith(RobolectricTestRunner.class)
-@EnableFlags(Flags.FLAG_MODES_UI)
-public class ZenModesBackendTest {
-
-    private static final String ZEN_RULE_ID = "rule";
-    private static final AutomaticZenRule ZEN_RULE =
-            new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                    .setType(AutomaticZenRule.TYPE_DRIVING)
-                    .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                    .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
-                    .build();
-
-    private static final AutomaticZenRule MANUAL_DND_RULE =
-            new AutomaticZenRule.Builder("Do Not Disturb", Uri.EMPTY)
-                    .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                    .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
-                    .build();
-
-    @Mock
-    private NotificationManager mNm;
-
-    private Context mContext;
-    private ZenModesBackend mBackend;
-
-    @Rule
-    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(
-            SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
-
-    // Helper methods to add active/inactive rule state to a config. Returns a copy.
-    private ZenModeConfig configWithManualRule(ZenModeConfig base, boolean active) {
-        ZenModeConfig out = base.copy();
-
-        if (active) {
-            out.manualRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-            out.manualRule.condition =
-                    new Condition(out.manualRule.conditionId, "", STATE_TRUE, SOURCE_UNKNOWN);
-        } else {
-            out.manualRule.zenMode = ZEN_MODE_OFF;
-            out.manualRule.condition =
-                    new Condition(out.manualRule.conditionId, "", STATE_FALSE, SOURCE_UNKNOWN);
-        }
-        return out;
-    }
-
-    private ZenModeConfig configWithRule(ZenModeConfig base, String ruleId, AutomaticZenRule rule,
-            boolean active) {
-        ZenModeConfig out = base.copy();
-
-        // Note that there are many other fields of zenRule, but here we only set the ones
-        // relevant to determining whether or not it is active.
-        ZenModeConfig.ZenRule zenRule = new ZenModeConfig.ZenRule();
-        zenRule.pkg = "package";
-        zenRule.enabled = active;
-        zenRule.snoozing = false;
-        zenRule.condition = new Condition(rule.getConditionId(), "",
-                active ? Condition.STATE_TRUE : Condition.STATE_FALSE,
-                Condition.SOURCE_USER_ACTION);
-        out.automaticRules.put(ruleId, zenRule);
-
-        return out;
-    }
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        ShadowApplication shadowApplication = ShadowApplication.getInstance();
-        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
-
-        mContext = RuntimeEnvironment.application;
-        mBackend = new ZenModesBackend(mContext);
-
-        // Default catch-all case with no data. This isn't realistic, but tests below that rely
-        // on the config to get data on rules active will create those individually.
-        when(mNm.getZenModeConfig()).thenReturn(new ZenModeConfig());
-    }
-
-    @Test
-    public void getModes_containsManualDndAndZenRules() {
-        AutomaticZenRule rule2 = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
-                .setType(AutomaticZenRule.TYPE_BEDTIME)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                .setZenPolicy(new ZenPolicy.Builder().disallowAllSounds().build())
-                .build();
-        Policy dndPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS,
-                Policy.PRIORITY_SENDERS_CONTACTS, Policy.PRIORITY_SENDERS_CONTACTS);
-        when(mNm.getAutomaticZenRules()).thenReturn(
-                ImmutableMap.of("rule1", ZEN_RULE, "rule2", rule2));
-        ZenModeConfig config = new ZenModeConfig();
-        config.applyNotificationPolicy(dndPolicy);
-        assertThat(config.manualRule.zenPolicy.getPriorityCategoryAlarms()).isEqualTo(STATE_ALLOW);
-        when(mNm.getZenModeConfig()).thenReturn(config);
-
-        List<ZenMode> modes = mBackend.getModes();
-
-        // all modes exist, but none of them are currently active
-        assertThat(modes).containsExactly(
-                ZenMode.manualDndMode(
-                        new AutomaticZenRule.Builder(
-                                mContext.getString(R.string.zen_mode_settings_title), Uri.EMPTY)
-                                .setType(AutomaticZenRule.TYPE_OTHER)
-                                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                                .setZenPolicy(ZenAdapters.notificationPolicyToZenPolicy(dndPolicy))
-                                .setManualInvocationAllowed(true)
-                                .build(),
-                        false),
-                new ZenMode("rule2", rule2, false),
-                new ZenMode("rule1", ZEN_RULE, false))
-                .inOrder();
-    }
-
-    @Test
-    public void getMode_manualDnd_returnsMode() {
-        Policy dndPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS,
-                Policy.PRIORITY_SENDERS_CONTACTS, Policy.PRIORITY_SENDERS_CONTACTS);
-        ZenModeConfig config = new ZenModeConfig();
-        config.applyNotificationPolicy(dndPolicy);
-        when(mNm.getZenModeConfig()).thenReturn(config);
-
-        ZenMode mode = mBackend.getMode(ZenMode.MANUAL_DND_MODE_ID);
-
-        assertThat(mode).isEqualTo(
-                ZenMode.manualDndMode(
-                        new AutomaticZenRule.Builder(
-                                mContext.getString(R.string.zen_mode_settings_title), Uri.EMPTY)
-                                .setType(AutomaticZenRule.TYPE_OTHER)
-                                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                                .setZenPolicy(ZenAdapters.notificationPolicyToZenPolicy(dndPolicy))
-                                .setManualInvocationAllowed(true)
-                                .build(), false));
-    }
-
-    @Test
-    public void getMode_zenRule_returnsMode() {
-        when(mNm.getAutomaticZenRule(eq(ZEN_RULE_ID))).thenReturn(ZEN_RULE);
-
-        ZenMode mode = mBackend.getMode(ZEN_RULE_ID);
-
-        assertThat(mode).isEqualTo(new ZenMode(ZEN_RULE_ID, ZEN_RULE, false));
-    }
-
-    @Test
-    public void getMode_missingRule_returnsNull() {
-        when(mNm.getAutomaticZenRule(any())).thenReturn(null);
-
-        ZenMode mode = mBackend.getMode(ZEN_RULE_ID);
-
-        assertThat(mode).isNull();
-        verify(mNm).getAutomaticZenRule(eq(ZEN_RULE_ID));
-    }
-
-    @Test
-    public void getMode_manualDnd_returnsCorrectActiveState() {
-        // Set up a base config with an active rule to make sure we're looking at the correct info
-        ZenModeConfig configWithActiveRule = configWithRule(new ZenModeConfig(), ZEN_RULE_ID,
-                ZEN_RULE, true);
-
-        // Equivalent to disallowAllSounds()
-        Policy dndPolicy = new Policy(0, 0, 0);
-        configWithActiveRule.applyNotificationPolicy(dndPolicy);
-        when(mNm.getZenModeConfig()).thenReturn(configWithActiveRule);
-
-        ZenMode mode = mBackend.getMode(ZenMode.MANUAL_DND_MODE_ID);
-
-        // By default, manual rule is inactive
-        assertThat(mode.isActive()).isFalse();
-
-        // Now the returned config will represent the manual rule being active
-        when(mNm.getZenModeConfig()).thenReturn(configWithManualRule(configWithActiveRule, true));
-        ZenMode activeMode = mBackend.getMode(ZenMode.MANUAL_DND_MODE_ID);
-        assertThat(activeMode.isActive()).isTrue();
-    }
-
-    @Test
-    public void getMode_zenRule_returnsCorrectActiveState() {
-        // Set up a base config that has an active manual rule and "rule2", to make sure we're
-        // looking at the correct rule's info.
-        ZenModeConfig configWithActiveRules = configWithRule(
-                configWithManualRule(new ZenModeConfig(), true),  // active manual rule
-                "rule2", ZEN_RULE, true);  // active rule 2
-
-        when(mNm.getAutomaticZenRule(eq(ZEN_RULE_ID))).thenReturn(ZEN_RULE);
-        when(mNm.getZenModeConfig()).thenReturn(
-                configWithRule(configWithActiveRules, ZEN_RULE_ID, ZEN_RULE, false));
-
-        // Round 1: the current config should indicate that the rule is not active
-        ZenMode mode = mBackend.getMode(ZEN_RULE_ID);
-        assertThat(mode.isActive()).isFalse();
-
-        when(mNm.getZenModeConfig()).thenReturn(
-                configWithRule(configWithActiveRules, ZEN_RULE_ID, ZEN_RULE, true));
-        ZenMode activeMode = mBackend.getMode(ZEN_RULE_ID);
-        assertThat(activeMode.isActive()).isTrue();
-    }
-
-    @Test
-    public void updateMode_manualDnd_setsDeviceEffects() throws Exception {
-        ZenMode manualDnd = ZenMode.manualDndMode(
-                new AutomaticZenRule.Builder("DND", Uri.EMPTY)
-                        .setZenPolicy(new ZenPolicy())
-                        .setDeviceEffects(new ZenDeviceEffects.Builder()
-                                .setShouldDimWallpaper(true)
-                                .build())
-                        .build(), false);
-
-        mBackend.updateMode(manualDnd);
-
-        verify(mNm).setManualZenRuleDeviceEffects(new ZenDeviceEffects.Builder()
-                .setShouldDimWallpaper(true)
-                .build());
-    }
-
-    @Test
-    public void updateMode_manualDnd_setsNotificationPolicy() {
-        ZenMode manualDnd = ZenMode.manualDndMode(
-                new AutomaticZenRule.Builder("DND", Uri.EMPTY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
-                        .build(), false);
-
-        mBackend.updateMode(manualDnd);
-
-        verify(mNm).setNotificationPolicy(eq(new ZenModeConfig().toNotificationPolicy(
-                new ZenPolicy.Builder().allowAllSounds().build())), eq(true));
-    }
-
-    @Test
-    public void updateMode_zenRule_updatesRule() {
-        ZenMode ruleMode = new ZenMode("rule", ZEN_RULE, false);
-
-        mBackend.updateMode(ruleMode);
-
-        verify(mNm).updateAutomaticZenRule(eq("rule"), eq(ZEN_RULE), eq(true));
-    }
-
-    @Test
-    public void activateMode_manualDnd_setsZenModeImportant() {
-        mBackend.activateMode(ZenMode.manualDndMode(MANUAL_DND_RULE, false), null);
-
-        verify(mNm).setZenMode(eq(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null),
-                any(), eq(true));
-    }
-
-    @Test
-    public void activateMode_manualDndWithDuration_setsZenModeImportantWithCondition() {
-        mBackend.activateMode(ZenMode.manualDndMode(MANUAL_DND_RULE, false),
-                Duration.ofMinutes(30));
-
-        verify(mNm).setZenMode(eq(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS),
-                eq(ZenModeConfig.toTimeCondition(mContext, 30, 0, true).id),
-                any(),
-                eq(true));
-    }
-
-    @Test
-    public void activateMode_zenRule_setsRuleStateActive() {
-        mBackend.activateMode(new ZenMode(ZEN_RULE_ID, ZEN_RULE, false), null);
-
-        verify(mNm).setAutomaticZenRuleState(eq(ZEN_RULE_ID),
-                eq(new Condition(ZEN_RULE.getConditionId(), "", Condition.STATE_TRUE,
-                        Condition.SOURCE_USER_ACTION)));
-    }
-
-    @Test
-    public void activateMode_zenRuleWithDuration_fails() {
-        assertThrows(IllegalArgumentException.class,
-                () -> mBackend.activateMode(new ZenMode(ZEN_RULE_ID, ZEN_RULE, false),
-                        Duration.ofMinutes(30)));
-    }
-
-    @Test
-    public void deactivateMode_manualDnd_setsZenModeOff() {
-        mBackend.deactivateMode(ZenMode.manualDndMode(MANUAL_DND_RULE, true));
-
-        verify(mNm).setZenMode(eq(ZEN_MODE_OFF), eq(null), any(), eq(true));
-    }
-
-    @Test
-    public void deactivateMode_zenRule_setsRuleStateInactive() {
-        mBackend.deactivateMode(new ZenMode(ZEN_RULE_ID, ZEN_RULE, false));
-
-        verify(mNm).setAutomaticZenRuleState(eq(ZEN_RULE_ID),
-                eq(new Condition(ZEN_RULE.getConditionId(), "", Condition.STATE_FALSE,
-                        Condition.SOURCE_USER_ACTION)));
-    }
-
-    @Test
-    public void removeMode_zenRule_deletesRule() {
-        mBackend.removeMode(new ZenMode(ZEN_RULE_ID, ZEN_RULE, false));
-
-        verify(mNm).removeAutomaticZenRule(ZEN_RULE_ID, true);
-    }
-
-    @Test
-    public void removeMode_manualDnd_fails() {
-        assertThrows(IllegalArgumentException.class,
-                () -> mBackend.removeMode(ZenMode.manualDndMode(MANUAL_DND_RULE, false)));
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModesListItemPreferenceTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModesListItemPreferenceTest.java
new file mode 100644
index 0000000..495a24c
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModesListItemPreferenceTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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 com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.service.notification.ZenModeConfig;
+
+import com.android.settingslib.notification.modes.ZenMode;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowLooper;
+
+@RunWith(RobolectricTestRunner.class)
+public class ZenModesListItemPreferenceTest {
+
+    private Context mContext;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+    }
+
+    @Test
+    public void constructor_setsMode() {
+        ZenModesListItemPreference preference = new ZenModesListItemPreference(mContext,
+                TestModeBuilder.EXAMPLE);
+
+        assertThat(preference.getKey()).isEqualTo(TestModeBuilder.EXAMPLE.getId());
+        assertThat(preference.getZenMode()).isEqualTo(TestModeBuilder.EXAMPLE);
+    }
+
+    @Test
+    public void setZenMode_modeEnabled() {
+        ZenMode mode = new TestModeBuilder()
+                .setName("Enabled mode")
+                .setTriggerDescription("When the thrush knocks")
+                .setEnabled(true)
+                .build();
+
+        ZenModesListItemPreference preference = new ZenModesListItemPreference(mContext, mode);
+        ShadowLooper.idleMainLooper(); // To load icon.
+
+        assertThat(preference.getTitle()).isEqualTo("Enabled mode");
+        assertThat(preference.getSummary()).isEqualTo("When the thrush knocks");
+        assertThat(preference.getIcon()).isNotNull();
+    }
+
+    @Test
+    public void setZenMode_modeActive() {
+        ZenMode mode = new TestModeBuilder()
+                .setName("Active mode")
+                .setTriggerDescription("When Birnam forest comes to Dunsinane")
+                .setEnabled(true)
+                .setActive(true)
+                .build();
+
+        ZenModesListItemPreference preference = new ZenModesListItemPreference(mContext, mode);
+        ShadowLooper.idleMainLooper();
+
+        assertThat(preference.getTitle()).isEqualTo("Active mode");
+        assertThat(preference.getSummary()).isEqualTo("ON • When Birnam forest comes to Dunsinane");
+        assertThat(preference.getIcon()).isNotNull();
+    }
+
+    @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)
+                .build();
+
+        ZenModesListItemPreference preference = new ZenModesListItemPreference(mContext, mode);
+        ShadowLooper.idleMainLooper();
+
+        assertThat(preference.getTitle()).isEqualTo("Mode disabled by app");
+        assertThat(preference.getSummary()).isEqualTo("Tap to set up");
+        assertThat(preference.getIcon()).isNotNull();
+    }
+
+    @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)
+                .build();
+
+        ZenModesListItemPreference preference = new ZenModesListItemPreference(mContext, mode);
+        ShadowLooper.idleMainLooper();
+
+        assertThat(preference.getTitle()).isEqualTo("Mode disabled by user");
+        assertThat(preference.getSummary()).isEqualTo("Paused");
+        assertThat(preference.getIcon()).isNotNull();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModesListPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModesListPreferenceControllerTest.java
index 9a4de60..f2624ac 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModesListPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModesListPreferenceControllerTest.java
@@ -37,6 +37,8 @@
 import androidx.preference.PreferenceManager;
 import androidx.preference.PreferenceScreen;
 
+import com.android.settingslib.notification.modes.ZenMode;
+import com.android.settingslib.notification.modes.ZenModesBackend;
 import com.android.settingslib.search.SearchIndexableRaw;
 
 import com.google.common.collect.ImmutableList;
@@ -58,14 +60,15 @@
 public class ZenModesListPreferenceControllerTest {
     private static final String TEST_MODE_ID = "test_mode";
     private static final String TEST_MODE_NAME = "Test Mode";
-    private static final ZenMode TEST_MODE = new ZenMode(
-            TEST_MODE_ID,
-            new AutomaticZenRule.Builder(TEST_MODE_NAME, Uri.parse("test_uri"))
+
+    private static final ZenMode TEST_MODE = new TestModeBuilder()
+            .setId(TEST_MODE_ID)
+            .setAzr(new AutomaticZenRule.Builder(TEST_MODE_NAME, Uri.parse("test_uri"))
                     .setType(AutomaticZenRule.TYPE_BEDTIME)
                     .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                     .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
-                    .build(),
-            false);
+                    .build())
+            .build();
 
     private static final ZenMode TEST_MANUAL_MODE = ZenMode.manualDndMode(
             new AutomaticZenRule.Builder("Do Not Disturb", Uri.EMPTY)
@@ -110,14 +113,9 @@
 
         assertThat(mPreference.getPreferenceCount()).isEqualTo(5);
         List<ZenModesListItemPreference> itemPreferences = getModeListItems(mPreference);
-        assertThat(itemPreferences.stream().map(pref -> pref.mZenMode).toList())
+        assertThat(itemPreferences.stream().map(ZenModesListItemPreference::getZenMode).toList())
                 .containsExactlyElementsIn(modes)
                 .inOrder();
-
-        for (int i = 0; i < modes.size(); i++) {
-            assertThat(((ZenModesListItemPreference) (mPreference.getPreference(i))).mZenMode)
-                    .isEqualTo(modes.get(i));
-        }
     }
 
     @Test
@@ -138,7 +136,7 @@
         mPrefController.updateState(mPreference);
 
         List<ZenModesListItemPreference> newPreferences = getModeListItems(mPreference);
-        assertThat(newPreferences.stream().map(pref -> pref.mZenMode).toList())
+        assertThat(newPreferences.stream().map(ZenModesListItemPreference::getZenMode).toList())
                 .containsExactlyElementsIn(updatedModes)
                 .inOrder();
 
@@ -194,7 +192,7 @@
         assertThat(newData).hasSize(1);
 
         SearchIndexableRaw newItem = newData.get(0);
-        assertThat(newItem.key).isEqualTo(ZenMode.MANUAL_DND_MODE_ID);
+        assertThat(newItem.key).isEqualTo(TEST_MANUAL_MODE.getId());
         assertThat(newItem.title).isEqualTo("Do Not Disturb");  // set above
     }
 
@@ -209,7 +207,7 @@
 
         // Should keep the order presented by getModes()
         SearchIndexableRaw item0 = data.get(0);
-        assertThat(item0.key).isEqualTo(ZenMode.MANUAL_DND_MODE_ID);
+        assertThat(item0.key).isEqualTo(TEST_MANUAL_MODE.getId());
         assertThat(item0.title).isEqualTo("Do Not Disturb");  // set above
 
         SearchIndexableRaw item1 = data.get(1);
@@ -218,13 +216,7 @@
     }
 
     private static ZenMode newMode(String id) {
-        return new ZenMode(
-                id,
-                new AutomaticZenRule.Builder("Mode " + id, Uri.parse("test_uri"))
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
-                        .build(),
-                false);
+        return new TestModeBuilder().setId(id).setName("Mode " + id).build();
     }
 
     /**
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 13ae4eb..62b5ee0 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java
@@ -16,7 +16,6 @@
 
 package com.android.settings.notification.modes;
 
-import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
 import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_ANYONE;
 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE;
 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS;
@@ -25,12 +24,12 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.app.AutomaticZenRule;
 import android.content.Context;
-import android.net.Uri;
 import android.service.notification.ZenDeviceEffects;
 import android.service.notification.ZenPolicy;
 
+import com.android.settingslib.notification.modes.ZenMode;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -40,7 +39,6 @@
 
 import java.util.LinkedHashSet;
 import java.util.Set;
-
 @RunWith(RobolectricTestRunner.class)
 public class ZenModesSummaryHelperTest {
     private Context mContext;
@@ -58,50 +56,38 @@
 
     @Test
     public void getPeopleSummary_noOne() {
-        AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
-                .setType(AutomaticZenRule.TYPE_BEDTIME)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+        ZenMode zenMode = new TestModeBuilder()
                 .setZenPolicy(new ZenPolicy.Builder().disallowAllSounds().build())
                 .build();
-        ZenMode zenMode = new ZenMode("id", rule, true);
 
         assertThat(mSummaryHelper.getPeopleSummary(zenMode)).isEqualTo("No one can interrupt");
     }
 
     @Test
     public void getPeopleSummary_some() {
-        AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
-                .setType(AutomaticZenRule.TYPE_BEDTIME)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+        ZenMode zenMode = new TestModeBuilder()
                 .setZenPolicy(new ZenPolicy.Builder().allowCalls(PEOPLE_TYPE_CONTACTS).build())
                 .build();
-        ZenMode zenMode = new ZenMode("id", rule, true);
 
         assertThat(mSummaryHelper.getPeopleSummary(zenMode)).isEqualTo("Some people can interrupt");
     }
 
     @Test
     public void getPeopleSummary_all() {
-        AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
-                .setType(AutomaticZenRule.TYPE_BEDTIME)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+        ZenMode zenMode = new TestModeBuilder()
                 .setZenPolicy(new ZenPolicy.Builder().allowCalls(PEOPLE_TYPE_ANYONE).
                         allowConversations(CONVERSATION_SENDERS_ANYONE)
                         .allowMessages(PEOPLE_TYPE_ANYONE).build())
                 .build();
-        ZenMode zenMode = new ZenMode("id", rule, true);
 
         assertThat(mSummaryHelper.getPeopleSummary(zenMode)).isEqualTo("All people can interrupt");
     }
 
     @Test
     public void getOtherSoundCategoriesSummary_single() {
-        AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
-                .setType(AutomaticZenRule.TYPE_BEDTIME)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+        ZenMode zenMode = new TestModeBuilder()
                 .setZenPolicy(new ZenPolicy.Builder().allowAlarms(true).build())
                 .build();
-        ZenMode zenMode = new ZenMode("id", rule, true);
 
         assertThat(mSummaryHelper.getOtherSoundCategoriesSummary(zenMode)).isEqualTo(
                 "Alarms can interrupt");
@@ -109,12 +95,9 @@
 
     @Test
     public void getOtherSoundCategoriesSummary_duo() {
-        AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
-                .setType(AutomaticZenRule.TYPE_BEDTIME)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+        ZenMode zenMode = new TestModeBuilder()
                 .setZenPolicy(new ZenPolicy.Builder().allowAlarms(true).allowMedia(true).build())
                 .build();
-        ZenMode zenMode = new ZenMode("id", rule, true);
 
         assertThat(mSummaryHelper.getOtherSoundCategoriesSummary(zenMode)).isEqualTo(
                 "Alarms and media can interrupt");
@@ -122,16 +105,13 @@
 
     @Test
     public void getOtherSoundCategoriesSummary_trio() {
-        AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
-                .setType(AutomaticZenRule.TYPE_BEDTIME)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+        ZenMode zenMode = new TestModeBuilder()
                 .setZenPolicy(new ZenPolicy.Builder()
                         .allowAlarms(true)
                         .allowMedia(true)
                         .allowSystem(true)
                         .build())
                 .build();
-        ZenMode zenMode = new ZenMode("id", rule, true);
 
         assertThat(mSummaryHelper.getOtherSoundCategoriesSummary(zenMode)).isEqualTo(
                 "Alarms, media, and touch sounds can interrupt");
@@ -139,9 +119,7 @@
 
     @Test
     public void getOtherSoundCategoriesSummary_quad() {
-        AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
-                .setType(AutomaticZenRule.TYPE_BEDTIME)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+        ZenMode zenMode = new TestModeBuilder()
                 .setZenPolicy(new ZenPolicy.Builder()
                         .allowAlarms(true)
                         .allowMedia(true)
@@ -149,7 +127,6 @@
                         .allowReminders(true)
                         .build())
                 .build();
-        ZenMode zenMode = new ZenMode("id", rule, true);
 
         assertThat(mSummaryHelper.getOtherSoundCategoriesSummary(zenMode)).isEqualTo(
                 "Alarms, media, and 2 more can interrupt");
@@ -157,9 +134,7 @@
 
     @Test
     public void getOtherSoundCategoriesSummary_all() {
-        AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
-                .setType(AutomaticZenRule.TYPE_BEDTIME)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+        ZenMode zenMode = new TestModeBuilder()
                 .setZenPolicy(new ZenPolicy.Builder()
                         .allowAlarms(true)
                         .allowMedia(true)
@@ -168,7 +143,6 @@
                         .allowEvents(true)
                         .build())
                 .build();
-        ZenMode zenMode = new ZenMode("id", rule, true);
 
         assertThat(mSummaryHelper.getOtherSoundCategoriesSummary(zenMode)).isEqualTo(
                 "Alarms, media, and 3 more can interrupt");
@@ -176,61 +150,52 @@
 
     @Test
     public void getBlockedEffectsSummary_none() {
-        AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
-                .setType(AutomaticZenRule.TYPE_BEDTIME)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+        ZenMode zenMode = new TestModeBuilder()
                 .setZenPolicy(new ZenPolicy.Builder()
                         .showAllVisualEffects()
                         .allowAlarms(true)
                         .build())
                 .build();
-        ZenMode zenMode = new ZenMode("id", rule, true);
+
         assertThat(mSummaryHelper.getBlockedEffectsSummary(zenMode))
                 .isEqualTo("Notifications shown");
     }
 
     @Test
     public void getBlockedEffectsSummary_some() {
-        AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
-                .setType(AutomaticZenRule.TYPE_BEDTIME)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+        ZenMode zenMode = new TestModeBuilder()
                 .setZenPolicy(new ZenPolicy.Builder()
                         .allowAlarms(true)
                         .showAllVisualEffects()
                         .showVisualEffect(VISUAL_EFFECT_AMBIENT, false)
                         .build())
                 .build();
-        ZenMode zenMode = new ZenMode("id", rule, true);
+
         assertThat(mSummaryHelper.getBlockedEffectsSummary(zenMode))
                 .isEqualTo("Notifications partially hidden");
     }
 
     @Test
     public void getBlockedEffectsSummary_all() {
-        AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
-                .setType(AutomaticZenRule.TYPE_BEDTIME)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+        ZenMode zenMode = new TestModeBuilder()
                 .setZenPolicy(new ZenPolicy.Builder()
                         .allowAlarms(true)
                         .hideAllVisualEffects()
                         .build())
                 .build();
-        ZenMode zenMode = new ZenMode("id", rule, true);
+
         assertThat(mSummaryHelper.getBlockedEffectsSummary(zenMode))
                 .isEqualTo("Notifications hidden");
     }
 
     @Test
     public void getDisplayEffectsSummary_single_notifVis() {
-        AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
-                .setType(AutomaticZenRule.TYPE_BEDTIME)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+        ZenMode zenMode = new TestModeBuilder()
                 .setZenPolicy(new ZenPolicy.Builder()
                         .showAllVisualEffects()
                         .showVisualEffect(VISUAL_EFFECT_AMBIENT, false)
                         .build())
                 .build();
-        ZenMode zenMode = new ZenMode("id", rule, true);
 
         assertThat(mSummaryHelper.getDisplayEffectsSummary(zenMode)).isEqualTo(
                 "Notifications partially hidden");
@@ -238,15 +203,12 @@
 
     @Test
     public void getDisplayEffectsSummary_single_notifVis_unusedEffect() {
-        AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
-                .setType(AutomaticZenRule.TYPE_BEDTIME)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+        ZenMode zenMode = new TestModeBuilder()
                 .setZenPolicy(new ZenPolicy.Builder()
                         .showAllVisualEffects()
                         .showVisualEffect(VISUAL_EFFECT_LIGHTS, false)
                         .build())
                 .build();
-        ZenMode zenMode = new ZenMode("id", rule, true);
 
         assertThat(mSummaryHelper.getDisplayEffectsSummary(zenMode)).isEqualTo(
                 "Notifications shown");
@@ -254,15 +216,12 @@
 
     @Test
     public void getDisplayEffectsSummary_single_displayEffect() {
-        AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
-                .setType(AutomaticZenRule.TYPE_BEDTIME)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+        ZenMode zenMode = new TestModeBuilder()
                 .setZenPolicy(new ZenPolicy.Builder().showAllVisualEffects().build())
                 .setDeviceEffects(new ZenDeviceEffects.Builder()
                         .setShouldDimWallpaper(true)
                         .build())
                 .build();
-        ZenMode zenMode = new ZenMode("id", rule, true);
 
         assertThat(mSummaryHelper.getDisplayEffectsSummary(zenMode)).isEqualTo(
                 "Dim the wallpaper");
@@ -270,16 +229,13 @@
 
     @Test
     public void getDisplayEffectsSummary_duo() {
-        AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
-                .setType(AutomaticZenRule.TYPE_BEDTIME)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+        ZenMode zenMode = new TestModeBuilder()
                 .setZenPolicy(new ZenPolicy.Builder().showAllVisualEffects().build())
                 .setDeviceEffects(new ZenDeviceEffects.Builder()
                         .setShouldDimWallpaper(true)
                         .setShouldDisplayGrayscale(true)
                         .build())
                 .build();
-        ZenMode zenMode = new ZenMode("id", rule, true);
 
         assertThat(mSummaryHelper.getDisplayEffectsSummary(zenMode)).isEqualTo(
                 "Grayscale and dim the wallpaper");
@@ -287,9 +243,7 @@
 
     @Test
     public void getDisplayEffectsSummary_trio() {
-        AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
-                .setType(AutomaticZenRule.TYPE_BEDTIME)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+        ZenMode zenMode = new TestModeBuilder()
                 .setZenPolicy(new ZenPolicy.Builder()
                         .hideAllVisualEffects()
                         .allowAlarms(true)
@@ -301,7 +255,6 @@
                         .setShouldDimWallpaper(true)
                         .build())
                 .build();
-        ZenMode zenMode = new ZenMode("id", rule, true);
 
         assertThat(mSummaryHelper.getDisplayEffectsSummary(zenMode)).isEqualTo(
                 "Notifications hidden, grayscale, and dim the wallpaper");
@@ -309,9 +262,7 @@
 
     @Test
     public void getDisplayEffectsSummary_quad() {
-        AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
-                .setType(AutomaticZenRule.TYPE_BEDTIME)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+        ZenMode zenMode = new TestModeBuilder()
                 .setZenPolicy(new ZenPolicy.Builder()
                         .showAllVisualEffects()
                         .showVisualEffect(VISUAL_EFFECT_AMBIENT, false)
@@ -325,50 +276,29 @@
                         .setShouldUseNightMode(true)
                         .build())
                 .build();
-        ZenMode zenMode = new ZenMode("id", rule, true);
 
         assertThat(mSummaryHelper.getDisplayEffectsSummary(zenMode)).isEqualTo(
                 "Notifications partially hidden, grayscale, and 2 more");
     }
 
     @Test
-    public void getAppsSummary_all() {
-        AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
-                .setType(AutomaticZenRule.TYPE_BEDTIME)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                .setZenPolicy(new ZenPolicy.Builder()
-                        .allowChannels(ZenMode.CHANNEL_POLICY_ALL)
-                        .build())
-                .build();
-        ZenMode zenMode = new ZenMode("id", rule, true);
-
-        assertThat(mSummaryHelper.getAppsSummary(zenMode, new LinkedHashSet<>())).isEqualTo("All");
-    }
-
-    @Test
     public void getAppsSummary_none() {
-        AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
-                .setType(AutomaticZenRule.TYPE_BEDTIME)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+        ZenMode zenMode = new TestModeBuilder()
                 .setZenPolicy(new ZenPolicy.Builder()
                         .allowChannels(ZenPolicy.CHANNEL_POLICY_NONE)
                         .build())
                 .build();
-        ZenMode zenMode = new ZenMode("id", rule, true);
 
         assertThat(mSummaryHelper.getAppsSummary(zenMode, new LinkedHashSet<>())).isEqualTo("None");
     }
 
     @Test
     public void getAppsSummary_priorityAppsNoList() {
-        AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
-                .setType(AutomaticZenRule.TYPE_BEDTIME)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+        ZenMode zenMode = new TestModeBuilder()
                 .setZenPolicy(new ZenPolicy.Builder()
                         .allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY)
                         .build())
                 .build();
-        ZenMode zenMode = new ZenMode("id", rule, true);
 
         assertThat(mSummaryHelper.getAppsSummary(zenMode, null)).isEqualTo("Selected apps");
     }
@@ -410,19 +340,15 @@
 
     @Test
     public void getAppsSummary_priorityApps() {
-        AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
-                .setType(AutomaticZenRule.TYPE_BEDTIME)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+        ZenMode zenMode = new TestModeBuilder()
                 .setZenPolicy(new ZenPolicy.Builder()
                         .allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY)
                         .build())
                 .build();
-        ZenMode zenMode = new ZenMode("id", rule, true);
         Set<String> apps = Set.of("My App", "SecondApp", "ThirdApp", "FourthApp",
                 "FifthApp", "SixthApp");
 
         assertThat(mSummaryHelper.getAppsSummary(zenMode, apps)).isEqualTo("FifthApp, FourthApp, "
                 + "and 4 more can interrupt");
     }
-
 }
diff --git a/tests/robotests/src/com/android/settings/users/TimeoutToDockUserPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/users/TimeoutToDockUserPreferenceControllerTest.java
index 7bc66c8..fe88148 100644
--- a/tests/robotests/src/com/android/settings/users/TimeoutToDockUserPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/users/TimeoutToDockUserPreferenceControllerTest.java
@@ -39,7 +39,6 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -126,7 +125,6 @@
                 BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
     }
 
-    @Ignore("b/313530297")
     @Test
     public void getAvailabilityStatus_isCurrentlyMainUser_returnDisabledForUser() {
         when(mUserManager.getMainUser()).thenReturn(UserHandle.CURRENT);
@@ -136,7 +134,6 @@
                 BasePreferenceController.DISABLED_FOR_USER);
     }
 
-    @Ignore("b/313530297")
     @Test
     public void getAvailabilityStatus_featureAndMultiUserEnabledAndNonMainUser_returnAvailable() {
         when(mUserManager.isUserForeground()).thenReturn(true);
diff --git a/tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java b/tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java
index 7f27324..44e1cc6 100644
--- a/tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java
@@ -22,7 +22,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -63,7 +63,6 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -222,8 +221,8 @@
     }
 
     @Test
-    @Ignore("b/313530297")
     public void onResume_canSwitch_shouldEnableSwitchPref() {
+        setupSelectedUser();
         mUserManager.setSwitchabilityStatus(SWITCHABILITY_STATUS_OK);
         mFragment.mSwitchUserPref = mSwitchUserPref;
         mFragment.onAttach(mContext);
@@ -234,8 +233,8 @@
     }
 
     @Test
-    @Ignore("b/313530297")
     public void onResume_userInCall_shouldDisableSwitchPref() {
+        setupSelectedUser();
         mUserManager.setSwitchabilityStatus(SWITCHABILITY_STATUS_USER_IN_CALL);
         mFragment.mSwitchUserPref = mSwitchUserPref;
         mFragment.onAttach(mContext);
@@ -246,8 +245,8 @@
     }
 
     @Test
-    @Ignore("b/313530297")
     public void onResume_switchDisallowed_shouldDisableSwitchPref() {
+        setupSelectedUser();
         mUserManager.setSwitchabilityStatus(SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED);
         mFragment.mSwitchUserPref = mSwitchUserPref;
         mFragment.onAttach(mContext);
@@ -258,8 +257,8 @@
     }
 
     @Test
-    @Ignore("b/313530297")
     public void onResume_systemUserLocked_shouldDisableSwitchPref() {
+        setupSelectedUser();
         mUserManager.setSwitchabilityStatus(UserManager.SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED);
         mFragment.mSwitchUserPref = mSwitchUserPref;
         mFragment.onAttach(mContext);
@@ -269,7 +268,6 @@
         verify(mSwitchUserPref).setEnabled(false);
     }
 
-    @Ignore("b/313530297")
     @Test
     public void initialize_adminWithTelephony_shouldShowPhonePreference() {
         setupSelectedUser();
@@ -315,7 +313,6 @@
         verify(mFragment).removePreference(KEY_APP_AND_CONTENT_ACCESS);
     }
 
-    @Ignore("b/313530297")
     @Test
     public void initialize_adminSelectsSecondaryUser_shouldShowRemovePreference() {
         setupSelectedUser();
@@ -328,7 +325,6 @@
         verify(mFragment, never()).removePreference(KEY_REMOVE_USER);
     }
 
-    @Ignore("b/313530297")
     @Test
     public void initialize_adminSelectsNewRestrictedUser_shouldOpenAppContentScreen() {
         setupSelectedRestrictedUser();
@@ -351,7 +347,6 @@
                 .isEqualTo(true);
     }
 
-    @Ignore("b/313530297")
     @Test
     public void initialize_adminSelectsRestrictedUser_shouldSetupPreferences() {
         setupSelectedRestrictedUser();
@@ -381,7 +376,6 @@
         verify(mActivity, never()).startActivity(any(Intent.class));
     }
 
-    @Ignore("b/313530297")
     @Test
     public void initialize_adminSelectsGuest_shouldShowRemovePreference() {
         setupSelectedGuest();
@@ -425,7 +419,6 @@
         verify(mFragment).removePreference(KEY_REMOVE_USER);
     }
 
-    @Ignore("b/313530297")
     @Test
     public void initialize_userHasCallRestriction_shouldSetPhoneSwitchUnChecked() {
         setupSelectedUser();
@@ -438,7 +431,6 @@
         verify(mPhonePref).setChecked(false);
     }
 
-    @Ignore("b/313530297")
     @Test
     public void initialize_noCallRestriction_shouldSetPhoneSwitchChecked() {
         setupSelectedUser();
@@ -537,7 +529,6 @@
         verify(mFragment, never()).switchUser();
     }
 
-    @Ignore("b/313530297")
     @Test
     public void onPreferenceClick_removeGuestClicked_canDelete_shouldShowDialog() {
         setupSelectedGuest();
@@ -555,7 +546,6 @@
         verify(mFragment).showDialog(DIALOG_CONFIRM_RESET_GUEST);
     }
 
-    @Ignore("b/313530297")
     @Test
     public void onPreferenceClick_removeRestrictedClicked_canDelete_shouldShowDialog() {
         setupSelectedRestrictedUser();
@@ -574,7 +564,6 @@
         verify(mFragment).showDialog(DIALOG_CONFIRM_REMOVE);
     }
 
-    @Ignore("b/313530297")
     @Test
     public void onPreferenceClick_removeClicked_canDelete_shouldShowDialog() {
         setupSelectedUser();
@@ -666,7 +655,6 @@
         assertThat(result).isFalse();
     }
 
-    @Ignore("b/313530297")
     @Test
     public void canDeleteUser_adminSelectsUser_noRestrictions_shouldReturnTrue() {
         setupSelectedUser();
@@ -700,17 +688,16 @@
         assertThat(result).isFalse();
     }
 
-    @Ignore("b/313530297")
     @Test
     public void initialize_userSelected_shouldShowGrantAdminPref_MultipleAdminEnabled() {
+        assumeTrue(UserManager.isHeadlessSystemUserMode());
         setupSelectedUser();
+        mUserManager.setIsAdminUser(true);
         ShadowUserManager.setIsMultipleAdminEnabled(true);
         mFragment.initialize(mActivity, mArguments);
-        assertTrue(UserManager.isMultipleAdminEnabled());
         verify(mFragment, never()).removePreference(KEY_GRANT_ADMIN);
     }
 
-    @Ignore("b/313530297")
     @Test
     public void initialize_userSelected_shouldNotShowGrantAdminPref() {
         setupSelectedUser();
@@ -718,7 +705,6 @@
         verify(mFragment).removePreference(KEY_GRANT_ADMIN);
     }
 
-    @Ignore("b/313530297")
     @Test
     public void initialize_restrictUserSelected_shouldNotShowGrantAdminPref_MultipleAdminEnabled() {
         setupSelectedUser();
@@ -729,7 +715,6 @@
         verify(mFragment).removePreference(KEY_GRANT_ADMIN);
     }
 
-    @Ignore("b/313530297")
     @Test
     public void initialize_mainUserSelected_shouldShowGrantAdminPref_MultipleAdminEnabled() {
         setupSelectedMainUser();
@@ -738,7 +723,6 @@
         verify(mFragment).removePreference(KEY_GRANT_ADMIN);
     }
 
-    @Ignore("b/313530297")
     @Test
     public void initialize_guestSelected_shouldNotShowGrantAdminPref_MultipleAdminEnabled() {
         setupSelectedGuest();
@@ -778,7 +762,7 @@
         mUserInfo = new UserInfo(1, "Tom", null,
                 UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED,
                 UserManager.USER_TYPE_FULL_SECONDARY);
-
+        mFragment.mUserInfo = mUserInfo;
         mUserManager.addProfile(mUserInfo);
     }
 
@@ -787,7 +771,7 @@
         mUserInfo = new UserInfo(11, "Jerry", null,
                 UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_MAIN,
                 UserManager.USER_TYPE_FULL_SECONDARY);
-
+        mFragment.mUserInfo = mUserInfo;
         mUserManager.addProfile(mUserInfo);
     }
 
@@ -796,7 +780,7 @@
         mUserInfo = new UserInfo(12, "Andy", null,
                 UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_ADMIN,
                 UserManager.USER_TYPE_FULL_SECONDARY);
-
+        mFragment.mUserInfo = mUserInfo;
         mUserManager.addProfile(mUserInfo);
     }
 
@@ -805,7 +789,7 @@
         mUserInfo = new UserInfo(23, "Guest", null,
                 UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_GUEST,
                 UserManager.USER_TYPE_FULL_GUEST);
-
+        mFragment.mUserInfo = mUserInfo;
         mUserManager.addProfile(mUserInfo);
     }
 
@@ -814,7 +798,7 @@
         mUserInfo = new UserInfo(21, "Bob", null,
                 UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_RESTRICTED,
                 UserManager.USER_TYPE_FULL_RESTRICTED);
-
+        mFragment.mUserInfo = mUserInfo;
         mUserManager.addProfile(mUserInfo);
     }
 }
diff --git a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java
index 4497a0a..4440bc9 100644
--- a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java
@@ -48,6 +48,8 @@
 import android.telephony.ims.ImsMmTelManager;
 import android.view.View;
 
+import androidx.annotation.NonNull;
+import androidx.lifecycle.LifecycleOwner;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
 
@@ -56,10 +58,14 @@
 import com.android.settings.SettingsActivity;
 import com.android.settings.network.ims.MockWifiCallingQueryImsState;
 import com.android.settings.network.ims.WifiCallingQueryImsState;
+import com.android.settings.network.telephony.wificalling.IWifiCallingRepository;
 import com.android.settings.testutils.shadow.ShadowFragment;
 import com.android.settings.widget.SettingsMainSwitchBar;
 import com.android.settings.widget.SettingsMainSwitchPreference;
 
+import kotlin.Unit;
+import kotlin.jvm.functions.Function1;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -183,36 +189,25 @@
     }
 
     @Test
-    public void onResume_provisioningAllowed_shouldNotFinish() {
-        // Call onResume while provisioning is allowed.
-        mFragment.onResume();
+    public void onViewCreated_provisioningAllowed_shouldNotFinish() {
+        // Call onViewCreated while provisioning is allowed.
+        mFragment.onViewCreated(mView, null);
 
         // Verify that finish() is not called.
         verify(mFragment, never()).finish();
     }
 
     @Test
-    public void onResume_provisioningDisallowed_shouldFinish() {
-        // Call onResume while provisioning is disallowed.
-        mQueryImsState.setIsProvisionedOnDevice(false);
-        mFragment.onResume();
+    public void onViewCreated_provisioningDisallowed_shouldFinish() {
+        // Call onViewCreated while provisioning is disallowed.
+        mFragment.mIsWifiCallingReady = false;
+        mFragment.onViewCreated(mView, null);
 
         // Verify that finish() is called
         verify(mFragment).finish();
     }
 
     @Test
-    public void onResumeOnPause_provisioningCallbackRegistration() throws Exception {
-        // Verify that provisioning callback is registered after call to onResume().
-        mFragment.onResume();
-        verify(mFragment).registerProvisioningChangedCallback();
-
-        // Verify that provisioning callback is unregistered after call to onPause.
-        mFragment.onPause();
-        verify(mFragment).unregisterProvisioningChangedCallback();
-    }
-
-    @Test
     public void onResume_useWfcHomeModeConfigFalseAndEditable_shouldShowWfcRoaming() {
         // Call onResume to update the WFC roaming preference.
         mFragment.onResume();
@@ -377,6 +372,7 @@
 
     protected class TestFragment extends WifiCallingSettingsForSub {
         private SettingsMainSwitchPreference mSwitchPref;
+        protected boolean mIsWifiCallingReady = true;
 
         protected void setSwitchBar(SettingsMainSwitchPreference switchPref) {
             mSwitchPref = switchPref;
@@ -422,6 +418,25 @@
         }
 
         @Override
+        @NonNull
+        IWifiCallingRepository getWifiCallingRepository() {
+            return new IWifiCallingRepository() {
+                @Override
+                public void collectIsWifiCallingReadyFlow(
+                        @NonNull LifecycleOwner lifecycleOwner,
+                        @NonNull Function1<? super Boolean, Unit> action) {
+                    action.invoke(mIsWifiCallingReady);
+                }
+            };
+        }
+
+        @NonNull
+        @Override
+        LifecycleOwner getLifecycleOwner() {
+            return this;
+        }
+
+        @Override
         void showAlert(Intent intent) {
         }
     }
diff --git a/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowUserManager.java b/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowUserManager.java
index 417b102..02ed03c 100644
--- a/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowUserManager.java
+++ b/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowUserManager.java
@@ -363,6 +363,10 @@
     }
 
     @Implementation
+    protected boolean isAdminUser() {
+        return getUserInfo(UserHandle.myUserId()).isAdmin();
+    }
+    @Implementation
     protected boolean isGuestUser() {
         return mIsGuestUser;
     }
diff --git a/tests/spa_unit/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogRepositoryTest.kt
index 01f32bf..1c1d9df 100644
--- a/tests/spa_unit/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogRepositoryTest.kt
@@ -17,65 +17,65 @@
 package com.android.settings.deviceinfo.simstatus
 
 import android.content.Context
-import android.os.PersistableBundle
 import android.telephony.CarrierConfigManager
 import androidx.lifecycle.testing.TestLifecycleOwner
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.settings.deviceinfo.simstatus.SimStatusDialogRepository.SimStatusDialogInfo
+import com.android.settings.network.telephony.CarrierConfigRepository
 import com.android.settings.network.telephony.SimSlotRepository
 import com.android.settings.network.telephony.ims.ImsMmTelRepository
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.runBlocking
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.kotlin.anyVararg
 import org.mockito.kotlin.doReturn
-import org.mockito.kotlin.eq
 import org.mockito.kotlin.mock
-import org.mockito.kotlin.spy
 
 @RunWith(AndroidJUnit4::class)
 class SimStatusDialogRepositoryTest {
 
-    private val carrierConfig = PersistableBundle().apply {
-        putBoolean(CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, true)
-    }
+    private val context: Context = ApplicationProvider.getApplicationContext()
 
-    private val mockCarrierConfigManager = mock<CarrierConfigManager> {
-        on { getConfigForSubId(eq(SUB_ID), anyVararg()) } doReturn carrierConfig
-    }
+    private val mockSimSlotRepository =
+        mock<SimSlotRepository> {
+            on { subIdInSimSlotFlow(SIM_SLOT_INDEX) } doReturn flowOf(SUB_ID)
+        }
 
-    private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
-        on { getSystemService(CarrierConfigManager::class.java) } doReturn mockCarrierConfigManager
-    }
+    private val mockSignalStrengthRepository =
+        mock<SignalStrengthRepository> {
+            on { signalStrengthDisplayFlow(SUB_ID) } doReturn flowOf(SIGNAL_STRENGTH)
+        }
 
-    private val mockSimSlotRepository = mock<SimSlotRepository> {
-        on { subIdInSimSlotFlow(SIM_SLOT_INDEX) } doReturn flowOf(SUB_ID)
-    }
+    private val mockImsMmTelRepository =
+        mock<ImsMmTelRepository> { on { imsRegisteredFlow() } doReturn flowOf(true) }
 
-    private val mockSignalStrengthRepository = mock<SignalStrengthRepository> {
-        on { signalStrengthDisplayFlow(SUB_ID) } doReturn flowOf(SIGNAL_STRENGTH)
-    }
+    private val controller =
+        SimStatusDialogRepository(
+            context = context,
+            simSlotRepository = mockSimSlotRepository,
+            signalStrengthRepository = mockSignalStrengthRepository,
+            imsMmTelRepositoryFactory = { subId ->
+                assertThat(subId).isEqualTo(SUB_ID)
+                mockImsMmTelRepository
+            },
+        )
 
-    private val mockImsMmTelRepository = mock<ImsMmTelRepository> {
-        on { imsRegisteredFlow() } doReturn flowOf(true)
+    @Before
+    fun setUp() {
+        CarrierConfigRepository.resetForTest()
     }
 
-    private val controller = SimStatusDialogRepository(
-        context = context,
-        simSlotRepository = mockSimSlotRepository,
-        signalStrengthRepository = mockSignalStrengthRepository,
-        imsMmTelRepositoryFactory = { subId ->
-            assertThat(subId).isEqualTo(SUB_ID)
-            mockImsMmTelRepository
-        },
-    )
-
     @Test
     fun collectSimStatusDialogInfo() = runBlocking {
+        CarrierConfigRepository.setBooleanForTest(
+            subId = SUB_ID,
+            key = CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL,
+            value = true,
+        )
         var simStatusDialogInfo = SimStatusDialogInfo()
 
         controller.collectSimStatusDialogInfo(TestLifecycleOwner(), SIM_SLOT_INDEX) {
@@ -83,19 +83,20 @@
         }
         delay(100)
 
-        assertThat(simStatusDialogInfo).isEqualTo(
-            SimStatusDialogInfo(
-                signalStrength = SIGNAL_STRENGTH,
-                imsRegistered = true,
-            )
-        )
+        assertThat(simStatusDialogInfo)
+            .isEqualTo(
+                SimStatusDialogInfo(
+                    signalStrength = SIGNAL_STRENGTH,
+                    imsRegistered = true,
+                ))
     }
 
     @Test
     fun collectSimStatusDialogInfo_doNotShowSignalStrength() = runBlocking {
-        carrierConfig.putBoolean(
-            CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL,
-            false
+        CarrierConfigRepository.setBooleanForTest(
+            subId = SUB_ID,
+            key = CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL,
+            value = false,
         )
         var simStatusDialogInfo = SimStatusDialogInfo()
 
@@ -109,7 +110,11 @@
 
     @Test
     fun collectSimStatusDialogInfo_doNotShowImsRegistration() = runBlocking {
-        carrierConfig.putBoolean(CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false)
+        CarrierConfigRepository.setBooleanForTest(
+            subId = SUB_ID,
+            key = CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL,
+            value = false,
+        )
         var simStatusDialogInfo = SimStatusDialogInfo()
 
         controller.collectSimStatusDialogInfo(TestLifecycleOwner(), SIM_SLOT_INDEX) {
diff --git a/tests/spa_unit/src/com/android/settings/network/MobileDataEnabledFlowTest.kt b/tests/spa_unit/src/com/android/settings/network/MobileDataEnabledFlowTest.kt
deleted file mode 100644
index c4611ac..0000000
--- a/tests/spa_unit/src/com/android/settings/network/MobileDataEnabledFlowTest.kt
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.network
-
-import android.content.Context
-import android.provider.Settings
-import android.telephony.SubscriptionManager
-import androidx.test.core.app.ApplicationProvider
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
-import com.android.settingslib.spa.testutils.toListWithTimeout
-import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalBoolean
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.async
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.runBlocking
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-class MobileDataEnabledFlowTest {
-    private val context: Context = ApplicationProvider.getApplicationContext()
-
-    @Test
-    fun mobileDataEnabledFlow_notified(): Unit = runBlocking {
-        val flow = context.mobileDataEnabledFlow(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
-
-        assertThat(flow.firstWithTimeoutOrNull()).isNotNull()
-    }
-
-    @Test
-    fun mobileDataEnabledFlow_changed_notified(): Unit = runBlocking {
-        var mobileDataEnabled by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA)
-        mobileDataEnabled = false
-
-        val flow = context.mobileDataEnabledFlow(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
-        mobileDataEnabled = true
-
-        assertThat(flow.firstWithTimeoutOrNull()).isNotNull()
-    }
-
-    @Test
-    fun mobileDataEnabledFlow_forSubIdNotChanged(): Unit = runBlocking {
-        var mobileDataEnabled by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA)
-        mobileDataEnabled = false
-        var mobileDataEnabledForSubId
-            by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA + SUB_ID)
-        mobileDataEnabledForSubId = false
-
-        val listDeferred = async {
-            context.mobileDataEnabledFlow(SUB_ID).toListWithTimeout()
-        }
-
-        assertThat(listDeferred.await()).hasSize(1)
-    }
-
-    @Test
-    fun mobileDataEnabledFlow_forSubIdChanged(): Unit = runBlocking {
-        var mobileDataEnabled by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA)
-        mobileDataEnabled = false
-        var mobileDataEnabledForSubId
-            by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA + SUB_ID)
-        mobileDataEnabledForSubId = false
-
-        val listDeferred = async {
-            context.mobileDataEnabledFlow(SUB_ID).toListWithTimeout()
-        }
-        delay(100)
-        mobileDataEnabledForSubId = true
-
-        assertThat(listDeferred.await().size).isAtLeast(2)
-    }
-
-    private companion object {
-        const val SUB_ID = 123
-    }
-}
diff --git a/tests/spa_unit/src/com/android/settings/network/apn/ApnEditCarrierEnabledTest.kt b/tests/spa_unit/src/com/android/settings/network/apn/ApnEditCarrierEnabledTest.kt
new file mode 100644
index 0000000..bd97482
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/apn/ApnEditCarrierEnabledTest.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.apn
+
+import android.content.Context
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.assertIsOff
+import androidx.compose.ui.test.assertIsOn
+import androidx.compose.ui.test.junit4.ComposeTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.stub
+
+@RunWith(AndroidJUnit4::class)
+class ApnEditCarrierEnabledTest {
+
+    @get:Rule val composeTestRule = createComposeRule()
+
+    private val context: Context = spy(ApplicationProvider.getApplicationContext()) {}
+
+    private val resources = spy(context.resources) {}
+
+    @Before
+    fun setUp() {
+        context.stub { on { resources } doReturn resources }
+    }
+
+    @Test
+    fun carrierEnabled_displayed() {
+        composeTestRule.setContent { ApnEditCarrierEnabled(ApnData()) {} }
+
+        composeTestRule.onCarrierEnabled().assertIsDisplayed()
+    }
+
+    @Test
+    fun carrierEnabled_isChecked() {
+        val apnData = ApnData(carrierEnabled = true)
+
+        composeTestRule.setContent { ApnEditCarrierEnabled(apnData) {} }
+
+        composeTestRule.onCarrierEnabled().assertIsOn()
+    }
+
+    @Test
+    fun carrierEnabled_allowEdit_checkChanged() {
+        resources.stub { on { getBoolean(R.bool.config_allow_edit_carrier_enabled) } doReturn true }
+        var apnData by mutableStateOf(ApnData(carrierEnabled = true))
+        composeTestRule.setContent {
+            CompositionLocalProvider(LocalContext provides context) {
+                ApnEditCarrierEnabled(apnData) { apnData = apnData.copy(carrierEnabled = it) }
+            }
+        }
+
+        composeTestRule.onCarrierEnabled().performClick()
+
+        composeTestRule.onCarrierEnabled().assertIsEnabled().assertIsOff()
+    }
+
+    @Test
+    fun carrierEnabled_notAllowEdit_checkNotChanged() {
+        resources.stub {
+            on { getBoolean(R.bool.config_allow_edit_carrier_enabled) } doReturn false
+        }
+        var apnData by mutableStateOf(ApnData(carrierEnabled = true))
+        composeTestRule.setContent {
+            CompositionLocalProvider(LocalContext provides context) {
+                ApnEditCarrierEnabled(apnData) { apnData = apnData.copy(carrierEnabled = it) }
+            }
+        }
+
+        composeTestRule.onCarrierEnabled().performClick()
+
+        composeTestRule.onCarrierEnabled().assertIsNotEnabled().assertIsOn()
+    }
+
+    private fun ComposeTestRule.onCarrierEnabled() =
+        onNodeWithText(context.getString(R.string.carrier_enabled))
+}
diff --git a/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt
index 3621948..d310604 100644
--- a/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt
@@ -21,24 +21,17 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.assertIsOff
-import androidx.compose.ui.test.assertIsOn
 import androidx.compose.ui.test.hasText
-import androidx.compose.ui.test.isFocused
 import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onAllNodesWithText
 import androidx.compose.ui.test.onChild
 import androidx.compose.ui.test.onChildAt
-import androidx.compose.ui.test.onLast
 import androidx.compose.ui.test.onNodeWithText
 import androidx.compose.ui.test.onRoot
-import androidx.compose.ui.test.performClick
 import androidx.compose.ui.test.performScrollToNode
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.settings.R
 import com.google.common.truth.Truth
-import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -55,7 +48,6 @@
     private val port = "port"
     private val apnType = context.resources.getString(R.string.apn_type)
     private val apnRoaming = "IPv4"
-    private val apnEnable = context.resources.getString(R.string.carrier_enabled)
     private val apnProtocolOptions =
         context.resources.getStringArray(R.array.apn_protocol_entries).toList()
     private val passwordTitle = context.resources.getString(R.string.apn_password)
@@ -65,7 +57,6 @@
         port = port,
         apnType = apnType,
         apnRoaming = apnProtocolOptions.indexOf(apnRoaming),
-        apnEnable = true
     )
     private val apnData = mutableStateOf(
         apnInit
@@ -133,39 +124,6 @@
         composeTestRule.onNodeWithText(apnRoaming, true).assertIsDisplayed()
     }
 
-    @Ignore("b/342374681")
-    @Test
-    fun carrier_enabled_displayed() {
-        composeTestRule.setContent {
-            ApnPage(apnInit, remember { apnData }, uri)
-        }
-        composeTestRule.onRoot().onChild().onChildAt(0)
-            .performScrollToNode(hasText(apnEnable, true))
-        composeTestRule.onNodeWithText(apnEnable, true).assertIsDisplayed()
-    }
-
-    @Test
-    fun carrier_enabled_isChecked() {
-        composeTestRule.setContent {
-            ApnPage(apnInit, remember { apnData }, uri)
-        }
-        composeTestRule.onRoot().onChild().onChildAt(0)
-            .performScrollToNode(hasText(apnEnable, true))
-        composeTestRule.onNodeWithText(apnEnable, true).assertIsOn()
-    }
-
-    @Ignore("b/342374681")
-    @Test
-    fun carrier_enabled_checkChanged() {
-        composeTestRule.setContent {
-            ApnPage(apnInit, remember { apnData }, uri)
-        }
-        composeTestRule.onRoot().onChild().onChildAt(0)
-            .performScrollToNode(hasText(apnEnable, true))
-        composeTestRule.onNodeWithText(apnEnable, true).performClick()
-        composeTestRule.onNodeWithText(apnEnable, true).assertIsOff()
-    }
-
     @Test
     fun password_displayed() {
         composeTestRule.setContent {
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/CarrierConfigRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/CarrierConfigRepositoryTest.kt
new file mode 100644
index 0000000..8c54751
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/CarrierConfigRepositoryTest.kt
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.telephony
+
+import android.content.Context
+import android.telephony.CarrierConfigManager
+import androidx.core.os.persistableBundleOf
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.anyVararg
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.doThrow
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+
+@RunWith(AndroidJUnit4::class)
+class CarrierConfigRepositoryTest {
+
+    private val mockCarrierConfigManager = mock<CarrierConfigManager>()
+
+    private val context =
+        mock<Context> {
+            on { applicationContext } doReturn mock
+            on { getSystemService(CarrierConfigManager::class.java) } doReturn
+                mockCarrierConfigManager
+        }
+
+    private val repository = CarrierConfigRepository(context)
+
+    @Before
+    fun setUp() {
+        CarrierConfigRepository.resetForTest()
+    }
+
+    @Test
+    fun getBoolean_returnValue() {
+        val key = CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL
+        mockCarrierConfigManager.stub {
+            on { getConfigForSubId(any(), eq(key)) } doReturn persistableBundleOf(key to true)
+        }
+
+        val value = repository.getBoolean(SUB_ID, key)
+
+        assertThat(value).isTrue()
+    }
+
+    @Test
+    fun getInt_returnValue() {
+        val key = CarrierConfigManager.KEY_GBA_MODE_INT
+        mockCarrierConfigManager.stub {
+            on { getConfigForSubId(any(), eq(key)) } doReturn persistableBundleOf(key to 99)
+        }
+
+        val value = repository.getInt(SUB_ID, key)
+
+        assertThat(value).isEqualTo(99)
+    }
+
+    @Test
+    fun getString_returnValue() {
+        val key = CarrierConfigManager.KEY_CARRIER_NAME_STRING
+        mockCarrierConfigManager.stub {
+            on { getConfigForSubId(any(), eq(key)) } doReturn
+                persistableBundleOf(key to STRING_VALUE)
+        }
+
+        val value = repository.getString(SUB_ID, key)
+
+        assertThat(value).isEqualTo(STRING_VALUE)
+    }
+
+    @Test
+    fun transformConfig_managerThrowIllegalStateException_returnDefaultValue() {
+        mockCarrierConfigManager.stub {
+            on { getConfigForSubId(any(), anyVararg()) } doThrow IllegalStateException()
+        }
+
+        val carrierName =
+            repository.transformConfig(SUB_ID) {
+                getInt(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT)
+            }
+
+        assertThat(carrierName)
+            .isEqualTo(
+                CarrierConfigManager.getDefaultConfig()
+                    .getInt(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT))
+    }
+
+    @Test
+    fun transformConfig_getValueTwice_cached() {
+        val key = CarrierConfigManager.KEY_CARRIER_NAME_STRING
+        mockCarrierConfigManager.stub {
+            on { getConfigForSubId(any(), eq(key)) } doReturn
+                persistableBundleOf(key to STRING_VALUE)
+        }
+
+        repository.transformConfig(SUB_ID) { getString(key) }
+        repository.transformConfig(SUB_ID) { getString(key) }
+
+        verify(mockCarrierConfigManager, times(1)).getConfigForSubId(any(), anyVararg())
+    }
+
+    @Test
+    fun transformConfig_registerCarrierConfigChangeListener() {
+        val key = CarrierConfigManager.KEY_CARRIER_NAME_STRING
+
+        repository.transformConfig(SUB_ID) { getString(key) }
+        repository.transformConfig(SUB_ID) { getString(key) }
+
+        verify(mockCarrierConfigManager, times(1)).registerCarrierConfigChangeListener(any(), any())
+    }
+
+    private companion object {
+        const val SUB_ID = 123
+        const val STRING_VALUE = "value"
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/MobileDataRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/MobileDataRepositoryTest.kt
new file mode 100644
index 0000000..fc762fa
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/MobileDataRepositoryTest.kt
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.telephony
+
+import android.content.Context
+import android.provider.Settings
+import android.telephony.SubscriptionManager
+import android.telephony.TelephonyManager
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
+import com.android.settingslib.spa.testutils.toListWithTimeout
+import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalBoolean
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.async
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.stub
+import org.mockito.kotlin.verify
+
+@RunWith(AndroidJUnit4::class)
+class MobileDataRepositoryTest {
+    private val mockTelephonyManager =
+        mock<TelephonyManager> { on { createForSubscriptionId(SUB_ID) } doReturn mock }
+
+    private val context: Context =
+        spy(ApplicationProvider.getApplicationContext()) {
+            on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
+        }
+
+    private val repository = MobileDataRepository(context, flowOf(Unit))
+
+    @Test
+    fun isMobileDataPolicyEnabledFlow_invalidSub_returnFalse() = runBlocking {
+        val flow =
+            repository.isMobileDataPolicyEnabledFlow(
+                subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+                policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
+            )
+
+        assertThat(flow.firstWithTimeoutOrNull()).isFalse()
+    }
+
+    @Test
+    fun isMobileDataPolicyEnabledFlow_validSub_returnPolicyState() = runBlocking {
+        mockTelephonyManager.stub {
+            on {
+                isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)
+            } doReturn true
+        }
+
+        val flow =
+            repository.isMobileDataPolicyEnabledFlow(
+                subId = SUB_ID,
+                policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
+            )
+
+        assertThat(flow.firstWithTimeoutOrNull()).isTrue()
+    }
+
+    @Test
+    fun setMobileDataPolicyEnabled() = runBlocking {
+        repository.setMobileDataPolicyEnabled(
+            subId = SUB_ID,
+            policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
+            enabled = true)
+
+        verify(mockTelephonyManager)
+            .setMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, true)
+    }
+
+    @Test
+    fun mobileDataEnabledChangedFlow_notified(): Unit = runBlocking {
+        val flow =
+            repository.mobileDataEnabledChangedFlow(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+
+        assertThat(flow.firstWithTimeoutOrNull()).isNotNull()
+    }
+
+    @Test
+    fun mobileDataEnabledChangedFlow_changed_notified(): Unit = runBlocking {
+        var mobileDataEnabled by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA)
+        mobileDataEnabled = false
+
+        val flow =
+            repository.mobileDataEnabledChangedFlow(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+        mobileDataEnabled = true
+
+        assertThat(flow.firstWithTimeoutOrNull()).isNotNull()
+    }
+
+    @Test
+    fun mobileDataEnabledChangedFlow_forSubIdNotChanged(): Unit = runBlocking {
+        var mobileDataEnabled by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA)
+        mobileDataEnabled = false
+        var mobileDataEnabledForSubId by
+            context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA + SUB_ID)
+        mobileDataEnabledForSubId = false
+
+        val listDeferred = async {
+            repository.mobileDataEnabledChangedFlow(SUB_ID).toListWithTimeout()
+        }
+
+        assertThat(listDeferred.await()).hasSize(1)
+    }
+
+    @Test
+    fun mobileDataEnabledChangedFlow_forSubIdChanged(): Unit = runBlocking {
+        var mobileDataEnabled by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA)
+        mobileDataEnabled = false
+        var mobileDataEnabledForSubId by
+            context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA + SUB_ID)
+        mobileDataEnabledForSubId = false
+
+        val listDeferred = async {
+            repository.mobileDataEnabledChangedFlow(SUB_ID).toListWithTimeout()
+        }
+        delay(100)
+        mobileDataEnabledForSubId = true
+
+        assertThat(listDeferred.await().size).isAtLeast(2)
+    }
+
+    @Test
+    fun isMobileDataEnabledFlow_invalidSub_returnFalse() = runBlocking {
+        val state =
+            repository.isMobileDataEnabledFlow(
+                subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+            )
+
+        assertThat(state.firstWithTimeoutOrNull()).isFalse()
+    }
+
+    @Test
+    fun isMobileDataEnabledFlow_validSub_returnPolicyState() = runBlocking {
+        mockTelephonyManager.stub {
+            on { isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER) } doReturn true
+        }
+
+        val state = repository.isMobileDataEnabledFlow(subId = SUB_ID)
+
+        assertThat(state.firstWithTimeoutOrNull()).isTrue()
+    }
+
+    @Test
+    fun isDataRoamingEnabledFlow_invalidSub_returnFalse() = runBlocking {
+        val isDataRoamingEnabled =
+            repository
+                .isDataRoamingEnabledFlow(subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+                .firstWithTimeoutOrNull()
+
+        assertThat(isDataRoamingEnabled).isFalse()
+    }
+
+    @Test
+    fun isDataRoamingEnabledFlow_validSub_returnCurrentValue() = runBlocking {
+        mockTelephonyManager.stub { on { isDataRoamingEnabled } doReturn true }
+
+        val isDataRoamingEnabled =
+            repository.isDataRoamingEnabledFlow(subId = SUB_ID).firstWithTimeoutOrNull()
+
+        assertThat(isDataRoamingEnabled).isTrue()
+    }
+
+    private companion object {
+        const val SUB_ID = 123
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/RoamingPreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/RoamingPreferenceControllerTest.kt
new file mode 100644
index 0000000..ee4cff6
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/RoamingPreferenceControllerTest.kt
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.telephony
+
+import android.content.Context
+import android.telephony.CarrierConfigManager
+import android.telephony.SubscriptionManager
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsOff
+import androidx.compose.ui.test.assertIsOn
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.fragment.app.FragmentManager
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.android.settings.core.BasePreferenceController.AVAILABLE
+import com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.flowOf
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
+
+@RunWith(AndroidJUnit4::class)
+class RoamingPreferenceControllerTest {
+    @get:Rule val composeTestRule = createComposeRule()
+
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    private val mockMobileDataRepository =
+        mock<MobileDataRepository> {
+            on { isDataRoamingEnabledFlow(SUB_ID) } doReturn flowOf(false)
+        }
+
+    private val controller =
+        RoamingPreferenceController(context, TEST_KEY, mockMobileDataRepository)
+
+    @Before
+    fun setUp() {
+        CarrierConfigRepository.resetForTest()
+    }
+
+    @Test
+    fun getAvailabilityStatus_validSubId_returnAvailable() {
+        controller.init(mock<FragmentManager>(), SUB_ID)
+
+        val availabilityStatus = controller.getAvailabilityStatus()
+
+        assertThat(availabilityStatus).isEqualTo(AVAILABLE)
+    }
+
+    @Test
+    fun getAvailabilityStatus_invalidSubId_returnConditionallyUnavailable() {
+        controller.init(mock<FragmentManager>(), SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+
+        val availabilityStatus = controller.getAvailabilityStatus()
+
+        assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE)
+    }
+
+    @Test
+    fun getAvailabilityStatus_forceHomeNetworkIsTrue_returnConditionallyUnavailable() {
+        CarrierConfigRepository.setBooleanForTest(
+            subId = SUB_ID,
+            key = CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL,
+            value = true,
+        )
+        controller.init(mock<FragmentManager>(), SUB_ID)
+
+        val availabilityStatus = controller.getAvailabilityStatus()
+
+        assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE)
+    }
+
+    @Test
+    fun getAvailabilityStatus_forceHomeNetworkIsFalse_returnAvailable() {
+        CarrierConfigRepository.setBooleanForTest(
+            subId = SUB_ID,
+            key = CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL,
+            value = false,
+        )
+        controller.init(mock<FragmentManager>(), SUB_ID)
+
+        val availabilityStatus = controller.getAvailabilityStatus()
+
+        assertThat(availabilityStatus).isEqualTo(AVAILABLE)
+    }
+
+    @Test
+    fun title_displayed() {
+        controller.init(mock<FragmentManager>(), SUB_ID)
+
+        composeTestRule.setContent {
+            CompositionLocalProvider(LocalContext provides context) { controller.Content() }
+        }
+
+        composeTestRule.onNodeWithText(context.getString(R.string.roaming)).assertIsDisplayed()
+    }
+
+    @Test
+    fun summary_displayed() {
+        controller.init(mock<FragmentManager>(), SUB_ID)
+
+        composeTestRule.setContent {
+            CompositionLocalProvider(LocalContext provides context) { controller.Content() }
+        }
+
+        composeTestRule
+            .onNodeWithText(context.getString(R.string.roaming_enable))
+            .assertIsDisplayed()
+    }
+
+    @Test
+    fun isDialogNeeded_enableChargeIndication_returnTrue() {
+        CarrierConfigRepository.setBooleanForTest(
+            subId = SUB_ID,
+            key = CarrierConfigManager.KEY_DISABLE_CHARGE_INDICATION_BOOL,
+            value = false,
+        )
+        controller.init(mock<FragmentManager>(), SUB_ID)
+
+        val isDialogNeeded = controller.isDialogNeeded()
+
+        assertThat(isDialogNeeded).isTrue()
+    }
+
+    @Test
+    fun isDialogNeeded_disableChargeIndication_returnFalse() {
+        CarrierConfigRepository.setBooleanForTest(
+            subId = SUB_ID,
+            key = CarrierConfigManager.KEY_DISABLE_CHARGE_INDICATION_BOOL,
+            value = true,
+        )
+        controller.init(mock<FragmentManager>(), SUB_ID)
+
+        val isDialogNeeded = controller.isDialogNeeded()
+
+        assertThat(isDialogNeeded).isFalse()
+    }
+
+    @Test
+    fun checked_roamingEnabled_isOn() {
+        mockMobileDataRepository.stub {
+            on { isDataRoamingEnabledFlow(SUB_ID) } doReturn flowOf(true)
+        }
+        controller.init(mock<FragmentManager>(), SUB_ID)
+
+        composeTestRule.setContent {
+            CompositionLocalProvider(LocalContext provides context) { controller.Content() }
+        }
+
+        composeTestRule.onNodeWithText(context.getString(R.string.roaming)).assertIsOn()
+    }
+
+    @Test
+    fun checked_roamingDisabled_isOff() {
+        mockMobileDataRepository.stub {
+            on { isDataRoamingEnabledFlow(SUB_ID) } doReturn flowOf(false)
+        }
+        controller.init(mock<FragmentManager>(), SUB_ID)
+
+        composeTestRule.setContent {
+            CompositionLocalProvider(LocalContext provides context) { controller.Content() }
+        }
+
+        composeTestRule.onNodeWithText(context.getString(R.string.roaming)).assertIsOff()
+    }
+
+    private companion object {
+        const val TEST_KEY = "test_key"
+        const val SUB_ID = 2
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyRepositoryTest.kt
index 65e8c47..12791b8 100644
--- a/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyRepositoryTest.kt
@@ -17,14 +17,12 @@
 package com.android.settings.network.telephony
 
 import android.content.Context
-import android.telephony.SubscriptionManager
 import android.telephony.TelephonyCallback
 import android.telephony.TelephonyManager
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.runBlocking
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -33,91 +31,29 @@
 import org.mockito.kotlin.doReturn
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.spy
-import org.mockito.kotlin.stub
 import org.mockito.kotlin.verify
 
 @RunWith(AndroidJUnit4::class)
 class TelephonyRepositoryTest {
     private var telephonyCallback: TelephonyCallback? = null
 
-    private val mockTelephonyManager = mock<TelephonyManager> {
-        on { createForSubscriptionId(SUB_ID) } doReturn mock
-        on { registerTelephonyCallback(any(), any()) } doAnswer {
-            telephonyCallback = it.arguments[1] as TelephonyCallback
-        }
-    }
-
-    private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
-        on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
-    }
-
-    private val repository = TelephonyRepository(context, flowOf(Unit))
-
-    @Test
-    fun isMobileDataPolicyEnabledFlow_invalidSub_returnFalse() = runBlocking {
-        val flow = repository.isMobileDataPolicyEnabledFlow(
-            subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID,
-            policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
-        )
-
-        assertThat(flow.firstWithTimeoutOrNull()).isFalse()
-    }
-
-    @Test
-    fun isMobileDataPolicyEnabledFlow_validSub_returnPolicyState() = runBlocking {
-        mockTelephonyManager.stub {
-            on {
-                isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)
-            } doReturn true
+    private val mockTelephonyManager =
+        mock<TelephonyManager> {
+            on { createForSubscriptionId(SUB_ID) } doReturn mock
+            on { registerTelephonyCallback(any(), any()) } doAnswer
+                {
+                    telephonyCallback = it.arguments[1] as TelephonyCallback
+                }
         }
 
-        val flow = repository.isMobileDataPolicyEnabledFlow(
-            subId = SUB_ID,
-            policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
-        )
-
-        assertThat(flow.firstWithTimeoutOrNull()).isTrue()
-    }
-
-    @Test
-    fun setMobileDataPolicyEnabled() = runBlocking {
-        repository.setMobileDataPolicyEnabled(
-            subId = SUB_ID,
-            policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
-            enabled = true
-        )
-
-        verify(mockTelephonyManager)
-            .setMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, true)
-    }
-
-    @Test
-    fun isDataEnabled_invalidSub_returnFalse() = runBlocking {
-        val state = repository.isDataEnabledFlow(
-            subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID,
-        )
-
-        assertThat(state.firstWithTimeoutOrNull()).isFalse()
-    }
-
-    @Test
-    fun isDataEnabled_validSub_returnPolicyState() = runBlocking {
-        mockTelephonyManager.stub {
-            on {
-                isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER)
-            } doReturn true
+    private val context: Context =
+        spy(ApplicationProvider.getApplicationContext()) {
+            on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
         }
 
-        val state = repository.isDataEnabledFlow(subId = SUB_ID)
-
-        assertThat(state.firstWithTimeoutOrNull()).isTrue()
-    }
-
     @Test
     fun telephonyCallbackFlow_callbackRegistered() = runBlocking {
-        val flow = context.telephonyCallbackFlow<Unit>(SUB_ID) {
-            object : TelephonyCallback() {}
-        }
+        val flow = context.telephonyCallbackFlow<Unit>(SUB_ID) { object : TelephonyCallback() {} }
 
         flow.firstWithTimeoutOrNull()
 
@@ -126,9 +62,7 @@
 
     @Test
     fun telephonyCallbackFlow_callbackUnregistered() = runBlocking {
-        val flow = context.telephonyCallbackFlow<Unit>(SUB_ID) {
-            object : TelephonyCallback() {}
-        }
+        val flow = context.telephonyCallbackFlow<Unit>(SUB_ID) { object : TelephonyCallback() {} }
 
         flow.firstWithTimeoutOrNull()
 
diff --git a/tests/spa_unit/src/com/android/settings/print/PrintSettingsPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/print/PrintSettingsPageProviderTest.kt
index 746816b..2571406 100644
--- a/tests/spa_unit/src/com/android/settings/print/PrintSettingsPageProviderTest.kt
+++ b/tests/spa_unit/src/com/android/settings/print/PrintSettingsPageProviderTest.kt
@@ -17,6 +17,7 @@
 package com.android.settings.print
 
 import android.content.Context
+import android.net.Uri
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.test.isDisplayed
@@ -31,7 +32,9 @@
 import com.android.settings.print.PrintSettingsFragment.EXTRA_CHECKED
 import com.android.settings.print.PrintSettingsFragment.EXTRA_SERVICE_COMPONENT_NAME
 import com.android.settings.print.PrintSettingsFragment.EXTRA_TITLE
+import com.android.settings.print.PrintSettingsPageProvider.AddPrintService
 import com.android.settings.print.PrintSettingsPageProvider.PrintService
+import kotlinx.coroutines.flow.flowOf
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -44,35 +47,32 @@
 
 @RunWith(AndroidJUnit4::class)
 class PrintSettingsPageProviderTest {
-    @get:Rule
-    val composeTestRule = createComposeRule()
+    @get:Rule val composeTestRule = createComposeRule()
 
-    private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
-        doNothing().whenever(mock).startActivity(any())
-    }
+    private val context: Context =
+        spy(ApplicationProvider.getApplicationContext()) {
+            doNothing().whenever(mock).startActivity(any())
+        }
 
-    private val displayInfo = PrintServiceDisplayInfo(
-        title = TITLE,
-        isEnabled = true,
-        summary = SUMMARY,
-        icon = context.getDrawable(R.drawable.ic_settings_print)!!,
-        componentName = "ComponentName",
-    )
+    private val displayInfo =
+        PrintServiceDisplayInfo(
+            title = TITLE,
+            isEnabled = true,
+            summary = SUMMARY,
+            icon = context.getDrawable(R.drawable.ic_settings_print)!!,
+            componentName = "ComponentName",
+        )
 
     @Test
     fun printService_titleDisplayed() {
-        composeTestRule.setContent {
-            PrintService(displayInfo)
-        }
+        composeTestRule.setContent { PrintService(displayInfo) }
 
         composeTestRule.onNodeWithText(TITLE).isDisplayed()
     }
 
     @Test
     fun printService_summaryDisplayed() {
-        composeTestRule.setContent {
-            PrintService(displayInfo)
-        }
+        composeTestRule.setContent { PrintService(displayInfo) }
 
         composeTestRule.onNodeWithText(SUMMARY).isDisplayed()
     }
@@ -80,25 +80,43 @@
     @Test
     fun printService_onClick() {
         composeTestRule.setContent {
-            CompositionLocalProvider(LocalContext provides context) {
-                PrintService(displayInfo)
-            }
+            CompositionLocalProvider(LocalContext provides context) { PrintService(displayInfo) }
         }
 
         composeTestRule.onNodeWithText(TITLE).performClick()
 
-        verify(context).startActivity(argThat {
-            val fragment = getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)
-            val arguments = getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS)!!
-            fragment == PrintServiceSettingsFragment::class.qualifiedName &&
-                arguments.getBoolean(EXTRA_CHECKED) == displayInfo.isEnabled &&
-                arguments.getString(EXTRA_TITLE) == displayInfo.title &&
-                arguments.getString(EXTRA_SERVICE_COMPONENT_NAME) == displayInfo.componentName
-        })
+        verify(context)
+            .startActivity(
+                argThat {
+                    val fragment = getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)
+                    val arguments = getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS)!!
+                    fragment == PrintServiceSettingsFragment::class.qualifiedName &&
+                        arguments.getBoolean(EXTRA_CHECKED) == displayInfo.isEnabled &&
+                        arguments.getString(EXTRA_TITLE) == displayInfo.title &&
+                        arguments.getString(EXTRA_SERVICE_COMPONENT_NAME) ==
+                            displayInfo.componentName
+                }
+            )
+    }
+
+    @Test
+    fun addPrintService_onClick() {
+        composeTestRule.setContent {
+            CompositionLocalProvider(LocalContext provides context) {
+                AddPrintService(flowOf(SEARCH_URI))
+            }
+        }
+
+        composeTestRule
+            .onNodeWithText(context.getString(R.string.print_menu_item_add_service))
+            .performClick()
+
+        verify(context).startActivity(argThat { data == Uri.parse(SEARCH_URI) })
     }
 
     private companion object {
         const val TITLE = "Title"
         const val SUMMARY = "Summary"
+        const val SEARCH_URI = "search.uri"
     }
 }
diff --git a/tests/unit/src/com/android/settings/accessibility/ReduceBrightColorsPreferenceControllerTest.java b/tests/unit/src/com/android/settings/accessibility/ReduceBrightColorsPreferenceControllerTest.java
index e1c0277..7229996 100644
--- a/tests/unit/src/com/android/settings/accessibility/ReduceBrightColorsPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/accessibility/ReduceBrightColorsPreferenceControllerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.accessibility;
 
+import static com.android.internal.accessibility.AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_TILE_SERVICE_COMPONENT_NAME;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.doReturn;
@@ -24,7 +26,12 @@
 
 import android.content.Context;
 import android.content.res.Resources;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.provider.Settings;
+import android.view.accessibility.Flags;
 
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -33,6 +40,7 @@
 
 import org.junit.Before;
 import org.junit.Ignore;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -40,6 +48,8 @@
 public class ReduceBrightColorsPreferenceControllerTest {
     private static final String PREF_KEY = "rbc_preference";
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
     private Context mContext;
     private Resources mResources;;
     private ReduceBrightColorsPreferenceController mController;
@@ -88,6 +98,20 @@
         assertThat(mController.isAvailable()).isFalse();
     }
 
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_A11Y_QS_SHORTCUT)
+    public void getTileComponentName_a11yQsFlagOff_returnComponentName() {
+        assertThat(mController.getTileComponentName())
+                .isEqualTo(REDUCE_BRIGHT_COLORS_TILE_SERVICE_COMPONENT_NAME);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_A11Y_QS_SHORTCUT)
+    public void getTileComponentName_a11yQsFlagOff_returnNull() {
+        assertThat(mController.getTileComponentName()).isNull();
+    }
+
     private int resourceId(String type, String name) {
         return mContext.getResources().getIdentifier(name, type, mContext.getPackageName());
     }
diff --git a/tests/unit/src/com/android/settings/network/ResetNetworkOperationBuilderTest.java b/tests/unit/src/com/android/settings/network/ResetNetworkOperationBuilderTest.java
index 5f54406..7f1c475 100644
--- a/tests/unit/src/com/android/settings/network/ResetNetworkOperationBuilderTest.java
+++ b/tests/unit/src/com/android/settings/network/ResetNetworkOperationBuilderTest.java
@@ -16,20 +16,16 @@
 
 package com.android.settings.network;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
-import android.content.ContentProvider;
-import android.content.ContentResolver;
+import android.content.ContentProviderClient;
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.NetworkPolicyManager;
@@ -67,7 +63,7 @@
     @Mock
     private NetworkPolicyManager mNetworkPolicyManager;
     @Mock
-    private ContentProvider mContentProvider;;
+    private ContentProviderClient mContentProviderClient;
 
 
     private Context mContext;
@@ -77,9 +73,8 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mContext = spy(ApplicationProvider.getApplicationContext());
-        doReturn(ContentResolver.wrap(mContentProvider)).when(mContext).getContentResolver();
-
         mBuilder = spy(new ResetNetworkOperationBuilder(mContext));
+        doReturn(mContentProviderClient).when(mBuilder).getUnstableTelephonyContentProviderClient();
     }
 
     @Test
@@ -184,38 +179,38 @@
     }
 
     @Test
-    public void restartPhoneProcess_withoutTelephonyContentProvider_shouldNotCrash() {
-        doThrow(new IllegalArgumentException()).when(mContentProvider).call(
-                anyString(), anyString(), anyString(), any());
+    public void restartPhoneProcess_withoutTelephonyContentProvider_shouldNotCrash()
+            throws Exception {
+        doReturn(null).when(mBuilder).getUnstableTelephonyContentProviderClient();
 
         mBuilder.restartPhoneProcess().build().run();
     }
 
     @Test
-    public void restartRild_withoutTelephonyContentProvider_shouldNotCrash() {
-        doThrow(new IllegalArgumentException()).when(mContentProvider).call(
-                anyString(), anyString(), anyString(), any());
+    public void restartRild_withoutTelephonyContentProvider_shouldNotCrash()
+            throws Exception {
+        doReturn(null).when(mBuilder).getUnstableTelephonyContentProviderClient();
 
         mBuilder.restartRild().build().run();
     }
 
     @Test
-    public void restartPhoneProcess_withTelephonyContentProvider_shouldCallRestartPhoneProcess() {
+    public void restartPhoneProcess_withTelephonyContentProvider_shouldCallRestartPhoneProcess()
+            throws Exception {
         mBuilder.restartPhoneProcess().build().run();
 
-        verify(mContentProvider).call(
-                eq(mBuilder.getResetTelephonyContentProviderAuthority()),
+        verify(mContentProviderClient).call(
                 eq(ResetNetworkOperationBuilder.METHOD_RESTART_PHONE_PROCESS),
                 isNull(),
                 isNull());
     }
 
     @Test
-    public void restartRild_withTelephonyContentProvider_shouldCallRestartRild() {
+    public void restartRild_withTelephonyContentProvider_shouldCallRestartRild()
+            throws Exception {
         mBuilder.restartRild().build().run();
 
-        verify(mContentProvider).call(
-                eq(mBuilder.getResetTelephonyContentProviderAuthority()),
+        verify(mContentProviderClient).call(
                 eq(ResetNetworkOperationBuilder.METHOD_RESTART_RILD),
                 isNull(),
                 isNull());
diff --git a/tests/unit/src/com/android/settings/network/telephony/RoamingPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/RoamingPreferenceControllerTest.java
deleted file mode 100644
index d221280..0000000
--- a/tests/unit/src/com/android/settings/network/telephony/RoamingPreferenceControllerTest.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.network.telephony;
-
-import 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.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.os.Looper;
-import android.os.PersistableBundle;
-import android.telephony.CarrierConfigManager;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-
-import androidx.fragment.app.FragmentManager;
-import androidx.fragment.app.FragmentTransaction;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.lifecycle.LifecycleRegistry;
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.android.settings.core.BasePreferenceController;
-import com.android.settingslib.RestrictedSwitchPreference;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-import com.android.settingslib.mobile.dataservice.MobileNetworkInfoEntity;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@RunWith(AndroidJUnit4.class)
-public class RoamingPreferenceControllerTest {
-    private static final int SUB_ID = 2;
-
-    @Mock
-    private FragmentManager mFragmentManager;
-    @Mock
-    private TelephonyManager mTelephonyManager;
-    @Mock
-    private TelephonyManager mInvalidTelephonyManager;
-    @Mock
-    private SubscriptionManager mSubscriptionManager;
-    @Mock
-    private FragmentTransaction mFragmentTransaction;
-    @Mock
-    private CarrierConfigManager mCarrierConfigManager;
-    @Mock
-    private Lifecycle mLifecycle;
-    @Mock
-    private LifecycleOwner mLifecycleOwner;
-
-    private LifecycleRegistry mLifecycleRegistry;
-    private RoamingPreferenceController mController;
-    private RestrictedSwitchPreference mPreference;
-    private Context mContext;
-    private MobileNetworkInfoEntity mMobileNetworkInfoEntity;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        if (Looper.myLooper() == null) {
-            Looper.prepare();
-        }
-
-        mContext = spy(ApplicationProvider.getApplicationContext());
-        doReturn(mTelephonyManager).when(mContext).getSystemService(Context.TELEPHONY_SERVICE);
-        doReturn(mSubscriptionManager).when(mContext).getSystemService(
-                Context.TELEPHONY_SUBSCRIPTION_SERVICE);
-
-        doReturn(mCarrierConfigManager).when(mContext).getSystemService(
-                Context.CARRIER_CONFIG_SERVICE);
-        doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(SUB_ID);
-        doReturn(mInvalidTelephonyManager).when(mTelephonyManager).createForSubscriptionId(
-                SubscriptionManager.INVALID_SUBSCRIPTION_ID);
-        doReturn(mFragmentTransaction).when(mFragmentManager).beginTransaction();
-
-        mPreference = spy(new RestrictedSwitchPreference(mContext));
-        mController = spy(
-                new RoamingPreferenceController(mContext, "roaming", mLifecycle, mLifecycleOwner,
-                        SUB_ID));
-        mLifecycleRegistry = new LifecycleRegistry(mLifecycleOwner);
-        when(mLifecycleOwner.getLifecycle()).thenReturn(mLifecycleRegistry);
-        mController.init(mFragmentManager, SUB_ID, mMobileNetworkInfoEntity);
-        mPreference.setKey(mController.getPreferenceKey());
-    }
-
-    private MobileNetworkInfoEntity setupMobileNetworkInfoEntity(String subId,
-            boolean isDataRoaming) {
-        return new MobileNetworkInfoEntity(subId, false, false, true, false, false, false, false,
-                false, false, false, isDataRoaming);
-    }
-
-    @Test
-    public void getAvailabilityStatus_validSubId_returnAvailable() {
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(
-                AVAILABLE);
-    }
-
-    @Test
-    public void getAvailabilityStatus_invalidSubId_returnUnsearchable() {
-        mController.init(mFragmentManager, SubscriptionManager.INVALID_SUBSCRIPTION_ID,
-                mMobileNetworkInfoEntity);
-
-        assertThat(mController.getAvailabilityStatus(
-                SubscriptionManager.INVALID_SUBSCRIPTION_ID)).isEqualTo(
-                BasePreferenceController.AVAILABLE_UNSEARCHABLE);
-    }
-
-    @Test
-    public void isDialogNeeded_roamingDisabledWithoutFlag_returnTrue() {
-        final PersistableBundle bundle = new PersistableBundle();
-        bundle.putBoolean(CarrierConfigManager.KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
-        doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(SUB_ID);
-        mMobileNetworkInfoEntity = setupMobileNetworkInfoEntity(String.valueOf(SUB_ID), false);
-        mController.setMobileNetworkInfoEntity(mMobileNetworkInfoEntity);
-
-        assertThat(mController.isDialogNeeded()).isTrue();
-    }
-
-    @Test
-    public void isDialogNeeded_roamingEnabled_returnFalse() {
-        mMobileNetworkInfoEntity = setupMobileNetworkInfoEntity(String.valueOf(SUB_ID), true);
-        mController.setMobileNetworkInfoEntity(mMobileNetworkInfoEntity);
-
-        assertThat(mController.isDialogNeeded()).isFalse();
-    }
-
-    @Test
-    @UiThreadTest
-    public void setChecked_needDialog_showDialog() {
-        mMobileNetworkInfoEntity = setupMobileNetworkInfoEntity(String.valueOf(SUB_ID), false);
-        mController.setMobileNetworkInfoEntity(mMobileNetworkInfoEntity);
-        doReturn(null).when(mCarrierConfigManager).getConfigForSubId(SUB_ID);
-
-        mController.setChecked(true);
-
-        verify(mFragmentManager).beginTransaction();
-    }
-
-    @Test
-    public void updateState_invalidSubId_disabled() {
-        mMobileNetworkInfoEntity = setupMobileNetworkInfoEntity(
-                String.valueOf(SubscriptionManager.INVALID_SUBSCRIPTION_ID), false);
-        mController.setMobileNetworkInfoEntity(mMobileNetworkInfoEntity);
-        mController.init(mFragmentManager, SubscriptionManager.INVALID_SUBSCRIPTION_ID,
-                mMobileNetworkInfoEntity);
-
-        mController.updateState(mPreference);
-
-        assertThat(mPreference.isEnabled()).isFalse();
-    }
-
-    @Test
-    public void updateState_validSubId_enabled() {
-        mMobileNetworkInfoEntity = setupMobileNetworkInfoEntity(String.valueOf(SUB_ID), true);
-        mController.setMobileNetworkInfoEntity(mMobileNetworkInfoEntity);
-
-        mController.updateState(mPreference);
-
-        assertThat(mPreference.isEnabled()).isTrue();
-        assertThat(mPreference.isChecked()).isTrue();
-    }
-
-    @Test
-    public void updateState_isNotDisabledByAdmin_shouldInvokeSetEnabled() {
-        when(mPreference.isDisabledByAdmin()).thenReturn(false);
-
-        mController.updateState(mPreference);
-
-        verify(mPreference).setEnabled(anyBoolean());
-    }
-
-    @Test
-    public void updateState_isDisabledByAdmin_shouldNotInvokeSetEnabled() {
-        when(mPreference.isDisabledByAdmin()).thenReturn(true);
-
-        mController.updateState(mPreference);
-
-        verify(mPreference, never()).setEnabled(anyBoolean());
-    }
-
-    @Test
-    public void getAvailabilityStatus_carrierConfigIsNull_shouldReturnAvailable() {
-        doReturn(null).when(mCarrierConfigManager).getConfigForSubId(SUB_ID);
-
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
-    }
-
-    @Test
-    public void getAvailabilityStatus_forceHomeNetworkIsFalse_shouldReturnAvailable() {
-        final PersistableBundle bundle = new PersistableBundle();
-        bundle.putBoolean(CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL, false);
-        doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(SUB_ID);
-
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
-    }
-
-    @Test
-    public void getAvailabilityStatus_forceHomeNetworkIsTrue_shouldReturnConditionallyAvailable() {
-        final PersistableBundle bundle = new PersistableBundle();
-        bundle.putBoolean(CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL, true);
-        doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(SUB_ID);
-
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
-    }
-}