Merge "Make HIGH_QUALITY_AUDIO_PREF_TAG public" into main
diff --git a/aconfig/settings_biometrics_framework_flag_declarations.aconfig b/aconfig/settings_biometrics_framework_flag_declarations.aconfig
index e787da0..e81c68e 100644
--- a/aconfig/settings_biometrics_framework_flag_declarations.aconfig
+++ b/aconfig/settings_biometrics_framework_flag_declarations.aconfig
@@ -14,3 +14,10 @@
   description: "This flag enables or disables the Fingerprint v2 enrollment"
   bug: "295206723"
 }
+
+flag {
+  name: "active_unlock_finish_parent"
+  namespace: "biometrics_framework"
+  description: "This flag enables the Active Unlock settings activity to finish its parent activity"
+  bug: "343576960"
+}
diff --git a/aconfig/settings_connecteddevice_flag_declarations.aconfig b/aconfig/settings_connecteddevice_flag_declarations.aconfig
index b255096..ee0c233 100644
--- a/aconfig/settings_connecteddevice_flag_declarations.aconfig
+++ b/aconfig/settings_connecteddevice_flag_declarations.aconfig
@@ -2,13 +2,6 @@
 container: "system_ext"
 
 flag {
-  name: "enable_subsequent_pair_settings_integration"
-  namespace: "pixel_cross_device_control"
-  description: "Gates whether to enable subsequent pair Settings integration."
-  bug: "299405720"
-}
-
-flag {
   name: "rotation_connected_display_setting"
   namespace: "display_manager"
   description: "Allow changing rotation of the connected display."
@@ -47,4 +40,4 @@
     metadata {
         purpose: PURPOSE_BUGFIX
     }
-}
\ No newline at end of file
+}
diff --git a/aconfig/settings_experience_flag_declarations.aconfig b/aconfig/settings_experience_flag_declarations.aconfig
index e79b515..d5caccf 100644
--- a/aconfig/settings_experience_flag_declarations.aconfig
+++ b/aconfig/settings_experience_flag_declarations.aconfig
@@ -17,13 +17,3 @@
         purpose: PURPOSE_BUGFIX
     }
 }
-
-flag {
-    name: "internet_preference_controller_v2"
-    namespace: "settings_experience"
-    description: "New InternetPreferenceControllerV2."
-    bug: "339884322"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-}
diff --git a/res-product/values/strings.xml b/res-product/values/strings.xml
index 83963cb..d5ef1c8 100644
--- a/res-product/values/strings.xml
+++ b/res-product/values/strings.xml
@@ -758,8 +758,16 @@
     <string name="audio_streams_dialog_no_le_device_subtitle" product="default">To listen to an audio stream, first connect headphones that support LE Audio to this phone.</string>
     <string name="audio_streams_dialog_no_le_device_subtitle" product="tablet">To listen to an audio stream, first connect headphones that support LE Audio to this tablet.</string>
     <string name="audio_streams_dialog_no_le_device_subtitle" product="device">To listen to an audio stream, first connect headphones that support LE Audio to this device.</string>
+    <!-- Le audio streams no le device dialog subtitle with learn more link [CHAR LIMIT=NONE] -->
+    <string name="audio_streams_dialog_no_le_device_subtitle_with_link" product="default">To listen to an audio stream, first connect headphones that support LE Audio to this phone. <annotation id="link">Learn more</annotation></string>
+    <string name="audio_streams_dialog_no_le_device_subtitle_with_link" product="tablet">To listen to an audio stream, first connect headphones that support LE Audio to this tablet. <annotation id="link">Learn more</annotation></string>
+    <string name="audio_streams_dialog_no_le_device_subtitle_with_link" product="device">To listen to an audio stream, first connect headphones that support LE Audio to this device. <annotation id="link">Learn more</annotation></string>
     <!-- Le audio streams unsupported device subtitle [CHAR LIMIT=NONE] -->
     <string name="audio_streams_dialog_unsupported_device_subtitle" product="default">This phone doesn\'t support LE Audio, which is needed to listen to audio streams.</string>
     <string name="audio_streams_dialog_unsupported_device_subtitle" product="tablet">This tablet doesn\'t support LE Audio, which is needed to listen to audio streams.</string>
     <string name="audio_streams_dialog_unsupported_device_subtitle" product="device">This device doesn\'t support LE Audio, which is needed to listen to audio streams.</string>
+    <!-- Content for audio sharing confirm dialog with learn more link [CHAR LIMIT=none]-->
+    <string name="audio_sharing_confirm_dialog_content_with_link" product="default">To start sharing audio, first connect LE Audio headphones to your phone. <annotation id="link">Learn more about compatible devices</annotation></string>
+    <string name="audio_sharing_confirm_dialog_content_with_link" product="tablet">To start sharing audio, first connect LE Audio headphones to your tablet. <annotation id="link">Learn more about compatible devices</annotation></string>
+    <string name="audio_sharing_confirm_dialog_content_with_link" product="device">To start sharing audio, first connect LE Audio headphones to your device. <annotation id="link">Learn more about compatible devices</annotation></string>
 </resources>
diff --git a/res/layout/dialog_custom_title_audio_sharing.xml b/res/layout/dialog_custom_title_audio_sharing.xml
index 86e0010..692cbc7 100644
--- a/res/layout/dialog_custom_title_audio_sharing.xml
+++ b/res/layout/dialog_custom_title_audio_sharing.xml
@@ -35,7 +35,8 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="center"
-        android:maxLines="2"
+        android:maxLines="3"
+        android:ellipsize="end"
         android:paddingTop="14dp"
         android:textAlignment="center"
         android:textSize="24sp" />
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 8cbf5dc..8e5519f 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -1536,6 +1536,7 @@
         <item>@*android:drawable/ic_zen_mode_icon_group_of_people</item>
         <item>@*android:drawable/ic_zen_mode_icon_lightbulb</item>
         <item>@*android:drawable/ic_zen_mode_type_schedule_calendar</item>
+        <item>@*android:drawable/ic_zen_mode_type_special_dnd</item>
         <!-- Sports -->
         <item>@*android:drawable/ic_zen_mode_icon_running</item>
         <item>@*android:drawable/ic_zen_mode_icon_golf</item>
@@ -1561,7 +1562,6 @@
         <!-- Other activities -->
         <item>@*android:drawable/ic_zen_mode_icon_train</item>
         <item>@*android:drawable/ic_zen_mode_type_driving</item> <!-- Car -->
-        <item>@*android:drawable/ic_zen_mode_icon_croissant</item>
         <item>@*android:drawable/ic_zen_mode_icon_fork_and_knife</item>
         <item>@*android:drawable/ic_zen_mode_icon_shopping_cart</item>
         <item>@*android:drawable/ic_zen_mode_icon_child</item>
@@ -1587,6 +1587,7 @@
         <item>Group of people</item>
         <item>Lightbulb</item>
         <item>Calendar</item>
+        <item>Do Not Disturb</item>
         <!-- Sports -->
         <item>Person running</item>
         <item>Golf</item>
@@ -1612,7 +1613,6 @@
         <!-- Other activities -->
         <item>Train</item>
         <item>Car</item>
-        <item>Croissant</item>
         <item>Fork and knife</item>
         <item>Shopping cart</item>
         <item>Child</item>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 7b7dd19..3ec5904 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -937,7 +937,7 @@
     <!-- Message showing that multiple fingerprints, face, and the current watch is set up. Shown for a menu item that launches fingerprint, face,  and active unlock settings or enrollment. [CHAR LIMIT=80]-->
     <string name="security_settings_fingerprint_multiple_face_watch_preference_summary">Face, fingerprints, and <xliff:g id="watch" example="Dani's Watch">%s</xliff:g> added</string>
     <!-- Description for mandatory biometrics prompt-->
-    <string name="mandatory_biometrics_prompt_description">Identity Check is on</string>
+    <string name="mandatory_biometrics_prompt_description">Identity Check is on and requires a biometric</string>
     <!-- RemoteAuth unlock enrollment and settings --><skip />
     <!-- Title shown for menu item that launches watch unlock settings. [CHAR LIMIT=40] -->
     <string name ="security_settings_remoteauth_preference_title">Remote Authenticator Unlock</string>
@@ -7979,7 +7979,7 @@
     <string name="zen_mode_settings_title">Do Not Disturb</string>
 
     <!-- Zen Modes: Title for the Modes option and associated settings page. [CHAR LIMIT=50]-->
-    <string name="zen_modes_list_title">Priority Modes</string>
+    <string name="zen_modes_list_title">Modes</string>
 
     <!-- Zen Modes: Intro text describing the feature. [CHAR LIMIT=NONE]-->
     <string name="zen_modes_list_intro">Minimize distractions and take control of your attention with modes for sleep, work, driving, and everything in between.</string>
@@ -7993,18 +7993,18 @@
     <!-- Zen Modes: Option to add an automatic schedule for a mode. [CHAR_LIMIT=40] -->
     <string name="zen_mode_select_schedule">Set a schedule</string>
 
-    <!-- Priority Modes: Title of the dialog used to choose an automatic schedule for a mode. [CHAR_LIMIT=40] -->
+    <!-- Modes: Title of the dialog used to choose an automatic schedule for a mode. [CHAR_LIMIT=40] -->
     <string name="zen_mode_select_schedule_title">Schedule based on</string>
 
-    <!-- Priority Modes: Option to choose a time-based schedule for a mode. [CHAR_LIMIT=40] -->
+    <!-- Modes: Option to choose a time-based schedule for a mode. [CHAR_LIMIT=40] -->
     <string name="zen_mode_select_schedule_time">Day and time</string>
-    <!-- Priority Modes: Example text for the option to choose a time-based schedule for a mode. [CHAR_LIMIT=60] -->
+    <!-- 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">\"9 AM - 5 PM weekdays\"</string>
 
-    <!-- Priority Modes: Option to choose a calendar-events-based schedule for a mode. [CHAR_LIMIT=40] -->
+    <!-- Modes: Option to choose a calendar-events-based schedule for a mode. [CHAR_LIMIT=40] -->
     <string name="zen_mode_select_schedule_calendar">Calendar events</string>
 
-    <!-- Priority Modes: Summary for the modes segment, when at least one mode is active. [CHAR LIMIT=NONE]-->
+    <!-- Modes: Summary for the modes segment, when at least one mode is active. [CHAR LIMIT=NONE]-->
     <string name="zen_modes_summary_some_active">
         {count, plural, offset:2
             =0    {}
@@ -8015,7 +8015,7 @@
         }
     </string>
 
-    <!-- Priority Modes: Summary for the modes segment, when no modes are active. [CHAR LIMIT=NONE]-->
+    <!-- Modes: Summary for the modes segment, when no modes are active. [CHAR LIMIT=NONE]-->
     <string name="zen_modes_summary_none_active">
         {count, plural,
             =0    {}
@@ -8024,40 +8024,40 @@
         }
     </string>
 
-    <!-- Priority Modes: Short text that indicates that a mode is currently on (active). [CHAR_LIMIT=10] -->
+    <!-- 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] -->
+    <!-- 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: Indicates that a mode is disabled and needs to be configured. [CHAR_LIMIT=40] -->
+    <!-- Modes: Indicates that a mode is disabled and needs to be configured. [CHAR_LIMIT=40] -->
     <string name="zen_mode_disabled_needs_setup">Not set</string>
 
-    <!-- Priority Modes: Indicates that a mode is disabled by the user. [CHAR_LIMIT=40] -->
+    <!-- Modes: Indicates that a mode is disabled by the user. [CHAR_LIMIT=40] -->
     <string name="zen_mode_disabled_by_user">Disabled</string>
 
-    <!-- Priority Modes: Title of the "Create a mode" dialog, to choose the mode type. [CHAR_LIMIT=30] -->
+    <!-- Modes: Title of the "Create a mode" dialog, to choose the mode type. [CHAR_LIMIT=30] -->
     <string name="zen_mode_new_title">Create a mode</string>
 
-    <!-- Priority Modes: Option to add a "custom" mode in the "Add a mode" dialog. [CHAR_LIMIT=20] -->
+    <!-- Modes: Option to add a "custom" mode in the "Add a mode" dialog. [CHAR_LIMIT=20] -->
     <string name="zen_mode_new_option_custom">Custom</string>
 
-    <!-- Priority Modes: Caption of the button to turn on a mode [CHAR LIMIT=20] -->
+    <!-- Modes: Caption of the button to turn on a mode [CHAR LIMIT=20] -->
     <string name="zen_mode_action_activate">Turn on now</string>
 
-    <!-- Priority Modes: Caption of the button to turn off a currently active mode [CHAR LIMIT=20] -->
+    <!-- Modes: Caption of the button to turn off a currently active mode [CHAR LIMIT=20] -->
     <string name="zen_mode_action_deactivate">Turn off</string>
 
-    <!-- Priority Modes: Text to display if a mode isn't found [CHAR LIMIT=40] -->
+    <!-- Modes: Text to display if a mode isn't found [CHAR LIMIT=40] -->
     <string name="zen_mode_not_found_text">Mode not found</string>
 
     <!-- Subtitle for the Do not Disturb slice. [CHAR LIMIT=50]-->
     <string name="zen_mode_slice_subtitle">Limit interruptions</string>
 
-    <!-- Priority Modes: Summary on a page prompting the user to set up/enable a mode [CHAR_LIMIT=NONE] -->
+    <!-- Modes: Summary on a page prompting the user to set up/enable a mode [CHAR_LIMIT=NONE] -->
     <string name="zen_mode_setup_page_summary">Block interruptions and distractions</string>
 
-    <!-- Priority Modes: Label on a button prompting the user to set up the mode with the given name. [CHAR_LIMIT=40] -->
+    <!-- Modes: Label on a button prompting the user to set up the mode with the given name. [CHAR_LIMIT=40] -->
     <string name="zen_mode_setup_button_label">Set up <xliff:g id="mode" example="My Mode">%1$s</xliff:g></string>
 
     <!-- Do not disturb: Title for the Do not Disturb dialog to turn on Do not disturb. [CHAR LIMIT=50]-->
@@ -8075,7 +8075,7 @@
     <!-- 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>
 
-    <!-- Priority Modes: Menu option for renaming a mode on its configuration page [CHAR LIMIT=40] -->
+    <!-- Modes: Menu option for renaming a mode on its configuration page [CHAR LIMIT=40] -->
     <string name="zen_mode_menu_rename_mode">Rename</string>
 
     <!-- Do not disturb: Menu option for deleting a mode on its configuration page [CHAR LIMIT=40] -->
@@ -8096,11 +8096,11 @@
     <!-- Do not disturb: Title for settings section describing when the rule turns on automatically [CHAR LIMIT=30] -->
     <string name="zen_mode_automatic_trigger_title">When to turn on automatically</string>
 
-    <!-- Priority Modes: Title prompting a user to choose a calendar to use for an automatic rule [CHAR LIMIT=30] -->
+    <!-- Modes: Title prompting a user to choose a calendar to use for an automatic rule [CHAR LIMIT=30] -->
     <string name="zen_mode_set_calendar_title">Event schedule</string>
-    <!-- Priority Modes: Title prompting a user to choose a calendar to use for an automatic rule [CHAR LIMIT=30] -->
+    <!-- Modes: Title prompting a user to choose a calendar to use for an automatic rule [CHAR LIMIT=30] -->
     <string name="zen_mode_set_calendar_which_calendar">Turn on during events for</string>
-    <!-- Priority Modes: Title prompting a user to choose a calendar to use for an automatic rule [CHAR LIMIT=30] -->
+    <!-- Modes: Title prompting a user to choose a calendar to use for an automatic rule [CHAR LIMIT=30] -->
     <string name="zen_mode_set_calendar_which_reply">Where invite reply is</string>
 
     <!-- Do not disturb: Title prompting a user to set a time-based schedule to use for an automatic rule [CHAR LIMIT=30] -->
@@ -8139,9 +8139,9 @@
     <!--  Do not disturb: Subtitle for the Visual signals option to toggle on/off visual signals/alerts when the screen is on/when screen is off. [CHAR LIMIT=30] -->
     <string name="zen_mode_visual_signals_settings_subtitle">Allow visual signals</string>
 
-    <!-- Priority Modes: mode page section title [CHAR LIMIT=80] -->
+    <!-- Modes: mode page section title [CHAR LIMIT=80] -->
     <string name="mode_interruption_filter_title">Notification filters</string>
-    <!-- Priority Modes: mode page section title [CHAR LIMIT=80] -->
+    <!-- Modes: mode page section title [CHAR LIMIT=80] -->
     <string name="mode_device_effects_title">More settings</string>
 
     <!-- Summary for the Sound Do not Disturb option when DND isn't currently on. [CHAR LIMIT=NONE]-->
@@ -8859,12 +8859,12 @@
     <string name="nls_feature_reply_summary">It can reply to messages and take action on buttons in notifications, including snoozing or dismissing notifications and answering calls.</string>
     <string name="nls_feature_settings_title">Change settings</string>
     <string name="nls_feature_settings_summary">It can turn Do Not Disturb on or off and change related settings.</string>
-    <string name="nls_feature_modes_settings_summary">It can manage and activate Priority Modes, and change related settings.</string>
+    <string name="nls_feature_modes_settings_summary">It can manage and activate Modes, and change related settings.</string>
     <string name="notification_listener_disable_warning_summary">
         If you turn off notification access for <xliff:g id="notification_listener_name">%1$s</xliff:g>, Do Not Disturb access may also be turned off.
     </string>
     <string name="notification_listener_disable_modes_warning_summary">
-        If you turn off notification access for <xliff:g id="notification_listener_name">%1$s</xliff:g>, Priority Modes access may also be turned off.
+        If you turn off notification access for <xliff:g id="notification_listener_name">%1$s</xliff:g>, Modes access may also be turned off.
     </string>
     <string name="notification_listener_disable_warning_confirm">Turn off</string>
     <string name="notification_listener_disable_warning_cancel">Cancel</string>
@@ -9042,14 +9042,14 @@
     <!-- Sound & notification > Do Not Disturb access > Text to display when the list is empty. [CHAR LIMIT=NONE] -->
     <string name="zen_access_empty_text">No installed apps have requested Do Not Disturb access</string>
 
-    <!-- Special App Access: Title for managing Priority Modes access option. [CHAR LIMIT=40] -->
-    <string name="manage_zen_modes_access_title">Priority Modes access</string>
+    <!-- Special App Access: Title for managing Modes access option. [CHAR LIMIT=40] -->
+    <string name="manage_zen_modes_access_title">Modes access</string>
 
-    <!-- Button title that grants 'Priority Modes' permission to an app [CHAR_LIMIT=60]-->
-    <string name="zen_modes_access_detail_switch">Allow Priority Modes access</string>
+    <!-- Button title that grants 'Modes' permission to an app [CHAR_LIMIT=60]-->
+    <string name="zen_modes_access_detail_switch">Allow Modes access</string>
 
     <!-- Special App Access > Do Not Disturb access > Text to display when the list is empty. [CHAR LIMIT=NONE] -->
-    <string name="zen_modes_access_empty_text">No installed apps have requested Priority Modes access</string>
+    <string name="zen_modes_access_empty_text">No installed apps have requested Modes access</string>
 
     <!-- [CHAR LIMIT=NONE] Text appearing when app notifications are off -->
     <string name="app_notifications_off_desc">You haven\'t allowed notifications from this app</string>
@@ -9392,11 +9392,11 @@
             other {{app_1}, {app_2}, and # more can interrupt}
         }
     </string>
-    <!-- Priority Modes: Entry in the "apps that can bypass DND" list that corresponds to a work profile app (e.g. "Chrome (Work)" [CHAR LIMIT=15]. -->
+    <!-- Modes: Entry in the "apps that can bypass DND" list that corresponds to a work profile app (e.g. "Chrome (Work)" [CHAR LIMIT=15]. -->
     <string name="zen_mode_apps_work_app"><xliff:g id="app_label" example="Chrome">%s</xliff:g> (Work)</string>
     <!-- Text displayed (for a brief time) while the list of bypassing apps is being fetched. Will be replaced by a zen_mode_apps_subtext. [CHAR_LIMIT=60] -->
     <string name="zen_mode_apps_calculating">Calculating\u2026</string>
-    <!-- Priority Modes: Format for a string displayed when there are more items (e.g. apps, contacts) that can be shown. For example, we show (A)(B)(C)(+5), where this string represents the "+5" value. Needs to be as compact as possible, since it will be drawn in a really small area. [CHAR_LIMIT=4] -->
+    <!-- Modes: Format for a string displayed when there are more items (e.g. apps, contacts) that can be shown. For example, we show (A)(B)(C)(+5), where this string represents the "+5" value. Needs to be as compact as possible, since it will be drawn in a really small area. [CHAR_LIMIT=4] -->
     <string name="zen_mode_plus_n_items">+<xliff:g id="number" example="42">%d</xliff:g></string>
 
     <!-- [CHAR LIMIT=100] Zen mode settings: Allow apps to bypass DND -->
@@ -9501,76 +9501,76 @@
     <!-- [CHAR LIMIT=NONE] Zen mode summary spoken when changing mode by voice: Turn on all notifications. -->
     <string name="zen_mode_summary_always">Change to always interrupt</string>
 
-    <!-- Priority Modes: Title for the "rename mode" screen [CHAR LIMIT=20] -->
+    <!-- Modes: Title for the "rename mode" screen [CHAR LIMIT=20] -->
     <string name="zen_mode_rename_title">Edit mode</string>
 
-    <!-- Priority Modes: Title for the "add mode" screen [CHAR LIMIT=20] -->
+    <!-- Modes: Title for the "add mode" screen [CHAR LIMIT=20] -->
     <string name="zen_mode_new_custom_title">Create a mode</string>
 
-    <!-- Priority Modes: Default name for new custom modes [CHAR LIMIT=30] -->
+    <!-- Modes: Default name for new custom modes [CHAR LIMIT=30] -->
     <string name="zen_mode_new_custom_default_name">Custom mode</string>
 
-    <!-- Priority Modes: Hint for the EditText for editing a mode's name [CHAR LIMIT=30] -->
+    <!-- Modes: Hint for the EditText for editing a mode's name [CHAR LIMIT=30] -->
     <string name="zen_mode_edit_name_hint">Mode name</string>
 
-    <!-- Priority Modes: Trigger title for modes of type SCHEDULE_CALENDAR. [CHAR LIMIT=30] -->
+    <!-- Modes: Trigger title for modes of type SCHEDULE_CALENDAR. [CHAR LIMIT=30] -->
     <string name="zen_mode_trigger_title_schedule_calendar">Calendar events</string>
-    <!-- Priority Modes: Trigger title for modes of type BEDTIME. [CHAR LIMIT=30] -->
+    <!-- Modes: Trigger title for modes of type BEDTIME. [CHAR LIMIT=30] -->
     <string name="zen_mode_trigger_title_bedtime">Bedtime routine</string>
-    <!-- Priority Modes: Trigger title for modes of type DRIVING. [CHAR LIMIT=30] -->
+    <!-- Modes: Trigger title for modes of type DRIVING. [CHAR LIMIT=30] -->
     <string name="zen_mode_trigger_title_driving">While driving</string>
-    <!-- Priority Modes: Generic trigger title for modes of other types [CHAR LIMIT=30] -->
+    <!-- Modes: Generic trigger title for modes of other types [CHAR LIMIT=30] -->
     <string name="zen_mode_trigger_title_generic">App settings</string>
-    <!-- Priority Modes: Generic trigger summary for modes where the owner app did not provide a triggerDescription but did provide a configurationActivity to call [CHAR LIMIT=60] -->
+    <!-- Modes: Generic trigger summary for modes where the owner app did not provide a triggerDescription but did provide a configurationActivity to call [CHAR LIMIT=60] -->
     <string name="zen_mode_trigger_summary_settings_in_app">Info and settings in <xliff:g id="app_name" example="The Awesome App">%1$s</xliff:g></string>
-    <!-- Priority Modes: Generic trigger summary for modes where the owner app did not provide neither a triggerDescription nor a configurationActivity to call [CHAR LIMIT=60] -->
+    <!-- Modes: Generic trigger summary for modes where the owner app did not provide neither a triggerDescription nor a configurationActivity to call [CHAR LIMIT=60] -->
     <string name="zen_mode_trigger_summary_managed_by_app">Managed by <xliff:g id="app_name" example="The Awesome App">%1$s</xliff:g></string>
 
-    <!-- Priority Modes: Title of the confirmation dialog for disabling an enabled mode [CHAR LIMIT=20] -->
+    <!-- Modes: Title of the confirmation dialog for disabling an enabled mode [CHAR LIMIT=20] -->
     <string name="zen_mode_confirm_disable_mode_title">Disable <xliff:g id="mode_name" example="Driving Mode">%1$s</xliff:g>?</string>
-    <!-- Priority Modes: Message body of the confirmation dialog for disabling an enabled mode [CHAR LIMIT=NONE] -->
+    <!-- Modes: Message body of the confirmation dialog for disabling an enabled mode [CHAR LIMIT=NONE] -->
     <string name="zen_mode_confirm_disable_message">This mode will never turn on when disabled</string>
-    <!-- Priority Modes: Button to disable a mode [CHAR LIMIT=20] -->
+    <!-- Modes: Button to disable a mode [CHAR LIMIT=20] -->
     <string name="zen_mode_action_disable">Disable</string>
-    <!-- Priority Modes: Title of the confirmation dialog for enabling a disabled mode [CHAR LIMIT=20] -->
+    <!-- Modes: Title of the confirmation dialog for enabling a disabled mode [CHAR LIMIT=20] -->
     <string name="zen_mode_confirm_enable_mode_title">Enable <xliff:g id="mode_name" example="Driving Mode">%1$s</xliff:g>?</string>
-    <!-- Priority Modes: Message body of the confirmation dialog for enabling a disabled mode [CHAR LIMIT=NONE] -->
+    <!-- Modes: Message body of the confirmation dialog for enabling a disabled mode [CHAR LIMIT=NONE] -->
     <string name="zen_mode_confirm_enable_message">This mode may turn on automatically based on its settings</string>
-    <!-- Priority Modes: Button to disable a mode [CHAR LIMIT=20] -->
+    <!-- Modes: Button to disable a mode [CHAR LIMIT=20] -->
     <string name="zen_mode_action_enable">Enable</string>
 
-    <!-- Priority Modes: Blurb for a mode that activates during a fixed time schedule (e.g. 9:00-17:00). [CHAR LIMIT=NONE] -->
+    <!-- Modes: Blurb for a mode that activates during a fixed time schedule (e.g. 9:00-17:00). [CHAR LIMIT=NONE] -->
     <string name="zen_mode_blurb_schedule_time">Set a mode that follows a regular schedule</string>
-    <!-- Priority Modes: Blurb for a mode that activates during calendar events. [CHAR LIMIT=NONE] -->
+    <!-- Modes: Blurb for a mode that activates during calendar events. [CHAR LIMIT=NONE] -->
     <string name="zen_mode_blurb_schedule_calendar">Set a mode to sync with calendar events and invite responses</string>
-    <!-- Priority Modes: Blurb for a mode of type BEDTIME. [CHAR LIMIT=NONE] -->
+    <!-- Modes: Blurb for a mode of type BEDTIME. [CHAR LIMIT=NONE] -->
     <string name="zen_mode_blurb_bedtime">Design a calming sleep routine. Set alarms, dim the screen, and block notifications.</string>
-    <!-- Priority Modes: Blurb for a mode of type DRIVING. [CHAR LIMIT=NONE] -->
+    <!-- Modes: Blurb for a mode of type DRIVING. [CHAR LIMIT=NONE] -->
     <string name="zen_mode_blurb_driving">Prioritize safety on the road for a focused and distraction-free drive</string>
-    <!-- Priority Modes: Blurb for a mode of type IMMERSIVE. [CHAR LIMIT=NONE] -->
+    <!-- Modes: Blurb for a mode of type IMMERSIVE. [CHAR LIMIT=NONE] -->
     <string name="zen_mode_blurb_immersive">Block distractions or interruptions from your device to gain focus</string>
-    <!-- Priority Modes: Blurb for a mode of type THEATER. [CHAR LIMIT=NONE] -->
+    <!-- Modes: Blurb for a mode of type THEATER. [CHAR LIMIT=NONE] -->
     <string name="zen_mode_blurb_theater">Eliminate all distractions for a quiet environment</string>
-    <!-- Priority Modes: Blurb for a mode of type MANAGED. [CHAR LIMIT=NONE] -->
+    <!-- Modes: Blurb for a mode of type MANAGED. [CHAR LIMIT=NONE] -->
     <string name="zen_mode_blurb_managed">Personalize device experiences and settings for different users</string>
-    <!-- Priority Modes: Blurb for modes that are not of a specific type (OTHER, UNKNOWN). [CHAR LIMIT=NONE] -->
+    <!-- Modes: Blurb for modes that are not of a specific type (OTHER, UNKNOWN). [CHAR LIMIT=NONE] -->
     <string name="zen_mode_blurb_generic">Minimize interruptions by only allowing important people and apps to reach you</string>
 
-    <!-- Priority Modes: Inspirational text for a mode that activates during a fixed time schedule (e.g. 9:00-17:00). [CHAR LIMIT=NONE] -->
+    <!-- Modes: Inspirational text for a mode that activates during a fixed time schedule (e.g. 9:00-17:00). [CHAR LIMIT=NONE] -->
     <string name="zen_mode_inspiration_schedule_time">Set a mode that follows a regular schedule</string>
-    <!-- Priority Modes: Inspirational text for a mode that activates during calendar events. [CHAR LIMIT=NONE] -->
+    <!-- Modes: Inspirational text for a mode that activates during calendar events. [CHAR LIMIT=NONE] -->
     <string name="zen_mode_inspiration_schedule_calendar">Keep your device in sync with your day’s events</string>
-    <!-- Priority Modes: Inspirational text for a mode of type BEDTIME. [CHAR LIMIT=NONE] -->
+    <!-- Modes: Inspirational text for a mode of type BEDTIME. [CHAR LIMIT=NONE] -->
     <string name="zen_mode_inspiration_bedtime">Wake up feeling like 100%</string>
-    <!-- Priority Modes: Inspirational text for a mode of type DRIVING. [CHAR LIMIT=NONE] -->
+    <!-- Modes: Inspirational text for a mode of type DRIVING. [CHAR LIMIT=NONE] -->
     <string name="zen_mode_inspiration_driving">Put safety first while on the road</string>
-    <!-- Priority Modes: Inspirational text for a mode of type IMMERSIVE. [CHAR LIMIT=NONE] -->
+    <!-- Modes: Inspirational text for a mode of type IMMERSIVE. [CHAR LIMIT=NONE] -->
     <string name="zen_mode_inspiration_immersive">Gain focus to get in the zone</string>
-    <!-- Priority Modes: Inspirational text for a mode of type THEATER. [CHAR LIMIT=NONE] -->
+    <!-- Modes: Inspirational text for a mode of type THEATER. [CHAR LIMIT=NONE] -->
     <string name="zen_mode_inspiration_theater">For moments when courtesy counts</string>
-    <!-- Priority Modes: Inspirational text for a mode of type MANAGED. [CHAR LIMIT=NONE] -->
+    <!-- Modes: Inspirational text for a mode of type MANAGED. [CHAR LIMIT=NONE] -->
     <string name="zen_mode_inspiration_managed">Guided usage to help you stay in good hands</string>
-    <!-- Priority Modes: Inspirational text for modes that are not of a specific type (OTHER, UNKNOWN). [CHAR LIMIT=NONE] -->
+    <!-- Modes: Inspirational text for modes that are not of a specific type (OTHER, UNKNOWN). [CHAR LIMIT=NONE] -->
     <string name="zen_mode_inspiration_generic">Take control of your attention</string>
 
     <!-- Content description for help icon button [CHAR LIMIT=20] -->
@@ -10172,16 +10172,16 @@
     <!-- Zen mode access settings - summary for warning dialog when revoking access [CHAR LIMIT=NONE] -->
     <string name="zen_access_revoke_warning_dialog_summary">All Do Not Disturb rules created by this app will be removed.</string>
 
-    <!-- Priority modes access settings - title for warning dialog when enabling access [CHAR LIMIT=NONE] -->
-    <string name="zen_modes_access_warning_dialog_title">Allow access to Priority Modes for <xliff:g id="app" example="Tasker">%1$s</xliff:g>?</string>
+    <!-- Modes access settings - title for warning dialog when enabling access [CHAR LIMIT=NONE] -->
+    <string name="zen_modes_access_warning_dialog_title">Allow access to Modes for <xliff:g id="app" example="Tasker">%1$s</xliff:g>?</string>
 
-    <!-- Priority modes access settings - summary for warning dialog when enabling access [CHAR LIMIT=NONE] -->
-    <string name="zen_modes_access_warning_dialog_summary">The app will be able to turn on/off Do Not Disturb, manage and activate Priority Modes, and make changes to related settings.</string>
+    <!-- Modes access settings - summary for warning dialog when enabling access [CHAR LIMIT=NONE] -->
+    <string name="zen_modes_access_warning_dialog_summary">The app will be able to turn on/off Do Not Disturb, manage and activate Modes, and make changes to related settings.</string>
 
-    <!-- Priority modes access settings - title for warning dialog when revoking access [CHAR LIMIT=NONE] -->
-    <string name="zen_modes_access_revoke_warning_dialog_title">Revoke access Priority Modes for <xliff:g id="app" example="Tasker">%1$s</xliff:g>?</string>
+    <!-- Modes access settings - title for warning dialog when revoking access [CHAR LIMIT=NONE] -->
+    <string name="zen_modes_access_revoke_warning_dialog_title">Revoke access Modes for <xliff:g id="app" example="Tasker">%1$s</xliff:g>?</string>
 
-    <!-- Priority modes access settings - summary for warning dialog when revoking access [CHAR LIMIT=NONE] -->
+    <!-- Modes access settings - summary for warning dialog when revoking access [CHAR LIMIT=NONE] -->
     <string name="zen_modes_access_revoke_warning_dialog_summary">All modes created by this app will be removed.</string>
 
     <!-- Ignore battery optimizations on label [CHAR LIMIT=30] -->
@@ -13749,6 +13749,8 @@
     <string name="audio_sharing_confirm_dialog_title">Connect a compatible device</string>
     <!-- Content for audio sharing confirm dialog [CHAR LIMIT=none]-->
     <string name="audio_sharing_comfirm_dialog_content">To start sharing audio, first connect LE Audio headphones to your phone</string>
+    <!-- Text for audio sharing switch active device button [CHAR LIMIT=none]-->
+    <string name="audio_sharing_switch_active_button_label">Switch to <xliff:g example="My buds" id="device_name">%1$s</xliff:g></string>
 
     <!-- Title for audio streams preference category [CHAR LIMIT=none]-->
     <string name="audio_streams_category_title">Connect to a LE audio stream</string>
@@ -13838,6 +13840,8 @@
     <string name="audio_streams_main_page_password_dialog_cannot_edit">Can\u0027t edit password while sharing. To change the password, first turn off audio sharing.</string>
     <!-- Text for audio sharing qrcode scanner [CHAR LIMIT=none]-->
     <string name="audio_streams_qr_code_scanner_label">QR code scanner</string>
+    <!-- Learn more link for audio sharing qrcode [CHAR LIMIT=none]-->
+    <string name="audio_streams_qr_code_help_with_link"><annotation id="link">Need help?</annotation></string>
 
 
     <!-- url for learning more about bluetooth audio sharing -->
diff --git a/res/xml/mobile_network_settings.xml b/res/xml/mobile_network_settings.xml
index 4f16e12..29e7c25 100644
--- a/res/xml/mobile_network_settings.xml
+++ b/res/xml/mobile_network_settings.xml
@@ -98,9 +98,11 @@
             settings:searchable="false"
             settings:controller="com.android.settings.network.telephony.DataUsagePreferenceController"/>
 
+        <!-- Settings search is handled by BillingCycleSearchItem. -->
         <com.android.settings.datausage.BillingCyclePreference
             android:key="billing_preference"
             android:title="@string/billing_cycle"
+            settings:searchable="false"
             settings:controller="com.android.settings.datausage.BillingCyclePreferenceController"/>
 
         <SwitchPreferenceCompat
@@ -221,10 +223,11 @@
                 </intent>
             </Preference>
 
+            <!-- Settings search is handled by WifiCallingSearchItem. -->
             <SwitchPreferenceCompat
                 android:key="video_calling_key"
                 android:title="@string/video_calling_settings_title"
-                android:persistent="true"
+                settings:searchable="false"
                 settings:controller="com.android.settings.network.telephony.VideoCallingPreferenceController"/>
 
         </PreferenceCategory>
diff --git a/res/xml/network_provider_internet.xml b/res/xml/network_provider_internet.xml
index 2a08aae..e4ebe788 100644
--- a/res/xml/network_provider_internet.xml
+++ b/res/xml/network_provider_internet.xml
@@ -29,7 +29,8 @@
         android:order="-30"
         settings:allowDividerAbove="true"
         settings:keywords="@string/keywords_internet"
-        settings:useAdminDisabledSummary="true" />
+        settings:useAdminDisabledSummary="true"
+        settings:controller="com.android.settings.network.InternetPreferenceControllerV2" />
 
     <com.android.settingslib.RestrictedPreference
         android:key="calls_and_sms"
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 3646938..62a4ce3 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -1543,19 +1543,42 @@
      */
     public static void launchBiometricPromptForMandatoryBiometrics(@NonNull Fragment fragment,
             int requestCode, int userId, boolean hideBackground) {
+        fragment.startActivityForResult(getIntentForBiometricAuthentication(fragment.getResources(),
+                userId, hideBackground), requestCode);
+    }
+
+    /**
+     * Launch biometric prompt for mandatory biometrics. Call
+     * {@link #requestBiometricAuthenticationForMandatoryBiometrics(Context, boolean, int)}
+     * to check if all requirements for mandatory biometrics is satisfied
+     * before launching biometric prompt.
+     *
+     * @param activity       corresponding activity of the surface
+     * @param requestCode    for starting the new activity
+     * @param userId         user id for the authentication request
+     * @param hideBackground if the background activity screen needs to be hidden
+     */
+    public static void launchBiometricPromptForMandatoryBiometrics(@NonNull Activity activity,
+            int requestCode, int userId, boolean hideBackground) {
+        activity.startActivityForResult(getIntentForBiometricAuthentication(
+                activity.getResources(), userId, hideBackground), requestCode);
+    }
+
+    private static Intent getIntentForBiometricAuthentication(Resources resources, int userId,
+            boolean hideBackground) {
         final Intent intent = new Intent();
         intent.putExtra(BIOMETRIC_PROMPT_AUTHENTICATORS,
                 BiometricManager.Authenticators.MANDATORY_BIOMETRICS);
         intent.putExtra(BIOMETRIC_PROMPT_NEGATIVE_BUTTON_TEXT,
-                fragment.getString(R.string.cancel));
+                resources.getString(R.string.cancel));
         intent.putExtra(KeyguardManager.EXTRA_DESCRIPTION,
-                fragment.getString(R.string.mandatory_biometrics_prompt_description));
+                resources.getString(R.string.mandatory_biometrics_prompt_description));
         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_ALLOW_ANY_USER, true);
         intent.putExtra(EXTRA_USER_ID, userId);
         intent.putExtra(BIOMETRIC_PROMPT_HIDE_BACKGROUND, hideBackground);
         intent.setClassName(SETTINGS_PACKAGE_NAME,
                 ConfirmDeviceCredentialActivity.InternalActivity.class.getName());
-        fragment.startActivityForResult(intent, requestCode);
+        return intent;
     }
 
     private static void disableComponent(PackageManager pm, ComponentName componentName) {
diff --git a/src/com/android/settings/accessibility/HearingAidCompatibilityPreferenceController.java b/src/com/android/settings/accessibility/HearingAidCompatibilityPreferenceController.java
index 727cdd5..821fddd 100644
--- a/src/com/android/settings/accessibility/HearingAidCompatibilityPreferenceController.java
+++ b/src/com/android/settings/accessibility/HearingAidCompatibilityPreferenceController.java
@@ -25,6 +25,7 @@
 import com.android.internal.telephony.flags.Flags;
 import com.android.settings.R;
 import com.android.settings.core.TogglePreferenceController;
+import com.android.settings.overlay.FeatureFactory;
 
 /** Preference controller for Hearing Aid Compatibility (HAC) settings */
 public class HearingAidCompatibilityPreferenceController extends TogglePreferenceController {
@@ -73,6 +74,8 @@
 
     @Override
     public boolean setChecked(boolean isChecked) {
+        FeatureFactory.getFeatureFactory().getMetricsFeatureProvider().changed(
+                getMetricsCategory(), getPreferenceKey(), isChecked ? 1 : 0);
         setAudioParameterHacEnabled(isChecked);
         return Settings.System.putInt(mContext.getContentResolver(), Settings.System.HEARING_AID,
                 (isChecked ? HAC_ENABLED : HAC_DISABLED));
diff --git a/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceController.java b/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceController.java
index 818eb5e..45a19b7 100644
--- a/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceController.java
+++ b/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceController.java
@@ -27,6 +27,7 @@
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.VibrationAttributes;
 import android.os.Vibrator;
 import android.provider.Settings;
@@ -69,7 +70,8 @@
     public KeyboardVibrationTogglePreferenceController(Context context, String preferenceKey) {
         super(context, preferenceKey);
         mVibrator = context.getSystemService(Vibrator.class);
-        mContentObserver = new ContentObserver(new Handler(/* async= */ true)) {
+        Handler handler = Looper.myLooper() != null ? new Handler(/* async= */ true) : null;
+        mContentObserver = new ContentObserver(handler) {
             @Override
             public void onChange(boolean selfChange, Uri uri) {
                 if (uri.equals(MAIN_VIBRATION_SWITCH_URI)) {
diff --git a/src/com/android/settings/accessibility/VibrationMainSwitchPreferenceController.java b/src/com/android/settings/accessibility/VibrationMainSwitchPreferenceController.java
index db184bf..5b553e3 100644
--- a/src/com/android/settings/accessibility/VibrationMainSwitchPreferenceController.java
+++ b/src/com/android/settings/accessibility/VibrationMainSwitchPreferenceController.java
@@ -23,6 +23,7 @@
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.VibrationAttributes;
 import android.os.Vibrator;
 import android.provider.Settings;
@@ -49,7 +50,8 @@
     public VibrationMainSwitchPreferenceController(Context context, String preferenceKey) {
         super(context, preferenceKey);
         mVibrator = context.getSystemService(Vibrator.class);
-        mSettingObserver = new ContentObserver(new Handler(/* async= */ true)) {
+        Handler handler = Looper.myLooper() != null ? new Handler(/* async= */ true) : null;
+        mSettingObserver = new ContentObserver(handler) {
             @Override
             public void onChange(boolean selfChange, Uri uri) {
                 updateState(mSwitchPreference);
diff --git a/src/com/android/settings/accessibility/VibrationPreferenceConfig.java b/src/com/android/settings/accessibility/VibrationPreferenceConfig.java
index ec1fab1..3887cec 100644
--- a/src/com/android/settings/accessibility/VibrationPreferenceConfig.java
+++ b/src/com/android/settings/accessibility/VibrationPreferenceConfig.java
@@ -27,6 +27,7 @@
 import android.media.AudioManager;
 import android.net.Uri;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
@@ -165,7 +166,7 @@
 
         /** Creates observer for given preference. */
         public SettingObserver(VibrationPreferenceConfig preferenceConfig) {
-            super(new Handler(/* async= */ true));
+            super(Looper.myLooper() != null ? new Handler(/* async= */ true) : null);
             mUri = Settings.System.getUriFor(preferenceConfig.getSettingKey());
 
             if (preferenceConfig.isRestrictedByRingerModeSilent()) {
diff --git a/src/com/android/settings/accessibility/VibrationRampingRingerTogglePreferenceController.java b/src/com/android/settings/accessibility/VibrationRampingRingerTogglePreferenceController.java
index 149bed3..69b1e15 100644
--- a/src/com/android/settings/accessibility/VibrationRampingRingerTogglePreferenceController.java
+++ b/src/com/android/settings/accessibility/VibrationRampingRingerTogglePreferenceController.java
@@ -21,6 +21,7 @@
 import android.media.AudioManager;
 import android.net.Uri;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.Vibrator;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
@@ -74,7 +75,8 @@
         mRingVibrationPreferenceConfig = new RingVibrationPreferenceConfig(context);
         mRingSettingObserver = new VibrationPreferenceConfig.SettingObserver(
                 mRingVibrationPreferenceConfig);
-        mSettingObserver = new ContentObserver(new Handler(/* async= */ true)) {
+        Handler handler = Looper.myLooper() != null ? new Handler(/* async= */ true) : null;
+        mSettingObserver = new ContentObserver(handler) {
             @Override
             public void onChange(boolean selfChange, Uri uri) {
                 updateState(mPreference);
diff --git a/src/com/android/settings/accessibility/ViewAllBluetoothDevicesPreferenceController.java b/src/com/android/settings/accessibility/ViewAllBluetoothDevicesPreferenceController.java
index 622d5d8..b35575e 100644
--- a/src/com/android/settings/accessibility/ViewAllBluetoothDevicesPreferenceController.java
+++ b/src/com/android/settings/accessibility/ViewAllBluetoothDevicesPreferenceController.java
@@ -26,6 +26,7 @@
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.core.SubSettingLauncher;
 import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.overlay.FeatureFactory;
 
 /** Preference controller for all bluetooth device preference. */
 public class ViewAllBluetoothDevicesPreferenceController extends BasePreferenceController {
@@ -52,6 +53,8 @@
     @Override
     public boolean handlePreferenceTreeClick(Preference preference) {
         if (TextUtils.equals(preference.getKey(), getPreferenceKey())) {
+            FeatureFactory.getFeatureFactory().getMetricsFeatureProvider().clicked(
+                    getMetricsCategory(), getPreferenceKey());
             launchConnectedDevicePage();
             return true;
         }
diff --git a/src/com/android/settings/applications/AppInfoBase.java b/src/com/android/settings/applications/AppInfoBase.java
index c056add..1d77482 100644
--- a/src/com/android/settings/applications/AppInfoBase.java
+++ b/src/com/android/settings/applications/AppInfoBase.java
@@ -136,14 +136,14 @@
                 mPackageName = intent.getData().getSchemeSpecificPart();
             }
         }
-        if (!hasInteractAcrossUsersPermission()) {
-            Log.w(TAG, "Intent not valid.");
-            finish();
-            return "";
-        }
         if (intent != null && intent.hasExtra(Intent.EXTRA_USER_HANDLE)) {
-            mUserId = ((UserHandle) intent.getParcelableExtra(
-                    Intent.EXTRA_USER_HANDLE)).getIdentifier();
+            mUserId = ((UserHandle) intent.getParcelableExtra(Intent.EXTRA_USER_HANDLE))
+                    .getIdentifier();
+            if (mUserId != UserHandle.myUserId() && !hasInteractAcrossUsersFullPermission()) {
+                Log.w(TAG, "Intent not valid.");
+                finish();
+                return "";
+            }
         } else {
             mUserId = UserHandle.myUserId();
         }
@@ -171,7 +171,7 @@
     }
 
     @VisibleForTesting
-    protected boolean hasInteractAcrossUsersPermission() {
+    protected boolean hasInteractAcrossUsersFullPermission() {
         Activity activity = getActivity();
         if (!(activity instanceof SettingsActivity)) {
             return false;
diff --git a/src/com/android/settings/biometrics/BiometricEnrollActivity.java b/src/com/android/settings/biometrics/BiometricEnrollActivity.java
index c9616f1..dbbe30b 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollActivity.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollActivity.java
@@ -19,6 +19,7 @@
 import static android.provider.Settings.ACTION_BIOMETRIC_ENROLL;
 import static android.provider.Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED;
 
+import static com.android.settings.biometrics.BiometricEnrollBase.BIOMETRIC_AUTH_REQUEST;
 import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_CONSENT_DENIED;
 import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_CONSENT_GRANTED;
 
@@ -51,6 +52,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.settings.R;
 import com.android.settings.SetupWizardUtils;
+import com.android.settings.Utils;
 import com.android.settings.core.InstrumentedActivity;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.password.ChooseLockGeneric;
@@ -442,6 +444,16 @@
                     if (!mParentalConsentHelper.launchNext(this, REQUEST_CHOOSE_OPTIONS)) {
                         Log.e(TAG, "Nothing to prompt for consent (no modalities enabled)!");
                         finish();
+                    } else {
+                        final Utils.BiometricStatus biometricStatus =
+                                Utils.requestBiometricAuthenticationForMandatoryBiometrics(this,
+                                        false /* biometricsAuthenticationRequested */, mUserId);
+                        if (biometricStatus == Utils.BiometricStatus.OK) {
+                            Utils.launchBiometricPromptForMandatoryBiometrics(this,
+                                    BIOMETRIC_AUTH_REQUEST, mUserId, true /* hideBackground */);
+                        } else if (biometricStatus != Utils.BiometricStatus.NOT_ACTIVE) {
+                            finish();
+                        }
                     }
                 } else {
                     Log.d(TAG, "Unknown result for set/choose lock: " + resultCode);
@@ -473,6 +485,10 @@
                     finish();
                 }
                 break;
+            case BIOMETRIC_AUTH_REQUEST:
+                if (resultCode != RESULT_OK) {
+                    finish();
+                }
             default:
                 Log.w(TAG, "Unknown consenting requestCode: " + requestCode + ", finishing");
                 finish();
diff --git a/src/com/android/settings/biometrics/BiometricEnrollBase.java b/src/com/android/settings/biometrics/BiometricEnrollBase.java
index 37ada23..3285a95 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollBase.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollBase.java
@@ -117,7 +117,6 @@
     public static final int LEARN_MORE_REQUEST = 3;
     public static final int CONFIRM_REQUEST = 4;
     public static final int ENROLL_REQUEST = 5;
-    public static final int BIOMETRIC_AUTH_REQUEST = 6;
 
     /**
      * Request code when starting another biometric enrollment from within a biometric flow. For
@@ -125,6 +124,7 @@
      */
     public static final int ENROLL_NEXT_BIOMETRIC_REQUEST = 6;
     public static final int REQUEST_POSTURE_GUIDANCE = 7;
+    public static final int BIOMETRIC_AUTH_REQUEST = 8;
 
     protected boolean mLaunchedConfirmLock;
     protected boolean mLaunchedPostureGuidance;
diff --git a/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java b/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java
index 5ebe9c7..6f9b94a 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java
@@ -36,6 +36,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.settings.R;
 import com.android.settings.SetupWizardUtils;
+import com.android.settings.Utils;
 import com.android.settings.password.ChooseLockGeneric;
 import com.android.settings.password.ChooseLockSettingsHelper;
 import com.android.settings.password.SetupSkipDialog;
@@ -417,6 +418,15 @@
                         getNextButton().setEnabled(true);
                     }));
                 }
+                final Utils.BiometricStatus biometricStatus =
+                        Utils.requestBiometricAuthenticationForMandatoryBiometrics(this,
+                                false /* biometricsAuthenticationRequested */, mUserId);
+                if (biometricStatus == Utils.BiometricStatus.OK) {
+                    Utils.launchBiometricPromptForMandatoryBiometrics(this,
+                            BIOMETRIC_AUTH_REQUEST, mUserId, true /* hideBackground */);
+                } else if (biometricStatus != Utils.BiometricStatus.NOT_ACTIVE) {
+                    finish();
+                }
             } else {
                 setResult(resultCode, data);
                 finish();
@@ -445,6 +455,10 @@
                 setResult(resultCode, data);
                 finish();
             }
+        } else if (requestCode == BIOMETRIC_AUTH_REQUEST) {
+            if (resultCode != RESULT_OK) {
+                finish();
+            }
         }
         super.onActivityResult(requestCode, resultCode, data);
     }
diff --git a/src/com/android/settings/biometrics/combination/CombinedBiometricSettings.java b/src/com/android/settings/biometrics/combination/CombinedBiometricSettings.java
index 671a5b6..7b3a724 100644
--- a/src/com/android/settings/biometrics/combination/CombinedBiometricSettings.java
+++ b/src/com/android/settings/biometrics/combination/CombinedBiometricSettings.java
@@ -24,14 +24,19 @@
 import android.os.Bundle;
 import android.os.UserHandle;
 
+import androidx.activity.result.ActivityResult;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
 import androidx.annotation.Nullable;
 import androidx.preference.Preference;
 
 import com.android.settings.R;
+import com.android.settings.biometrics.BiometricEnrollBase;
 import com.android.settings.biometrics.activeunlock.ActiveUnlockContentListener.OnContentChangedListener;
 import com.android.settings.biometrics.activeunlock.ActiveUnlockDeviceNameListener;
 import com.android.settings.biometrics.activeunlock.ActiveUnlockRequireBiometricSetup;
 import com.android.settings.biometrics.activeunlock.ActiveUnlockStatusUtils;
+import com.android.settings.flags.Flags;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settingslib.search.SearchIndexable;
 
@@ -52,6 +57,10 @@
     private CombinedBiometricStatusUtils mCombinedBiometricStatusUtils;
     @Nullable private ActiveUnlockDeviceNameListener mActiveUnlockDeviceNameListener;
 
+    private final ActivityResultLauncher<Intent> mActiveUnlockPreferenceLauncher =
+            registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
+                    this::onActiveUnlockPreferenceResult);
+
     @Override
     public void onAttach(Context context) {
         super.onAttach(context);
@@ -158,12 +167,25 @@
             intent = mActiveUnlockStatusUtils.getIntent();
         }
         if (intent != null) {
-            startActivityForResult(intent, ACTIVE_UNLOCK_REQUEST);
+            if (Flags.activeUnlockFinishParent()) {
+                mActiveUnlockPreferenceLauncher.launch(intent);
+            } else {
+                startActivityForResult(intent, ACTIVE_UNLOCK_REQUEST);
+            }
         }
         return true;
 
     }
 
+    private void onActiveUnlockPreferenceResult(@Nullable ActivityResult result) {
+        if (result != null && result.getResultCode() == BiometricEnrollBase.RESULT_TIMEOUT) {
+            mDoNotFinishActivity = false;
+            // When "Watch Unlock" is closed due to entering onStop(),
+            // "Face & Fingerprint Unlock" shall also close itself and back to "Security" page.
+            finish();
+        }
+    }
+
     @Override
     protected String getUseAnyBiometricSummary() {
         // either Active Unlock is not enabled or no device is enrolled.
diff --git a/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java
index bb56c50..22a39c8 100644
--- a/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java
@@ -26,6 +26,7 @@
 import com.android.settingslib.bluetooth.BluetoothUtils;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.utils.ThreadUtils;
 
 /** Controller to maintain available media Bluetooth devices */
 public class AvailableMediaBluetoothDeviceUpdater extends BluetoothDeviceUpdater
@@ -135,7 +136,9 @@
     @Override
     public boolean onPreferenceClick(Preference preference) {
         mMetricsFeatureProvider.logClickedPreference(preference, mMetricsCategory);
-        mDevicePreferenceCallback.onDeviceClick(preference);
+        var unused =
+                ThreadUtils.postOnBackgroundThread(
+                        () -> mDevicePreferenceCallback.onDeviceClick(preference));
         return true;
     }
 
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsHearingAidsPresetsController.java b/src/com/android/settings/bluetooth/BluetoothDetailsHearingAidsPresetsController.java
index f7ccc61..25a99ed 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsHearingAidsPresetsController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsHearingAidsPresetsController.java
@@ -38,6 +38,7 @@
 import androidx.preference.PreferenceScreen;
 
 import com.android.settings.R;
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.HapClientProfile;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -111,6 +112,7 @@
                 final int index = listPreference.findIndexOfValue(value);
                 final String presetName = listPreference.getEntries()[index].toString();
                 final int presetIndex = Integer.parseInt(value);
+                logPresetChangedIfNeeded();
                 listPreference.setSummary(presetName);
                 if (DEBUG) {
                     Log.d(TAG, "onPreferenceChange"
@@ -373,4 +375,15 @@
             mHapClientProfile.selectPreset(memberDevice.getDevice(), presetIndex);
         }
     }
+
+    private void logPresetChangedIfNeeded() {
+        if (mPreference == null || mPreference.getEntries() == null) {
+            return;
+        }
+        if (mFragment instanceof BluetoothDeviceDetailsFragment) {
+            int category = ((BluetoothDeviceDetailsFragment) mFragment).getMetricsCategory();
+            FeatureFactory.getFeatureFactory().getMetricsFeatureProvider().changed(category,
+                    getPreferenceKey(), mPreference.getEntries().length);
+        }
+    }
 }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
index d6ad4bc..8b4c7f2 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
@@ -59,6 +59,7 @@
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+import com.android.settingslib.utils.ThreadUtils;
 
 import java.util.Locale;
 import java.util.concurrent.Executor;
@@ -287,7 +288,8 @@
             if (AudioSharingUtils.isAudioSharingProfileReady(mProfileManager)) {
                 if (!mIntentHandled.get()) {
                     Log.d(TAG, "displayPreference: profile ready, handleDeviceClickFromIntent");
-                    handleDeviceClickFromIntent();
+                    var unused =
+                            ThreadUtils.postOnBackgroundThread(() -> handleDeviceClickFromIntent());
                     mIntentHandled.set(true);
                 }
             }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java
index 6f55a13..396144a 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java
@@ -25,6 +25,7 @@
 import android.bluetooth.BluetoothLeBroadcastMetadata;
 import android.bluetooth.BluetoothLeBroadcastReceiveState;
 import android.content.Context;
+import android.media.AudioManager;
 import android.os.Bundle;
 import android.util.Log;
 import android.util.Pair;
@@ -64,6 +65,7 @@
     @Nullable private final CachedBluetoothDeviceManager mDeviceManager;
     @Nullable private final LocalBluetoothLeBroadcast mBroadcast;
     @Nullable private final LocalBluetoothLeBroadcastAssistant mAssistant;
+    @Nullable private final AudioManager mAudioManager;
     private final MetricsFeatureProvider mMetricsFeatureProvider;
     private boolean mIsStoppingBroadcast = false;
 
@@ -157,6 +159,7 @@
                 mLocalBtManager != null
                         ? mLocalBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile()
                         : null;
+        mAudioManager = context.getSystemService(AudioManager.class);
         mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
     }
 
@@ -178,6 +181,22 @@
     public void handleDeviceConnected(
             @NonNull CachedBluetoothDevice cachedDevice, boolean userTriggered) {
         String anonymizedAddress = cachedDevice.getDevice().getAnonymizedAddress();
+        if (mAudioManager != null) {
+            int audioMode = mAudioManager.getMode();
+            if (audioMode == AudioManager.MODE_RINGTONE
+                    || audioMode == AudioManager.MODE_IN_CALL
+                    || audioMode == AudioManager.MODE_IN_COMMUNICATION) {
+                Log.d(TAG, "Skip handleDeviceConnected, audio mode = " + audioMode);
+                // TODO: add metric for this case
+                if (userTriggered) {
+                    // If this method is called with user triggered, e.g. manual click on the
+                    // "Connected devices" page, we need call setActive for the device, since user
+                    // intend to switch active device for the call.
+                    cachedDevice.setActive();
+                }
+                return;
+            }
+        }
         boolean isBroadcasting = isBroadcasting();
         boolean isLeAudioSupported = AudioSharingUtils.isLeAudioSupported(cachedDevice);
         if (!isLeAudioSupported) {
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java
index 9571241..ef461eb 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java
@@ -158,7 +158,11 @@
                                     dismiss();
                                 })
                         .setCustomNegativeButton(
-                                R.string.audio_sharing_no_thanks_button_label,
+                                getMetricsCategory() == SettingsEnums.DIALOG_START_AUDIO_SHARING
+                                        ? getString(
+                                                R.string.audio_sharing_switch_active_button_label,
+                                                newDeviceName)
+                                        : getString(R.string.audio_sharing_no_thanks_button_label),
                                 v -> {
                                     if (sListener != null) {
                                         sListener.onCancelClick();
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
index 8e09188..6501084 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
@@ -25,6 +25,7 @@
 import android.bluetooth.BluetoothLeBroadcastAssistant;
 import android.bluetooth.BluetoothLeBroadcastMetadata;
 import android.bluetooth.BluetoothLeBroadcastReceiveState;
+import android.bluetooth.BluetoothProfile;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -52,7 +53,10 @@
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.widget.SettingsMainSwitchBar;
+import com.android.settingslib.bluetooth.BluetoothCallback;
+import com.android.settingslib.bluetooth.BluetoothEventManager;
 import com.android.settingslib.bluetooth.BluetoothUtils;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -75,7 +79,8 @@
 public class AudioSharingSwitchBarController extends BasePreferenceController
         implements DefaultLifecycleObserver,
                 OnCheckedChangeListener,
-                LocalBluetoothProfileManager.ServiceListener {
+                LocalBluetoothProfileManager.ServiceListener,
+                BluetoothCallback {
     private static final String TAG = "AudioSharingSwitchCtlr";
     private static final String PREF_KEY = "audio_sharing_main_switch";
 
@@ -99,6 +104,7 @@
     private final SettingsMainSwitchBar mSwitchBar;
     private final BluetoothAdapter mBluetoothAdapter;
     @Nullable private final LocalBluetoothManager mBtManager;
+    @Nullable private final BluetoothEventManager mEventManager;
     @Nullable private final LocalBluetoothProfileManager mProfileManager;
     @Nullable private final LocalBluetoothLeBroadcast mBroadcast;
     @Nullable private final LocalBluetoothLeBroadcastAssistant mAssistant;
@@ -201,6 +207,19 @@
                                     + reason
                                     + ", broadcastId = "
                                     + broadcastId);
+                    if (mAssistant == null
+                            || mAssistant.getAllConnectedDevices().stream()
+                                    .anyMatch(
+                                            device -> BluetoothUtils
+                                                    .hasActiveLocalBroadcastSourceForBtDevice(
+                                                            device, mBtManager))) {
+                        Log.d(
+                                TAG,
+                                "Skip handleOnBroadcastReady: null assistant or "
+                                        + "sink has active local source.");
+                        cleanUp();
+                        return;
+                    }
                     handleOnBroadcastReady();
                 }
 
@@ -289,6 +308,7 @@
         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
         mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
         mBtManager = Utils.getLocalBtManager(context);
+        mEventManager = mBtManager == null ? null : mBtManager.getEventManager();
         mProfileManager = mBtManager == null ? null : mBtManager.getProfileManager();
         mBroadcast = mProfileManager == null ? null : mProfileManager.getLeAudioBroadcastProfile();
         mAssistant =
@@ -414,6 +434,13 @@
         // Do nothing.
     }
 
+    @Override
+    public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {
+        if (activeDevice != null && bluetoothProfile == BluetoothProfile.LE_AUDIO) {
+            updateSwitch();
+        }
+    }
+
     /**
      * Initialize the controller.
      *
@@ -434,7 +461,7 @@
             Log.d(TAG, "Skip registerCallbacks(). Feature is not available.");
             return;
         }
-        if (mBroadcast == null || mAssistant == null) {
+        if (mBroadcast == null || mAssistant == null || mEventManager == null) {
             Log.d(TAG, "Skip registerCallbacks(). Profile not support on this device.");
             return;
         }
@@ -443,6 +470,7 @@
             mSwitchBar.addOnSwitchChangeListener(this);
             mBroadcast.registerServiceCallBack(mExecutor, mBroadcastCallback);
             mAssistant.registerServiceCallBack(mExecutor, mBroadcastAssistantCallback);
+            mEventManager.registerCallback(this);
             mCallbacksRegistered.set(true);
         }
     }
@@ -452,7 +480,7 @@
             Log.d(TAG, "Skip unregisterCallbacks(). Feature is not available.");
             return;
         }
-        if (mBroadcast == null || mAssistant == null) {
+        if (mBroadcast == null || mAssistant == null || mEventManager == null) {
             Log.d(TAG, "Skip unregisterCallbacks(). Profile not support on this device.");
             return;
         }
@@ -461,6 +489,7 @@
             mSwitchBar.removeOnSwitchChangeListener(this);
             mBroadcast.unregisterServiceCallBack(mBroadcastCallback);
             mAssistant.unregisterServiceCallBack(mBroadcastAssistantCallback);
+            mEventManager.unregisterCallback(this);
             mCallbacksRegistered.set(false);
         }
     }
@@ -505,10 +534,15 @@
                 ThreadUtils.postOnBackgroundThread(
                         () -> {
                             boolean isBroadcasting = BluetoothUtils.isBroadcasting(mBtManager);
+                            boolean hasActiveDevice =
+                                    AudioSharingUtils.hasActiveConnectedLeadDevice(mBtManager);
                             boolean isStateReady =
                                     isBluetoothOn()
                                             && AudioSharingUtils.isAudioSharingProfileReady(
-                                                    mProfileManager);
+                                                    mProfileManager)
+                                            // Disable toggle till device gets active after
+                                            // broadcast ends.
+                                            && (isBroadcasting || hasActiveDevice);
                             AudioSharingUtils.postOnMainThread(
                                     mContext,
                                     () -> {
@@ -554,8 +588,7 @@
                         mGroupedConnectedDevices.getOrDefault(
                                 mDeviceItemsForSharing.get(0).getGroupId(), ImmutableList.of()),
                         mBtManager);
-                mGroupedConnectedDevices.clear();
-                mDeviceItemsForSharing.clear();
+                cleanUp();
                 // TODO: Add metric for auto add by intent
                 return;
             }
@@ -565,8 +598,7 @@
                 StartIntentHandleStage.HANDLED.ordinal());
         if (mFragment == null) {
             Log.d(TAG, "handleOnBroadcastReady: dialog fail to show due to null fragment.");
-            mGroupedConnectedDevices.clear();
-            mDeviceItemsForSharing.clear();
+            cleanUp();
             return;
         }
         showDialog(eventData);
@@ -581,14 +613,12 @@
                                 mGroupedConnectedDevices.getOrDefault(
                                         item.getGroupId(), ImmutableList.of()),
                                 mBtManager);
-                        mGroupedConnectedDevices.clear();
-                        mDeviceItemsForSharing.clear();
+                        cleanUp();
                     }
 
                     @Override
                     public void onCancelClick() {
-                        mGroupedConnectedDevices.clear();
-                        mDeviceItemsForSharing.clear();
+                        cleanUp();
                     }
                 };
         AudioSharingUtils.postOnMainThread(
@@ -657,6 +687,11 @@
                         });
     }
 
+    private void cleanUp() {
+        mGroupedConnectedDevices.clear();
+        mDeviceItemsForSharing.clear();
+    }
+
     private enum StartIntentHandleStage {
         TO_HANDLE,
         HANDLE_AUTO_ADD,
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java
index 0c2dc36..43256bc 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java
@@ -115,7 +115,7 @@
      * sharing. The active device is placed in the first place if it exists. The devices can be
      * filtered by whether it is already in the audio sharing session.
      *
-     * @param localBtManager The BT manager to provide BT functions. *
+     * @param localBtManager The BT manager to provide BT functions.
      * @param groupedConnectedDevices devices connected to broadcast assistant grouped by CSIP group
      *     id.
      * @param filterByInSharing Whether to filter the device by if is already in the sharing
@@ -190,7 +190,7 @@
      * sharing. The active device is placed in the first place if it exists. The devices can be
      * filtered by whether it is already in the audio sharing session.
      *
-     * @param localBtManager The BT manager to provide BT functions. *
+     * @param localBtManager The BT manager to provide BT functions.
      * @param groupedConnectedDevices devices connected to broadcast assistant grouped by CSIP group
      *     id.
      * @param filterByInSharing Whether to filter the device by if is already in the sharing
@@ -210,6 +210,22 @@
                 .collect(toList());
     }
 
+    /** Return if there exists active connected lead device. */
+    public static boolean hasActiveConnectedLeadDevice(
+            @Nullable LocalBluetoothManager localBtManager) {
+        CachedBluetoothDeviceManager deviceManager =
+                localBtManager == null ? null : localBtManager.getCachedDeviceManager();
+        Map<Integer, List<BluetoothDevice>> groupedConnectedDevices =
+                fetchConnectedDevicesByGroupId(localBtManager);
+        for (List<BluetoothDevice> devices : groupedConnectedDevices.values()) {
+            CachedBluetoothDevice leadDevice = getLeadDevice(deviceManager, devices);
+            if (isActiveLeAudioDevice(leadDevice)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /** Build {@link AudioSharingDeviceItem} from {@link CachedBluetoothDevice}. */
     public static AudioSharingDeviceItem buildAudioSharingDeviceItem(
             CachedBluetoothDevice cachedDevice) {
@@ -225,8 +241,8 @@
      * @param cachedDevice The cached bluetooth device to check.
      * @return Whether the device is an active le audio device.
      */
-    public static boolean isActiveLeAudioDevice(CachedBluetoothDevice cachedDevice) {
-        return BluetoothUtils.isActiveLeAudioDevice(cachedDevice);
+    public static boolean isActiveLeAudioDevice(@Nullable CachedBluetoothDevice cachedDevice) {
+        return cachedDevice != null && BluetoothUtils.isActiveLeAudioDevice(cachedDevice);
     }
 
     /** Toast message on main thread. */
diff --git a/src/com/android/settings/connecteddevice/fastpair/FastPairDeviceGroupController.java b/src/com/android/settings/connecteddevice/fastpair/FastPairDeviceGroupController.java
index 52ad91c..6b77b47 100644
--- a/src/com/android/settings/connecteddevice/fastpair/FastPairDeviceGroupController.java
+++ b/src/com/android/settings/connecteddevice/fastpair/FastPairDeviceGroupController.java
@@ -35,7 +35,6 @@
 import com.android.settings.connecteddevice.DevicePreferenceCallback;
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settings.flags.Flags;
 import com.android.settings.overlay.FeatureFactory;
 
 /**
@@ -66,15 +65,11 @@
 
     public FastPairDeviceGroupController(Context context) {
         super(context, KEY);
-        if (Flags.enableSubsequentPairSettingsIntegration()) {
-            FastPairFeatureProvider fastPairFeatureProvider =
-                    FeatureFactory.getFeatureFactory().getFastPairFeatureProvider();
-            mFastPairDeviceUpdater =
-                    fastPairFeatureProvider.getFastPairDeviceUpdater(context, this);
-        } else {
-            Log.d(TAG, "Flag disabled. Ignored.");
-            mFastPairDeviceUpdater = null;
-        }
+        FastPairFeatureProvider fastPairFeatureProvider =
+                FeatureFactory.getFeatureFactory().getFastPairFeatureProvider();
+        mFastPairDeviceUpdater =
+                fastPairFeatureProvider.getFastPairDeviceUpdater(context, this);
+
         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
         mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
     }
diff --git a/src/com/android/settings/connecteddevice/fastpair/FastPairDevicePreferenceController.java b/src/com/android/settings/connecteddevice/fastpair/FastPairDevicePreferenceController.java
index 2df7d11..cb365a5 100644
--- a/src/com/android/settings/connecteddevice/fastpair/FastPairDevicePreferenceController.java
+++ b/src/com/android/settings/connecteddevice/fastpair/FastPairDevicePreferenceController.java
@@ -34,7 +34,6 @@
 
 import com.android.settings.connecteddevice.DevicePreferenceCallback;
 import com.android.settings.core.BasePreferenceController;
-import com.android.settings.flags.Flags;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.utils.ThreadUtils;
 
@@ -76,15 +75,10 @@
     public FastPairDevicePreferenceController(Context context, String preferenceKey) {
         super(context, preferenceKey);
 
-        if (Flags.enableSubsequentPairSettingsIntegration()) {
-            FastPairFeatureProvider fastPairFeatureProvider =
-                    FeatureFactory.getFeatureFactory().getFastPairFeatureProvider();
-            mFastPairDeviceUpdater =
-                    fastPairFeatureProvider.getFastPairDeviceUpdater(context, this);
-        } else {
-            Log.d(TAG, "Flag disabled. Ignore.");
-            mFastPairDeviceUpdater = null;
-        }
+        FastPairFeatureProvider fastPairFeatureProvider =
+                FeatureFactory.getFeatureFactory().getFastPairFeatureProvider();
+        mFastPairDeviceUpdater =
+                fastPairFeatureProvider.getFastPairDeviceUpdater(context, this);
         mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
     }
diff --git a/src/com/android/settings/datausage/BillingCyclePreferenceController.java b/src/com/android/settings/datausage/BillingCyclePreferenceController.java
deleted file mode 100644
index 8b55585..0000000
--- a/src/com/android/settings/datausage/BillingCyclePreferenceController.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2019 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.datausage;
-
-import android.content.Context;
-import android.net.NetworkTemplate;
-
-import androidx.preference.PreferenceScreen;
-
-import com.android.settings.core.BasePreferenceController;
-import com.android.settings.datausage.lib.DataUsageLib;
-
-public class BillingCyclePreferenceController extends BasePreferenceController {
-    private int mSubscriptionId;
-
-    public BillingCyclePreferenceController(Context context, String preferenceKey) {
-        super(context, preferenceKey);
-    }
-
-    public void init(int subscriptionId) {
-        mSubscriptionId = subscriptionId;
-    }
-
-    @Override
-    public void displayPreference(PreferenceScreen screen) {
-        super.displayPreference(screen);
-        BillingCyclePreference preference = screen.findPreference(getPreferenceKey());
-
-        NetworkTemplate template = DataUsageLib.getMobileTemplate(mContext, mSubscriptionId);
-
-        preference.setTemplate(template, mSubscriptionId);
-    }
-
-    @Override
-    public int getAvailabilityStatus() {
-        return DataUsageUtils.hasMobileData(mContext) ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
-    }
-}
diff --git a/src/com/android/settings/datausage/BillingCyclePreferenceController.kt b/src/com/android/settings/datausage/BillingCyclePreferenceController.kt
new file mode 100644
index 0000000..f699743
--- /dev/null
+++ b/src/com/android/settings/datausage/BillingCyclePreferenceController.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.datausage
+
+import android.content.Context
+import android.telephony.SubscriptionManager
+import androidx.preference.PreferenceScreen
+import com.android.settings.R
+import com.android.settings.core.BasePreferenceController
+import com.android.settings.datausage.lib.DataUsageLib.getMobileTemplate
+import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem
+import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchResult
+
+class BillingCyclePreferenceController(context: Context, preferenceKey: String) :
+    BasePreferenceController(context, preferenceKey) {
+    private var subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID
+
+    fun init(subId: Int) {
+        this.subId = subId
+    }
+
+    override fun getAvailabilityStatus() =
+        if (DataUsageUtils.hasMobileData(mContext)) AVAILABLE else CONDITIONALLY_UNAVAILABLE
+
+    override fun displayPreference(screen: PreferenceScreen) {
+        super.displayPreference(screen)
+        val preference = screen.findPreference<BillingCyclePreference>(preferenceKey)
+        val template = getMobileTemplate(mContext, subId)
+        preference?.setTemplate(template, subId)
+    }
+
+    companion object {
+        class BillingCycleSearchItem(private val context: Context) :
+            MobileNetworkSettingsSearchItem {
+            override fun getSearchResult(subId: Int): MobileNetworkSettingsSearchResult? {
+                if (!DataUsageUtils.hasMobileData(context)) return null
+                return MobileNetworkSettingsSearchResult(
+                    key = "billing_preference",
+                    title = context.getString(R.string.billing_cycle),
+                )
+            }
+        }
+    }
+}
diff --git a/src/com/android/settings/datetime/AutoTimeZonePreferenceController.java b/src/com/android/settings/datetime/AutoTimeZonePreferenceController.java
index 3a1f995..8eccf31 100644
--- a/src/com/android/settings/datetime/AutoTimeZonePreferenceController.java
+++ b/src/com/android/settings/datetime/AutoTimeZonePreferenceController.java
@@ -27,6 +27,8 @@
 import android.app.time.TimeZoneConfiguration;
 import android.content.Context;
 
+import androidx.preference.Preference;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.R;
 import com.android.settings.core.TogglePreferenceController;
@@ -116,6 +118,12 @@
     }
 
     @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+        refreshSummary(preference);
+    }
+
+    @Override
     public CharSequence getSummary() {
         // If auto time zone cannot enable telephony fallback and is capable of location, then auto
         // time zone must use location.
diff --git a/src/com/android/settings/development/DevelopmentMemtagFooterPreferenceController.java b/src/com/android/settings/development/DevelopmentMemtagFooterPreferenceController.java
index a5612ca..f2c9f59 100644
--- a/src/com/android/settings/development/DevelopmentMemtagFooterPreferenceController.java
+++ b/src/com/android/settings/development/DevelopmentMemtagFooterPreferenceController.java
@@ -14,7 +14,7 @@
  * limitations under the License
  */
 
-package com.android.settings.security;
+package com.android.settings.development;
 
 import android.content.Context;
 import android.text.TextUtils;
diff --git a/src/com/android/settings/deviceinfo/StorageItemPreference.java b/src/com/android/settings/deviceinfo/StorageItemPreference.java
index 91102a0..62c0a6a 100644
--- a/src/com/android/settings/deviceinfo/StorageItemPreference.java
+++ b/src/com/android/settings/deviceinfo/StorageItemPreference.java
@@ -22,6 +22,7 @@
 import android.util.AttributeSet;
 import android.widget.ProgressBar;
 
+import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceViewHolder;
 
@@ -48,6 +49,7 @@
         setLayoutResource(R.layout.storage_item);
     }
 
+    @VisibleForTesting
     public void setStorageSize(long size, long total) {
         setStorageSize(size, total, false /* animate */);
     }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
index da8481d..ebe7996 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
@@ -172,13 +172,21 @@
     /** Gets the {@link BatteryUsageStats} from system service. */
     @Nullable
     public static BatteryUsageStats getBatteryUsageStats(final Context context) {
+        final long startTime = System.currentTimeMillis();
         final BatteryUsageStatsQuery batteryUsageStatsQuery =
                 new BatteryUsageStatsQuery.Builder()
                         .includeBatteryHistory()
                         .includeProcessStateData()
                         .build();
-        return context.getSystemService(BatteryStatsManager.class)
-                .getBatteryUsageStats(batteryUsageStatsQuery);
+        final BatteryUsageStats batteryUsageStats =
+                context.getSystemService(BatteryStatsManager.class)
+                        .getBatteryUsageStats(batteryUsageStatsQuery);
+        Log.d(
+                TAG,
+                String.format(
+                        "getBatteryUsageStats() from BatteryStatsManager in %d/ms",
+                        System.currentTimeMillis() - startTime));
+        return batteryUsageStats;
     }
 
     /** Gets the {@link UsageEvents} from system service for all unlocked users. */
@@ -1693,8 +1701,14 @@
         final UserManager userManager = context.getSystemService(UserManager.class);
         final SparseArray<BatteryEntry> batteryEntryList = new SparseArray<>();
         final ArrayList<BatteryEntry> results = new ArrayList<>();
+        final long startTime = System.currentTimeMillis();
         final List<UidBatteryConsumer> uidBatteryConsumers =
                 batteryUsageStats.getUidBatteryConsumers();
+        Log.d(
+                TAG,
+                String.format(
+                        "get %d uidBatteryConsumers from BatteryUsageStats in %d/ms",
+                        uidBatteryConsumers.size(), (System.currentTimeMillis() - startTime)));
 
         // Sort to have all apps with "real" UIDs first, followed by apps that are supposed
         // to be combined with the real ones.
@@ -1763,9 +1777,11 @@
                             deviceConsumer.getConsumedPowerForCustomComponent(componentId)));
         }
 
+        final int numComponentEntries = batteryEntryList.size();
         final List<UserBatteryConsumer> userBatteryConsumers =
                 batteryUsageStats.getUserBatteryConsumers();
-        for (int i = 0, size = userBatteryConsumers.size(); i < size; i++) {
+        final int numUserEntries = userBatteryConsumers.size();
+        for (int i = 0; i < numUserEntries; i++) {
             final UserBatteryConsumer consumer = userBatteryConsumers.get(i);
             results.add(
                     new BatteryEntry(
@@ -1785,6 +1801,13 @@
             results.add(batteryEntryList.valueAt(i));
         }
 
+        Log.d(
+                TAG,
+                String.format(
+                        "getCoalescedUsageList(): uidEntries = %d, "
+                                + "userEntries = %d, componentEntries = %d",
+                        numUidSippers, numUserEntries, numComponentEntries));
+
         // The sort order must have changed, so re-sort based on total power use.
         results.sort(BatteryEntry.COMPARATOR);
         return results;
diff --git a/src/com/android/settings/network/InternetPreferenceController.java b/src/com/android/settings/network/InternetPreferenceController.java
deleted file mode 100644
index aed44d5..0000000
--- a/src/com/android/settings/network/InternetPreferenceController.java
+++ /dev/null
@@ -1,316 +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;
-
-import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE;
-import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
-
-import static com.android.settings.network.InternetUpdater.INTERNET_CELLULAR;
-import static com.android.settings.network.InternetUpdater.INTERNET_ETHERNET;
-import static com.android.settings.network.InternetUpdater.INTERNET_NETWORKS_AVAILABLE;
-import static com.android.settings.network.InternetUpdater.INTERNET_OFF;
-import static com.android.settings.network.InternetUpdater.INTERNET_WIFI;
-import static com.android.settingslib.wifi.WifiUtils.getHotspotIconResource;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.telephony.SubscriptionManager;
-
-import androidx.annotation.IdRes;
-import androidx.annotation.VisibleForTesting;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleObserver;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.lifecycle.OnLifecycleEvent;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
-
-import com.android.settings.R;
-import com.android.settings.widget.SummaryUpdater;
-import com.android.settings.wifi.WifiPickerTrackerHelper;
-import com.android.settings.wifi.WifiSummaryUpdater;
-import com.android.settings.wifi.repository.SharedConnectivityRepository;
-import com.android.settingslib.Utils;
-import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
-import com.android.settingslib.utils.ThreadUtils;
-import com.android.wifitrackerlib.HotspotNetworkEntry;
-import com.android.wifitrackerlib.WifiEntry;
-import com.android.wifitrackerlib.WifiPickerTracker;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * PreferenceController to update the internet state.
- */
-public class InternetPreferenceController extends AbstractPreferenceController implements
-        LifecycleObserver, SummaryUpdater.OnSummaryChangeListener,
-        InternetUpdater.InternetChangeListener, MobileNetworkRepository.MobileNetworkCallback,
-        DefaultSubscriptionReceiver.DefaultSubscriptionListener,
-        WifiPickerTracker.WifiPickerTrackerCallback {
-
-    public static final String KEY = "internet_settings";
-
-    private Preference mPreference;
-    @VisibleForTesting
-    WifiSummaryUpdater mSummaryHelper;
-    private InternetUpdater mInternetUpdater;
-    private @InternetUpdater.InternetType int mInternetType;
-    private LifecycleOwner mLifecycleOwner;
-    private MobileNetworkRepository mMobileNetworkRepository;
-    private List<SubscriptionInfoEntity> mSubInfoEntityList = new ArrayList<>();
-    private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-    private DefaultSubscriptionReceiver mDataSubscriptionChangedReceiver;
-    private boolean mIsHotspotNetworkEnabled = SharedConnectivityRepository.isDeviceConfigEnabled();
-    @VisibleForTesting
-    WifiPickerTrackerHelper mWifiPickerTrackerHelper;
-
-    @VisibleForTesting
-    static Map<Integer, Integer> sIconMap = new HashMap<>();
-    static {
-        sIconMap.put(INTERNET_OFF, R.drawable.ic_no_internet_unavailable);
-        sIconMap.put(INTERNET_NETWORKS_AVAILABLE, R.drawable.ic_no_internet_available);
-        sIconMap.put(INTERNET_WIFI, R.drawable.ic_wifi_signal_4);
-        sIconMap.put(INTERNET_CELLULAR, R.drawable.ic_network_cell);
-        sIconMap.put(INTERNET_ETHERNET, R.drawable.ic_settings_ethernet);
-    }
-
-    private static Map<Integer, Integer> sSummaryMap = new HashMap<>();
-    static {
-        sSummaryMap.put(INTERNET_OFF, R.string.condition_airplane_title);
-        sSummaryMap.put(INTERNET_NETWORKS_AVAILABLE, R.string.networks_available);
-        sSummaryMap.put(INTERNET_WIFI, 0);
-        sSummaryMap.put(INTERNET_CELLULAR, 0);
-        sSummaryMap.put(INTERNET_ETHERNET, R.string.to_switch_networks_disconnect_ethernet);
-    }
-
-    public InternetPreferenceController(Context context, Lifecycle lifecycle,
-            LifecycleOwner lifecycleOwner) {
-        super(context);
-        if (lifecycle == null) {
-            throw new IllegalArgumentException("Lifecycle must be set");
-        }
-        mSummaryHelper = new WifiSummaryUpdater(mContext, this);
-        mInternetUpdater = new InternetUpdater(context, lifecycle, this);
-        mInternetType = mInternetUpdater.getInternetType();
-        mLifecycleOwner = lifecycleOwner;
-        mMobileNetworkRepository = MobileNetworkRepository.getInstance(context);
-        mDataSubscriptionChangedReceiver = new DefaultSubscriptionReceiver(context, this);
-        if (mIsHotspotNetworkEnabled) {
-            mWifiPickerTrackerHelper = new WifiPickerTrackerHelper(lifecycle, context, this);
-        }
-        lifecycle.addObserver(this);
-    }
-
-    @Override
-    public void displayPreference(PreferenceScreen screen) {
-        super.displayPreference(screen);
-        mPreference = screen.findPreference(KEY);
-    }
-
-    private void drawIcon(int iconResId) {
-        Drawable drawable = mContext.getDrawable(iconResId);
-        if (drawable != null) {
-            drawable.setTintList(Utils.getColorAttr(mContext, android.R.attr.colorControlNormal));
-            mPreference.setIcon(drawable);
-        }
-    }
-
-    @Override
-    public void updateState(Preference preference) {
-        if (mPreference == null) {
-            return;
-        }
-
-        if (mInternetType == INTERNET_WIFI && updateHotspotNetwork()) {
-            return;
-        }
-
-        final @IdRes int icon = sIconMap.get(mInternetType);
-        if (icon != 0) {
-            drawIcon(icon);
-        }
-
-        if (mInternetType == INTERNET_WIFI) {
-            mPreference.setSummary(mSummaryHelper.getSummary());
-            return;
-        }
-
-        if (mInternetType == INTERNET_CELLULAR) {
-            updateCellularSummary();
-            return;
-        }
-
-        final @IdRes int summary = sSummaryMap.get(mInternetType);
-        if (summary != 0) {
-            mPreference.setSummary(summary);
-        }
-    }
-
-    @VisibleForTesting
-    boolean updateHotspotNetwork() {
-        if (mWifiPickerTrackerHelper == null) {
-            return false;
-        }
-        WifiEntry entry = mWifiPickerTrackerHelper.getWifiPickerTracker().getConnectedWifiEntry();
-        if (!(entry instanceof HotspotNetworkEntry)) {
-            return false;
-        }
-        drawIcon(getHotspotIconResource(((HotspotNetworkEntry) entry).getDeviceType()));
-        mPreference.setSummary(((HotspotNetworkEntry) entry).getAlternateSummary());
-        return true;
-    }
-
-    @Override
-    public boolean isAvailable() {
-        return mContext.getResources().getBoolean(R.bool.config_show_internet_settings);
-    }
-
-    @Override
-    public String getPreferenceKey() {
-        return KEY;
-    }
-
-    /** @OnLifecycleEvent(ON_RESUME) */
-    @OnLifecycleEvent(ON_RESUME)
-    public void onResume() {
-        mMobileNetworkRepository.addRegister(mLifecycleOwner, this,
-                SubscriptionManager.INVALID_SUBSCRIPTION_ID);
-        mMobileNetworkRepository.updateEntity();
-        mSummaryHelper.register(true);
-        mDataSubscriptionChangedReceiver.registerReceiver();
-        mDefaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
-    }
-
-    /** @OnLifecycleEvent(ON_PAUSE) */
-    @OnLifecycleEvent(ON_PAUSE)
-    public void onPause() {
-        mMobileNetworkRepository.removeRegister(this);
-        mSummaryHelper.register(false);
-        mDataSubscriptionChangedReceiver.unRegisterReceiver();
-    }
-
-    /**
-     * Called when internet type is changed.
-     *
-     * @param internetType the internet type
-     */
-    @Override
-    public void onInternetTypeChanged(@InternetUpdater.InternetType int internetType) {
-        final boolean needUpdate = (internetType != mInternetType);
-        mInternetType = internetType;
-        if (needUpdate) {
-            ThreadUtils.postOnMainThread(() -> {
-                updateState(mPreference);
-            });
-        }
-    }
-
-    /**
-     * Called when airplane mode state is changed.
-     */
-    @Override
-    public void onAirplaneModeChanged(boolean isAirplaneModeOn) {
-        ThreadUtils.postOnMainThread(() -> {
-            updateState(mPreference);
-        });
-    }
-
-    @Override
-    public void onSummaryChanged(String summary) {
-        if (mInternetType == INTERNET_WIFI) {
-            updateState(mPreference);
-        }
-    }
-
-    @VisibleForTesting
-    void updateCellularSummary() {
-        CharSequence summary = null;
-        SubscriptionInfoEntity activeSubInfo = null;
-        SubscriptionInfoEntity defaultSubInfo = null;
-
-        for (SubscriptionInfoEntity subInfo : getSubscriptionInfoList()) {
-            if (subInfo.isActiveDataSubscriptionId) {
-                activeSubInfo = subInfo;
-            }
-            if (subInfo.getSubId() == getDefaultDataSubscriptionId()) {
-                defaultSubInfo = subInfo;
-            }
-        }
-        if (activeSubInfo == null || defaultSubInfo == null) {
-            return;
-        }
-        activeSubInfo = activeSubInfo.isSubscriptionVisible ? activeSubInfo : defaultSubInfo;
-
-        if (activeSubInfo.equals(defaultSubInfo)) {
-            // DDS is active
-            summary = activeSubInfo.uniqueName;
-        } else {
-            summary = mContext.getString(
-                    R.string.mobile_data_temp_using, activeSubInfo.uniqueName);
-        }
-
-        mPreference.setSummary(summary);
-    }
-
-    @VisibleForTesting
-    protected List<SubscriptionInfoEntity> getSubscriptionInfoList() {
-        return mSubInfoEntityList;
-    }
-
-    @VisibleForTesting
-    protected int getDefaultDataSubscriptionId() {
-        return mDefaultDataSubId;
-    }
-
-    @Override
-    public void onAvailableSubInfoChanged(List<SubscriptionInfoEntity> subInfoEntityList) {
-        mSubInfoEntityList = subInfoEntityList;
-        updateState(mPreference);
-    }
-
-    @Override
-    public void onDefaultDataChanged(int defaultDataSubId) {
-        mDefaultDataSubId = defaultDataSubId;
-        updateState(mPreference);
-    }
-
-    @Override
-    public void onWifiEntriesChanged() {
-        if (mInternetType == INTERNET_WIFI) {
-            updateState(mPreference);
-        }
-    }
-
-    @Override
-    public void onWifiStateChanged() {
-        // Do nothing
-    }
-
-    @Override
-    public void onNumSavedNetworksChanged() {
-        // Do nothing
-    }
-
-    @Override
-    public void onNumSavedSubscriptionsChanged() {
-        // Do nothing
-    }
-}
diff --git a/src/com/android/settings/network/MobileNetworkListFragment.kt b/src/com/android/settings/network/MobileNetworkListFragment.kt
index eb0d16c..bb88330 100644
--- a/src/com/android/settings/network/MobileNetworkListFragment.kt
+++ b/src/com/android/settings/network/MobileNetworkListFragment.kt
@@ -27,7 +27,7 @@
 import com.android.settings.SettingsPreferenceFragment
 import com.android.settings.dashboard.DashboardFragment
 import com.android.settings.flags.Flags
-import com.android.settings.network.telephony.MobileNetworkUtils
+import com.android.settings.network.telephony.euicc.EuiccRepository
 import com.android.settings.search.BaseSearchIndexProvider
 import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
 import com.android.settings.spa.network.NetworkCellularGroupProvider
@@ -58,7 +58,7 @@
         listView.itemAnimator = null
 
         findPreference<Preference>(KEY_ADD_SIM)!!.isVisible =
-            MobileNetworkUtils.showEuiccSettings(context)
+            EuiccRepository(requireContext()).showEuiccSettings()
     }
 
     override fun getPreferenceScreenResId() = R.xml.network_provider_sims_list
diff --git a/src/com/android/settings/network/MobileNetworkSummaryController.java b/src/com/android/settings/network/MobileNetworkSummaryController.java
index 4627a25..9bf6915 100644
--- a/src/com/android/settings/network/MobileNetworkSummaryController.java
+++ b/src/com/android/settings/network/MobileNetworkSummaryController.java
@@ -35,7 +35,7 @@
 import com.android.settings.R;
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.dashboard.DashboardFragment;
-import com.android.settings.network.telephony.MobileNetworkUtils;
+import com.android.settings.network.telephony.euicc.EuiccRepository;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.RestrictedPreference;
 import com.android.settingslib.Utils;
@@ -118,7 +118,7 @@
         if ((mSubInfoEntityList == null || mSubInfoEntityList.isEmpty()) || (
                 mUiccInfoEntityList == null || mUiccInfoEntityList.isEmpty()) || (
                 mMobileNetworkInfoEntityList == null || mMobileNetworkInfoEntityList.isEmpty())) {
-            if (MobileNetworkUtils.showEuiccSettingsDetecting(mContext)) {
+            if (new EuiccRepository(mContext).showEuiccSettings()) {
                 return mContext.getResources().getString(
                         R.string.mobile_network_summary_add_a_network);
             }
@@ -168,7 +168,7 @@
                 || (mUiccInfoEntityList == null || mUiccInfoEntityList.isEmpty())
                 || (mMobileNetworkInfoEntityList == null
                 || mMobileNetworkInfoEntityList.isEmpty()))) {
-            if (MobileNetworkUtils.showEuiccSettingsDetecting(mContext)) {
+            if (new EuiccRepository(mContext).showEuiccSettings()) {
                 mPreference.setOnPreferenceClickListener((Preference pref) -> {
                     logPreferenceClick(pref);
                     startAddSimFlow();
diff --git a/src/com/android/settings/network/NetworkDashboardFragment.java b/src/com/android/settings/network/NetworkDashboardFragment.java
index 3bc5358..aff9130 100644
--- a/src/com/android/settings/network/NetworkDashboardFragment.java
+++ b/src/com/android/settings/network/NetworkDashboardFragment.java
@@ -25,7 +25,6 @@
 import com.android.settings.SettingsDumpService;
 import com.android.settings.core.OnActivityResultListener;
 import com.android.settings.dashboard.DashboardFragment;
-import com.android.settings.flags.Flags;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.core.lifecycle.Lifecycle;
@@ -90,14 +89,6 @@
 
         controllers.add(new MobileNetworkSummaryController(context, lifecycle, lifecycleOwner));
         controllers.add(vpnPreferenceController);
-
-        if (Flags.internetPreferenceControllerV2()) {
-            controllers.add(
-                    new InternetPreferenceControllerV2(context, InternetPreferenceController.KEY));
-        } else {
-            controllers.add(new InternetPreferenceController(context, lifecycle, lifecycleOwner));
-        }
-
         controllers.add(privateDnsPreferenceController);
 
         // Start SettingsDumpService after the MobileNetworkRepository is created.
diff --git a/src/com/android/settings/network/apn/ApnRepository.kt b/src/com/android/settings/network/apn/ApnRepository.kt
index 8433715..7ed0c86 100644
--- a/src/com/android/settings/network/apn/ApnRepository.kt
+++ b/src/com/android/settings/network/apn/ApnRepository.kt
@@ -21,10 +21,10 @@
 import android.database.Cursor
 import android.net.Uri
 import android.provider.Telephony
-import android.telephony.SubscriptionManager
 import android.telephony.TelephonyManager
 import android.util.Log
 import com.android.settings.R
+import com.android.settings.network.telephony.telephonyManager
 import com.android.settingslib.utils.ThreadUtils
 import java.util.Locale
 
@@ -178,12 +178,11 @@
 }
 
 fun Context.getApnIdMap(subId: Int): Map<String, Any> {
-    val subInfo = getSystemService(SubscriptionManager::class.java)!!
-        .getActiveSubscriptionInfo(subId)
-    val carrierId = subInfo.carrierId
+    val telephonyManager = telephonyManager(subId)
+    val carrierId = telephonyManager.simSpecificCarrierId
     return if (carrierId != TelephonyManager.UNKNOWN_CARRIER_ID) {
         mapOf(Telephony.Carriers.CARRIER_ID to carrierId)
     } else {
-        mapOf(Telephony.Carriers.NUMERIC to subInfo.mccString + subInfo.mncString)
+        mapOf(Telephony.Carriers.NUMERIC to telephonyManager.simOperator)
     }.also { Log.d(TAG, "[$subId] New APN item with id: $it") }
 }
diff --git a/src/com/android/settings/network/ims/VtQueryImsState.java b/src/com/android/settings/network/ims/VtQueryImsState.java
index 5c48ff0..7351b83 100644
--- a/src/com/android/settings/network/ims/VtQueryImsState.java
+++ b/src/com/android/settings/network/ims/VtQueryImsState.java
@@ -18,24 +18,17 @@
 
 import android.content.Context;
 import android.telecom.TelecomManager;
-import android.telephony.AccessNetworkConstants;
 import android.telephony.SubscriptionManager;
-import android.telephony.ims.ImsException;
-import android.telephony.ims.feature.MmTelFeature;
-import android.telephony.ims.stub.ImsRegistrationImplBase;
-import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
 
 /**
  * Controller class for querying VT status
  */
-public class VtQueryImsState extends ImsQueryController {
+public class VtQueryImsState {
 
-    private static final String LOG_TAG = "VtQueryImsState";
-
-    private Context mContext;
-    private int mSubId;
+    private final Context mContext;
+    private final int mSubId;
 
     /**
      * Constructor
@@ -44,9 +37,6 @@
      * @param subId subscription's id
      */
     public VtQueryImsState(Context context, int subId) {
-        super(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO,
-                ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
-                AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
         mContext = context;
         mSubId = subId;
     }
@@ -63,24 +53,6 @@
     }
 
     /**
-     * Check whether Video Call can be perform or not on this subscription
-     *
-     * @return true when Video Call can be performed, otherwise false
-     */
-    public boolean isReadyToVideoCall() {
-        if (!isProvisionedOnDevice(mSubId)) {
-            return false;
-        }
-
-        try {
-            return isEnabledByPlatform(mSubId) && isServiceStateReady(mSubId);
-        } catch (InterruptedException | IllegalArgumentException | ImsException exception) {
-            Log.w(LOG_TAG, "fail to get Vt ready. subId=" + mSubId, exception);
-        }
-        return false;
-    }
-
-    /**
      * Get allowance status for user to alter configuration
      *
      * @return true when changing configuration by user is allowed.
@@ -89,8 +61,7 @@
         if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
             return false;
         }
-        return ((!isTtyEnabled(mContext))
-                || (isTtyOnVolteEnabled(mSubId)));
+        return !isTtyEnabled(mContext) || new ImsQueryTtyOnVolteStat(mSubId).query();
     }
 
     @VisibleForTesting
diff --git a/src/com/android/settings/network/telephony/CarrierConfigRepository.kt b/src/com/android/settings/network/telephony/CarrierConfigRepository.kt
index 3f5c06e..8852540 100644
--- a/src/com/android/settings/network/telephony/CarrierConfigRepository.kt
+++ b/src/com/android/settings/network/telephony/CarrierConfigRepository.kt
@@ -50,7 +50,7 @@
         private val keysToRetrieve = mutableMapOf<String, KeyType>()
 
         override fun getBoolean(key: String): Boolean {
-            check(key.endsWith("_bool")) { "Boolean key should ends with _bool" }
+            checkBooleanKey(key)
             val value = cache[key]
             return if (value == null) {
                 keysToRetrieve += key to KeyType.BOOLEAN
@@ -186,9 +186,18 @@
             ListenerRegistered.getAndSet(false)
         }
 
+        private val BooleanKeysWhichNotFollowingsNamingConventions =
+            listOf(CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS)
+
+        private fun checkBooleanKey(key: String) {
+            check(key.endsWith("_bool") || key in BooleanKeysWhichNotFollowingsNamingConventions) {
+                "Boolean key should ends with _bool"
+            }
+        }
+
         @VisibleForTesting
         fun setBooleanForTest(subId: Int, key: String, value: Boolean) {
-            check(key.endsWith("_bool")) { "Boolean key should ends with _bool" }
+            checkBooleanKey(key)
             getPerSubCache(subId)[key] = BooleanConfigValue(value)
         }
 
diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
index a5cdb95..91874c4 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
@@ -41,6 +41,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
+import androidx.lifecycle.LifecycleOwner;
 import androidx.lifecycle.ViewModelProvider;
 import androidx.preference.Preference;
 
@@ -66,6 +67,8 @@
 import com.android.settingslib.search.SearchIndexable;
 import com.android.settingslib.utils.ThreadUtils;
 
+import kotlin.Unit;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -359,6 +362,16 @@
     public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
         collectAirplaneModeAndFinishIfOn(this);
+
+        LifecycleOwner viewLifecycleOwner = getViewLifecycleOwner();
+        new SubscriptionRepository(requireContext())
+                .collectSubscriptionVisible(mSubId, viewLifecycleOwner, (isVisible) -> {
+                    if (!isVisible) {
+                        Log.d(LOG_TAG, "Due to subscription not visible, closes page");
+                        finishFragment();
+                    }
+                    return Unit.INSTANCE;
+                });
     }
 
     @Override
@@ -532,11 +545,6 @@
                 Log.d(LOG_TAG, "Set subInfo to default subInfo.");
             }
         }
-        if (mSubscriptionInfoEntity == null && getActivity() != null) {
-            // If the current subId is not existed, finish it.
-            finishFragment();
-            return;
-        }
         onSubscriptionDetailChanged();
     }
 }
diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt b/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt
index c63e7d2..83e3a31 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt
+++ b/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt
@@ -20,12 +20,14 @@
 import android.provider.Settings
 import android.telephony.SubscriptionInfo
 import com.android.settings.R
+import com.android.settings.datausage.BillingCyclePreferenceController.Companion.BillingCycleSearchItem
 import com.android.settings.network.SubscriptionUtil
 import com.android.settings.network.telephony.CarrierSettingsVersionPreferenceController.Companion.CarrierSettingsVersionSearchItem
 import com.android.settings.network.telephony.DataUsagePreferenceController.Companion.DataUsageSearchItem
 import com.android.settings.network.telephony.MmsMessagePreferenceController.Companion.MmsMessageSearchItem
 import com.android.settings.network.telephony.NrAdvancedCallingPreferenceController.Companion.NrAdvancedCallingSearchItem
 import com.android.settings.network.telephony.RoamingPreferenceController.Companion.RoamingSearchItem
+import com.android.settings.network.telephony.VideoCallingPreferenceController.Companion.VideoCallingSearchItem
 import com.android.settings.network.telephony.WifiCallingPreferenceController.Companion.WifiCallingSearchItem
 import com.android.settings.spa.SpaSearchLanding.BundleValue
 import com.android.settings.spa.SpaSearchLanding.SpaSearchLandingFragment
@@ -116,12 +118,14 @@
 
         fun createSearchItems(context: Context): List<MobileNetworkSettingsSearchItem> =
             listOf(
+                BillingCycleSearchItem(context),
                 CarrierSettingsVersionSearchItem(context),
                 DataUsageSearchItem(context),
                 MmsMessageSearchItem(context),
                 NrAdvancedCallingSearchItem(context),
                 PreferredNetworkModeSearchItem(context),
                 RoamingSearchItem(context),
+                VideoCallingSearchItem(context),
                 WifiCallingSearchItem(context),
             )
     }
diff --git a/src/com/android/settings/network/telephony/MobileNetworkUtils.java b/src/com/android/settings/network/telephony/MobileNetworkUtils.java
index 517f66a..235418e 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkUtils.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkUtils.java
@@ -32,7 +32,6 @@
 import static com.android.settings.network.telephony.TelephonyConstants.TelephonyManagerConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA;
 
 import android.app.KeyguardManager;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -51,8 +50,6 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.PersistableBundle;
-import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -89,32 +86,17 @@
 import com.android.settings.network.telephony.TelephonyConstants.TelephonyManagerConstants;
 import com.android.settings.network.telephony.wificalling.WifiCallingRepository;
 import com.android.settingslib.core.instrumentation.Instrumentable;
-import com.android.settingslib.development.DevelopmentSettingsEnabler;
 import com.android.settingslib.graph.SignalDrawable;
 import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
-import com.android.settingslib.utils.ThreadUtils;
 
-import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 
 public class MobileNetworkUtils {
 
     private static final String TAG = "MobileNetworkUtils";
 
-    // CID of the device.
-    private static final String KEY_CID = "ro.boot.cid";
-    // CIDs of devices which should not show anything related to eSIM.
-    private static final String KEY_ESIM_CID_IGNORE = "ro.setupwizard.esim_cid_ignore";
-    // System Property which is used to decide whether the default eSIM UI will be shown,
-    // the default value is false.
-    private static final String KEY_ENABLE_ESIM_UI_BY_DEFAULT =
-            "esim.enable_esim_system_ui_by_default";
     private static final String LEGACY_ACTION_CONFIGURE_PHONE_ACCOUNT =
             "android.telecom.action.CONNECTION_SERVICE_CONFIGURE";
     private static final String RTL_MARK = "\u200F";
@@ -283,64 +265,6 @@
     }
 
     /**
-     * Whether to show the entry point to eUICC settings.
-     *
-     * <p>We show the entry point on any device which supports eUICC as long as either the eUICC
-     * was ever provisioned (that is, at least one profile was ever downloaded onto it), or if
-     * the user has enabled development mode.
-     */
-    public static boolean showEuiccSettings(Context context) {
-        if (!SubscriptionUtil.isSimHardwareVisible(context)) {
-            return false;
-        }
-        long timeForAccess = SystemClock.elapsedRealtime();
-        try {
-            Boolean isShow = ((Future<Boolean>) ThreadUtils.postOnBackgroundThread(() -> {
-                        try {
-                            return showEuiccSettingsDetecting(context);
-                        } catch (Exception threadException) {
-                            Log.w(TAG, "Accessing Euicc failure", threadException);
-                        }
-                        return Boolean.FALSE;
-                    })).get(3, TimeUnit.SECONDS);
-            return ((isShow != null) && isShow.booleanValue());
-        } catch (ExecutionException | InterruptedException | TimeoutException exception) {
-            timeForAccess = SystemClock.elapsedRealtime() - timeForAccess;
-            Log.w(TAG, "Accessing Euicc takes too long: +" + timeForAccess + "ms");
-        }
-        return false;
-    }
-
-    // The same as #showEuiccSettings(Context context)
-    public static Boolean showEuiccSettingsDetecting(Context context) {
-        final EuiccManager euiccManager =
-                (EuiccManager) context.getSystemService(EuiccManager.class);
-        if (euiccManager == null || !euiccManager.isEnabled()) {
-            Log.w(TAG, "EuiccManager is not enabled.");
-            return false;
-        }
-
-        final ContentResolver cr = context.getContentResolver();
-        final boolean esimIgnoredDevice =
-                Arrays.asList(TextUtils.split(SystemProperties.get(KEY_ESIM_CID_IGNORE, ""), ","))
-                        .contains(SystemProperties.get(KEY_CID));
-        final boolean enabledEsimUiByDefault =
-                SystemProperties.getBoolean(KEY_ENABLE_ESIM_UI_BY_DEFAULT, true);
-        final boolean euiccProvisioned =
-                Settings.Global.getInt(cr, Settings.Global.EUICC_PROVISIONED, 0) != 0;
-        final boolean inDeveloperMode =
-                DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context);
-        Log.i(TAG,
-                String.format("showEuiccSettings: esimIgnoredDevice: %b, enabledEsimUiByDefault: "
-                        + "%b, euiccProvisioned: %b, inDeveloperMode: %b.",
-                esimIgnoredDevice, enabledEsimUiByDefault, euiccProvisioned, inDeveloperMode));
-        return (euiccProvisioned
-                || (!esimIgnoredDevice && inDeveloperMode)
-                || (!esimIgnoredDevice && enabledEsimUiByDefault
-                        && isCurrentCountrySupported(context)));
-    }
-
-    /**
      * Return {@code true} if mobile data is enabled
      */
     public static boolean isMobileDataEnabled(Context context) {
diff --git a/src/com/android/settings/network/telephony/SubscriptionRepository.kt b/src/com/android/settings/network/telephony/SubscriptionRepository.kt
index cc8c8b4..26ea9b3 100644
--- a/src/com/android/settings/network/telephony/SubscriptionRepository.kt
+++ b/src/com/android/settings/network/telephony/SubscriptionRepository.kt
@@ -50,6 +50,31 @@
     fun getSelectableSubscriptionInfoList(): List<SubscriptionInfo> =
         context.getSelectableSubscriptionInfoList()
 
+    /** Flow of whether the subscription visible for the given [subId]. */
+    fun isSubscriptionVisibleFlow(subId: Int): Flow<Boolean> {
+        return subscriptionsChangedFlow()
+            .map {
+                val subInfo =
+                    subscriptionManager.availableSubscriptionInfoList?.firstOrNull { subInfo ->
+                        subInfo.subscriptionId == subId
+                    }
+                subInfo != null &&
+                    SubscriptionUtil.isSubscriptionVisible(subscriptionManager, context, subInfo)
+            }
+            .conflate()
+            .onEach { Log.d(TAG, "[$subId] isSubscriptionVisibleFlow: $it") }
+            .flowOn(Dispatchers.Default)
+    }
+
+    /** TODO: Move this to UI layer, when UI layer migrated to Kotlin. */
+    fun collectSubscriptionVisible(
+        subId: Int,
+        lifecycleOwner: LifecycleOwner,
+        action: (Boolean) -> Unit,
+    ) {
+        isSubscriptionVisibleFlow(subId).collectLatestWithLifecycle(lifecycleOwner, action = action)
+    }
+
     /** Flow of whether the subscription enabled for the given [subId]. */
     fun isSubscriptionEnabledFlow(subId: Int): Flow<Boolean> {
         if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false)
diff --git a/src/com/android/settings/network/telephony/VideoCallingPreferenceController.java b/src/com/android/settings/network/telephony/VideoCallingPreferenceController.java
deleted file mode 100644
index f803efd..0000000
--- a/src/com/android/settings/network/telephony/VideoCallingPreferenceController.java
+++ /dev/null
@@ -1,234 +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 android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.PersistableBundle;
-import android.telephony.CarrierConfigManager;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyCallback;
-import android.telephony.TelephonyManager;
-import android.telephony.ims.ImsMmTelManager;
-import android.util.Log;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
-import androidx.preference.TwoStatePreference;
-
-import com.android.internal.telephony.flags.Flags;
-import com.android.settings.network.CarrierConfigCache;
-import com.android.settings.network.MobileDataEnabledListener;
-import com.android.settings.network.ims.VolteQueryImsState;
-import com.android.settings.network.ims.VtQueryImsState;
-import com.android.settingslib.core.lifecycle.LifecycleObserver;
-import com.android.settingslib.core.lifecycle.events.OnStart;
-import com.android.settingslib.core.lifecycle.events.OnStop;
-
-/**
- * Preference controller for "Video Calling"
- */
-public class VideoCallingPreferenceController extends TelephonyTogglePreferenceController implements
-        LifecycleObserver, OnStart, OnStop,
-        MobileDataEnabledListener.Client,
-        Enhanced4gBasePreferenceController.On4gLteUpdateListener {
-
-    private static final String TAG = "VideoCallingPreference";
-
-    private Preference mPreference;
-    private PhoneTelephonyCallback mTelephonyCallback;
-    @VisibleForTesting
-    Integer mCallState;
-    private MobileDataEnabledListener mDataContentObserver;
-    private CallingPreferenceCategoryController mCallingPreferenceCategoryController;
-
-    public VideoCallingPreferenceController(Context context, String key) {
-        super(context, key);
-        mDataContentObserver = new MobileDataEnabledListener(context, this);
-        mTelephonyCallback = new PhoneTelephonyCallback();
-    }
-
-    @Override
-    public int getAvailabilityStatus(int subId) {
-        return SubscriptionManager.isValidSubscriptionId(subId)
-                && isVideoCallEnabled(subId)
-                ? AVAILABLE
-                : CONDITIONALLY_UNAVAILABLE;
-    }
-
-    @Override
-    public void displayPreference(PreferenceScreen screen) {
-        super.displayPreference(screen);
-        mPreference = screen.findPreference(getPreferenceKey());
-    }
-
-    @Override
-    public void onStart() {
-        mTelephonyCallback.register(mContext, mSubId);
-        mDataContentObserver.start(mSubId);
-    }
-
-    @Override
-    public void onStop() {
-        mTelephonyCallback.unregister();
-        mDataContentObserver.stop();
-    }
-
-    @Override
-    public void updateState(Preference preference) {
-        super.updateState(preference);
-        if ((mCallState == null) || (preference == null)) {
-            Log.d(TAG, "Skip update under mCallState=" + mCallState);
-            return;
-        }
-        final TwoStatePreference switchPreference = (TwoStatePreference) preference;
-        final boolean videoCallEnabled = isVideoCallEnabled(mSubId);
-        switchPreference.setVisible(videoCallEnabled);
-        mCallingPreferenceCategoryController
-                .updateChildVisible(getPreferenceKey(), videoCallEnabled);
-        if (videoCallEnabled) {
-            final boolean videoCallEditable = queryVoLteState(mSubId).isEnabledByUser()
-                    && queryImsState(mSubId).isAllowUserControl();
-            preference.setEnabled(videoCallEditable
-                    && mCallState == TelephonyManager.CALL_STATE_IDLE);
-            switchPreference.setChecked(videoCallEditable && isChecked());
-        }
-    }
-
-    @Override
-    public boolean setChecked(boolean isChecked) {
-        if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
-            return false;
-        }
-        final ImsMmTelManager imsMmTelManager = ImsMmTelManager.createForSubscriptionId(mSubId);
-        if (imsMmTelManager == null) {
-            return false;
-        }
-        try {
-            imsMmTelManager.setVtSettingEnabled(isChecked);
-            return true;
-        } catch (IllegalArgumentException exception) {
-            Log.w(TAG, "Unable to set VT status " + isChecked + ". subId=" + mSubId,
-                    exception);
-        }
-        return false;
-    }
-
-    @Override
-    public boolean isChecked() {
-        return queryImsState(mSubId).isEnabledByUser();
-    }
-
-    @VisibleForTesting
-    protected boolean isImsSupported() {
-        return mContext.getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_TELEPHONY_IMS);
-    }
-
-    /**
-     * Init instance of VideoCallingPreferenceController.
-     */
-    public VideoCallingPreferenceController init(
-            int subId, CallingPreferenceCategoryController callingPreferenceCategoryController) {
-        mSubId = subId;
-        mCallingPreferenceCategoryController = callingPreferenceCategoryController;
-
-        return this;
-    }
-
-    @VisibleForTesting
-    boolean isVideoCallEnabled(int subId) {
-        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
-            return false;
-        }
-
-        final PersistableBundle carrierConfig =
-                CarrierConfigCache.getInstance(mContext).getConfigForSubId(subId);
-        if (carrierConfig == null) {
-            return false;
-        }
-
-        if (!carrierConfig.getBoolean(
-                CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS)
-                && (!mContext.getSystemService(TelephonyManager.class)
-                    .createForSubscriptionId(subId).isDataEnabled())) {
-            return false;
-        }
-
-        return isImsSupported() && queryImsState(subId).isReadyToVideoCall();
-    }
-
-    @Override
-    public void on4gLteUpdated() {
-        updateState(mPreference);
-    }
-
-    private class PhoneTelephonyCallback extends TelephonyCallback implements
-            TelephonyCallback.CallStateListener {
-
-        private TelephonyManager mTelephonyManager;
-
-        @Override
-        public void onCallStateChanged(int state) {
-            mCallState = state;
-            updateState(mPreference);
-        }
-
-        public void register(Context context, int subId) {
-            mTelephonyManager = context.getSystemService(TelephonyManager.class);
-            if (SubscriptionManager.isValidSubscriptionId(subId)) {
-                mTelephonyManager = mTelephonyManager.createForSubscriptionId(subId);
-            }
-            // assign current call state so that it helps to show correct preference state even
-            // before first onCallStateChanged() by initial registration.
-            if (Flags.enforceTelephonyFeatureMappingForPublicApis()) {
-                try {
-                    mCallState = mTelephonyManager.getCallState(subId);
-                } catch (UnsupportedOperationException e) {
-                    // Device doesn't support FEATURE_TELEPHONY_CALLING
-                    mCallState = TelephonyManager.CALL_STATE_IDLE;
-                }
-            } else {
-                mCallState = mTelephonyManager.getCallState(subId);
-            }
-            mTelephonyManager.registerTelephonyCallback(context.getMainExecutor(), this);
-        }
-
-        public void unregister() {
-            mCallState = null;
-            mTelephonyManager.unregisterTelephonyCallback(this);
-        }
-    }
-
-    /**
-     * Implementation of MobileDataEnabledListener.Client
-     */
-    public void onMobileDataEnabledChange() {
-        updateState(mPreference);
-    }
-
-    @VisibleForTesting
-    VtQueryImsState queryImsState(int subId) {
-        return new VtQueryImsState(mContext, subId);
-    }
-
-    @VisibleForTesting
-    VolteQueryImsState queryVoLteState(int subId) {
-        return new VolteQueryImsState(mContext, subId);
-    }
-}
diff --git a/src/com/android/settings/network/telephony/VideoCallingPreferenceController.kt b/src/com/android/settings/network/telephony/VideoCallingPreferenceController.kt
new file mode 100644
index 0000000..e6b3f31
--- /dev/null
+++ b/src/com/android/settings/network/telephony/VideoCallingPreferenceController.kt
@@ -0,0 +1,147 @@
+/*
+ * 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.SubscriptionManager
+import android.telephony.TelephonyManager
+import android.telephony.ims.ImsManager
+import android.util.Log
+import androidx.annotation.VisibleForTesting
+import androidx.lifecycle.LifecycleOwner
+import androidx.preference.Preference
+import androidx.preference.PreferenceScreen
+import androidx.preference.TwoStatePreference
+import com.android.settings.R
+import com.android.settings.core.TogglePreferenceController
+import com.android.settings.network.ims.VolteQueryImsState
+import com.android.settings.network.ims.VtQueryImsState
+import com.android.settings.network.telephony.Enhanced4gBasePreferenceController.On4gLteUpdateListener
+import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem
+import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchResult
+import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.runBlocking
+
+/** Preference controller for "Video Calling" */
+class VideoCallingPreferenceController
+@JvmOverloads
+constructor(
+    context: Context,
+    key: String,
+    private val callStateRepository: CallStateRepository = CallStateRepository(context),
+) : TogglePreferenceController(context, key), On4gLteUpdateListener {
+
+    private var subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID
+    private var preference: TwoStatePreference? = null
+    private var callingPreferenceCategoryController: CallingPreferenceCategoryController? = null
+    private val repository = VideoCallingRepository(context)
+
+    private var videoCallEditable = false
+    private var isInCall = false
+
+    /** Init instance of VideoCallingPreferenceController. */
+    fun init(
+        subId: Int,
+        callingPreferenceCategoryController: CallingPreferenceCategoryController?,
+    ): VideoCallingPreferenceController {
+        this.subId = subId
+        this.callingPreferenceCategoryController = callingPreferenceCategoryController
+
+        return this
+    }
+
+    // Availability is controlled in onViewCreated() and VideoCallingSearchItem.
+    override fun getAvailabilityStatus() = AVAILABLE
+
+    override fun displayPreference(screen: PreferenceScreen) {
+        super.displayPreference(screen)
+        preference = screen.findPreference(preferenceKey)
+    }
+
+    override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
+        repository.isVideoCallReadyFlow(subId).collectLatestWithLifecycle(viewLifecycleOwner) {
+            isReady ->
+            preference?.isVisible = isReady
+            callingPreferenceCategoryController?.updateChildVisible(preferenceKey, isReady)
+        }
+        callStateRepository.callStateFlow(subId).collectLatestWithLifecycle(viewLifecycleOwner) {
+            callState ->
+            isInCall = callState != TelephonyManager.CALL_STATE_IDLE
+            updatePreference()
+        }
+    }
+
+    override fun updateState(preference: Preference) {
+        super.updateState(preference)
+        videoCallEditable =
+            queryVoLteState(subId).isEnabledByUser && queryImsState(subId).isAllowUserControl
+        updatePreference()
+    }
+
+    private fun updatePreference() {
+        preference?.isEnabled = videoCallEditable && !isInCall
+        preference?.isChecked = videoCallEditable && isChecked
+    }
+
+    override fun getSliceHighlightMenuRes() = NO_RES
+
+    override fun setChecked(isChecked: Boolean): Boolean {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            return false
+        }
+        val imsMmTelManager = ImsManager(mContext).getImsMmTelManager(subId)
+        try {
+            imsMmTelManager.isVtSettingEnabled = isChecked
+            return true
+        } catch (exception: IllegalArgumentException) {
+            Log.w(TAG, "[$subId] Unable to set VT status $isChecked", exception)
+        }
+        return false
+    }
+
+    override fun isChecked(): Boolean = queryImsState(subId).isEnabledByUser
+
+    override fun on4gLteUpdated() {
+        preference?.let { updateState(it) }
+    }
+
+    @VisibleForTesting fun queryImsState(subId: Int) = VtQueryImsState(mContext, subId)
+
+    @VisibleForTesting fun queryVoLteState(subId: Int) = VolteQueryImsState(mContext, subId)
+
+    companion object {
+        private const val TAG = "VideoCallingPreferenceController"
+
+        class VideoCallingSearchItem(private val context: Context) :
+            MobileNetworkSettingsSearchItem {
+            private val repository = VideoCallingRepository(context)
+
+            private fun isAvailable(subId: Int): Boolean = runBlocking {
+                repository.isVideoCallReadyFlow(subId).first()
+            }
+
+            override fun getSearchResult(subId: Int): MobileNetworkSettingsSearchResult? {
+                if (!isAvailable(subId)) return null
+                return MobileNetworkSettingsSearchResult(
+                    key = "video_calling_key",
+                    title = context.getString(R.string.video_calling_settings_title),
+                )
+            }
+        }
+    }
+}
diff --git a/src/com/android/settings/network/telephony/VideoCallingRepository.kt b/src/com/android/settings/network/telephony/VideoCallingRepository.kt
new file mode 100644
index 0000000..634eb28
--- /dev/null
+++ b/src/com/android/settings/network/telephony/VideoCallingRepository.kt
@@ -0,0 +1,65 @@
+/*
+ * 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.AccessNetworkConstants
+import android.telephony.CarrierConfigManager
+import android.telephony.SubscriptionManager
+import android.telephony.ims.feature.MmTelFeature
+import android.telephony.ims.stub.ImsRegistrationImplBase
+import com.android.settings.network.telephony.ims.ImsFeatureRepository
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class VideoCallingRepository(
+    context: Context,
+    private val mobileDataRepository: MobileDataRepository = MobileDataRepository(context),
+    private val imsFeatureRepositoryFactory: (Int) -> ImsFeatureRepository = { subId ->
+        ImsFeatureRepository(context, subId)
+    },
+) {
+    private val carrierConfigRepository = CarrierConfigRepository(context)
+
+    fun isVideoCallReadyFlow(subId: Int): Flow<Boolean> {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false)
+
+        return isPreconditionMeetFlow(subId).flatMapLatest { isPreconditionMeet ->
+            if (isPreconditionMeet) {
+                imsFeatureRepositoryFactory(subId)
+                    .isReadyFlow(
+                        capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO,
+                        tech = ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
+                        transportType = AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+                    )
+            } else {
+                flowOf(false)
+            }
+        }
+    }
+
+    private fun isPreconditionMeetFlow(subId: Int): Flow<Boolean> =
+        if (carrierConfigRepository.getBoolean(
+            subId, CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS)) {
+            flowOf(true)
+        } else {
+            mobileDataRepository.isMobileDataEnabledFlow(subId)
+        }
+}
diff --git a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt
index e04763a..9b68970 100644
--- a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt
+++ b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt
@@ -81,6 +81,12 @@
     }
 
     override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            // Sub id could invalid, if this page is opened from external action and no sim is
+            // active.
+            // Ignore this case, since this page will be finished soon.
+            return
+        }
         wifiCallingRepositoryFactory(subId).wifiCallingReadyFlow()
             .collectLatestWithLifecycle(viewLifecycleOwner) { isReady ->
                 preference.isVisible = isReady
diff --git a/src/com/android/settings/network/telephony/euicc/EuiccRepository.kt b/src/com/android/settings/network/telephony/euicc/EuiccRepository.kt
new file mode 100644
index 0000000..74b5313
--- /dev/null
+++ b/src/com/android/settings/network/telephony/euicc/EuiccRepository.kt
@@ -0,0 +1,129 @@
+/*
+ * 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.euicc
+
+import android.content.Context
+import android.os.SystemProperties
+import android.provider.Settings
+import android.telephony.TelephonyManager
+import android.telephony.euicc.EuiccManager
+import android.util.Log
+import com.android.settings.network.SubscriptionUtil
+import com.android.settingslib.development.DevelopmentSettingsEnabler
+import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalBoolean
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOn
+
+class EuiccRepository
+@JvmOverloads
+constructor(
+    private val context: Context,
+    private val isEuiccProvisioned: () -> Boolean = {
+        val euiccProvisioned by context.settingsGlobalBoolean(Settings.Global.EUICC_PROVISIONED)
+        euiccProvisioned
+    },
+    private val isDevelopmentSettingsEnabled: () -> Boolean = {
+        DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context)
+    },
+) {
+
+    private val euiccManager = context.getSystemService(EuiccManager::class.java)
+    private val telephonyManager = context.getSystemService(TelephonyManager::class.java)
+
+    fun showEuiccSettingsFlow() =
+        flow { emit(showEuiccSettings()) }
+            .distinctUntilChanged()
+            .conflate()
+            .flowOn(Dispatchers.Default)
+
+    /**
+     * Whether to show the entry point to eUICC settings.
+     *
+     * We show the entry point on any device which supports eUICC as long as either the eUICC was
+     * ever provisioned (that is, at least one profile was ever downloaded onto it), or if the user
+     * has enabled development mode.
+     */
+    fun showEuiccSettings(): Boolean {
+        if (!SubscriptionUtil.isSimHardwareVisible(context)) return false
+        if (euiccManager == null || !euiccManager.isEnabled) {
+            Log.w(TAG, "EuiccManager is not enabled.")
+            return false
+        }
+        if (isEuiccProvisioned()) {
+            Log.i(TAG, "showEuiccSettings: euicc provisioned")
+            return true
+        }
+        val ignoredCids =
+            SystemProperties.get(KEY_ESIM_CID_IGNORE).split(',').filter { it.isNotEmpty() }
+        val cid = SystemProperties.get(KEY_CID)
+        if (cid in ignoredCids) {
+            Log.i(TAG, "showEuiccSettings: cid ignored")
+            return false
+        }
+        if (isDevelopmentSettingsEnabled()) {
+            Log.i(TAG, "showEuiccSettings: development settings enabled")
+            return true
+        }
+        val enabledEsimUiByDefault =
+            SystemProperties.getBoolean(KEY_ENABLE_ESIM_UI_BY_DEFAULT, true)
+        Log.i(TAG, "showEuiccSettings: enabledEsimUiByDefault=$enabledEsimUiByDefault")
+        return enabledEsimUiByDefault && isCurrentCountrySupported()
+    }
+
+    /**
+     * Loop through all the device logical slots to check whether the user's current country
+     * supports eSIM.
+     */
+    private fun isCurrentCountrySupported(): Boolean {
+        val euiccManager = euiccManager ?: return false
+        val telephonyManager = telephonyManager ?: return false
+        val visitedCountrySet = mutableSetOf<String>()
+        for (slotIndex in 0 until telephonyManager.getActiveModemCount()) {
+            val countryCode = telephonyManager.getNetworkCountryIso(slotIndex)
+            if (
+                countryCode.isNotEmpty() &&
+                    visitedCountrySet.add(countryCode) &&
+                    euiccManager.isSupportedCountry(countryCode)
+            ) {
+                Log.i(TAG, "isCurrentCountrySupported: $countryCode is supported")
+                return true
+            }
+        }
+        Log.i(TAG, "isCurrentCountrySupported: no country is supported")
+        return false
+    }
+
+    companion object {
+        private const val TAG = "EuiccRepository"
+
+        /** CID of the device. */
+        private const val KEY_CID: String = "ro.boot.cid"
+
+        /** CIDs of devices which should not show anything related to eSIM. */
+        private const val KEY_ESIM_CID_IGNORE: String = "ro.setupwizard.esim_cid_ignore"
+
+        /**
+         * System Property which is used to decide whether the default eSIM UI will be shown, the
+         * default value is false.
+         */
+        private const val KEY_ENABLE_ESIM_UI_BY_DEFAULT: String =
+            "esim.enable_esim_system_ui_by_default"
+    }
+}
diff --git a/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt b/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt
index c5d1200..e891204 100644
--- a/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt
+++ b/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt
@@ -27,6 +27,7 @@
 import android.telephony.ims.RegistrationManager
 import android.telephony.ims.feature.MmTelFeature
 import android.util.Log
+import androidx.annotation.VisibleForTesting
 import kotlin.coroutines.resume
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.asExecutor
@@ -53,11 +54,6 @@
         @AccessNetworkConstants.TransportType transportType: Int,
     ): Flow<Boolean>
 
-    suspend fun isSupported(
-        @MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int,
-        @AccessNetworkConstants.TransportType transportType: Int,
-    ): Boolean
-
     suspend fun setCrossSimCallingEnabled(enabled: Boolean)
 }
 
@@ -143,7 +139,8 @@
     override fun isSupportedFlow(capability: Int, transportType: Int): Flow<Boolean> =
         imsReadyFlow().map { imsReady -> imsReady && isSupported(capability, transportType) }
 
-    override suspend fun isSupported(
+    @VisibleForTesting
+    suspend fun isSupported(
         @MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int,
         @AccessNetworkConstants.TransportType transportType: Int,
     ): Boolean = withContext(Dispatchers.Default) {
diff --git a/src/com/android/settings/network/telephony/wificalling/CrossSimCallingViewModel.kt b/src/com/android/settings/network/telephony/wificalling/CrossSimCallingViewModel.kt
index dda147b..cab27ca 100644
--- a/src/com/android/settings/network/telephony/wificalling/CrossSimCallingViewModel.kt
+++ b/src/com/android/settings/network/telephony/wificalling/CrossSimCallingViewModel.kt
@@ -21,6 +21,7 @@
 import android.telephony.CarrierConfigManager
 import android.telephony.SubscriptionManager
 import android.telephony.TelephonyManager
+import android.util.Log
 import androidx.lifecycle.AndroidViewModel
 import androidx.lifecycle.viewModelScope
 import com.android.settings.R
@@ -34,6 +35,7 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.conflate
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
@@ -43,9 +45,8 @@
 import kotlinx.coroutines.plus
 
 @OptIn(ExperimentalCoroutinesApi::class)
-class CrossSimCallingViewModel(
-    private val application: Application,
-) : AndroidViewModel(application) {
+class CrossSimCallingViewModel(private val application: Application) :
+    AndroidViewModel(application) {
 
     private val subscriptionRepository = SubscriptionRepository(application)
     private val dataSubscriptionRepository = DataSubscriptionRepository(application)
@@ -61,38 +62,45 @@
                     subscriptionRepository.activeSubscriptionIdListFlow(),
                     dataSubscriptionRepository.defaultDataSubscriptionIdFlow(),
                 ) { activeSubIds, defaultDataSubId ->
-                    activeSubIds to crossSimCallNewEnabled(activeSubIds, defaultDataSubId)
+                    updatableSubIdsFlow(activeSubIds) to
+                        crossSimCallNewEnabledFlow(activeSubIds, defaultDataSubId)
                 }
-                .flatMapLatest { (activeSubIds, newEnabledFlow) ->
-                    newEnabledFlow.map { newEnabled -> activeSubIds to newEnabled }
+                .flatMapLatest { (updatableSubIdsFlow, crossSimCallNewEnabledFlow) ->
+                    combine(updatableSubIdsFlow, crossSimCallNewEnabledFlow) {
+                        updatableSubIds,
+                        newEnabled ->
+                        updatableSubIds to newEnabled
+                    }
                 }
                 .distinctUntilChanged()
-                .onEach { (activeSubIds, newEnabled) ->
-                    updateCrossSimCalling(activeSubIds, newEnabled)
+                .conflate()
+                .onEach { (updatableSubIds, newEnabled) ->
+                    Log.d(TAG, "updatableSubIds: $updatableSubIds newEnabled: $newEnabled")
+                    updateCrossSimCalling(updatableSubIds, newEnabled)
                 }
                 .launchIn(scope)
         }
     }
 
-    private suspend fun updateCrossSimCalling(activeSubIds: List<Int>, newEnabled: Boolean) {
-        metricsFeatureProvider.action(
-            application,
-            SettingsEnums.ACTION_UPDATE_CROSS_SIM_CALLING_ON_AUTO_DATA_SWITCH_EVENT,
-            newEnabled,
-        )
-        activeSubIds
-            .filter { subId -> crossSimAvailable(subId) }
-            .forEach { subId ->
-                ImsMmTelRepositoryImpl(application, subId).setCrossSimCallingEnabled(newEnabled)
+    private fun updatableSubIdsFlow(activeSubIds: List<Int>): Flow<List<Int>> {
+        val updatableSubIdFlows =
+            activeSubIds.map { subId ->
+                WifiCallingRepository(application, subId).wifiCallingReadyFlow().map { isReady ->
+                    subId.takeIf { isReady && isCrossSimImsAvailable(subId) }
+                }
             }
+        return combine(updatableSubIdFlows) { subIds -> subIds.filterNotNull() }
+            .distinctUntilChanged()
+            .conflate()
     }
 
-    private suspend fun crossSimAvailable(subId: Int): Boolean =
-        WifiCallingRepository(application, subId).isWifiCallingSupported() &&
-            carrierConfigRepository.getBoolean(
-                subId, CarrierConfigManager.KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL)
+    private fun isCrossSimImsAvailable(subId: Int) =
+        carrierConfigRepository.getBoolean(
+            subId,
+            CarrierConfigManager.KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL,
+        )
 
-    private fun crossSimCallNewEnabled(
+    private fun crossSimCallNewEnabledFlow(
         activeSubscriptionIdList: List<Int>,
         defaultDataSubId: Int,
     ): Flow<Boolean> {
@@ -102,8 +110,27 @@
                 .filter { subId -> subId != defaultDataSubId }
                 .map { subId ->
                     mobileDataRepository.isMobileDataPolicyEnabledFlow(
-                        subId, TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)
+                        subId,
+                        TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
+                    )
                 }
         return combine(isMobileDataPolicyEnabledFlows) { true in it }
+            .distinctUntilChanged()
+            .conflate()
+    }
+
+    private suspend fun updateCrossSimCalling(subIds: List<Int>, newEnabled: Boolean) {
+        metricsFeatureProvider.action(
+            application,
+            SettingsEnums.ACTION_UPDATE_CROSS_SIM_CALLING_ON_AUTO_DATA_SWITCH_EVENT,
+            newEnabled,
+        )
+        for (subId in subIds) {
+            ImsMmTelRepositoryImpl(application, subId).setCrossSimCallingEnabled(newEnabled)
+        }
+    }
+
+    companion object {
+        private const val TAG = "CrossSimCallingVM"
     }
 }
diff --git a/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt b/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt
index 6af0559..a6a47fa 100644
--- a/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt
+++ b/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt
@@ -29,9 +29,7 @@
 import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl
 import com.android.settings.network.telephony.telephonyManager
 import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
-import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.withContext
 
 interface IWifiCallingRepository {
     /** TODO: Move this to UI layer, when UI layer migrated to Kotlin. */
@@ -75,11 +73,4 @@
             tech = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
             transportType = AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
         )
-
-    suspend fun isWifiCallingSupported(): Boolean = withContext(Dispatchers.Default) {
-        imsMmTelRepository.isSupported(
-            capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
-            transportType = AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
-        )
-    }
 }
diff --git a/src/com/android/settings/notification/modes/AbstractZenModeHeaderController.java b/src/com/android/settings/notification/modes/AbstractZenModeHeaderController.java
index af64231..1d1b07d 100644
--- a/src/com/android/settings/notification/modes/AbstractZenModeHeaderController.java
+++ b/src/com/android/settings/notification/modes/AbstractZenModeHeaderController.java
@@ -43,15 +43,18 @@
 abstract class AbstractZenModeHeaderController extends AbstractZenModePreferenceController {
 
     private final DashboardFragment mFragment;
+    private final ZenIconLoader mIconLoader;
     private EntityHeaderController mHeaderController;
     @Nullable private ZenIcon.Key mCurrentIconKey;
 
     AbstractZenModeHeaderController(
             @NonNull Context context,
+            @NonNull ZenIconLoader iconLoader,
             @NonNull String key,
             @NonNull DashboardFragment fragment) {
         super(context, key);
         mFragment = fragment;
+        mIconLoader = iconLoader;
     }
 
     @Override
@@ -90,7 +93,7 @@
         if (!Objects.equal(mCurrentIconKey, zenMode.getIconKey())) {
             mCurrentIconKey = zenMode.getIconKey();
             FutureUtil.whenDone(
-                    ZenIconLoader.getInstance().getIcon(mContext, zenMode),
+                    mIconLoader.getIcon(mContext, zenMode),
                     icon -> {
                         checkNotNull(mHeaderController)
                                 .setIcon(iconStylist.apply(icon.drawable()))
diff --git a/src/com/android/settings/notification/modes/InterruptionFilterPreferenceController.java b/src/com/android/settings/notification/modes/InterruptionFilterPreferenceController.java
index d69b317..e5c6570 100644
--- a/src/com/android/settings/notification/modes/InterruptionFilterPreferenceController.java
+++ b/src/com/android/settings/notification/modes/InterruptionFilterPreferenceController.java
@@ -44,8 +44,8 @@
 
     @Override
     public void updateState(Preference preference, @NonNull ZenMode zenMode) {
-        preference.setEnabled(zenMode.isEnabled());
-        boolean allowingAll = zenMode.getRule().getInterruptionFilter() == INTERRUPTION_FILTER_ALL;
+        preference.setEnabled(zenMode.isEnabled() && zenMode.canEditPolicy());
+        boolean allowingAll = zenMode.getInterruptionFilter() == INTERRUPTION_FILTER_ALL;
 
         ((TwoStatePreference) preference).setChecked(allowingAll);
         preference.setSummary(allowingAll
@@ -57,7 +57,7 @@
     public boolean onPreferenceChange(@NonNull Preference preference, Object newValue) {
         final boolean allowAll = ((Boolean) newValue);
         return saveMode(zenMode -> {
-            zenMode.getRule().setInterruptionFilter(allowAll
+            zenMode.setInterruptionFilter(allowAll
                     ? INTERRUPTION_FILTER_ALL
                     : INTERRUPTION_FILTER_PRIORITY);
             return zenMode;
diff --git a/src/com/android/settings/notification/modes/SetupInterstitialActivity.java b/src/com/android/settings/notification/modes/SetupInterstitialActivity.java
index c5beb36..fcd5fd5 100644
--- a/src/com/android/settings/notification/modes/SetupInterstitialActivity.java
+++ b/src/com/android/settings/notification/modes/SetupInterstitialActivity.java
@@ -228,7 +228,7 @@
             return false;
         }
 
-        modeToUpdate.getRule().setEnabled(true);
+        modeToUpdate.setEnabled(true);
         mBackend.updateMode(modeToUpdate);
         return true;
     }
diff --git a/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java
index 7b17f0c..c133f51 100644
--- a/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java
@@ -92,7 +92,7 @@
 
     @Override
     public boolean isAvailable(ZenMode zenMode) {
-        return zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL;
+        return zenMode.getInterruptionFilter() != INTERRUPTION_FILTER_ALL;
     }
 
     @Override
@@ -102,7 +102,7 @@
         preference.setIntent(
                 ZenSubSettingLauncher.forModeFragment(mContext, ZenModeAppsFragment.class,
                         zenMode.getId(), SettingsEnums.ZEN_PRIORITY_MODE).toIntent());
-        preference.setEnabled(zenMode.isEnabled());
+        preference.setEnabled(zenMode.isEnabled() && zenMode.canEditPolicy());
 
         mZenMode = zenMode;
         mPreference = (CircularIconsPreference) preference;
diff --git a/src/com/android/settings/notification/modes/ZenModeDisplayEffectPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeDisplayEffectPreferenceController.java
index b0d3952..afd9b76 100644
--- a/src/com/android/settings/notification/modes/ZenModeDisplayEffectPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeDisplayEffectPreferenceController.java
@@ -74,7 +74,7 @@
                     updatedEffects.setShouldUseNightMode(allow);
                     break;
             }
-            zenMode.getRule().setDeviceEffects(updatedEffects.build());
+            zenMode.setDeviceEffects(updatedEffects.build());
             return zenMode;
         });
     }
diff --git a/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceController.java
index 57dce89..14c8cb6 100644
--- a/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceController.java
@@ -45,7 +45,7 @@
         preference.setIntent(
                 ZenSubSettingLauncher.forModeFragment(mContext, ZenModeDisplayFragment.class,
                         zenMode.getId(), SettingsEnums.ZEN_PRIORITY_MODE).toIntent());
-        preference.setEnabled(zenMode.isEnabled());
+        preference.setEnabled(zenMode.isEnabled() && zenMode.canEditPolicy());
     }
 
     @Override
diff --git a/src/com/android/settings/notification/modes/ZenModeEditNameIconFragmentBase.java b/src/com/android/settings/notification/modes/ZenModeEditNameIconFragmentBase.java
index 96cbf91..6dd9076 100644
--- a/src/com/android/settings/notification/modes/ZenModeEditNameIconFragmentBase.java
+++ b/src/com/android/settings/notification/modes/ZenModeEditNameIconFragmentBase.java
@@ -30,6 +30,7 @@
 import com.android.settings.R;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.notification.modes.ZenIconLoader;
 import com.android.settingslib.notification.modes.ZenMode;
 import com.android.settingslib.notification.modes.ZenModesBackend;
 
@@ -102,7 +103,8 @@
     protected final List<AbstractPreferenceController> createPreferenceControllers(
             Context context) {
         return ImmutableList.of(
-                new ZenModeIconPickerIconPreferenceController(context, "chosen_icon", this),
+                new ZenModeIconPickerIconPreferenceController(context, ZenIconLoader.getInstance(),
+                        "chosen_icon", this),
                 new ZenModeEditNamePreferenceController(context, "name", this::setModeName),
                 new ZenModeIconPickerListPreferenceController(context, "icon_list",
                         this::setModeIcon),
diff --git a/src/com/android/settings/notification/modes/ZenModeExitAtAlarmPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeExitAtAlarmPreferenceController.java
index 326bc97..18e3fc1 100644
--- a/src/com/android/settings/notification/modes/ZenModeExitAtAlarmPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeExitAtAlarmPreferenceController.java
@@ -50,7 +50,8 @@
         if (mSchedule.exitAtAlarm != exitAtAlarm) {
             mSchedule.exitAtAlarm = exitAtAlarm;
             return saveMode(mode -> {
-                mode.getRule().setConditionId(ZenModeConfig.toScheduleConditionId(mSchedule));
+                mode.setCustomModeConditionId(mContext,
+                        ZenModeConfig.toScheduleConditionId(mSchedule));
                 return mode;
             });
         }
diff --git a/src/com/android/settings/notification/modes/ZenModeFragment.java b/src/com/android/settings/notification/modes/ZenModeFragment.java
index 6889cac..08075b4 100644
--- a/src/com/android/settings/notification/modes/ZenModeFragment.java
+++ b/src/com/android/settings/notification/modes/ZenModeFragment.java
@@ -32,6 +32,7 @@
 
 import com.android.settings.R;
 import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.notification.modes.ZenIconLoader;
 import com.android.settingslib.notification.modes.ZenMode;
 
 import java.util.ArrayList;
@@ -54,7 +55,8 @@
     @Override
     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
         List<AbstractPreferenceController> prefControllers = new ArrayList<>();
-        prefControllers.add(new ZenModeHeaderController(context, "header", this));
+        prefControllers.add(
+                new ZenModeHeaderController(context, ZenIconLoader.getInstance(), "header", this));
         prefControllers.add(new ZenModeBlurbPreferenceController(context, "mode_blurb"));
         prefControllers.add(
                 new ZenModeButtonPreferenceController(context, "activate", this, mBackend));
@@ -169,7 +171,7 @@
             } else if (menuItem.getItemId() == DELETE_MODE) {
                 new AlertDialog.Builder(mContext)
                         .setTitle(mContext.getString(R.string.zen_mode_delete_mode_confirmation,
-                                mZenMode.getRule().getName()))
+                                mZenMode.getName()))
                         .setPositiveButton(R.string.zen_mode_schedule_delete,
                                 (dialog, which) -> {
                                     // start finishing before calling removeMode() so that we
diff --git a/src/com/android/settings/notification/modes/ZenModeHeaderController.java b/src/com/android/settings/notification/modes/ZenModeHeaderController.java
index ae6eacc..e901b9f 100644
--- a/src/com/android/settings/notification/modes/ZenModeHeaderController.java
+++ b/src/com/android/settings/notification/modes/ZenModeHeaderController.java
@@ -23,15 +23,14 @@
 
 import com.android.settings.R;
 import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.notification.modes.ZenIconLoader;
 import com.android.settingslib.notification.modes.ZenMode;
 
 class ZenModeHeaderController extends AbstractZenModeHeaderController {
 
-    ZenModeHeaderController(
-            @NonNull  Context context,
-            @NonNull String key,
-            @NonNull DashboardFragment fragment) {
-        super(context, key, fragment);
+    ZenModeHeaderController(@NonNull Context context, @NonNull ZenIconLoader iconLoader,
+            @NonNull String key, @NonNull DashboardFragment fragment) {
+        super(context, iconLoader, key, fragment);
     }
 
     @Override
diff --git a/src/com/android/settings/notification/modes/ZenModeIconPickerIconPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeIconPickerIconPreferenceController.java
index 6c8d41f..dc0be10 100644
--- a/src/com/android/settings/notification/modes/ZenModeIconPickerIconPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeIconPickerIconPreferenceController.java
@@ -24,14 +24,16 @@
 
 import com.android.settings.R;
 import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.notification.modes.ZenIconLoader;
 import com.android.settingslib.notification.modes.ZenMode;
 
 /** Controller used for displaying the currently-chosen icon at the top of the icon picker. */
 class ZenModeIconPickerIconPreferenceController extends AbstractZenModeHeaderController {
 
-    ZenModeIconPickerIconPreferenceController(@NonNull Context context, @NonNull String key,
+    ZenModeIconPickerIconPreferenceController(@NonNull Context context,
+            @NonNull ZenIconLoader iconLoader, @NonNull String key,
             @NonNull DashboardFragment fragment) {
-        super(context, key, fragment);
+        super(context, iconLoader, key, fragment);
     }
 
     @Override
diff --git a/src/com/android/settings/notification/modes/ZenModeNotifVisLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeNotifVisLinkPreferenceController.java
index cd1e8c7..f08a05d 100644
--- a/src/com/android/settings/notification/modes/ZenModeNotifVisLinkPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeNotifVisLinkPreferenceController.java
@@ -41,7 +41,7 @@
 
     @Override
     public boolean isAvailable(ZenMode zenMode) {
-        return zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL;
+        return zenMode.getInterruptionFilter() != INTERRUPTION_FILTER_ALL;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceController.java
index 9613d98..9adc768 100644
--- a/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceController.java
@@ -61,7 +61,7 @@
 
     @Override
     public boolean isAvailable(ZenMode zenMode) {
-        return zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL;
+        return zenMode.getInterruptionFilter() != INTERRUPTION_FILTER_ALL;
     }
 
     @Override
@@ -70,7 +70,7 @@
                 ZenSubSettingLauncher.forModeFragment(mContext, ZenModeOtherFragment.class,
                         zenMode.getId(), SettingsEnums.ZEN_PRIORITY_MODE).toIntent());
 
-        preference.setEnabled(zenMode.isEnabled());
+        preference.setEnabled(zenMode.isEnabled() && zenMode.canEditPolicy());
         preference.setSummary(mSummaryHelper.getOtherSoundCategoriesSummary(zenMode));
         ((CircularIconsPreference) preference).setIcons(getSoundIcons(zenMode.getPolicy()));
     }
diff --git a/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceController.java
index bf55471..c635ad8 100644
--- a/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceController.java
@@ -84,7 +84,7 @@
 
     @Override
     public boolean isAvailable(ZenMode zenMode) {
-        return zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL;
+        return zenMode.getInterruptionFilter() != INTERRUPTION_FILTER_ALL;
     }
 
     @Override
@@ -94,7 +94,7 @@
                 ZenSubSettingLauncher.forModeFragment(mContext, ZenModePeopleFragment.class,
                         zenMode.getId(), SettingsEnums.ZEN_PRIORITY_MODE).toIntent());
 
-        preference.setEnabled(zenMode.isEnabled());
+        preference.setEnabled(zenMode.isEnabled() && zenMode.canEditPolicy());
         preference.setSummary(mSummaryHelper.getPeopleSummary(zenMode.getPolicy()));
         ((CircularIconsPreference) preference).setIcons(getPeopleIcons(zenMode.getPolicy()),
                 PEOPLE_ITEM_EQUIVALENCE);
diff --git a/src/com/android/settings/notification/modes/ZenModeScheduleChooserDialog.java b/src/com/android/settings/notification/modes/ZenModeScheduleChooserDialog.java
index d129aad..370199a 100644
--- a/src/com/android/settings/notification/modes/ZenModeScheduleChooserDialog.java
+++ b/src/com/android/settings/notification/modes/ZenModeScheduleChooserDialog.java
@@ -81,6 +81,17 @@
         dialog.show(parent.getParentFragmentManager(), TAG);
     }
 
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        if (mOptionListener == null) {
+            // Probably the dialog fragment was recreated after its activity being destroyed.
+            // It's pointless to re-show the dialog if we can't do anything when its options are
+            // selected, so we don't.
+            dismiss();
+        }
+    }
+
     @NonNull
     @Override
     public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
diff --git a/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java b/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java
index c5300da..483b8f0 100644
--- a/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java
+++ b/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java
@@ -209,48 +209,46 @@
         boolean isFirst = true;
         List<String> enabledEffects = new ArrayList<>();
         if (!zenMode.getPolicy().shouldShowAllVisualEffects()
-                && zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL) {
+                && zenMode.getInterruptionFilter() != INTERRUPTION_FILTER_ALL) {
             enabledEffects.add(getBlockedEffectsSummary(zenMode));
             isFirst = false;
         }
-        ZenDeviceEffects currEffects = zenMode.getRule().getDeviceEffects();
-        if (currEffects != null) {
-            if (currEffects.shouldDisplayGrayscale()) {
-                if (isFirst) {
-                    enabledEffects.add(mContext.getString(R.string.mode_grayscale_title));
-                } else {
-                    enabledEffects.add(mContext.getString(
-                            R.string.mode_grayscale_title_secondary_list));
-                }
-                isFirst = false;
+        ZenDeviceEffects currEffects = zenMode.getDeviceEffects();
+        if (currEffects.shouldDisplayGrayscale()) {
+            if (isFirst) {
+                enabledEffects.add(mContext.getString(R.string.mode_grayscale_title));
+            } else {
+                enabledEffects.add(mContext.getString(
+                        R.string.mode_grayscale_title_secondary_list));
             }
-            if (currEffects.shouldSuppressAmbientDisplay()) {
-                if (isFirst) {
-                    enabledEffects.add(mContext.getString(R.string.mode_aod_title));
-                } else {
-                    enabledEffects.add(mContext.getString(
-                            R.string.mode_aod_title_secondary_list));
-                }
-                isFirst = false;
+            isFirst = false;
+        }
+        if (currEffects.shouldSuppressAmbientDisplay()) {
+            if (isFirst) {
+                enabledEffects.add(mContext.getString(R.string.mode_aod_title));
+            } else {
+                enabledEffects.add(mContext.getString(
+                        R.string.mode_aod_title_secondary_list));
             }
-            if (currEffects.shouldDimWallpaper()) {
-                if (isFirst) {
-                    enabledEffects.add(mContext.getString(R.string.mode_wallpaper_title));
-                } else {
-                    enabledEffects.add(mContext.getString(
-                            R.string.mode_wallpaper_title_secondary_list));
-                }
-                isFirst = false;
+            isFirst = false;
+        }
+        if (currEffects.shouldDimWallpaper()) {
+            if (isFirst) {
+                enabledEffects.add(mContext.getString(R.string.mode_wallpaper_title));
+            } else {
+                enabledEffects.add(mContext.getString(
+                        R.string.mode_wallpaper_title_secondary_list));
             }
-            if (currEffects.shouldUseNightMode()) {
-                if (isFirst) {
-                    enabledEffects.add(mContext.getString(R.string.mode_dark_theme_title));
-                } else {
-                    enabledEffects.add(mContext.getString(
-                            R.string.mode_dark_theme_title_secondary_list));
-                }
-                isFirst = false;
+            isFirst = false;
+        }
+        if (currEffects.shouldUseNightMode()) {
+            if (isFirst) {
+                enabledEffects.add(mContext.getString(R.string.mode_dark_theme_title));
+            } else {
+                enabledEffects.add(mContext.getString(
+                        R.string.mode_dark_theme_title_secondary_list));
             }
+            isFirst = false;
         }
 
         int numCategories = enabledEffects.size();
diff --git a/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java b/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java
index f2302c0..1933635 100644
--- a/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java
@@ -87,7 +87,7 @@
 
         mModeName = zenMode.getName();
         PrimarySwitchPreference triggerPref = (PrimarySwitchPreference) preference;
-        triggerPref.setChecked(zenMode.getRule().isEnabled());
+        triggerPref.setChecked(zenMode.isEnabled());
         triggerPref.setOnPreferenceChangeListener(mSwitchChangeListener);
         if (zenMode.isSystemOwned()) {
             setUpForSystemOwnedTrigger(triggerPref, zenMode);
@@ -213,7 +213,7 @@
 
     private void setModeEnabled(boolean enabled) {
         saveMode((zenMode) -> {
-            if (enabled != zenMode.getRule().isEnabled()) {
+            if (enabled != zenMode.isEnabled()) {
                 zenMode.getRule().setEnabled(enabled);
             }
             return zenMode;
diff --git a/src/com/android/settings/notification/modes/ZenModesListAddModeTypeChooserDialog.java b/src/com/android/settings/notification/modes/ZenModesListAddModeTypeChooserDialog.java
index e7905a8..0bf9c5b 100644
--- a/src/com/android/settings/notification/modes/ZenModesListAddModeTypeChooserDialog.java
+++ b/src/com/android/settings/notification/modes/ZenModesListAddModeTypeChooserDialog.java
@@ -70,6 +70,17 @@
         dialog.show(parent.getParentFragmentManager(), TAG);
     }
 
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        if (mChooseModeTypeListener == null) {
+            // Probably the dialog fragment was recreated after its activity being destroyed.
+            // It's pointless to re-show the dialog if we can't do anything when its options are
+            // selected, so we don't.
+            dismiss();
+        }
+    }
+
     @NonNull
     @Override
     public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
diff --git a/src/com/android/settings/notification/modes/ZenModesListFragment.java b/src/com/android/settings/notification/modes/ZenModesListFragment.java
index 588b320..37772b3 100644
--- a/src/com/android/settings/notification/modes/ZenModesListFragment.java
+++ b/src/com/android/settings/notification/modes/ZenModesListFragment.java
@@ -31,6 +31,7 @@
 import com.android.settings.notification.modes.ZenModesListAddModePreferenceController.OnAddModeListener;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settingslib.core.AbstractPreferenceController;
+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.search.SearchIndexable;
@@ -56,7 +57,7 @@
     private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
             ZenModesBackend backend, OnAddModeListener onAddModeListener) {
         return ImmutableList.of(
-                new ZenModesListPreferenceController(context, backend),
+                new ZenModesListPreferenceController(context, backend, ZenIconLoader.getInstance()),
                 new ZenModesListAddModePreferenceController(context, onAddModeListener)
         );
     }
diff --git a/src/com/android/settings/notification/modes/ZenModesListItemPreference.java b/src/com/android/settings/notification/modes/ZenModesListItemPreference.java
index 0c31d8f..0909c6f 100644
--- a/src/com/android/settings/notification/modes/ZenModesListItemPreference.java
+++ b/src/com/android/settings/notification/modes/ZenModesListItemPreference.java
@@ -31,6 +31,8 @@
 
 import com.google.common.base.Strings;
 
+import java.util.concurrent.Executor;
+
 /**
  * Preference representing a single mode item on the modes aggregator page. Clicking on this
  * preference leads to an individual mode's configuration page.
@@ -38,18 +40,29 @@
 class ZenModesListItemPreference extends RestrictedPreference {
 
     private final Context mContext;
+    private final ZenIconLoader mIconLoader;
+    private final Executor mUiExecutor;
     private ZenMode mZenMode;
 
     private TextView mTitleView;
     private TextView mSummaryView;
 
-    ZenModesListItemPreference(Context context, ZenMode zenMode) {
+    ZenModesListItemPreference(Context context, ZenIconLoader iconLoader, ZenMode zenMode) {
+        this(context, iconLoader, context.getMainExecutor(), zenMode);
+    }
+
+    @VisibleForTesting
+    ZenModesListItemPreference(Context context, ZenIconLoader iconLoader, Executor uiExecutor,
+            ZenMode zenMode) {
         super(context);
         mContext = context;
+        mIconLoader = iconLoader;
+        mUiExecutor = uiExecutor;
         setZenMode(zenMode);
         setKey(zenMode.getId());
     }
 
+
     @Override
     public void onBindViewHolder(PreferenceViewHolder holder) {
         super.onBindViewHolder(holder);
@@ -93,12 +106,12 @@
 
         setIconSize(ICON_SIZE_SMALL);
         FutureUtil.whenDone(
-                ZenIconLoader.getInstance().getIcon(mContext, mZenMode),
+                mIconLoader.getIcon(mContext, mZenMode),
                 icon -> setIcon(
                         zenMode.isActive()
                                 ? IconUtil.applyAccentTint(mContext, icon.drawable())
                                 : IconUtil.applyNormalTint(mContext, icon.drawable())),
-                mContext.getMainExecutor());
+                mUiExecutor);
 
         updateTextColor(zenMode);
     }
diff --git a/src/com/android/settings/notification/modes/ZenModesListPreferenceController.java b/src/com/android/settings/notification/modes/ZenModesListPreferenceController.java
index 12b7278..75d4ee3 100644
--- a/src/com/android/settings/notification/modes/ZenModesListPreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModesListPreferenceController.java
@@ -25,6 +25,7 @@
 
 import com.android.settings.R;
 import com.android.settings.core.BasePreferenceController;
+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.search.SearchIndexableRaw;
@@ -42,11 +43,14 @@
         implements BasePreferenceController.UiBlocker {
     protected static final String KEY = "zen_modes_list";
 
-    protected ZenModesBackend mBackend;
+    private final ZenModesBackend mBackend;
+    private final ZenIconLoader mIconLoader;
 
-    ZenModesListPreferenceController(Context context, @NonNull ZenModesBackend backend) {
+    ZenModesListPreferenceController(Context context, @NonNull ZenModesBackend backend, @NonNull
+            ZenIconLoader iconLoader) {
         super(context, KEY);
         mBackend = backend;
+        mIconLoader = iconLoader;
     }
 
     @Override
@@ -82,7 +86,7 @@
                 modePreference.setZenMode(mode);
             } else {
                 // new rule; create a new ZenRulePreference & add it to the preference category
-                modePreference = new ZenModesListItemPreference(mContext, mode);
+                modePreference = new ZenModesListItemPreference(mContext, mIconLoader, mode);
                 category.addPreference(modePreference);
             }
             modePreference.setOrder(modes.indexOf(mode));
@@ -114,7 +118,7 @@
         for (ZenMode mode : mBackend.getModes()) {
             SearchIndexableRaw data = new SearchIndexableRaw(mContext);
             data.key = mode.getId();
-            data.title = mode.getRule().getName();
+            data.title = mode.getName();
             data.screenTitle = res.getString(R.string.zen_modes_list_title);
             rawData.add(data);
         }
diff --git a/src/com/android/settings/spa/network/SimsSection.kt b/src/com/android/settings/spa/network/SimsSection.kt
index 842656e..276d121 100644
--- a/src/com/android/settings/spa/network/SimsSection.kt
+++ b/src/com/android/settings/spa/network/SimsSection.kt
@@ -40,6 +40,7 @@
 import com.android.settings.network.telephony.MobileNetworkUtils
 import com.android.settings.network.telephony.SubscriptionActivationRepository
 import com.android.settings.network.telephony.SubscriptionRepository
+import com.android.settings.network.telephony.euicc.EuiccRepository
 import com.android.settings.network.telephony.phoneNumberFlow
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
@@ -120,13 +121,17 @@
 @Composable
 private fun AddSim() {
     val context = LocalContext.current
-    if (remember { MobileNetworkUtils.showEuiccSettings(context) }) {
+    val isShow by
+        remember { EuiccRepository(context).showEuiccSettingsFlow() }
+            .collectAsStateWithLifecycle(initialValue = false)
+    if (isShow) {
         RestrictedPreference(
-            model = object : PreferenceModel {
-                override val title = stringResource(id = R.string.mobile_network_list_add_more)
-                override val icon = @Composable { SettingsIcon(Icons.Outlined.Add) }
-                override val onClick = { startAddSimFlow(context) }
-            },
+            model =
+                object : PreferenceModel {
+                    override val title = stringResource(id = R.string.mobile_network_list_add_more)
+                    override val icon = @Composable { SettingsIcon(Icons.Outlined.Add) }
+                    override val onClick = { startAddSimFlow(context) }
+                },
             restrictions = Restrictions(keys = listOf(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)),
         )
     }
diff --git a/src/com/android/settings/utils/FileSizeFormatter.java b/src/com/android/settings/utils/FileSizeFormatter.java
deleted file mode 100644
index 5950d5d..0000000
--- a/src/com/android/settings/utils/FileSizeFormatter.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright (C) 2017 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.utils;
-
-import android.content.Context;
-import android.icu.text.DecimalFormat;
-import android.icu.text.MeasureFormat;
-import android.icu.text.NumberFormat;
-import android.icu.util.Measure;
-import android.icu.util.MeasureUnit;
-import android.text.BidiFormatter;
-import android.text.TextUtils;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import java.math.BigDecimal;
-import java.util.Locale;
-
-/**
- * Utility class to aid in formatting file sizes always with the same unit. This is modified from
- * android.text.format.Formatter to fit this purpose.
- */
-public final class FileSizeFormatter {
-    public static final long KILOBYTE_IN_BYTES = 1000;
-    public static final long MEGABYTE_IN_BYTES = KILOBYTE_IN_BYTES * 1000;
-    public static final long GIGABYTE_IN_BYTES = MEGABYTE_IN_BYTES * 1000;
-
-    private static class RoundedBytesResult {
-        public final float value;
-        public final MeasureUnit units;
-        public final int fractionDigits;
-        public final long roundedBytes;
-
-        private RoundedBytesResult(
-                float value, MeasureUnit units, int fractionDigits, long roundedBytes) {
-            this.value = value;
-            this.units = units;
-            this.fractionDigits = fractionDigits;
-            this.roundedBytes = roundedBytes;
-        }
-    }
-
-    private static Locale localeFromContext(@NonNull Context context) {
-        return context.getResources().getConfiguration().locale;
-    }
-
-    private static String bidiWrap(@NonNull Context context, String source) {
-        final Locale locale = localeFromContext(context);
-        if (TextUtils.getLayoutDirectionFromLocale(locale) == View.LAYOUT_DIRECTION_RTL) {
-            return BidiFormatter.getInstance(true /* RTL*/).unicodeWrap(source);
-        } else {
-            return source;
-        }
-    }
-
-    private static NumberFormat getNumberFormatter(Locale locale, int fractionDigits) {
-        final NumberFormat numberFormatter = NumberFormat.getInstance(locale);
-        numberFormatter.setMinimumFractionDigits(fractionDigits);
-        numberFormatter.setMaximumFractionDigits(fractionDigits);
-        numberFormatter.setGroupingUsed(false);
-        if (numberFormatter instanceof DecimalFormat) {
-            // We do this only for DecimalFormat, since in the general NumberFormat case, calling
-            // setRoundingMode may throw an exception.
-            numberFormatter.setRoundingMode(BigDecimal.ROUND_HALF_UP);
-        }
-        return numberFormatter;
-    }
-
-    private static String formatMeasureShort(Locale locale, NumberFormat numberFormatter,
-            float value, MeasureUnit units) {
-        final MeasureFormat measureFormatter = MeasureFormat.getInstance(
-                locale, MeasureFormat.FormatWidth.SHORT, numberFormatter);
-        return measureFormatter.format(new Measure(value, units));
-    }
-
-    private static String formatRoundedBytesResult(
-            @NonNull Context context, @NonNull RoundedBytesResult input) {
-        final Locale locale = localeFromContext(context);
-        final NumberFormat numberFormatter = getNumberFormatter(locale, input.fractionDigits);
-        return formatMeasureShort(locale, numberFormatter, input.value, input.units);
-    }
-
-    /**
-     * Formats a content size to be in the form of bytes, kilobytes, megabytes, etc.
-     *
-     * <p>As of O, the prefixes are used in their standard meanings in the SI system, so kB = 1000
-     * bytes, MB = 1,000,000 bytes, etc.
-     *
-     * <p class="note">In {@link android.os.Build.VERSION_CODES#N} and earlier, powers of 1024 are
-     * used instead, with KB = 1024 bytes, MB = 1,048,576 bytes, etc.
-     *
-     * <p>If the context has a right-to-left locale, the returned string is wrapped in bidi
-     * formatting characters to make sure it's displayed correctly if inserted inside a
-     * right-to-left string. (This is useful in cases where the unit strings, like "MB", are
-     * left-to-right, but the locale is right-to-left.)
-     *
-     * @param context Context to use to load the localized units
-     * @param sizeBytes size value to be formatted, in bytes
-     * @param unit The unit used for formatting.
-     * @param mult Amount of bytes in the unit.
-     * @return formatted string with the number
-     */
-    public static String formatFileSize(
-            @Nullable Context context, long sizeBytes, MeasureUnit unit, long mult) {
-        if (context == null) {
-            return "";
-        }
-        final RoundedBytesResult res = formatBytes(sizeBytes, unit, mult);
-        return bidiWrap(context, formatRoundedBytesResult(context, res));
-    }
-
-    /**
-     * A simplified version of the SettingsLib file size formatter. The primary difference is that
-     * this version always assumes it is doing a "short file size" and allows for a suffix to be
-     * provided.
-     *
-     * @param res Resources to fetch strings with.
-     * @param sizeBytes File size in bytes to format.
-     * @param suffix String id for the unit suffix.
-     * @param mult Amount of bytes in the unit.
-     */
-    private static RoundedBytesResult formatBytes(
-            long sizeBytes, MeasureUnit unit, long mult) {
-        final boolean isNegative = (sizeBytes < 0);
-        float result = isNegative ? -sizeBytes : sizeBytes;
-        result = result / mult;
-        // Note we calculate the rounded long by ourselves, but still let String.format()
-        // compute the rounded value. String.format("%f", 0.1) might not return "0.1" due to
-        // floating point errors.
-        final int roundFactor;
-        final int roundDigits;
-        if (mult == 1) {
-            roundFactor = 1;
-            roundDigits = 0;
-        } else if (result < 1) {
-            roundFactor = 100;
-            roundDigits = 2;
-        } else if (result < 10) {
-            roundFactor = 10;
-            roundDigits = 1;
-        } else { // 10 <= result < 100
-            roundFactor = 1;
-            roundDigits = 0;
-        }
-
-        if (isNegative) {
-            result = -result;
-        }
-
-        // Note this might overflow if abs(result) >= Long.MAX_VALUE / 100, but that's like 80PB so
-        // it's okay (for now)...
-        final long roundedBytes = (((long) Math.round(result * roundFactor)) * mult / roundFactor);
-
-        return new RoundedBytesResult(result, unit, roundDigits, roundedBytes);
-    }
-}
diff --git a/src/com/android/settings/wifi/WifiSummaryUpdater.java b/src/com/android/settings/wifi/WifiSummaryUpdater.java
deleted file mode 100644
index 645d2ea..0000000
--- a/src/com/android/settings/wifi/WifiSummaryUpdater.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2017 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.wifi;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.ConnectivityManager;
-import android.net.NetworkScoreManager;
-import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
-import android.text.TextUtils;
-
-import androidx.annotation.VisibleForTesting;
-
-import com.android.settings.R;
-import com.android.settings.widget.SummaryUpdater;
-import com.android.settingslib.wifi.WifiStatusTracker;
-
-/**
- * Helper class that listeners to wifi callback and notify client when there is update in
- * wifi summary info.
- */
-public class WifiSummaryUpdater extends SummaryUpdater {
-
-    private final WifiStatusTracker mWifiTracker;
-    private final BroadcastReceiver mReceiver;
-
-    private static final IntentFilter INTENT_FILTER;
-    static {
-        INTENT_FILTER = new IntentFilter();
-        INTENT_FILTER.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
-        INTENT_FILTER.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
-        INTENT_FILTER.addAction(WifiManager.RSSI_CHANGED_ACTION);
-    }
-
-    public WifiSummaryUpdater(Context context, OnSummaryChangeListener listener) {
-        this(context, listener, null);
-    }
-
-    @VisibleForTesting
-    public WifiSummaryUpdater(Context context, OnSummaryChangeListener listener,
-        WifiStatusTracker wifiTracker) {
-        super(context, listener);
-        mWifiTracker = wifiTracker != null ? wifiTracker :
-                new WifiStatusTracker(context, context.getSystemService(WifiManager.class),
-                context.getSystemService(NetworkScoreManager.class),
-                context.getSystemService(ConnectivityManager.class),
-                        this::notifyChangeIfNeeded);
-        mReceiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                mWifiTracker.handleBroadcast(intent);
-                notifyChangeIfNeeded();
-            }
-        };
-    }
-
-    @Override
-    public void register(boolean register) {
-        if (register) {
-            mWifiTracker.fetchInitialState();
-            notifyChangeIfNeeded();
-            mContext.registerReceiver(mReceiver, INTENT_FILTER,
-                    Context.RECEIVER_EXPORTED_UNAUDITED);
-        } else {
-            mContext.unregisterReceiver(mReceiver);
-        }
-        mWifiTracker.setListening(register);
-    }
-
-    @Override
-    public String getSummary() {
-        if (!mWifiTracker.enabled) {
-            return mContext.getString(R.string.switch_off_text);
-        }
-        if (!mWifiTracker.connected) {
-            return mContext.getString(R.string.disconnected);
-        }
-        String ssid = WifiInfo.sanitizeSsid(mWifiTracker.ssid);
-        if (TextUtils.isEmpty(mWifiTracker.statusLabel)) {
-            return ssid;
-        }
-        return mContext.getResources().getString(
-                com.android.settingslib.R.string.preference_summary_default_combination,
-                ssid, mWifiTracker.statusLabel);
-    }
-
-    /**
-     * return true if Wi-Fi connected.
-     */
-    public boolean isWifiConnected() {
-        return mWifiTracker.connected;
-    }
-}
diff --git a/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
index 9992cc0..927d2ee 100644
--- a/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
+++ b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
@@ -556,6 +556,7 @@
             return mContext.getDrawable(getHotspotIconResource(deviceType));
         }
         if (mWifiEntry.getLevel() == WifiEntry.WIFI_LEVEL_UNREACHABLE) {
+            Log.w(TAG, "WiFi level is WIFI_LEVEL_UNREACHABLE(-1)");
             return mContext.getDrawable(R.drawable.empty_icon);
         }
         return mIconInjector.getIcon(wifiEntry.shouldShowXLevelIcon(), wifiEntry.getLevel());
diff --git a/src/com/android/settings/wifi/details2/WifiPrivacyPageProvider.kt b/src/com/android/settings/wifi/details2/WifiPrivacyPageProvider.kt
index 7744a73..8888f0d 100644
--- a/src/com/android/settings/wifi/details2/WifiPrivacyPageProvider.kt
+++ b/src/com/android/settings/wifi/details2/WifiPrivacyPageProvider.kt
@@ -55,6 +55,7 @@
 import com.android.wifitrackerlib.WifiEntry
 import java.time.Clock
 import java.time.ZoneOffset
+import java.util.Base64
 
 const val WIFI_ENTRY_KEY = "wifiEntryKey"
 
@@ -68,7 +69,8 @@
 
     @Composable
     override fun Page(arguments: Bundle?) {
-        val wifiEntryKey = arguments!!.getString(WIFI_ENTRY_KEY)
+        val wifiEntryKey =
+            String(Base64.getUrlDecoder().decode(arguments!!.getString(WIFI_ENTRY_KEY)))
         if (wifiEntryKey != null) {
             val context = LocalContext.current
             val lifecycle = LocalLifecycleOwner.current.lifecycle
@@ -81,7 +83,7 @@
 
     fun getRoute(
         wifiEntryKey: String,
-    ): String = "${name}/$wifiEntryKey"
+    ): String = "${name}/${Base64.getUrlEncoder().encodeToString(wifiEntryKey.toByteArray())}"
 }
 
 @Composable
diff --git a/tests/robotests/src/com/android/settings/applications/AppInfoWithHeaderTest.java b/tests/robotests/src/com/android/settings/applications/AppInfoWithHeaderTest.java
index cb121ea..0ed56c0 100644
--- a/tests/robotests/src/com/android/settings/applications/AppInfoWithHeaderTest.java
+++ b/tests/robotests/src/com/android/settings/applications/AppInfoWithHeaderTest.java
@@ -250,7 +250,7 @@
         }
 
         @Override
-        protected boolean hasInteractAcrossUsersPermission() {
+        protected boolean hasInteractAcrossUsersFullPermission() {
             return true;
         }
 
@@ -267,7 +267,7 @@
 
     private static final class TestFragmentWithoutPermission extends TestFragment {
         @Override
-        protected boolean hasInteractAcrossUsersPermission() {
+        protected boolean hasInteractAcrossUsersFullPermission() {
             return false;
         }
     }
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragmentTest.java
index 33292af..ecf6d00 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragmentTest.java
@@ -36,7 +36,6 @@
 import com.android.settings.connecteddevice.fastpair.FastPairDeviceUpdater;
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.core.PreferenceControllerListHelper;
-import com.android.settings.flags.Flags;
 import com.android.settings.slices.SlicePreferenceController;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
@@ -94,7 +93,6 @@
 
         mContext = spy(RuntimeEnvironment.application);
         mFragment = new ConnectedDeviceDashboardFragment();
-        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_SUBSEQUENT_PAIR_SETTINGS_INTEGRATION);
         mSetFlagsRule.enableFlags(com.android.settingslib.flags.Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
         mFeatureFactory = FakeFeatureFactory.setupForTest();
         when(mFeatureFactory
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandlerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandlerTest.java
index 4933c43..ad6dd7f 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandlerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandlerTest.java
@@ -39,6 +39,7 @@
 import android.bluetooth.BluetoothStatusCodes;
 import android.content.Context;
 import android.content.Intent;
+import android.media.AudioManager;
 import android.os.Bundle;
 import android.os.Looper;
 import android.platform.test.flag.junit.SetFlagsRule;
@@ -113,6 +114,7 @@
     @Mock private CachedBluetoothDeviceManager mCacheManager;
     @Mock private LocalBluetoothLeBroadcast mBroadcast;
     @Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
+    @Mock private AudioManager mAudioManager;
     @Mock private CachedBluetoothDevice mCachedDevice1;
     @Mock private CachedBluetoothDevice mCachedDevice2;
     @Mock private CachedBluetoothDevice mCachedDevice3;
@@ -152,6 +154,8 @@
         when(mLocalBtManager.getProfileManager()).thenReturn(mLocalBtProfileManager);
         when(mLocalBtProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast);
         when(mLocalBtProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant);
+        when(mContext.getSystemService(AudioManager.class)).thenReturn(mAudioManager);
+        when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL);
         List<Long> bisSyncState = new ArrayList<>();
         bisSyncState.add(1L);
         when(mState.getBisSyncState()).thenReturn(bisSyncState);
@@ -188,6 +192,18 @@
     }
 
     @Test
+    public void handleUserTriggeredDeviceConnected_inCall_setActive() {
+        when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_IN_CALL);
+        setUpBroadcast(true);
+        ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice1);
+        when(mAssistant.getAllConnectedDevices()).thenReturn(deviceList);
+        when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of());
+        mHandler.handleDeviceConnected(mCachedDevice1, /* userTriggered= */ true);
+        shadowOf(Looper.getMainLooper()).idle();
+        verify(mCachedDevice1).setActive();
+    }
+
+    @Test
     public void handleUserTriggeredNonLeaDeviceConnected_noSharing_setActive() {
         setUpBroadcast(false);
         ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice2);
@@ -404,6 +420,18 @@
     }
 
     @Test
+    public void handleDeviceConnected_inCall_doNothing() {
+        when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_IN_CALL);
+        setUpBroadcast(true);
+        when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of());
+        mHandler.handleDeviceConnected(mCachedDevice2, /* userTriggered= */ false);
+        shadowOf(Looper.getMainLooper()).idle();
+        verify(mCachedDevice2, never()).setActive();
+        List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
+        assertThat(childFragments).isEmpty();
+    }
+
+    @Test
     public void handleNonLeaDeviceConnected_noSharing_doNothing() {
         setUpBroadcast(false);
         ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice2);
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragmentTest.java
index 2310d75..32f9e83 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragmentTest.java
@@ -31,6 +31,7 @@
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.Pair;
 import android.view.View;
+import android.widget.Button;
 
 import androidx.appcompat.app.AlertDialog;
 import androidx.fragment.app.Fragment;
@@ -180,6 +181,7 @@
     @Test
     public void onCreateDialog_flagOn_dialogShowTextForSingleDevice() {
         mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+        when(mBroadcast.isEnabled(null)).thenReturn(true);
         AudioSharingJoinDialogFragment.show(
                 mParent,
                 new ArrayList<>(),
@@ -192,6 +194,10 @@
         assertThat(dialog.isShowing()).isTrue();
         ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
         assertThat(shadowDialog.getMessage().toString()).isEqualTo(TEST_DEVICE_NAME2);
+        Button btnView = dialog.findViewById(R.id.negative_btn);
+        assertThat(btnView).isNotNull();
+        assertThat(btnView.getText().toString())
+                .isEqualTo(mParent.getString(R.string.audio_sharing_no_thanks_button_label));
     }
 
     @Test
@@ -212,6 +218,13 @@
                                 R.string.audio_sharing_share_dialog_subtitle,
                                 TEST_DEVICE_NAME1,
                                 TEST_DEVICE_NAME2));
+        Button btnView = dialog.findViewById(R.id.negative_btn);
+        assertThat(btnView).isNotNull();
+        assertThat(btnView.getText().toString())
+                .isEqualTo(
+                        mParent.getString(
+                                R.string.audio_sharing_switch_active_button_label,
+                                TEST_DEVICE_NAME2));
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java
index 558bc10..d1533c9 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java
@@ -75,6 +75,8 @@
 import com.android.settings.testutils.shadow.ShadowThreadUtils;
 import com.android.settings.widget.SettingsMainSwitchBar;
 import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.bluetooth.BluetoothCallback;
+import com.android.settingslib.bluetooth.BluetoothEventManager;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
@@ -117,6 +119,8 @@
 public class AudioSharingSwitchBarControllerTest {
     private static final String TEST_DEVICE_NAME1 = "test1";
     private static final String TEST_DEVICE_NAME2 = "test2";
+    private static final String TEST_DEVICE_ANONYMIZED_ADDR1 = "XX:XX:01";
+    private static final String TEST_DEVICE_ANONYMIZED_ADDR2 = "XX:XX:02";
     private static final int TEST_DEVICE_GROUP_ID1 = 1;
     private static final int TEST_DEVICE_GROUP_ID2 = 2;
     private static final Correspondence<Fragment, String> TAG_EQUALS =
@@ -133,6 +137,7 @@
     @Spy Context mContext = ApplicationProvider.getApplicationContext();
     @Mock private LocalBluetoothManager mLocalBtManager;
     @Mock private CachedBluetoothDeviceManager mDeviceManager;
+    @Mock private BluetoothEventManager mEventManager;
     @Mock private LocalBluetoothProfileManager mBtProfileManager;
     @Mock private LocalBluetoothLeBroadcast mBroadcast;
     @Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
@@ -168,11 +173,14 @@
         mFeatureFactory = FakeFeatureFactory.setupForTest();
         when(localBluetoothManager.getProfileManager()).thenReturn(mBtProfileManager);
         when(localBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
+        when(localBluetoothManager.getEventManager()).thenReturn(mEventManager);
+        when(mDevice1.getAnonymizedAddress()).thenReturn(TEST_DEVICE_ANONYMIZED_ADDR1);
         when(mDeviceManager.findDevice(mDevice1)).thenReturn(mCachedDevice1);
         when(mCachedDevice1.getDevice()).thenReturn(mDevice1);
         when(mCachedDevice1.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID1);
         when(mCachedDevice1.getName()).thenReturn(TEST_DEVICE_NAME1);
         when(mCachedDevice1.isActiveDevice(BluetoothProfile.LE_AUDIO)).thenReturn(false);
+        when(mDevice2.getAnonymizedAddress()).thenReturn(TEST_DEVICE_ANONYMIZED_ADDR2);
         when(mDeviceManager.findDevice(mDevice2)).thenReturn(mCachedDevice2);
         when(mCachedDevice2.getDevice()).thenReturn(mDevice2);
         when(mCachedDevice2.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID2);
@@ -308,6 +316,7 @@
         verify(mAssistant, never())
                 .registerServiceCallBack(
                         any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class));
+        verify(mEventManager, never()).registerCallback(any(BluetoothCallback.class));
         verify(mBtProfileManager).addServiceListener(mController);
         assertThat(mSwitchBar.isChecked()).isFalse();
         assertThat(mSwitchBar.isEnabled()).isFalse();
@@ -328,6 +337,7 @@
         verify(mAssistant)
                 .registerServiceCallBack(
                         any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class));
+        verify(mEventManager).registerCallback(any(BluetoothCallback.class));
         verify(mBtProfileManager, never()).addServiceListener(mController);
         assertThat(mSwitchBar.isChecked()).isTrue();
         assertThat(mSwitchBar.isEnabled()).isTrue();
@@ -342,6 +352,7 @@
                 .unregisterServiceCallBack(any(BluetoothLeBroadcast.Callback.class));
         verify(mAssistant, never())
                 .unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
+        verify(mEventManager, never()).unregisterCallback(any(BluetoothCallback.class));
         verify(mBtProfileManager, never()).removeServiceListener(mController);
     }
 
@@ -358,6 +369,7 @@
                 .unregisterServiceCallBack(any(BluetoothLeBroadcast.Callback.class));
         verify(mAssistant, never())
                 .unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
+        verify(mEventManager, never()).unregisterCallback(any(BluetoothCallback.class));
     }
 
     @Test
@@ -374,6 +386,7 @@
         verify(mBroadcast).unregisterServiceCallBack(any(BluetoothLeBroadcast.Callback.class));
         verify(mAssistant)
                 .unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
+        verify(mEventManager).unregisterCallback(any(BluetoothCallback.class));
     }
 
     @Test
@@ -443,6 +456,7 @@
                 mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
         when(mBtnView.isEnabled()).thenReturn(true);
         when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1));
+        when(mAssistant.getAllSources(any(BluetoothDevice.class))).thenReturn(ImmutableList.of());
         when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
         doNothing().when(mBroadcast).startPrivateBroadcast();
         mController =
@@ -469,11 +483,37 @@
     }
 
     @Test
+    public void onPlaybackStarted_hasLocalSource_noDialog() {
+        FeatureFlagUtils.setEnabled(
+                mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
+        when(mBtnView.isEnabled()).thenReturn(true);
+        when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1));
+        BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class);
+        when(state.getBroadcastId()).thenReturn(1);
+        when(mBroadcast.getLatestBroadcastId()).thenReturn(1);
+        when(mAssistant.getAllSources(mDevice2)).thenReturn(ImmutableList.of(state));
+        when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
+        doNothing().when(mBroadcast).startPrivateBroadcast();
+        mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
+        verify(mBroadcast).startPrivateBroadcast();
+        mController.mBroadcastCallback.onPlaybackStarted(0, 0);
+        shadowOf(Looper.getMainLooper()).idle();
+
+        verify(mAssistant, never()).addSource(any(), any(), anyBoolean());
+        verify(mFeatureFactory.metricsFeatureProvider, never())
+                .action(any(Context.class), eq(SettingsEnums.ACTION_AUTO_JOIN_AUDIO_SHARING));
+
+        List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
+        assertThat(childFragments).isEmpty();
+    }
+
+    @Test
     public void onPlaybackStarted_showJoinAudioSharingDialog() {
         FeatureFlagUtils.setEnabled(
                 mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
         when(mBtnView.isEnabled()).thenReturn(true);
         when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1));
+        when(mAssistant.getAllSources(any(BluetoothDevice.class))).thenReturn(ImmutableList.of());
         doNothing().when(mBroadcast).startPrivateBroadcast();
         mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
         when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
@@ -519,6 +559,7 @@
                 mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
         when(mBtnView.isEnabled()).thenReturn(true);
         when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1));
+        when(mAssistant.getAllSources(any(BluetoothDevice.class))).thenReturn(ImmutableList.of());
         when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
         doNothing().when(mBroadcast).startPrivateBroadcast();
         mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
@@ -545,6 +586,7 @@
                 mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
         when(mBtnView.isEnabled()).thenReturn(true);
         when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1));
+        when(mAssistant.getAllSources(any(BluetoothDevice.class))).thenReturn(ImmutableList.of());
         when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
         doNothing().when(mBroadcast).startPrivateBroadcast();
         mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
@@ -570,9 +612,11 @@
         mOnAudioSharingStateChanged = false;
         mSwitchBar.setChecked(false);
         when(mBroadcast.isEnabled(any())).thenReturn(false);
+        when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice1, mDevice2));
         mController.mBroadcastCallback.onBroadcastStartFailed(/* reason= */ 1);
         shadowOf(Looper.getMainLooper()).idle();
         assertThat(mSwitchBar.isChecked()).isFalse();
+        assertThat(mSwitchBar.isEnabled()).isTrue();
         assertThat(mOnAudioSharingStateChanged).isFalse();
         verify(mFeatureFactory.metricsFeatureProvider)
                 .action(
@@ -584,6 +628,7 @@
         mController.mBroadcastCallback.onBroadcastStarted(/* reason= */ 1, /* broadcastId= */ 1);
         shadowOf(Looper.getMainLooper()).idle();
         assertThat(mSwitchBar.isChecked()).isTrue();
+        assertThat(mSwitchBar.isEnabled()).isTrue();
         assertThat(mOnAudioSharingStateChanged).isTrue();
 
         mOnAudioSharingStateChanged = false;
@@ -598,9 +643,11 @@
                         SettingsEnums.AUDIO_SHARING_SETTINGS);
 
         when(mBroadcast.isEnabled(any())).thenReturn(false);
+        when(mCachedDevice2.isActiveDevice(BluetoothProfile.LE_AUDIO)).thenReturn(false);
         mController.mBroadcastCallback.onBroadcastStopped(/* reason= */ 1, /* broadcastId= */ 1);
         shadowOf(Looper.getMainLooper()).idle();
         assertThat(mSwitchBar.isChecked()).isFalse();
+        assertThat(mSwitchBar.isEnabled()).isFalse();
         assertThat(mOnAudioSharingStateChanged).isTrue();
     }
 
@@ -654,6 +701,34 @@
     }
 
     @Test
+    public void onActiveDeviceChanged_leaProfile_updateSwitch() {
+        mSwitchBar.setChecked(true);
+        mSwitchBar.setEnabled(false);
+        when(mBroadcast.isEnabled(null)).thenReturn(false);
+        when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1));
+        mController.onActiveDeviceChanged(mCachedDevice2, BluetoothProfile.LE_AUDIO);
+        shadowOf(Looper.getMainLooper()).idle();
+        assertThat(mSwitchBar.isChecked()).isFalse();
+        verify(mSwitchBar).setEnabled(true);
+    }
+
+    @Test
+    public void onActiveDeviceChanged_nullActiveDevice_doNothing() {
+        mController.onActiveDeviceChanged(/* activeDevice= */ null, BluetoothProfile.LE_AUDIO);
+        shadowOf(Looper.getMainLooper()).idle();
+        verify(mSwitchBar, never()).setEnabled(anyBoolean());
+        verify(mSwitchBar, never()).setChecked(anyBoolean());
+    }
+
+    @Test
+    public void onActiveDeviceChanged_notLeaProfile_doNothing() {
+        mController.onActiveDeviceChanged(mCachedDevice2, BluetoothProfile.HEADSET);
+        shadowOf(Looper.getMainLooper()).idle();
+        verify(mSwitchBar, never()).setEnabled(anyBoolean());
+        verify(mSwitchBar, never()).setChecked(anyBoolean());
+    }
+
+    @Test
     public void testAccessibilityDelegate() {
         View view = new View(mContext);
         AccessibilityEvent event =
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/fastpair/FastPairDeviceGroupControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/fastpair/FastPairDeviceGroupControllerTest.java
index 25e927b..2b140ea 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/fastpair/FastPairDeviceGroupControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/fastpair/FastPairDeviceGroupControllerTest.java
@@ -33,8 +33,6 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.os.Looper;
-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;
 
@@ -45,7 +43,6 @@
 import androidx.preference.PreferenceScreen;
 
 import com.android.settings.dashboard.DashboardFragment;
-import com.android.settings.flags.Flags;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
 import com.android.settings.widget.GearPreference;
@@ -107,7 +104,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SUBSEQUENT_PAIR_SETTINGS_INTEGRATION)
     public void onStart_flagOn_registerCallback() {
         // register the callback in onStart()
         mFastPairDeviceGroupController.onStart(mLifecycleOwner);
@@ -120,7 +116,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SUBSEQUENT_PAIR_SETTINGS_INTEGRATION)
     public void onStop_flagOn_unregisterCallback() {
         // register broadcast first
         mContext.registerReceiver(
@@ -133,51 +128,6 @@
     }
 
     @Test
-    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_SUBSEQUENT_PAIR_SETTINGS_INTEGRATION)
-    public void onStart_flagOff_registerCallback() {
-        // register the callback in onStart()
-        mFastPairDeviceGroupController.onStart(mLifecycleOwner);
-        assertThat(mFastPairDeviceUpdater).isNull();
-        verify(mContext)
-                .registerReceiver(
-                        mFastPairDeviceGroupController.mReceiver,
-                        mFastPairDeviceGroupController.mIntentFilter,
-                        Context.RECEIVER_EXPORTED);
-    }
-
-    @Test
-    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_SUBSEQUENT_PAIR_SETTINGS_INTEGRATION)
-    public void onStop_flagOff_unregisterCallback() {
-        // register broadcast first
-        mContext.registerReceiver(
-                mFastPairDeviceGroupController.mReceiver, null, Context.RECEIVER_EXPORTED);
-
-        // unregister the callback in onStop()
-        mFastPairDeviceGroupController.onStop(mLifecycleOwner);
-        assertThat(mFastPairDeviceUpdater).isNull();
-        verify(mContext).unregisterReceiver(mFastPairDeviceGroupController.mReceiver);
-    }
-
-    @Test
-    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_SUBSEQUENT_PAIR_SETTINGS_INTEGRATION)
-    public void getAvailabilityStatus_noFastPairFeature_returnUnSupported() {
-        doReturn(true).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
-
-        assertThat(mFastPairDeviceGroupController.getAvailabilityStatus())
-                .isEqualTo(UNSUPPORTED_ON_DEVICE);
-    }
-
-    @Test
-    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_SUBSEQUENT_PAIR_SETTINGS_INTEGRATION)
-    public void getAvailabilityStatus_noBluetoothFastPairFeature_returnUnSupported() {
-        doReturn(false).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
-
-        assertThat(mFastPairDeviceGroupController.getAvailabilityStatus())
-                .isEqualTo(UNSUPPORTED_ON_DEVICE);
-    }
-
-    @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SUBSEQUENT_PAIR_SETTINGS_INTEGRATION)
     public void getAvailabilityStatus_noBluetoothFeature_returnUnSupported() {
         doReturn(false).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
 
@@ -186,7 +136,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SUBSEQUENT_PAIR_SETTINGS_INTEGRATION)
     public void getAvailabilityStatus_withBluetoothFastPairFeature_returnSupported() {
         doReturn(true).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
 
@@ -231,14 +180,6 @@
     }
 
     @Test
-    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_SUBSEQUENT_PAIR_SETTINGS_INTEGRATION)
-    public void displayPreference_notAvailable_doNothing() {
-        mFastPairDeviceGroupController.displayPreference(mScreen);
-        assertThat(mPreferenceGroup.isVisible()).isFalse();
-    }
-
-    @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SUBSEQUENT_PAIR_SETTINGS_INTEGRATION)
     public void displayPreference_isAvailable_fetchFastPairDevices() {
         doReturn(true).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
 
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/fastpair/FastPairDevicePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/fastpair/FastPairDevicePreferenceControllerTest.java
index be2ed56..8a84e23 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/fastpair/FastPairDevicePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/fastpair/FastPairDevicePreferenceControllerTest.java
@@ -32,8 +32,6 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.os.Looper;
-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;
 
@@ -44,7 +42,6 @@
 import androidx.preference.PreferenceManager;
 
 import com.android.settings.dashboard.DashboardFragment;
-import com.android.settings.flags.Flags;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
 import com.android.settings.widget.GearPreference;
@@ -108,7 +105,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SUBSEQUENT_PAIR_SETTINGS_INTEGRATION)
     public void onStart_flagOn_registerCallback() {
         // register the callback in onStart()
         mFastPairDevicePrefController.onStart(mLifecycleOwner);
@@ -121,20 +117,6 @@
     }
 
     @Test
-    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_SUBSEQUENT_PAIR_SETTINGS_INTEGRATION)
-    public void onStart_flagOff_registerCallback() {
-        // register the callback in onStart()
-        mFastPairDevicePrefController.onStart(mLifecycleOwner);
-        assertThat(mFastPairDeviceUpdater).isNull();
-        verify(mContext)
-                .registerReceiver(
-                        mFastPairDevicePrefController.mReceiver,
-                        mFastPairDevicePrefController.mIntentFilter,
-                        Context.RECEIVER_EXPORTED_UNAUDITED);
-    }
-
-    @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SUBSEQUENT_PAIR_SETTINGS_INTEGRATION)
     public void onStop_flagOn_unregisterCallback() {
         // register broadcast first
         mContext.registerReceiver(
@@ -147,20 +129,6 @@
     }
 
     @Test
-    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_SUBSEQUENT_PAIR_SETTINGS_INTEGRATION)
-    public void onStop_flagOff_unregisterCallback() {
-        // register broadcast first
-        mContext.registerReceiver(
-                mFastPairDevicePrefController.mReceiver, null, Context.RECEIVER_EXPORTED_UNAUDITED);
-
-        // unregister the callback in onStop()
-        mFastPairDevicePrefController.onStop(mLifecycleOwner);
-        assertThat(mFastPairDeviceUpdater).isNull();
-        verify(mContext).unregisterReceiver(mFastPairDevicePrefController.mReceiver);
-    }
-
-    @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SUBSEQUENT_PAIR_SETTINGS_INTEGRATION)
     public void getAvailabilityStatus_noBluetoothFeature_returnUnsupported() {
         doReturn(false).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
 
@@ -169,25 +137,6 @@
     }
 
     @Test
-    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_SUBSEQUENT_PAIR_SETTINGS_INTEGRATION)
-    public void getAvailabilityStatus_noFastPairFeature_returnUnsupported() {
-        doReturn(true).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
-
-        assertThat(mFastPairDevicePrefController.getAvailabilityStatus())
-                .isEqualTo(UNSUPPORTED_ON_DEVICE);
-    }
-
-    @Test
-    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_SUBSEQUENT_PAIR_SETTINGS_INTEGRATION)
-    public void getAvailabilityStatus_noBluetoothFastPairFeature_returnUnsupported() {
-        doReturn(false).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
-
-        assertThat(mFastPairDevicePrefController.getAvailabilityStatus())
-                .isEqualTo(UNSUPPORTED_ON_DEVICE);
-    }
-
-    @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SUBSEQUENT_PAIR_SETTINGS_INTEGRATION)
     public void getAvailabilityStatus_bothBluetoothFastPairFeature_returnSupported() {
         doReturn(true).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
 
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/StorageItemPreferenceTest.java b/tests/robotests/src/com/android/settings/deviceinfo/StorageItemPreferenceTest.java
index 984d945..eba8e11 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/StorageItemPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/StorageItemPreferenceTest.java
@@ -15,8 +15,6 @@
  */
 package com.android.settings.deviceinfo;
 
-import static com.android.settings.utils.FileSizeFormatter.MEGABYTE_IN_BYTES;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import android.content.Context;
@@ -36,6 +34,7 @@
 @RunWith(RobolectricTestRunner.class)
 public class StorageItemPreferenceTest {
 
+    private static final long MEGABYTE_IN_BYTES = 1000_000;
     private Context mContext;
     private StorageItemPreference mPreference;
 
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/NonCurrentUserControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/NonCurrentUserControllerTest.java
index 61d3bed..34cc4fd 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/storage/NonCurrentUserControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/NonCurrentUserControllerTest.java
@@ -16,8 +16,6 @@
 
 package com.android.settings.deviceinfo.storage;
 
-import static com.android.settings.utils.FileSizeFormatter.MEGABYTE_IN_BYTES;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.anyString;
@@ -61,6 +59,7 @@
 
     private static final String TEST_NAME = "Fred";
     private static final String TARGET_PREFERENCE_GROUP_KEY = "pref_secondary_users";
+    private static final long MEGABYTE_IN_BYTES = 1000_000;
     @Mock
     private UserManager mUserManager;
     @Mock
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
index 306d80c..a9e9eac 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
@@ -16,9 +16,6 @@
 package com.android.settings.deviceinfo.storage;
 
 import static com.android.settings.applications.manageapplications.ManageApplications.EXTRA_WORK_ID;
-import static com.android.settings.utils.FileSizeFormatter.GIGABYTE_IN_BYTES;
-import static com.android.settings.utils.FileSizeFormatter.KILOBYTE_IN_BYTES;
-import static com.android.settings.utils.FileSizeFormatter.MEGABYTE_IN_BYTES;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -74,6 +71,10 @@
 })
 public class StorageItemPreferenceControllerTest {
 
+    private static final long KILOBYTE_IN_BYTES = 1000;
+    private static final long MEGABYTE_IN_BYTES = KILOBYTE_IN_BYTES * 1000;
+    private static final long GIGABYTE_IN_BYTES = MEGABYTE_IN_BYTES * 1000;
+
     private Context mContext;
     private VolumeInfo mVolume;
     @Mock
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobReceiverTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobReceiverTest.java
index ea3c04c..8212166 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobReceiverTest.java
@@ -108,7 +108,7 @@
 
         mReceiver.onReceive(mContext, JOB_UPDATE_INTENT);
 
-        TimeUnit.MILLISECONDS.sleep(100);
+        TimeUnit.MILLISECONDS.sleep(1000);
         assertThat(mDao.getAllAfter(0)).hasSize(1);
     }
 
@@ -119,7 +119,7 @@
 
         mReceiver.onReceive(mContext, JOB_UPDATE_INTENT);
 
-        TimeUnit.MILLISECONDS.sleep(100);
+        TimeUnit.MILLISECONDS.sleep(1000);
         assertThat(mDao.getAllAfter(0)).hasSize(3);
     }
 
diff --git a/tests/robotests/src/com/android/settings/network/ims/MockVolteQueryImsState.java b/tests/robotests/src/com/android/settings/network/ims/MockVolteQueryImsState.java
deleted file mode 100644
index 515ab5b..0000000
--- a/tests/robotests/src/com/android/settings/network/ims/MockVolteQueryImsState.java
+++ /dev/null
@@ -1,105 +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.ims;
-
-import android.content.Context;
-import android.telephony.ims.ImsException;
-
-/**
- * Controller class for mock VoLte status
- */
-public class MockVolteQueryImsState extends VolteQueryImsState {
-
-    private Boolean mIsTtyOnVolteEnabled;
-    private Boolean mIsSupported;
-    private Boolean mIsProvisionedOnDevice;
-    private Boolean mIsServiceStateReady;
-    private Boolean mIsEnabledByUser;
-
-    /**
-     * Constructor
-     *
-     * @param context {@link Context}
-     * @param subId subscription's id
-     */
-    public MockVolteQueryImsState(Context context, int subId) {
-        super(context, subId);
-    }
-
-    public void setIsTtyOnVolteEnabled(boolean enabled) {
-        mIsTtyOnVolteEnabled = enabled;
-    }
-
-    @Override
-    boolean isTtyOnVolteEnabled(int subId) {
-        if (mIsTtyOnVolteEnabled != null) {
-            return mIsTtyOnVolteEnabled;
-        }
-        return super.isTtyOnVolteEnabled(subId);
-    }
-
-    public void setEnabledByPlatform(boolean isSupported) {
-        mIsSupported = isSupported;
-    }
-
-    @Override
-    boolean isEnabledByPlatform(int subId) throws InterruptedException, ImsException,
-            IllegalArgumentException {
-        if (mIsSupported != null) {
-            return mIsSupported;
-        }
-        return super.isEnabledByPlatform(subId);
-    }
-
-    public void setIsProvisionedOnDevice(boolean isProvisioned) {
-        mIsProvisionedOnDevice = isProvisioned;
-    }
-
-    @Override
-    boolean isProvisionedOnDevice(int subId) {
-        if (mIsProvisionedOnDevice != null) {
-            return mIsProvisionedOnDevice;
-        }
-        return super.isProvisionedOnDevice(subId);
-    }
-
-    public void setServiceStateReady(boolean isReady) {
-        mIsServiceStateReady = isReady;
-    }
-
-    @Override
-    boolean isServiceStateReady(int subId) throws InterruptedException, ImsException,
-            IllegalArgumentException {
-        if (mIsServiceStateReady != null) {
-            return mIsServiceStateReady;
-        }
-        return super.isServiceStateReady(subId);
-    }
-
-    public void setIsEnabledByUser(boolean enabled) {
-        mIsEnabledByUser = enabled;
-    }
-
-    @Override
-    boolean isEnabledByUser(int subId) {
-        if (mIsEnabledByUser != null) {
-            return mIsEnabledByUser;
-        }
-        return super.isEnabledByUser(subId);
-    }
-
-}
diff --git a/tests/robotests/src/com/android/settings/network/ims/MockVtQueryImsState.java b/tests/robotests/src/com/android/settings/network/ims/MockVtQueryImsState.java
deleted file mode 100644
index 0949f1c..0000000
--- a/tests/robotests/src/com/android/settings/network/ims/MockVtQueryImsState.java
+++ /dev/null
@@ -1,104 +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.ims;
-
-import android.content.Context;
-import android.telephony.ims.ImsException;
-
-/**
- * Controller class for mock VT status
- */
-public class MockVtQueryImsState extends VtQueryImsState {
-
-    private Boolean mIsTtyOnVolteEnabled;
-    private Boolean mIsEnabledOnPlatform;
-    private Boolean mIsProvisionedOnDevice;
-    private Boolean mIsEnabledByUser;
-    private Boolean mIsServiceStateReady;
-
-    /**
-     * Constructor
-     *
-     * @param context {@link Context}
-     * @param subId subscription's id
-     */
-    public MockVtQueryImsState(Context context, int subId) {
-        super(context, subId);
-    }
-
-    public void setIsTtyOnVolteEnabled(boolean enabled) {
-        mIsTtyOnVolteEnabled = enabled;
-    }
-
-    @Override
-    boolean isTtyOnVolteEnabled(int subId) {
-        if (mIsTtyOnVolteEnabled != null) {
-            return mIsTtyOnVolteEnabled;
-        }
-        return super.isTtyOnVolteEnabled(subId);
-    }
-
-    public void setIsEnabledByPlatform(boolean isEnabled) {
-        mIsEnabledOnPlatform = isEnabled;
-    }
-
-    @Override
-    boolean isEnabledByPlatform(int subId) throws InterruptedException, ImsException,
-            IllegalArgumentException {
-        if (mIsEnabledOnPlatform != null) {
-            return mIsEnabledOnPlatform;
-        }
-        return super.isEnabledByPlatform(subId);
-    }
-
-    public void setIsProvisionedOnDevice(boolean isProvisioned) {
-        mIsProvisionedOnDevice = isProvisioned;
-    }
-
-    @Override
-    boolean isProvisionedOnDevice(int subId) {
-        if (mIsProvisionedOnDevice != null) {
-            return mIsProvisionedOnDevice;
-        }
-        return super.isProvisionedOnDevice(subId);
-    }
-
-    public void setServiceStateReady(boolean isReady) {
-        mIsServiceStateReady = isReady;
-    }
-
-    @Override
-    boolean isServiceStateReady(int subId) throws InterruptedException, ImsException,
-            IllegalArgumentException {
-        if (mIsServiceStateReady != null) {
-            return mIsServiceStateReady;
-        }
-        return super.isServiceStateReady(subId);
-    }
-
-    public void setIsEnabledByUser(boolean enabled) {
-        mIsEnabledByUser = enabled;
-    }
-
-    @Override
-    boolean isEnabledByUser(int subId) {
-        if (mIsEnabledByUser != null) {
-            return mIsEnabledByUser;
-        }
-        return super.isEnabledByUser(subId);
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/network/telephony/VideoCallingPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/telephony/VideoCallingPreferenceControllerTest.java
deleted file mode 100644
index da8958d..0000000
--- a/tests/robotests/src/com/android/settings/network/telephony/VideoCallingPreferenceControllerTest.java
+++ /dev/null
@@ -1,160 +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 com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
-
-import android.content.Context;
-import android.os.PersistableBundle;
-import android.telephony.CarrierConfigManager;
-import android.telephony.TelephonyManager;
-import android.telephony.ims.ProvisioningManager;
-
-import androidx.preference.PreferenceScreen;
-import androidx.preference.SwitchPreference;
-
-import com.android.settings.network.CarrierConfigCache;
-import com.android.settings.network.ims.MockVolteQueryImsState;
-import com.android.settings.network.ims.MockVtQueryImsState;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-@RunWith(RobolectricTestRunner.class)
-public class VideoCallingPreferenceControllerTest {
-    private static final int SUB_ID = 2;
-
-    @Mock
-    private TelephonyManager mTelephonyManager;
-    @Mock
-    private ProvisioningManager mProvisioningManager;
-    @Mock
-    private CarrierConfigCache mCarrierConfigCache;
-    @Mock
-    private PreferenceScreen mPreferenceScreen;
-
-    private MockVtQueryImsState mQueryImsState;
-    private MockVolteQueryImsState mQueryVoLteState;
-
-    private VideoCallingPreferenceController mController;
-    private PersistableBundle mCarrierConfig;
-    private SwitchPreference mPreference;
-    private Context mContext;
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-
-        mContext = spy(RuntimeEnvironment.application);
-        doReturn(mTelephonyManager).when(mContext).getSystemService(TelephonyManager.class);
-        CarrierConfigCache.setTestInstance(mContext, mCarrierConfigCache);
-        doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(SUB_ID);
-
-        mCarrierConfig = new PersistableBundle();
-        mCarrierConfig.putBoolean(
-                CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS, true);
-        doReturn(mCarrierConfig).when(mCarrierConfigCache).getConfigForSubId(SUB_ID);
-
-        mQueryImsState = new MockVtQueryImsState(mContext, SUB_ID);
-        mQueryImsState.setIsEnabledByUser(true);
-
-        mQueryVoLteState = new MockVolteQueryImsState(mContext, SUB_ID);
-        mQueryVoLteState.setIsEnabledByUser(true);
-
-        mPreference = new SwitchPreference(mContext);
-        mController = spy(new VideoCallingPreferenceController(mContext, "wifi_calling"));
-        mController.init(
-                SUB_ID, new CallingPreferenceCategoryController(mContext, "calling_category"));
-        doReturn(mQueryImsState).when(mController).queryImsState(anyInt());
-        doReturn(mQueryVoLteState).when(mController).queryVoLteState(anyInt());
-        doReturn(true).when(mController).isImsSupported();
-        mPreference.setKey(mController.getPreferenceKey());
-
-        mQueryImsState.setIsEnabledByPlatform(true);
-        mQueryImsState.setIsProvisionedOnDevice(true);
-        mQueryImsState.setServiceStateReady(true);
-        doReturn(true).when(mTelephonyManager).isDataEnabled();
-
-        mController.mCallState = TelephonyManager.CALL_STATE_IDLE;
-    }
-
-    @Test
-    public void isVideoCallEnabled_allFlagsOn_returnTrue() {
-        assertThat(mController.isVideoCallEnabled(SUB_ID)).isTrue();
-    }
-
-    @Test
-    public void isVideoCallEnabled_disabledByPlatform_returnFalse() {
-        mQueryImsState.setIsProvisionedOnDevice(false);
-        mQueryImsState.setIsEnabledByPlatform(false);
-
-        assertThat(mController.isVideoCallEnabled(SUB_ID)).isFalse();
-    }
-
-    @Test
-    public void isVideoCallEnabled_dataDisabled_returnFalse() {
-        mCarrierConfig.putBoolean(
-                CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS, false);
-        doReturn(false).when(mTelephonyManager).isDataEnabled();
-
-        assertThat(mController.isVideoCallEnabled(SUB_ID)).isFalse();
-    }
-
-    @Test
-    public void updateState_4gLteOff_disabled() {
-        mQueryImsState.setIsEnabledByUser(false);
-        mQueryVoLteState.setIsEnabledByUser(false);
-
-        mController.updateState(mPreference);
-
-        assertThat(mPreference.isEnabled()).isFalse();
-        assertThat(mPreference.isChecked()).isFalse();
-    }
-
-    @Test
-    public void updateState_4gLteOnWithoutCall_checked() {
-        mQueryImsState.setIsEnabledByUser(true);
-        mQueryVoLteState.setIsEnabledByUser(true);
-        mQueryImsState.setIsTtyOnVolteEnabled(true);
-        mController.mCallState = TelephonyManager.CALL_STATE_IDLE;
-
-        mController.updateState(mPreference);
-
-        assertThat(mPreference.isEnabled()).isTrue();
-        assertThat(mPreference.isChecked()).isTrue();
-    }
-
-
-    @Test
-    public void displayPreference_notAvailable_setPreferenceInvisible() {
-        mQueryImsState.setIsEnabledByPlatform(false);
-
-        mController.displayPreference(mPreferenceScreen);
-
-        assertThat(mPreferenceScreen.isVisible()).isFalse();
-    }
-
-}
diff --git a/tests/robotests/src/com/android/settings/notification/modes/InterruptionFilterPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/InterruptionFilterPreferenceControllerTest.java
index 777d213..8653d95 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/InterruptionFilterPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/InterruptionFilterPreferenceControllerTest.java
@@ -17,6 +17,7 @@
 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 android.service.notification.ZenPolicy.STATE_DISALLOW;
 
@@ -68,6 +69,26 @@
     }
 
     @Test
+    public void updateState_dnd_enabled() {
+        TwoStatePreference preference = mock(TwoStatePreference.class);
+        ZenMode dnd = TestModeBuilder.MANUAL_DND_ACTIVE;
+
+        mController.updateState(preference, dnd);
+
+        verify(preference).setEnabled(true);
+    }
+
+    @Test
+    public void updateState_specialDnd_disabled() {
+        TwoStatePreference preference = mock(TwoStatePreference.class);
+        ZenMode specialDnd = TestModeBuilder.manualDnd(INTERRUPTION_FILTER_NONE, true);
+
+        mController.updateState(preference, specialDnd);
+
+        verify(preference).setEnabled(false);
+    }
+
+    @Test
     public void testUpdateState_disabled() {
         TwoStatePreference preference = mock(TwoStatePreference.class);
         ZenMode zenMode = new TestModeBuilder()
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 4148fa3..fa83f30 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.notification.modes;
 
+import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
 import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
 import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID;
 
@@ -150,6 +151,20 @@
     }
 
     @Test
+    public void updateState_dnd_enabled() {
+        ZenMode dnd = TestModeBuilder.MANUAL_DND_ACTIVE;
+        mController.updateState(mPreference, dnd);
+        assertThat(mPreference.isEnabled()).isTrue();
+    }
+
+    @Test
+    public void updateState_specialDnd_disabled() {
+        ZenMode specialDnd = TestModeBuilder.manualDnd(INTERRUPTION_FILTER_NONE, true);
+        mController.updateState(mPreference, specialDnd);
+        assertThat(mPreference.isEnabled()).isFalse();
+    }
+
+    @Test
     public void testUpdateState_disabled() {
         ZenMode zenMode = new TestModeBuilder()
                 .setEnabled(false)
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 7cf0109..05486e0 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceControllerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.notification.modes;
 
+import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -64,6 +66,26 @@
     }
 
     @Test
+    public void updateState_dnd_enabled() {
+        Preference preference = mock(Preference.class);
+        ZenMode dnd = TestModeBuilder.MANUAL_DND_ACTIVE;
+
+        mController.updateState(preference, dnd);
+
+        verify(preference).setEnabled(true);
+    }
+
+    @Test
+    public void updateState_specialDnd_disabled() {
+        Preference preference = mock(Preference.class);
+        ZenMode specialDnd = TestModeBuilder.manualDnd(INTERRUPTION_FILTER_NONE, true);
+
+        mController.updateState(preference, specialDnd);
+
+        verify(preference).setEnabled(false);
+    }
+
+    @Test
     public void testUpdateState_disabled() {
         Preference preference = mock(Preference.class);
         ZenMode zenMode = new TestModeBuilder()
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 3efa5f0..c949fb8 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeExitAtAlarmPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeExitAtAlarmPreferenceControllerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.notification.modes;
 
+import static android.service.notification.SystemZenRules.PACKAGE_ANDROID;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.mock;
@@ -77,6 +79,7 @@
         scheduleInfo.exitAtAlarm = false;
 
         ZenMode mode = new TestModeBuilder()
+                .setPackage(PACKAGE_ANDROID)
                 .setConditionId(ZenModeConfig.toScheduleConditionId(scheduleInfo))
                 .build();
 
@@ -105,6 +108,7 @@
         scheduleInfo.exitAtAlarm = true;
 
         ZenMode mode = new TestModeBuilder()
+                .setPackage(PACKAGE_ANDROID)
                 .setConditionId(ZenModeConfig.toScheduleConditionId(scheduleInfo))
                 .build();
         mPrefController.updateZenMode(preference, mode);
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 3db70fa..38790b2 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceControllerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.notification.modes;
 
+import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Mockito.mock;
@@ -62,6 +64,26 @@
     }
 
     @Test
+    public void updateState_dnd_enabled() {
+        CircularIconsPreference preference = mock(CircularIconsPreference.class);
+        ZenMode dnd = TestModeBuilder.MANUAL_DND_ACTIVE;
+
+        mController.updateState(preference, dnd);
+
+        verify(preference).setEnabled(true);
+    }
+
+    @Test
+    public void updateState_specialDnd_disabled() {
+        CircularIconsPreference preference = mock(CircularIconsPreference.class);
+        ZenMode specialDnd = TestModeBuilder.manualDnd(INTERRUPTION_FILTER_NONE, true);
+
+        mController.updateState(preference, specialDnd);
+
+        verify(preference).setEnabled(false);
+    }
+
+    @Test
     public void updateState_disabled() {
         CircularIconsPreference pref = mock(CircularIconsPreference.class);
         ZenMode zenMode = new TestModeBuilder()
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 8555d71..85fd004 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceControllerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.notification.modes;
 
+import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
 import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE;
 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS;
@@ -117,6 +118,20 @@
     }
 
     @Test
+    public void updateState_dnd_enabled() {
+        ZenMode dnd = TestModeBuilder.MANUAL_DND_ACTIVE;
+        mController.updateState(mPreference, dnd);
+        assertThat(mPreference.isEnabled()).isTrue();
+    }
+
+    @Test
+    public void updateState_specialDnd_disabled() {
+        ZenMode specialDnd = TestModeBuilder.manualDnd(INTERRUPTION_FILTER_NONE, true);
+        mController.updateState(mPreference, specialDnd);
+        assertThat(mPreference.isEnabled()).isFalse();
+    }
+
+    @Test
     public void updateState_disabled() {
         ZenMode zenMode = new TestModeBuilder()
                 .setEnabled(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
index f5d5160..3722e41 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModesListItemPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModesListItemPreferenceTest.java
@@ -36,7 +36,6 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
-import org.robolectric.shadows.ShadowLooper;
 
 @RunWith(RobolectricTestRunner.class)
 @EnableFlags(Flags.FLAG_MODES_UI)
@@ -45,18 +44,18 @@
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     private Context mContext;
+    private final ZenIconLoader mIconLoader = new ZenIconLoader(
+            MoreExecutors.newDirectExecutorService());
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
         mContext = RuntimeEnvironment.application;
-        ZenIconLoader.setInstance(new ZenIconLoader(MoreExecutors.newDirectExecutorService()));
     }
 
     @Test
     public void constructor_setsMode() {
-        ZenModesListItemPreference preference = new ZenModesListItemPreference(mContext,
-                TestModeBuilder.EXAMPLE);
+        ZenModesListItemPreference preference = newPreference(TestModeBuilder.EXAMPLE);
 
         assertThat(preference.getKey()).isEqualTo(TestModeBuilder.EXAMPLE.getId());
         assertThat(preference.getZenMode()).isEqualTo(TestModeBuilder.EXAMPLE);
@@ -70,8 +69,7 @@
                 .setEnabled(true)
                 .build();
 
-        ZenModesListItemPreference preference = new ZenModesListItemPreference(mContext, mode);
-        ShadowLooper.idleMainLooper(); // To load icon.
+        ZenModesListItemPreference preference = newPreference(mode);
 
         assertThat(preference.getTitle()).isEqualTo("Enabled mode");
         assertThat(preference.getSummary()).isEqualTo("When the thrush knocks");
@@ -87,8 +85,7 @@
                 .setActive(true)
                 .build();
 
-        ZenModesListItemPreference preference = new ZenModesListItemPreference(mContext, mode);
-        ShadowLooper.idleMainLooper();
+        ZenModesListItemPreference preference = newPreference(mode);
 
         assertThat(preference.getTitle()).isEqualTo("Active mode");
         assertThat(preference.getSummary()).isEqualTo("ON • When Birnam forest comes to Dunsinane");
@@ -103,8 +100,7 @@
                 .setEnabled(false, /* byUser= */ false)
                 .build();
 
-        ZenModesListItemPreference preference = new ZenModesListItemPreference(mContext, mode);
-        ShadowLooper.idleMainLooper();
+        ZenModesListItemPreference preference = newPreference(mode);
 
         assertThat(preference.getTitle()).isEqualTo("Mode disabled by app");
         assertThat(preference.getSummary()).isEqualTo("Not set");
@@ -119,11 +115,15 @@
                 .setEnabled(false, /* byUser= */ true)
                 .build();
 
-        ZenModesListItemPreference preference = new ZenModesListItemPreference(mContext, mode);
-        ShadowLooper.idleMainLooper();
+        ZenModesListItemPreference preference = newPreference(mode);
 
         assertThat(preference.getTitle()).isEqualTo("Mode disabled by user");
         assertThat(preference.getSummary()).isEqualTo("Disabled");
         assertThat(preference.getIcon()).isNotNull();
     }
+
+    private ZenModesListItemPreference newPreference(ZenMode zenMode) {
+        return new ZenModesListItemPreference(mContext, mIconLoader, MoreExecutors.directExecutor(),
+                zenMode);
+    }
 }
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 4fa8b8a..69568ce 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModesListPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModesListPreferenceControllerTest.java
@@ -40,11 +40,13 @@
 import androidx.preference.PreferenceScreen;
 
 import com.android.settingslib.notification.modes.TestModeBuilder;
+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.search.SearchIndexableRaw;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.MoreExecutors;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -95,7 +97,8 @@
         PreferenceScreen preferenceScreen = preferenceManager.createPreferenceScreen(mContext);
         preferenceScreen.addPreference(mPreference);
 
-        mPrefController = new ZenModesListPreferenceController(mContext, mBackend);
+        mPrefController = new ZenModesListPreferenceController(mContext, mBackend,
+                new ZenIconLoader(MoreExecutors.newDirectExecutorService()));
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/wifi/WifiSummaryUpdaterTest.java b/tests/robotests/src/com/android/settings/wifi/WifiSummaryUpdaterTest.java
deleted file mode 100644
index 99040db..0000000
--- a/tests/robotests/src/com/android/settings/wifi/WifiSummaryUpdaterTest.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2017 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.wifi;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.wifi.WifiManager;
-
-import com.android.settings.R;
-import com.android.settings.widget.SummaryUpdater.OnSummaryChangeListener;
-import com.android.settingslib.wifi.WifiStatusTracker;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-@RunWith(RobolectricTestRunner.class)
-public class WifiSummaryUpdaterTest {
-    @Mock private WifiStatusTracker mWifiTracker;
-    @Mock private SummaryListener mListener;
-
-    private Context mContext;
-    private WifiSummaryUpdater mSummaryUpdater;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mContext = spy(RuntimeEnvironment.application.getApplicationContext());
-        doReturn(mock(Intent.class)).when(mContext).registerReceiver(any(), any(), anyInt());
-        doNothing().when(mContext).unregisterReceiver(any(BroadcastReceiver.class));
-
-        mSummaryUpdater = new WifiSummaryUpdater(mContext, mListener, mWifiTracker);
-    }
-
-    @Test
-    public void register_true_shouldRegisterListenerAndTracker() {
-        mSummaryUpdater.register(true);
-
-        verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class),
-                anyInt());
-        verify(mWifiTracker).setListening(true);
-    }
-
-    @Test
-    public void register_true_shouldFetchInitialStateAndSendSummaryChange() {
-        mSummaryUpdater.register(true);
-
-        verify(mWifiTracker).fetchInitialState();
-        verify(mListener).onSummaryChanged(anyString());
-    }
-
-    @Test
-    public void register_false_shouldUnregisterListenerAndTracker() {
-        mSummaryUpdater.register(true);
-        mSummaryUpdater.register(false);
-
-        verify(mContext).unregisterReceiver(any(BroadcastReceiver.class));
-        verify(mWifiTracker).setListening(false);
-    }
-
-    @Test
-    public void onReceive_networkStateChanged_shouldSendSummaryChange() {
-        mSummaryUpdater.register(true);
-        mContext.sendBroadcast(new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION));
-
-        verify(mListener).onSummaryChanged(anyString());
-    }
-
-    @Test
-    public void onReceive_rssiChanged_shouldSendSummaryChange() {
-        mSummaryUpdater.register(true);
-        mContext.sendBroadcast(new Intent(WifiManager.RSSI_CHANGED_ACTION));
-
-        verify(mListener).onSummaryChanged(anyString());
-    }
-
-    @Test
-    public void getSummary_wifiDisabled_shouldReturnDisabled() {
-        mWifiTracker.enabled = false;
-
-        assertThat(mSummaryUpdater.getSummary())
-            .isEqualTo(mContext.getString(R.string.switch_off_text));
-    }
-
-    @Test
-    public void getSummary_wifiDisconnected_shouldReturnDisconnected() {
-        mWifiTracker.enabled = true;
-        mWifiTracker.connected = false;
-
-        assertThat(mSummaryUpdater.getSummary())
-            .isEqualTo(mContext.getString(R.string.disconnected));
-    }
-
-    @Test
-    public void getSummary_wifiConnected_shouldReturnSsid() {
-        mWifiTracker.enabled = true;
-        mWifiTracker.connected = true;
-        mWifiTracker.ssid = "Test Ssid";
-
-        assertThat(mSummaryUpdater.getSummary()).isEqualTo("Test Ssid");
-    }
-
-    @Test
-    public void getSummary_wifiConnected_withSpeedLabel_shouldReturnSsid_withSpeedLabel() {
-        mWifiTracker.enabled = true;
-        mWifiTracker.connected = true;
-        mWifiTracker.ssid = "Test Ssid";
-        mWifiTracker.statusLabel = "Very Fast";
-
-        assertThat(mSummaryUpdater.getSummary()).isEqualTo("Test Ssid / Very Fast");
-    }
-
-    private class SummaryListener implements OnSummaryChangeListener {
-        private String summary;
-
-        @Override
-        public void onSummaryChanged(String summary) {
-            this.summary = summary;
-        }
-    }
-}
diff --git a/tests/spa_unit/src/com/android/settings/network/apn/ApnRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/apn/ApnRepositoryTest.kt
index 4155318..d2f16d7 100644
--- a/tests/spa_unit/src/com/android/settings/network/apn/ApnRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/apn/ApnRepositoryTest.kt
@@ -21,8 +21,6 @@
 import android.database.MatrixCursor
 import android.net.Uri
 import android.provider.Telephony
-import android.telephony.SubscriptionInfo
-import android.telephony.SubscriptionManager
 import android.telephony.TelephonyManager
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -40,19 +38,15 @@
 
     private val contentResolver = mock<ContentResolver>()
 
-    private val mockSubscriptionInfo = mock<SubscriptionInfo> {
-        on { mccString } doReturn MCC
-        on { mncString } doReturn MNC
-    }
+    private val mockTelephonyManager =
+        mock<TelephonyManager> { on { createForSubscriptionId(SUB_ID) } doReturn mock }
 
-    private val mockSubscriptionManager = mock<SubscriptionManager> {
-        on { getActiveSubscriptionInfo(SUB_ID) } doReturn mockSubscriptionInfo
-    }
+    private val context: Context =
+        spy(ApplicationProvider.getApplicationContext()) {
+            on { contentResolver } doReturn contentResolver
+            on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
+        }
 
-    private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
-        on { contentResolver } doReturn contentResolver
-        on { getSystemService(SubscriptionManager::class.java) } doReturn mockSubscriptionManager
-    }
     private val uri = mock<Uri> {}
 
     @Test
@@ -91,9 +85,7 @@
 
     @Test
     fun getApnIdMap_knownCarrierId() {
-        mockSubscriptionInfo.stub {
-            on { carrierId } doReturn CARRIER_ID
-        }
+        mockTelephonyManager.stub { on { simSpecificCarrierId } doReturn CARRIER_ID }
 
         val idMap = context.getApnIdMap(SUB_ID)
 
@@ -102,19 +94,19 @@
 
     @Test
     fun getApnIdMap_unknownCarrierId() {
-        mockSubscriptionInfo.stub {
-            on { carrierId } doReturn TelephonyManager.UNKNOWN_CARRIER_ID
+        mockTelephonyManager.stub {
+            on { simSpecificCarrierId } doReturn TelephonyManager.UNKNOWN_CARRIER_ID
+            on { simOperator } doReturn SIM_OPERATOR
         }
 
         val idMap = context.getApnIdMap(SUB_ID)
 
-        assertThat(idMap).containsExactly(Telephony.Carriers.NUMERIC, MCC + MNC)
+        assertThat(idMap).containsExactly(Telephony.Carriers.NUMERIC, SIM_OPERATOR)
     }
 
     private companion object {
         const val SUB_ID = 2
         const val CARRIER_ID = 10
-        const val MCC = "310"
-        const val MNC = "101"
+        const val SIM_OPERATOR = "310101"
     }
 }
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionRepositoryTest.kt
index f75c14a..5dbc534 100644
--- a/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionRepositoryTest.kt
@@ -190,6 +190,32 @@
     }
 
     @Test
+    fun isSubscriptionVisibleFlow_available_returnTrue() = runBlocking {
+        mockSubscriptionManager.stub {
+            on { getAvailableSubscriptionInfoList() } doReturn
+                listOf(SubscriptionInfo.Builder().apply { setId(SUB_ID_IN_SLOT_0) }.build())
+        }
+
+        val isVisible =
+            repository.isSubscriptionVisibleFlow(SUB_ID_IN_SLOT_0).firstWithTimeoutOrNull()
+
+        assertThat(isVisible).isTrue()
+    }
+
+    @Test
+    fun isSubscriptionVisibleFlow_unavailable_returnFalse() = runBlocking {
+        mockSubscriptionManager.stub {
+            on { getAvailableSubscriptionInfoList() } doReturn
+                listOf(SubscriptionInfo.Builder().apply { setId(SUB_ID_IN_SLOT_0) }.build())
+        }
+
+        val isVisible =
+            repository.isSubscriptionVisibleFlow(SUB_ID_IN_SLOT_1).firstWithTimeoutOrNull()
+
+        assertThat(isVisible).isFalse()
+    }
+
+    @Test
     fun phoneNumberFlow() = runBlocking {
         mockSubscriptionManager.stub {
             on { getPhoneNumber(SUB_ID_IN_SLOT_1) } doReturn NUMBER_1
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/VideoCallingPreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/VideoCallingPreferenceControllerTest.kt
new file mode 100644
index 0000000..4babfaa
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/VideoCallingPreferenceControllerTest.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.telephony
+
+import android.content.Context
+import android.telephony.TelephonyManager
+import androidx.lifecycle.testing.TestLifecycleOwner
+import androidx.preference.PreferenceManager
+import androidx.preference.SwitchPreferenceCompat
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.network.ims.VolteQueryImsState
+import com.android.settings.network.ims.VtQueryImsState
+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.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.stub
+
+@RunWith(AndroidJUnit4::class)
+class VideoCallingPreferenceControllerTest {
+
+    private val mockVtQueryImsState = mock<VtQueryImsState> {}
+
+    private var mockQueryVoLteState = mock<VolteQueryImsState> {}
+
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    private val mockCallStateRepository = mock<CallStateRepository> {}
+
+    private var controller =
+        spy(
+            VideoCallingPreferenceController(
+                context = context,
+                key = TEST_KEY,
+                callStateRepository = mockCallStateRepository,
+            )
+        ) {
+            on { queryImsState(SUB_ID) } doReturn mockVtQueryImsState
+            on { queryVoLteState(SUB_ID) } doReturn mockQueryVoLteState
+        }
+
+    private val preference = SwitchPreferenceCompat(context).apply { key = TEST_KEY }
+    private val preferenceScreen = PreferenceManager(context).createPreferenceScreen(context)
+
+    @Before
+    fun setUp() {
+        controller.init(SUB_ID, CallingPreferenceCategoryController(context, "calling_category"))
+        preferenceScreen.addPreference(preference)
+        controller.displayPreference(preferenceScreen)
+    }
+
+    @Test
+    fun updateState_4gLteOff_disabledAndUnchecked() {
+        mockQueryVoLteState.stub { on { isEnabledByUser } doReturn false }
+
+        controller.updateState(preference)
+
+        assertThat(preference.isEnabled).isFalse()
+        assertThat(preference.isChecked).isFalse()
+    }
+
+    @Test
+    fun updateState_4gLteOnWithoutCall_enabledAndChecked() = runBlocking {
+        mockVtQueryImsState.stub {
+            on { isEnabledByUser } doReturn true
+            on { isAllowUserControl } doReturn true
+        }
+        mockQueryVoLteState.stub { on { isEnabledByUser } doReturn true }
+        mockCallStateRepository.stub {
+            on { callStateFlow(SUB_ID) } doReturn flowOf(TelephonyManager.CALL_STATE_IDLE)
+        }
+
+        controller.onViewCreated(TestLifecycleOwner())
+        delay(100)
+        controller.updateState(preference)
+
+        assertThat(preference.isEnabled).isTrue()
+        assertThat(preference.isChecked).isTrue()
+    }
+
+    @Test
+    fun updateState_4gLteOnWithCall_disabledAndChecked() = runBlocking {
+        mockVtQueryImsState.stub {
+            on { isEnabledByUser } doReturn true
+            on { isAllowUserControl } doReturn true
+        }
+        mockQueryVoLteState.stub { on { isEnabledByUser } doReturn true }
+        mockCallStateRepository.stub {
+            on { callStateFlow(SUB_ID) } doReturn flowOf(TelephonyManager.CALL_STATE_RINGING)
+        }
+
+        controller.onViewCreated(TestLifecycleOwner())
+        delay(100)
+        controller.updateState(preference)
+
+        assertThat(preference.isEnabled).isFalse()
+        assertThat(preference.isChecked).isTrue()
+    }
+
+    private companion object {
+        const val TEST_KEY = "test_key"
+        const val SUB_ID = 10
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/VideoCallingRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/VideoCallingRepositoryTest.kt
new file mode 100644
index 0000000..063e191
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/VideoCallingRepositoryTest.kt
@@ -0,0 +1,166 @@
+/*
+ * 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.AccessNetworkConstants
+import android.telephony.CarrierConfigManager
+import android.telephony.SubscriptionManager
+import android.telephony.ims.feature.MmTelFeature
+import android.telephony.ims.stub.ImsRegistrationImplBase
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.network.telephony.ims.ImsFeatureRepository
+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.Before
+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 VideoCallingRepositoryTest {
+
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    private val mockMobileDataRepository = mock<MobileDataRepository>()
+    private val mockImsFeatureRepository = mock<ImsFeatureRepository>()
+
+    private val repository =
+        VideoCallingRepository(
+            context = context,
+            mobileDataRepository = mockMobileDataRepository,
+            imsFeatureRepositoryFactory = { mockImsFeatureRepository },
+        )
+
+    @Before
+    fun setUp() {
+        CarrierConfigRepository.resetForTest()
+    }
+
+    @Test
+    fun isVideoCallReadyFlow_invalidSubId() = runBlocking {
+        val isVideoCallReady =
+            repository
+                .isVideoCallReadyFlow(subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+                .firstWithTimeoutOrNull()
+
+        assertThat(isVideoCallReady).isFalse()
+    }
+
+    @Test
+    fun isVideoCallReadyFlow_ignoreDataEnabledChangedAndIsReady_returnTrue() = runBlocking {
+        CarrierConfigRepository.setBooleanForTest(
+            subId = SUB_ID,
+            key = CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS,
+            value = true,
+        )
+        mockImsFeatureRepository.stub {
+            on {
+                isReadyFlow(
+                    capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO,
+                    tech = ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
+                    transportType = AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+                )
+            } doReturn flowOf(true)
+        }
+
+        val isVideoCallReady = repository.isVideoCallReadyFlow(SUB_ID).firstWithTimeoutOrNull()
+
+        assertThat(isVideoCallReady).isTrue()
+    }
+
+    @Test
+    fun isVideoCallReadyFlow_ignoreDataEnabledChangedAndNotReady_returnFalse() = runBlocking {
+        CarrierConfigRepository.setBooleanForTest(
+            subId = SUB_ID,
+            key = CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS,
+            value = true,
+        )
+        mockImsFeatureRepository.stub {
+            on {
+                isReadyFlow(
+                    capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO,
+                    tech = ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
+                    transportType = AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+                )
+            } doReturn flowOf(false)
+        }
+
+        val isVideoCallReady = repository.isVideoCallReadyFlow(SUB_ID).firstWithTimeoutOrNull()
+
+        assertThat(isVideoCallReady).isFalse()
+    }
+
+    @Test
+    fun isVideoCallReadyFlow_mobileDataEnabledAndIsReady_returnTrue() = runBlocking {
+        CarrierConfigRepository.setBooleanForTest(
+            subId = SUB_ID,
+            key = CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS,
+            value = false,
+        )
+        mockMobileDataRepository.stub {
+            on { isMobileDataEnabledFlow(SUB_ID) } doReturn flowOf(true)
+        }
+        mockImsFeatureRepository.stub {
+            on {
+                isReadyFlow(
+                    capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO,
+                    tech = ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
+                    transportType = AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+                )
+            } doReturn flowOf(true)
+        }
+
+        val isVideoCallReady = repository.isVideoCallReadyFlow(SUB_ID).firstWithTimeoutOrNull()
+
+        assertThat(isVideoCallReady).isTrue()
+    }
+
+    @Test
+    fun isVideoCallReadyFlow_ignoreDataEnabledChangedAndIsReady_returnFalse() = runBlocking {
+        CarrierConfigRepository.setBooleanForTest(
+            subId = SUB_ID,
+            key = CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS,
+            value = false,
+        )
+        mockMobileDataRepository.stub {
+            on { isMobileDataEnabledFlow(SUB_ID) } doReturn flowOf(false)
+        }
+        mockImsFeatureRepository.stub {
+            on {
+                isReadyFlow(
+                    capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO,
+                    tech = ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
+                    transportType = AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+                )
+            } doReturn flowOf(true)
+        }
+
+        val isVideoCallReady = repository.isVideoCallReadyFlow(SUB_ID).firstWithTimeoutOrNull()
+
+        assertThat(isVideoCallReady).isFalse()
+    }
+
+    private companion object {
+        const val SUB_ID = 10
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/euicc/EuiccRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/euicc/EuiccRepositoryTest.kt
new file mode 100644
index 0000000..24e6fd6
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/euicc/EuiccRepositoryTest.kt
@@ -0,0 +1,117 @@
+/*
+ * 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.euicc
+
+import android.content.Context
+import android.telephony.TelephonyManager
+import android.telephony.euicc.EuiccManager
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+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.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.stub
+
+@RunWith(AndroidJUnit4::class)
+class EuiccRepositoryTest {
+
+    private val mockEuiccManager = mock<EuiccManager> { on { isEnabled } doReturn true }
+
+    private val mockTelephonyManager =
+        mock<TelephonyManager> {
+            on { activeModemCount } doReturn 1
+            on { getNetworkCountryIso(any()) } doReturn COUNTRY_CODE
+        }
+
+    private val context: Context =
+        spy(ApplicationProvider.getApplicationContext()) {
+            on { getSystemService(EuiccManager::class.java) } doReturn mockEuiccManager
+            on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
+        }
+
+    private val resources =
+        spy(context.resources) { on { getBoolean(R.bool.config_show_sim_info) } doReturn true }
+
+    private var euiccProvisioned = false
+
+    private val repository =
+        EuiccRepository(
+            context,
+            isEuiccProvisioned = { euiccProvisioned },
+            isDevelopmentSettingsEnabled = { false },
+        )
+
+    @Before
+    fun setUp() {
+        context.stub { on { resources } doReturn resources }
+    }
+
+    @Test
+    fun showEuiccSettings_noSim_returnFalse() {
+        resources.stub { on { getBoolean(R.bool.config_show_sim_info) } doReturn false }
+
+        val showEuiccSettings = repository.showEuiccSettings()
+
+        assertThat(showEuiccSettings).isFalse()
+    }
+
+    @Test
+    fun showEuiccSettings_euiccDisabled_returnFalse() {
+        mockEuiccManager.stub { on { isEnabled } doReturn false }
+
+        val showEuiccSettings = repository.showEuiccSettings()
+
+        assertThat(showEuiccSettings).isFalse()
+    }
+
+    @Test
+    fun showEuiccSettings_euiccProvisioned_returnTrue() {
+        euiccProvisioned = true
+
+        val showEuiccSettings = repository.showEuiccSettings()
+
+        assertThat(showEuiccSettings).isTrue()
+    }
+
+    @Test
+    fun showEuiccSettings_countryNotSupported_returnFalse() {
+        mockEuiccManager.stub { on { isSupportedCountry(COUNTRY_CODE) } doReturn false }
+
+        val showEuiccSettings = repository.showEuiccSettings()
+
+        assertThat(showEuiccSettings).isFalse()
+    }
+
+    @Test
+    fun showEuiccSettings_countrySupported_returnTrue() {
+        mockEuiccManager.stub { on { isSupportedCountry(COUNTRY_CODE) } doReturn true }
+
+        val showEuiccSettings = repository.showEuiccSettings()
+
+        assertThat(showEuiccSettings).isTrue()
+    }
+
+    private companion object {
+        const val COUNTRY_CODE = "us"
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/wificalling/WifiCallingRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/wificalling/WifiCallingRepositoryTest.kt
index f0a23eb..9b71465 100644
--- a/tests/spa_unit/src/com/android/settings/network/telephony/wificalling/WifiCallingRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/wificalling/WifiCallingRepositoryTest.kt
@@ -102,22 +102,6 @@
         assertThat(wiFiCallingMode).isEqualTo(ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED)
     }
 
-    @Test
-    fun isWifiCallingSupported() = runBlocking {
-        mockImsMmTelRepository.stub {
-            onBlocking {
-                isSupported(
-                    capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
-                    transportType = AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
-                )
-            } doReturn true
-        }
-
-        val isSupported = repository.isWifiCallingSupported()
-
-        assertThat(isSupported).isTrue()
-    }
-
     private fun mockUseWfcHomeModeForRoaming(config: Boolean) {
         mockCarrierConfigManager.stub {
             on {
diff --git a/tests/unit/src/com/android/settings/network/InternetPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/InternetPreferenceControllerTest.java
deleted file mode 100644
index 1c482ef..0000000
--- a/tests/unit/src/com/android/settings/network/InternetPreferenceControllerTest.java
+++ /dev/null
@@ -1,297 +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;
-
-import static com.android.settings.network.InternetUpdater.INTERNET_NETWORKS_AVAILABLE;
-import static com.android.settings.network.InternetUpdater.INTERNET_WIFI;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-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.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.IntentFilter;
-import android.net.ConnectivityManager;
-import android.net.NetworkRequest;
-import android.net.NetworkScoreManager;
-import android.net.wifi.WifiManager;
-import android.os.Handler;
-import android.os.Looper;
-
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.lifecycle.LifecycleRegistry;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceManager;
-import androidx.preference.PreferenceScreen;
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.android.settings.R;
-import com.android.settings.testutils.ResourcesUtils;
-import com.android.settings.wifi.WifiPickerTrackerHelper;
-import com.android.settings.wifi.WifiSummaryUpdater;
-import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
-import com.android.wifitrackerlib.HotspotNetworkEntry;
-import com.android.wifitrackerlib.StandardWifiEntry;
-import com.android.wifitrackerlib.WifiPickerTracker;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.Spy;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(AndroidJUnit4.class)
-public class InternetPreferenceControllerTest {
-
-    private static final String TEST_SUMMARY = "test summary";
-    private static final String TEST_ALTERNATE_SUMMARY = "test alternate summary";
-    private static final String NOT_CONNECTED = "Not connected";
-    private static final String SUB_ID_1 = "1";
-    private static final String SUB_ID_2 = "2";
-    private static final String DISPLAY_NAME_1 = "Sub 1";
-    private static final String DISPLAY_NAME_2 = "Sub 2";
-
-    @Rule
-    public final MockitoRule mMockitoRule = MockitoJUnit.rule();
-    @Spy
-    private Context mContext = ApplicationProvider.getApplicationContext();
-    @Mock
-    private SubscriptionInfoEntity mActiveSubInfo;
-    @Mock
-    private SubscriptionInfoEntity mDefaultDataSubInfo;
-    @Mock
-    private ConnectivityManager mConnectivityManager;
-    @Mock
-    private LifecycleOwner mLifecycleOwner;
-    @Mock
-    private WifiManager mWifiManager;
-    @Mock
-    private WifiSummaryUpdater mSummaryHelper;
-    @Mock
-    private WifiPickerTrackerHelper mWifiPickerTrackerHelper;
-    @Mock
-    private WifiPickerTracker mWifiPickerTracker;
-    @Mock
-    private HotspotNetworkEntry mHotspotNetworkEntry;
-
-    private LifecycleRegistry mLifecycleRegistry;
-
-    private MockInternetPreferenceController mController;
-    private PreferenceScreen mScreen;
-    private Preference mPreference;
-    private List<SubscriptionInfoEntity> mSubscriptionInfoEntityList = new ArrayList<>();
-
-    @Before
-    public void setUp() {
-        when(mContext.getSystemService(ConnectivityManager.class)).thenReturn(mConnectivityManager);
-        when(mContext.getSystemService(NetworkScoreManager.class))
-                .thenReturn(mock(NetworkScoreManager.class));
-        when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager);
-        when(mWifiManager.getWifiState()).thenReturn(WifiManager.WIFI_STATE_DISABLED);
-        when(mWifiPickerTrackerHelper.getWifiPickerTracker()).thenReturn(mWifiPickerTracker);
-        when(mWifiPickerTracker.getConnectedWifiEntry()).thenReturn(null /* WifiEntry */);
-        when(mHotspotNetworkEntry.getAlternateSummary()).thenReturn(TEST_ALTERNATE_SUMMARY);
-
-        if (Looper.myLooper() == null) {
-            Looper.prepare();
-        }
-        mLifecycleRegistry = new LifecycleRegistry(mLifecycleOwner);
-        when(mLifecycleOwner.getLifecycle()).thenReturn(mLifecycleRegistry);
-        mController = new MockInternetPreferenceController(mContext, mock(Lifecycle.class),
-                mLifecycleOwner);
-        mController.sIconMap.put(INTERNET_WIFI, 0);
-        mController.mWifiPickerTrackerHelper = mWifiPickerTrackerHelper;
-
-        final PreferenceManager preferenceManager = new PreferenceManager(mContext);
-        mScreen = preferenceManager.createPreferenceScreen(mContext);
-        mPreference = new Preference(mContext);
-        mPreference.setKey(InternetPreferenceController.KEY);
-        mScreen.addPreference(mPreference);
-    }
-
-    private class MockInternetPreferenceController extends
-            com.android.settings.network.InternetPreferenceController {
-
-        private int mDefaultDataSubscriptionId;
-        public MockInternetPreferenceController(Context context, Lifecycle lifecycle,
-                LifecycleOwner lifecycleOwner) {
-            super(context, lifecycle, lifecycleOwner);
-        }
-
-        private List<SubscriptionInfoEntity> mSubscriptionInfoEntity;
-
-        @Override
-        protected List<SubscriptionInfoEntity> getSubscriptionInfoList() {
-            return mSubscriptionInfoEntity;
-        }
-
-        public void setSubscriptionInfoList(List<SubscriptionInfoEntity> list) {
-            mSubscriptionInfoEntity = list;
-        }
-
-        @Override
-        protected int getDefaultDataSubscriptionId() {
-            return mDefaultDataSubscriptionId;
-        }
-
-        public void setDefaultDataSubscriptionId(int subscriptionId) {
-            mDefaultDataSubscriptionId = subscriptionId;
-        }
-
-    }
-
-    private SubscriptionInfoEntity setupSubscriptionInfoEntity(String subId, int slotId,
-            String displayName, boolean isVisible, boolean isValid, boolean isActive,
-            boolean isActiveData) {
-        return new SubscriptionInfoEntity(subId, slotId, false, false, displayName, isVisible,
-                false, isValid, isActive, isActiveData);
-    }
-
-    @Test
-    public void isAvailable_shouldMatchPrefFlag() {
-        assertThat(mController.isAvailable()).isEqualTo(
-                mContext.getResources().getBoolean(R.bool.config_show_internet_settings));
-    }
-
-    @Test
-    @UiThreadTest
-    public void onResume_shouldRegisterCallback() {
-        mController.onResume();
-
-        verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class),
-                any(int.class));
-        verify(mConnectivityManager).registerNetworkCallback(
-                any(NetworkRequest.class),
-                any(ConnectivityManager.NetworkCallback.class),
-                any(Handler.class));
-    }
-
-    @Test
-    @UiThreadTest
-    public void onPause_shouldUnregisterCallback() {
-        mController.onResume();
-        mController.onPause();
-
-        verify(mContext, times(2)).unregisterReceiver(any(BroadcastReceiver.class));
-        verify(mConnectivityManager, times(2)).unregisterNetworkCallback(
-                any(ConnectivityManager.NetworkCallback.class));
-    }
-
-    @Test
-    public void onSummaryChanged_internetWifi_updateSummary() {
-        when(mSummaryHelper.getSummary()).thenReturn(TEST_SUMMARY);
-        mController.mSummaryHelper = mSummaryHelper;
-        mController.onInternetTypeChanged(INTERNET_WIFI);
-        mController.displayPreference(mScreen);
-
-        mController.onSummaryChanged(TEST_SUMMARY);
-
-        assertThat(mPreference.getSummary()).isEqualTo(TEST_SUMMARY);
-    }
-
-    @Test
-    public void onSummaryChanged_internetNetworksAvailable_notUpdateSummary() {
-        when(mSummaryHelper.getSummary()).thenReturn(TEST_SUMMARY);
-        mController.mSummaryHelper = mSummaryHelper;
-        mController.onInternetTypeChanged(INTERNET_NETWORKS_AVAILABLE);
-        mController.displayPreference(mScreen);
-        mPreference.setSummary(NOT_CONNECTED);
-
-        mController.onSummaryChanged(TEST_SUMMARY);
-
-        assertThat(mPreference.getSummary()).isNotEqualTo(TEST_SUMMARY);
-    }
-
-    @Test
-    public void updateCellularSummary_getNullSubscriptionInfo_shouldNotCrash() {
-        mController.setSubscriptionInfoList(mSubscriptionInfoEntityList);
-
-        mController.updateCellularSummary();
-    }
-
-    @Test
-    public void updateCellularSummary_getActiveSubscriptionInfo_cbrs() {
-        mController.setDefaultDataSubscriptionId(Integer.parseInt(SUB_ID_2));
-        mActiveSubInfo = setupSubscriptionInfoEntity(SUB_ID_1, 1, DISPLAY_NAME_1,
-                false, true, true, true);
-        mDefaultDataSubInfo = setupSubscriptionInfoEntity(SUB_ID_2, 1, DISPLAY_NAME_2,
-                false, true, true, false);
-        mSubscriptionInfoEntityList.add(mActiveSubInfo);
-        mSubscriptionInfoEntityList.add(mDefaultDataSubInfo);
-        mController.setSubscriptionInfoList(mSubscriptionInfoEntityList);
-        mController.displayPreference(mScreen);
-
-        mController.updateCellularSummary();
-        assertThat(mPreference.getSummary()).isEqualTo(DISPLAY_NAME_2);
-
-        mActiveSubInfo = setupSubscriptionInfoEntity(SUB_ID_1, 1, DISPLAY_NAME_1,
-                true, true, true, true);
-        mSubscriptionInfoEntityList.add(mActiveSubInfo);
-        mController.setSubscriptionInfoList(mSubscriptionInfoEntityList);
-        mController.onAvailableSubInfoChanged(mSubscriptionInfoEntityList);
-        final String expectedSummary =
-                ResourcesUtils.getResourcesString(mContext, "mobile_data_temp_using",
-                        DISPLAY_NAME_1);
-        mController.updateCellularSummary();
-        assertThat(mPreference.getSummary()).isEqualTo(expectedSummary);
-    }
-
-    @Test
-    public void updateHotspotNetwork_isHotspotNetworkEntry_updateAlternateSummary() {
-        when(mWifiPickerTracker.getConnectedWifiEntry()).thenReturn(mHotspotNetworkEntry);
-        mController.onInternetTypeChanged(INTERNET_WIFI);
-        mController.displayPreference(mScreen);
-        mPreference.setSummary(TEST_SUMMARY);
-
-        mController.updateHotspotNetwork();
-
-        assertThat(mPreference.getSummary().toString()).isEqualTo(TEST_ALTERNATE_SUMMARY);
-    }
-
-    @Test
-    public void updateHotspotNetwork_notHotspotNetworkEntry_notChangeSummary() {
-        when(mWifiPickerTracker.getConnectedWifiEntry()).thenReturn(mock(StandardWifiEntry.class));
-        mController.onInternetTypeChanged(INTERNET_WIFI);
-        mController.displayPreference(mScreen);
-        mPreference.setSummary(TEST_SUMMARY);
-
-        mController.updateHotspotNetwork();
-
-        assertThat(mPreference.getSummary().toString()).isEqualTo(TEST_SUMMARY);
-    }
-
-    @Test
-    public void updateHotspotNetwork_hotspotNetworkNotEnabled_returnFalse() {
-        mController.mWifiPickerTrackerHelper = null;
-
-        assertThat(mController.updateHotspotNetwork()).isFalse();
-    }
-}
diff --git a/tests/unit/src/com/android/settings/network/ims/MockVtQueryImsState.java b/tests/unit/src/com/android/settings/network/ims/MockVtQueryImsState.java
deleted file mode 100644
index 0949f1c..0000000
--- a/tests/unit/src/com/android/settings/network/ims/MockVtQueryImsState.java
+++ /dev/null
@@ -1,104 +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.ims;
-
-import android.content.Context;
-import android.telephony.ims.ImsException;
-
-/**
- * Controller class for mock VT status
- */
-public class MockVtQueryImsState extends VtQueryImsState {
-
-    private Boolean mIsTtyOnVolteEnabled;
-    private Boolean mIsEnabledOnPlatform;
-    private Boolean mIsProvisionedOnDevice;
-    private Boolean mIsEnabledByUser;
-    private Boolean mIsServiceStateReady;
-
-    /**
-     * Constructor
-     *
-     * @param context {@link Context}
-     * @param subId subscription's id
-     */
-    public MockVtQueryImsState(Context context, int subId) {
-        super(context, subId);
-    }
-
-    public void setIsTtyOnVolteEnabled(boolean enabled) {
-        mIsTtyOnVolteEnabled = enabled;
-    }
-
-    @Override
-    boolean isTtyOnVolteEnabled(int subId) {
-        if (mIsTtyOnVolteEnabled != null) {
-            return mIsTtyOnVolteEnabled;
-        }
-        return super.isTtyOnVolteEnabled(subId);
-    }
-
-    public void setIsEnabledByPlatform(boolean isEnabled) {
-        mIsEnabledOnPlatform = isEnabled;
-    }
-
-    @Override
-    boolean isEnabledByPlatform(int subId) throws InterruptedException, ImsException,
-            IllegalArgumentException {
-        if (mIsEnabledOnPlatform != null) {
-            return mIsEnabledOnPlatform;
-        }
-        return super.isEnabledByPlatform(subId);
-    }
-
-    public void setIsProvisionedOnDevice(boolean isProvisioned) {
-        mIsProvisionedOnDevice = isProvisioned;
-    }
-
-    @Override
-    boolean isProvisionedOnDevice(int subId) {
-        if (mIsProvisionedOnDevice != null) {
-            return mIsProvisionedOnDevice;
-        }
-        return super.isProvisionedOnDevice(subId);
-    }
-
-    public void setServiceStateReady(boolean isReady) {
-        mIsServiceStateReady = isReady;
-    }
-
-    @Override
-    boolean isServiceStateReady(int subId) throws InterruptedException, ImsException,
-            IllegalArgumentException {
-        if (mIsServiceStateReady != null) {
-            return mIsServiceStateReady;
-        }
-        return super.isServiceStateReady(subId);
-    }
-
-    public void setIsEnabledByUser(boolean enabled) {
-        mIsEnabledByUser = enabled;
-    }
-
-    @Override
-    boolean isEnabledByUser(int subId) {
-        if (mIsEnabledByUser != null) {
-            return mIsEnabledByUser;
-        }
-        return super.isEnabledByUser(subId);
-    }
-}