Merge "Catch IllegalArgumentException when unregister callback" into main
diff --git a/res/layout/search_bar_unified_version.xml b/res/layout/search_bar_unified_version.xml
index 14f46ec..e9b3c10 100644
--- a/res/layout/search_bar_unified_version.xml
+++ b/res/layout/search_bar_unified_version.xml
@@ -44,7 +44,7 @@
             style="@style/TextAppearance.SearchBar"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:paddingStart="8dp"
+            android:layout_marginStart="8dp"
             android:paddingEnd="8dp"
             android:text="@string/homepage_search"/>
     </LinearLayout>
diff --git a/res/layout/settings_homepage_container_v2.xml b/res/layout/settings_homepage_container_v2.xml
index b244579..a67b743 100644
--- a/res/layout/settings_homepage_container_v2.xml
+++ b/res/layout/settings_homepage_container_v2.xml
@@ -69,7 +69,8 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:orientation="horizontal"
-            android:paddingVertical="8dp"
+            android:paddingTop="8dp"
+            android:paddingBottom="16dp"
             android:paddingStart="?android:attr/listPreferredItemPaddingStart"
             android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
 
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 939befe..f7bb2e3 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1864,6 +1864,10 @@
     <string name="device_details_title">Device details</string>
     <!-- Title for keyboard settings preferences. [CHAR LIMIT=50] -->
     <string name="bluetooth_device_keyboard_settings_preference_title">Keyboard settings</string>
+    <!-- Title for more settings preferences. [CHAR LIMIT=50] -->
+    <string name="bluetooth_device_more_settings_preference_title">More settings</string>
+    <!-- Title for more settings summary. [CHAR LIMIT=50] -->
+    <string name="bluetooth_device_more_settings_preference_summary">Firmware updates, about, and more</string>
     <!-- Title of the item to show device MAC address -->
     <string name="bluetooth_device_mac_address">Device\'s Bluetooth address: <xliff:g id="address">%1$s</xliff:g></string>
     <!-- Title of the items to show multuple devices MAC address [CHAR LIMIT=NONE]-->
@@ -1884,6 +1888,9 @@
     <!-- Bluetooth device details companion apps. In the confirmation dialog for removing an associated app, this is the label on the button that will complete the disassociate action. [CHAR LIMIT=80] -->
     <string name = "bluetooth_companion_app_remove_association_confirm_button">Disconnect app</string>
 
+    <!-- Title of device details screen [CHAR LIMIT=28]-->
+    <string name="device_details_more_settings">More settings</string>
+
     <!-- Bluetooth developer settings: Maximum number of connected audio devices -->
     <string name="bluetooth_max_connected_audio_devices_string">Maximum connected Bluetooth audio devices</string>
     <!-- Bluetooth developer settings: Maximum number of connected audio devices -->
@@ -8851,9 +8858,13 @@
     <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="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.
+    </string>
     <string name="notification_listener_disable_warning_confirm">Turn off</string>
     <string name="notification_listener_disable_warning_cancel">Cancel</string>
     <string name="notif_type_ongoing">Real-time</string>
@@ -9030,6 +9041,15 @@
     <!-- 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>
+
+    <!-- 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>
+
+    <!-- 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>
+
     <!-- [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>
 
@@ -10151,6 +10171,18 @@
     <!-- 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>
+
+    <!-- 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>
+
+    <!-- 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>
+
+    <!-- Priority 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] -->
     <string name="ignore_optimizations_on">Don\u2019t optimize</string>
 
diff --git a/res/xml/bluetooth_device_more_settings_fragment.xml b/res/xml/bluetooth_device_more_settings_fragment.xml
new file mode 100644
index 0000000..4fb4aca
--- /dev/null
+++ b/res/xml/bluetooth_device_more_settings_fragment.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:key="bluetooth_device_more_settings_screen"
+    android:title="@string/device_details_more_settings">
+
+    <PreferenceCategory
+        android:key="bluetooth_profiles"/>
+</PreferenceScreen>
diff --git a/res/xml/mobile_network_settings.xml b/res/xml/mobile_network_settings.xml
index eb80ac8..4f16e12 100644
--- a/res/xml/mobile_network_settings.xml
+++ b/res/xml/mobile_network_settings.xml
@@ -91,9 +91,11 @@
             settings:searchable="false"
             settings:controller="com.android.settings.network.telephony.RoamingPreferenceController"/>
 
+        <!-- Settings search is handled by DataUsageSearchItem. -->
         <Preference
             android:key="data_usage_summary"
             android:title="@string/app_cellular_data_usage"
+            settings:searchable="false"
             settings:controller="com.android.settings.network.telephony.DataUsagePreferenceController"/>
 
         <com.android.settings.datausage.BillingCyclePreference
@@ -174,12 +176,11 @@
             settings:searchable="false"
             settings:controller="com.android.settings.network.telephony.EnabledNetworkModePreferenceController"/>
 
+        <!-- Settings search is handled by CarrierSettingsVersionSearchItem. -->
         <Preference
             android:key="carrier_settings_version_key"
             android:title="@string/carrier_settings_version"
-            android:enabled="false"
-            android:shouldDisableView="false"
-            android:selectable="false"
+            settings:searchable="false"
             settings:controller="com.android.settings.network.telephony.CarrierSettingsVersionPreferenceController"
             settings:enableCopying="true"/>
 
diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/FriendlyWarningDialogFragment.java b/src/com/android/settings/applications/specialaccess/notificationaccess/FriendlyWarningDialogFragment.java
index 3577946..c92f734 100644
--- a/src/com/android/settings/applications/specialaccess/notificationaccess/FriendlyWarningDialogFragment.java
+++ b/src/com/android/settings/applications/specialaccess/notificationaccess/FriendlyWarningDialogFragment.java
@@ -16,6 +16,7 @@
 package com.android.settings.applications.specialaccess.notificationaccess;
 
 import android.app.Dialog;
+import android.app.Flags;
 import android.app.settings.SettingsEnums;
 import android.content.ComponentName;
 import android.content.DialogInterface;
@@ -55,7 +56,10 @@
         NotificationAccessDetails parent = (NotificationAccessDetails) getTargetFragment();
 
         final String summary = getResources().getString(
-                R.string.notification_listener_disable_warning_summary, label);
+                Flags.modesApi() && Flags.modesUi()
+                        ? R.string.notification_listener_disable_modes_warning_summary
+                        : R.string.notification_listener_disable_warning_summary,
+                label);
         return new AlertDialog.Builder(getContext())
                 .setMessage(summary)
                 .setCancelable(true)
diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/ScaryWarningDialogFragment.java b/src/com/android/settings/applications/specialaccess/notificationaccess/ScaryWarningDialogFragment.java
index 747a125..53181fd 100644
--- a/src/com/android/settings/applications/specialaccess/notificationaccess/ScaryWarningDialogFragment.java
+++ b/src/com/android/settings/applications/specialaccess/notificationaccess/ScaryWarningDialogFragment.java
@@ -16,6 +16,7 @@
 package com.android.settings.applications.specialaccess.notificationaccess;
 
 import android.app.Dialog;
+import android.app.Flags;
 import android.app.settings.SettingsEnums;
 import android.content.ComponentName;
 import android.content.Context;
@@ -96,6 +97,11 @@
                 R.string.nls_warning_prompt, label);
         ((TextView) content.findViewById(R.id.prompt)).setText(prompt);
 
+        ((TextView) content.findViewById(R.id.settings_description)).setText(
+                Flags.modesApi() && Flags.modesUi()
+                        ? R.string.nls_feature_modes_settings_summary
+                        : R.string.nls_feature_settings_summary);
+
         Button allowButton = content.findViewById(R.id.allow_button);
         allowButton.setOnClickListener((view) -> {
             parent.enable(cn);
diff --git a/src/com/android/settings/applications/specialaccess/zenaccess/FriendlyWarningDialogFragment.java b/src/com/android/settings/applications/specialaccess/zenaccess/FriendlyWarningDialogFragment.java
index 5da2990..38317ed 100644
--- a/src/com/android/settings/applications/specialaccess/zenaccess/FriendlyWarningDialogFragment.java
+++ b/src/com/android/settings/applications/specialaccess/zenaccess/FriendlyWarningDialogFragment.java
@@ -17,6 +17,7 @@
 package com.android.settings.applications.specialaccess.zenaccess;
 
 import android.app.Dialog;
+import android.app.Flags;
 import android.app.settings.SettingsEnums;
 import android.os.Bundle;
 import android.text.TextUtils;
@@ -58,9 +59,14 @@
         final String label = args.getString(KEY_LABEL);
 
         final String title = getResources().getString(
-                R.string.zen_access_revoke_warning_dialog_title, label);
+                Flags.modesApi() && Flags.modesUi()
+                        ? R.string.zen_modes_access_revoke_warning_dialog_title
+                        : R.string.zen_access_revoke_warning_dialog_title,
+                label);
         final String summary = getResources()
-                .getString(R.string.zen_access_revoke_warning_dialog_summary);
+                .getString(Flags.modesApi() && Flags.modesUi()
+                        ? R.string.zen_modes_access_revoke_warning_dialog_summary
+                        : R.string.zen_access_revoke_warning_dialog_summary);
 
         ZenAccessDetails parent = (ZenAccessDetails) getTargetFragment();
         return new AlertDialog.Builder(getContext())
diff --git a/src/com/android/settings/applications/specialaccess/zenaccess/ScaryWarningDialogFragment.java b/src/com/android/settings/applications/specialaccess/zenaccess/ScaryWarningDialogFragment.java
index e4ef48b..b489602 100644
--- a/src/com/android/settings/applications/specialaccess/zenaccess/ScaryWarningDialogFragment.java
+++ b/src/com/android/settings/applications/specialaccess/zenaccess/ScaryWarningDialogFragment.java
@@ -17,6 +17,7 @@
 package com.android.settings.applications.specialaccess.zenaccess;
 
 import android.app.Dialog;
+import android.app.Flags;
 import android.app.settings.SettingsEnums;
 import android.os.Bundle;
 import android.text.TextUtils;
@@ -55,10 +56,15 @@
         final String pkg = args.getString(KEY_PKG);
         final String label = args.getString(KEY_LABEL);
 
-        final String title = getResources().getString(R.string.zen_access_warning_dialog_title,
+        final String title = getResources().getString(
+                Flags.modesApi() && Flags.modesUi()
+                        ? R.string.zen_modes_access_warning_dialog_title
+                        : R.string.zen_access_warning_dialog_title,
                 label);
         final String summary = getResources()
-                .getString(R.string.zen_access_warning_dialog_summary);
+                .getString(Flags.modesApi() && Flags.modesUi()
+                        ? R.string.zen_modes_access_warning_dialog_summary
+                        : R.string.zen_access_warning_dialog_summary);
 
         ZenAccessDetails parent = (ZenAccessDetails) getTargetFragment();
 
diff --git a/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessController.java b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessController.java
index 6f4137c..cfeeb0d 100644
--- a/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessController.java
+++ b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessController.java
@@ -18,6 +18,7 @@
 
 import android.app.ActivityManager;
 import android.app.AppGlobals;
+import android.app.Flags;
 import android.app.NotificationManager;
 import android.app.settings.SettingsEnums;
 import android.content.Context;
@@ -28,7 +29,10 @@
 import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
 
+import com.android.settings.R;
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.overlay.FeatureFactory;
 
@@ -48,6 +52,16 @@
         return AVAILABLE;
     }
 
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        Preference preference = screen.findPreference(getPreferenceKey());
+        if (preference != null) {
+            preference.setTitle(Flags.modesApi() && Flags.modesUi()
+                    ? R.string.manage_zen_modes_access_title
+                    : R.string.manage_zen_access_title);
+        }
+    }
+
     public static Set<String> getPackagesRequestingNotificationPolicyAccess() {
         final String[] PERM = {
                 android.Manifest.permission.ACCESS_NOTIFICATION_POLICY
diff --git a/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessDetails.java b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessDetails.java
index ffe13e6..74903c0 100644
--- a/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessDetails.java
+++ b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessDetails.java
@@ -16,9 +16,11 @@
 
 package com.android.settings.applications.specialaccess.zenaccess;
 
+import android.app.Flags;
 import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.os.Bundle;
+import android.os.UserManager;
 
 import androidx.appcompat.app.AlertDialog;
 import androidx.preference.TwoStatePreference;
@@ -42,6 +44,9 @@
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         addPreferencesFromResource(R.xml.zen_access_permission_details);
+        requireActivity().setTitle(Flags.modesApi() && Flags.modesUi()
+                ? R.string.manage_zen_modes_access_title
+                : R.string.manage_zen_access_title);
         getSettingsLifecycle().addObserver(
                 new ZenAccessSettingObserverMixin(getContext(), this /* listener */));
     }
@@ -49,6 +54,11 @@
     @Override
     protected boolean refreshUi() {
         final Context context = getContext();
+        // don't show for managed profiles
+        if (UserManager.get(context).isManagedProfile(context.getUserId())
+            && !ZenAccessController.hasAccess(context, mPackageName)) {
+            finish();
+        }
         // If this app didn't declare this permission in their manifest, don't bother showing UI.
         final Set<String> needAccessApps =
                 ZenAccessController.getPackagesRequestingNotificationPolicyAccess();
@@ -74,6 +84,9 @@
             preference.setSummary(getString(R.string.zen_access_disabled_package_warning));
             return;
         }
+        preference.setTitle(Flags.modesApi() && Flags.modesUi()
+                ? R.string.zen_modes_access_detail_switch
+                : R.string.zen_access_detail_switch);
         preference.setChecked(ZenAccessController.hasAccess(context, mPackageName));
         preference.setOnPreferenceChangeListener((p, newValue) -> {
             final boolean access = (Boolean) newValue;
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
index 25b392c..76bff57 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
@@ -48,6 +48,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.settings.R;
+import com.android.settings.bluetooth.ui.model.FragmentTypeModel;
 import com.android.settings.bluetooth.ui.view.DeviceDetailsFragmentFormatter;
 import com.android.settings.connecteddevice.stylus.StylusDevicesController;
 import com.android.settings.core.SettingsUIDeviceConfig;
@@ -352,7 +353,7 @@
     public void onCreatePreferences(@NonNull Bundle savedInstanceState, @NonNull String rootKey) {
         super.onCreatePreferences(savedInstanceState, rootKey);
         if (Flags.enableBluetoothDeviceDetailsPolish()) {
-            mFormatter.updateLayout();
+            mFormatter.updateLayout(FragmentTypeModel.DeviceDetailsMainFragment.INSTANCE);
         }
     }
 
@@ -409,7 +410,9 @@
     @Override
     protected void addPreferenceController(AbstractPreferenceController controller) {
         if (Flags.enableBluetoothDeviceDetailsPolish()) {
-            List<String> keys = mFormatter.getVisiblePreferenceKeysForMainPage();
+            List<String> keys =
+                    mFormatter.getVisiblePreferenceKeys(
+                            FragmentTypeModel.DeviceDetailsMainFragment.INSTANCE);
             Lifecycle lifecycle = getSettingsLifecycle();
             if (keys == null || keys.contains(controller.getPreferenceKey())) {
                 super.addPreferenceController(controller);
diff --git a/src/com/android/settings/bluetooth/ui/composable/MultiTogglePreferenceGroup.kt b/src/com/android/settings/bluetooth/ui/composable/MultiTogglePreferenceGroup.kt
index 8fe3c25..d29795e 100644
--- a/src/com/android/settings/bluetooth/ui/composable/MultiTogglePreferenceGroup.kt
+++ b/src/com/android/settings/bluetooth/ui/composable/MultiTogglePreferenceGroup.kt
@@ -66,15 +66,14 @@
 import androidx.compose.ui.unit.sp
 import androidx.compose.ui.window.DialogProperties
 import com.android.settings.R
+import com.android.settings.bluetooth.ui.model.DeviceSettingPreferenceModel
 import com.android.settings.bluetooth.ui.composable.Icon as DeviceSettingComposeIcon
-import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingModel
-import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingStateModel
 import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.widget.dialog.getDialogWidth
 
 @Composable
 fun MultiTogglePreferenceGroup(
-    preferenceModels: List<DeviceSettingModel.MultiTogglePreference>,
+    preferenceModels: List<DeviceSettingPreferenceModel.MultiTogglePreference>,
 ) {
     var settingIdForPopUp by remember { mutableStateOf<Int?>(null) }
 
@@ -115,7 +114,7 @@
                                 colors = getButtonColors(preferenceModel.isActive),
                                 contentPadding = PaddingValues(0.dp)) {
                                     DeviceSettingComposeIcon(
-                                        preferenceModel.toggles[preferenceModel.state.selectedIndex]
+                                        preferenceModel.toggles[preferenceModel.selectedIndex]
                                             .icon,
                                         modifier = Modifier.size(24.dp))
                                 }
@@ -144,7 +143,7 @@
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 private fun dialog(
-    multiTogglePreference: DeviceSettingModel.MultiTogglePreference,
+    multiTogglePreference: DeviceSettingPreferenceModel.MultiTogglePreference,
     onDismiss: () -> Unit
 ) {
     BasicAlertDialog(
@@ -179,7 +178,7 @@
 }
 
 @Composable
-private fun dialogContent(multiTogglePreference: DeviceSettingModel.MultiTogglePreference) {
+private fun dialogContent(multiTogglePreference: DeviceSettingPreferenceModel.MultiTogglePreference) {
     Column {
         Row(
             modifier = Modifier.fillMaxWidth().height(24.dp),
@@ -219,7 +218,7 @@
                 }
                 Row {
                     for ((idx, toggle) in multiTogglePreference.toggles.withIndex()) {
-                        val selected = idx == multiTogglePreference.state.selectedIndex
+                        val selected = idx == multiTogglePreference.selectedIndex
                         Column(
                             modifier =
                                 Modifier.weight(1f)
@@ -237,8 +236,7 @@
                         ) {
                             Button(
                                 onClick = {
-                                    multiTogglePreference.updateState(
-                                        DeviceSettingStateModel.MultiTogglePreferenceState(idx))
+                                    multiTogglePreference.onSelectedChange(idx)
                                 },
                                 modifier = Modifier.fillMaxSize(),
                                 colors =
diff --git a/src/com/android/settings/bluetooth/ui/model/DeviceSettingPreferenceModel.kt b/src/com/android/settings/bluetooth/ui/model/DeviceSettingPreferenceModel.kt
new file mode 100644
index 0000000..6612591
--- /dev/null
+++ b/src/com/android/settings/bluetooth/ui/model/DeviceSettingPreferenceModel.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.bluetooth.ui.model
+
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId
+import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingIcon
+import com.android.settingslib.bluetooth.devicesettings.shared.model.ToggleModel
+
+/** Models a device setting preference. */
+sealed interface DeviceSettingPreferenceModel {
+    @DeviceSettingId
+    val id: Int
+
+    /** Models a plain preference. */
+    data class PlainPreference(
+        @DeviceSettingId override val id: Int,
+        val title: String,
+        val summary: String? = null,
+        val icon: DeviceSettingIcon? = null,
+        val onClick: (() -> Unit)? = null,
+    ) : DeviceSettingPreferenceModel
+
+    /** Models a switch preference. */
+    data class SwitchPreference(
+        @DeviceSettingId override val id: Int,
+        val title: String,
+        val summary: String? = null,
+        val icon: DeviceSettingIcon? = null,
+        val checked: Boolean,
+        val onCheckedChange: ((Boolean) -> Unit),
+        val onPrimaryClick: (() -> Unit)? = null,
+    ) : DeviceSettingPreferenceModel
+
+    /** Models a multi-toggle preference. */
+    data class MultiTogglePreference(
+        @DeviceSettingId override val id: Int,
+        val title: String,
+        val toggles: List<ToggleModel>,
+        val isActive: Boolean,
+        val selectedIndex: Int,
+        val isAllowedChangingState: Boolean,
+        val onSelectedChange: (Int) -> Unit,
+    ) : DeviceSettingPreferenceModel
+
+    /** Models a footer preference. */
+    data class FooterPreference(
+        @DeviceSettingId override val id: Int,
+        val footerText: String,
+    ) : DeviceSettingPreferenceModel
+
+    /** Models a preference which could navigate to more settings fragment. */
+    data class MoreSettingsPreference(
+        @DeviceSettingId override val id: Int,
+    ) : DeviceSettingPreferenceModel
+}
diff --git a/src/com/android/settings/bluetooth/ui/model/FragmentTypeModel.kt b/src/com/android/settings/bluetooth/ui/model/FragmentTypeModel.kt
new file mode 100644
index 0000000..19858c4
--- /dev/null
+++ b/src/com/android/settings/bluetooth/ui/model/FragmentTypeModel.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.bluetooth.ui.model
+
+/** Models a device details fragment type. */
+sealed interface FragmentTypeModel {
+    /** Device details main page. */
+    data object DeviceDetailsMainFragment : FragmentTypeModel
+    /** Device details more settings page. */
+    data object DeviceDetailsMoreSettingsFragment : FragmentTypeModel
+}
diff --git a/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatter.kt b/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatter.kt
index b75579d..c933c75 100644
--- a/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatter.kt
+++ b/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatter.kt
@@ -19,47 +19,52 @@
 import android.bluetooth.BluetoothAdapter
 import android.content.Context
 import android.media.AudioManager
-import android.util.Log
+import android.os.Bundle
 import androidx.compose.foundation.layout.size
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
 import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import androidx.lifecycle.lifecycleScope
 import androidx.preference.Preference
+import com.android.settings.R
 import com.android.settings.SettingsPreferenceFragment
 import com.android.settings.bluetooth.ui.composable.Icon
 import com.android.settings.bluetooth.ui.composable.MultiTogglePreferenceGroup
 import com.android.settings.bluetooth.ui.layout.DeviceSettingLayout
+import com.android.settings.bluetooth.ui.model.DeviceSettingPreferenceModel
+import com.android.settings.bluetooth.ui.model.FragmentTypeModel
+import com.android.settings.bluetooth.ui.view.DeviceDetailsMoreSettingsFragment.Companion.KEY_DEVICE_ADDRESS
 import com.android.settings.bluetooth.ui.viewmodel.BluetoothDeviceDetailsViewModel
+import com.android.settings.core.SubSettingLauncher
 import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
 import com.android.settings.spa.preference.ComposePreference
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
 import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel
-import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingModel
-import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingStateModel
+import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingIcon
 import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.widget.preference.Preference as SpaPreference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spa.widget.preference.SwitchPreference
 import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
 import com.android.settingslib.spa.widget.preference.TwoTargetSwitchPreference
+import com.android.settingslib.spa.widget.ui.Footer
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.runBlocking
-import com.android.settingslib.spa.widget.preference.Preference as SpaPreference
-
 
 /** Handles device details fragment layout according to config. */
 interface DeviceDetailsFragmentFormatter {
     /** Gets keys of visible preferences in built-in preference in xml. */
-    fun getVisiblePreferenceKeysForMainPage(): List<String>?
+    fun getVisiblePreferenceKeys(fragmentType: FragmentTypeModel): List<String>?
 
     /** Updates device details fragment layout. */
-    fun updateLayout()
+    fun updateLayout(fragmentType: FragmentTypeModel)
 }
 
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -79,23 +84,25 @@
         ViewModelProvider(
                 fragment,
                 BluetoothDeviceDetailsViewModel.Factory(
+                    fragment.requireActivity().application,
                     repository,
                     spatialAudioInteractor,
                     cachedDevice,
                 ))
             .get(BluetoothDeviceDetailsViewModel::class.java)
 
-    override fun getVisiblePreferenceKeysForMainPage(): List<String>? = runBlocking {
-        viewModel
-            .getItems()
-            ?.filterIsInstance<DeviceSettingConfigItemModel.BuiltinItem>()
-            ?.mapNotNull { it.preferenceKey }
-    }
+    override fun getVisiblePreferenceKeys(fragmentType: FragmentTypeModel): List<String>? =
+        runBlocking {
+            viewModel
+                .getItems(fragmentType)
+                ?.filterIsInstance<DeviceSettingConfigItemModel.BuiltinItem>()
+                ?.mapNotNull { it.preferenceKey }
+        }
 
     /** Updates bluetooth device details fragment layout. */
-    override fun updateLayout() = runBlocking {
-        val items = viewModel.getItems() ?: return@runBlocking
-        val layout = viewModel.getLayout() ?: return@runBlocking
+    override fun updateLayout(fragmentType: FragmentTypeModel) = runBlocking {
+        val items = viewModel.getItems(fragmentType) ?: return@runBlocking
+        val layout = viewModel.getLayout(fragmentType) ?: return@runBlocking
         val prefKeyToSettingId =
             items
                 .filterIsInstance<DeviceSettingConfigItemModel.BuiltinItem>()
@@ -124,6 +131,8 @@
                 fragment.preferenceScreen.addPreference(pref)
             }
         }
+        // TODO(b/343317785): figure out how to remove the foot preference.
+        fragment.preferenceScreen.addPreference(Preference(context).apply { order = 10000 })
     }
 
     @Composable
@@ -132,7 +141,7 @@
             remember(row) {
                     layout.rows[row].settingIds.flatMapLatest { settingIds ->
                         if (settingIds.isEmpty()) {
-                            flowOf(emptyList<DeviceSettingModel>())
+                            flowOf(emptyList<DeviceSettingPreferenceModel>())
                         } else {
                             combine(
                                 settingIds.map { settingId ->
@@ -150,72 +159,104 @@
             0 -> {}
             1 -> {
                 when (val setting = settings[0]) {
-                    is DeviceSettingModel.ActionSwitchPreference -> {
-                        buildActionSwitchPreference(setting)
+                    is DeviceSettingPreferenceModel.PlainPreference -> {
+                        buildPlainPreference(setting)
                     }
-                    is DeviceSettingModel.MultiTogglePreference -> {
+                    is DeviceSettingPreferenceModel.SwitchPreference -> {
+                        buildSwitchPreference(setting)
+                    }
+                    is DeviceSettingPreferenceModel.MultiTogglePreference -> {
                         buildMultiTogglePreference(listOf(setting))
                     }
-                    null -> {}
-                    else -> {
-                        Log.w(TAG, "Unknown preference type ${setting.id}, skip.")
+                    is DeviceSettingPreferenceModel.FooterPreference -> {
+                        buildFooterPreference(setting)
                     }
+                    is DeviceSettingPreferenceModel.MoreSettingsPreference -> {
+                        buildMoreSettingsPreference()
+                    }
+                    null -> {}
                 }
             }
             else -> {
-                if (!settings.all { it is DeviceSettingModel.MultiTogglePreference }) {
+                if (!settings.all { it is DeviceSettingPreferenceModel.MultiTogglePreference }) {
                     return
                 }
                 buildMultiTogglePreference(
-                    settings.filterIsInstance<DeviceSettingModel.MultiTogglePreference>())
+                    settings.filterIsInstance<DeviceSettingPreferenceModel.MultiTogglePreference>())
             }
         }
     }
 
     @Composable
-    private fun buildMultiTogglePreference(prefs: List<DeviceSettingModel.MultiTogglePreference>) {
+    private fun buildMultiTogglePreference(
+        prefs: List<DeviceSettingPreferenceModel.MultiTogglePreference>
+    ) {
         MultiTogglePreferenceGroup(prefs)
     }
 
     @Composable
-    private fun buildActionSwitchPreference(model: DeviceSettingModel.ActionSwitchPreference) {
-        if (model.switchState != null) {
-            val switchPrefModel =
-                object : SwitchPreferenceModel {
-                    override val title = model.title
-                    override val summary = { model.summary ?: "" }
-                    override val checked = { model.switchState?.checked }
-                    override val onCheckedChange = { newChecked: Boolean ->
-                        model.updateState?.invoke(
-                            DeviceSettingStateModel.ActionSwitchPreferenceState(newChecked))
-                        Unit
-                    }
-                    override val icon = @Composable { deviceSettingIcon(model) }
+    private fun buildSwitchPreference(model: DeviceSettingPreferenceModel.SwitchPreference) {
+        val switchPrefModel =
+            object : SwitchPreferenceModel {
+                override val title = model.title
+                override val summary = { model.summary ?: "" }
+                override val checked = { model.checked }
+                override val onCheckedChange = { newChecked: Boolean ->
+                    model.onCheckedChange(newChecked)
                 }
-            if (model.intent != null) {
-                TwoTargetSwitchPreference(switchPrefModel) { context.startActivity(model.intent) }
-            } else {
-                SwitchPreference(switchPrefModel)
+                override val icon = @Composable { deviceSettingIcon(model.icon) }
             }
+        if (model.onPrimaryClick != null) {
+            TwoTargetSwitchPreference(
+                switchPrefModel, primaryOnClick = model.onPrimaryClick::invoke)
         } else {
-            SpaPreference(
-                object : PreferenceModel {
-                    override val title = model.title
-                    override val summary = { model.summary ?: "" }
-                    override val onClick = {
-                        model.intent?.let { context.startActivity(it) }
-                        Unit
-                    }
-                    override val icon = @Composable { deviceSettingIcon(model) }
-                })
+            SwitchPreference(switchPrefModel)
         }
     }
 
     @Composable
-    private fun deviceSettingIcon(model: DeviceSettingModel.ActionSwitchPreference) {
-        model.icon?.let { icon ->
-            Icon(icon, modifier = Modifier.size(SettingsDimension.itemIconSize))
-        }
+    private fun buildPlainPreference(model: DeviceSettingPreferenceModel.PlainPreference) {
+        SpaPreference(
+            object : PreferenceModel {
+                override val title = model.title
+                override val summary = { model.summary ?: "" }
+                override val onClick = {
+                    model.onClick?.invoke()
+                    Unit
+                }
+                override val icon = @Composable { deviceSettingIcon(model.icon) }
+            })
+    }
+
+    @Composable
+    fun buildMoreSettingsPreference() {
+        SpaPreference(
+            object : PreferenceModel {
+                override val title =
+                    stringResource(R.string.bluetooth_device_more_settings_preference_title)
+                override val summary = {
+                    context.getString(R.string.bluetooth_device_more_settings_preference_summary)
+                }
+                override val onClick = {
+                    SubSettingLauncher(context)
+                        .setDestination(DeviceDetailsMoreSettingsFragment::class.java.name)
+                        .setSourceMetricsCategory(fragment.getMetricsCategory())
+                        .setArguments(
+                            Bundle().apply { putString(KEY_DEVICE_ADDRESS, cachedDevice.address) })
+                        .launch()
+                }
+                override val icon = @Composable { deviceSettingIcon(null) }
+            })
+    }
+
+    @Composable
+    fun buildFooterPreference(model: DeviceSettingPreferenceModel.FooterPreference) {
+        Footer(footerText = model.footerText)
+    }
+
+    @Composable
+    private fun deviceSettingIcon(icon: DeviceSettingIcon?) {
+        icon?.let { Icon(it, modifier = Modifier.size(SettingsDimension.itemIconSize)) }
     }
 
     private fun getPreferenceKey(settingId: Int) = "DEVICE_SETTING_${settingId}"
diff --git a/src/com/android/settings/bluetooth/ui/view/DeviceDetailsMoreSettingsFragment.kt b/src/com/android/settings/bluetooth/ui/view/DeviceDetailsMoreSettingsFragment.kt
new file mode 100644
index 0000000..c648a3e
--- /dev/null
+++ b/src/com/android/settings/bluetooth/ui/view/DeviceDetailsMoreSettingsFragment.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.bluetooth.ui.view
+
+import android.bluetooth.BluetoothDevice
+import android.bluetooth.BluetoothManager
+import android.content.Context
+import android.os.Bundle
+import com.android.settings.R
+import com.android.settings.bluetooth.BluetoothDetailsProfilesController
+import com.android.settings.bluetooth.Utils
+import com.android.settings.bluetooth.ui.model.FragmentTypeModel
+import com.android.settings.dashboard.DashboardFragment
+import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.settingslib.core.AbstractPreferenceController
+import com.android.settingslib.core.lifecycle.LifecycleObserver
+
+class DeviceDetailsMoreSettingsFragment : DashboardFragment() {
+    private lateinit var formatter: DeviceDetailsFragmentFormatter
+    private lateinit var localBluetoothManager: LocalBluetoothManager
+    private lateinit var cachedDevice: CachedBluetoothDevice
+
+    // TODO(b/343317785): add metrics category
+    override fun getMetricsCategory(): Int = 0
+
+    override fun getPreferenceScreenResId(): Int {
+        return R.xml.bluetooth_device_more_settings_fragment
+    }
+
+    override fun addPreferenceController(controller: AbstractPreferenceController) {
+        val keys: List<String>? =
+            formatter.getVisiblePreferenceKeys(FragmentTypeModel.DeviceDetailsMoreSettingsFragment)
+        val lifecycle = settingsLifecycle
+        if (keys == null || keys.contains(controller.preferenceKey)) {
+            super.addPreferenceController(controller)
+        } else if (controller is LifecycleObserver) {
+            lifecycle.removeObserver((controller as LifecycleObserver))
+        }
+    }
+
+    private fun getCachedDevice(): CachedBluetoothDevice? {
+        val bluetoothAddress = arguments?.getString(KEY_DEVICE_ADDRESS) ?: return null
+        localBluetoothManager = Utils.getLocalBtManager(context) ?: return null
+        val remoteDevice: BluetoothDevice =
+            localBluetoothManager.bluetoothAdapter.getRemoteDevice(bluetoothAddress) ?: return null
+        return Utils.getLocalBtManager(context).cachedDeviceManager.findDevice(remoteDevice)
+    }
+
+    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+        super.onCreatePreferences(savedInstanceState, rootKey)
+        formatter.updateLayout(FragmentTypeModel.DeviceDetailsMoreSettingsFragment)
+    }
+
+    override fun createPreferenceControllers(context: Context): List<AbstractPreferenceController> {
+        val bluetoothManager = context.getSystemService(BluetoothManager::class.java)
+        cachedDevice =
+            getCachedDevice()
+                ?: run {
+                    finish()
+                    return emptyList()
+                }
+        formatter =
+            featureFactory.bluetoothFeatureProvider.getDeviceDetailsFragmentFormatter(
+                requireContext(), this, bluetoothManager.adapter, cachedDevice)
+        return listOf(
+            BluetoothDetailsProfilesController(
+                context, this, localBluetoothManager, cachedDevice, settingsLifecycle))
+    }
+
+    override fun getLogTag(): String = TAG
+
+    companion object {
+        const val TAG: String = "DeviceMoreSettingsFrg"
+        const val KEY_DEVICE_ADDRESS: String = "device_address"
+    }
+}
diff --git a/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModel.kt b/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModel.kt
index befff83..c85015c 100644
--- a/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModel.kt
+++ b/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModel.kt
@@ -16,17 +16,22 @@
 
 package com.android.settings.bluetooth.ui.viewmodel
 
+import android.app.Application
+import androidx.lifecycle.AndroidViewModel
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.viewModelScope
 import com.android.settings.bluetooth.domain.interactor.SpatialAudioInteractor
 import com.android.settings.bluetooth.ui.layout.DeviceSettingLayout
 import com.android.settings.bluetooth.ui.layout.DeviceSettingLayoutRow
+import com.android.settings.bluetooth.ui.model.DeviceSettingPreferenceModel
+import com.android.settings.bluetooth.ui.model.FragmentTypeModel
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
 import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId
 import com.android.settingslib.bluetooth.devicesettings.data.repository.DeviceSettingRepository
 import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel
 import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingModel
+import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingStateModel
 import kotlinx.coroutines.CoroutineStart
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.async
@@ -38,30 +43,81 @@
 import kotlinx.coroutines.flow.stateIn
 
 class BluetoothDeviceDetailsViewModel(
+    private val application: Application,
     private val deviceSettingRepository: DeviceSettingRepository,
     private val spatialAudioInteractor: SpatialAudioInteractor,
     private val cachedDevice: CachedBluetoothDevice,
-) : ViewModel() {
+) : AndroidViewModel(application){
+
     private val items =
         viewModelScope.async(Dispatchers.IO, start = CoroutineStart.LAZY) {
             deviceSettingRepository.getDeviceSettingsConfig(cachedDevice)
         }
 
-    suspend fun getItems(): List<DeviceSettingConfigItemModel>? = items.await()?.mainItems
+    suspend fun getItems(fragment: FragmentTypeModel): List<DeviceSettingConfigItemModel>? =
+        when (fragment) {
+            is FragmentTypeModel.DeviceDetailsMainFragment -> items.await()?.mainItems
+            is FragmentTypeModel.DeviceDetailsMoreSettingsFragment ->
+                items.await()?.moreSettingsItems
+        }
 
     fun getDeviceSetting(
         cachedDevice: CachedBluetoothDevice,
         @DeviceSettingId settingId: Int
-    ): Flow<DeviceSettingModel?> {
+    ): Flow<DeviceSettingPreferenceModel?> {
+        if (settingId == DeviceSettingId.DEVICE_SETTING_ID_MORE_SETTINGS) {
+            return flowOf(DeviceSettingPreferenceModel.MoreSettingsPreference(settingId))
+        }
         return when (settingId) {
             DeviceSettingId.DEVICE_SETTING_ID_SPATIAL_AUDIO_MULTI_TOGGLE ->
                 spatialAudioInteractor.getDeviceSetting(cachedDevice)
             else -> deviceSettingRepository.getDeviceSetting(cachedDevice, settingId)
+        }.map { it?.toPreferenceModel() }
+    }
+
+    private fun DeviceSettingModel.toPreferenceModel(): DeviceSettingPreferenceModel? {
+        return when (this) {
+            is DeviceSettingModel.ActionSwitchPreference -> {
+                if (switchState != null) {
+                    DeviceSettingPreferenceModel.SwitchPreference(
+                        id = id,
+                        title = title,
+                        summary = summary,
+                        icon = icon,
+                        checked = switchState?.checked ?: false,
+                        onCheckedChange = { newState ->
+                            updateState?.invoke(
+                                DeviceSettingStateModel.ActionSwitchPreferenceState(newState))
+                        },
+                        onPrimaryClick = { intent?.let { application.startActivity(it) } })
+                } else {
+                    DeviceSettingPreferenceModel.PlainPreference(
+                        id = id,
+                        title = title,
+                        summary = summary,
+                        icon = icon,
+                        onClick = { intent?.let { application.startActivity(it) } })
+                }
+            }
+            is DeviceSettingModel.FooterPreference ->
+                DeviceSettingPreferenceModel.FooterPreference(id = id, footerText = footerText)
+            is DeviceSettingModel.MultiTogglePreference ->
+                DeviceSettingPreferenceModel.MultiTogglePreference(
+                    id = id,
+                    title = title,
+                    toggles = toggles,
+                    isActive = isActive,
+                    selectedIndex = state.selectedIndex,
+                    isAllowedChangingState = isAllowedChangingState,
+                    onSelectedChange = { newState ->
+                        updateState(DeviceSettingStateModel.MultiTogglePreferenceState(newState))
+                    })
+            is DeviceSettingModel.Unknown -> null
         }
     }
 
-    suspend fun getLayout(): DeviceSettingLayout? {
-        val configItems = getItems() ?: return null
+    suspend fun getLayout(fragment: FragmentTypeModel): DeviceSettingLayout? {
+        val configItems = getItems(fragment) ?: return null
         val idToDeviceSetting =
             configItems
                 .filterIsInstance<DeviceSettingConfigItemModel.AppProvidedItem>()
@@ -80,7 +136,7 @@
                         if (!isXmlPreference && setting == null) {
                             continue
                         }
-                        if (setting !is DeviceSettingModel.MultiTogglePreference) {
+                        if (setting !is DeviceSettingPreferenceModel.MultiTogglePreference) {
                             multiToggleSettingIds = null
                             positionMapping[i] = listOf(configItem.settingId)
                             continue
@@ -103,6 +159,7 @@
     }
 
     class Factory(
+        private val application: Application,
         private val deviceSettingRepository: DeviceSettingRepository,
         private val spatialAudioInteractor: SpatialAudioInteractor,
         private val cachedDevice: CachedBluetoothDevice,
@@ -110,7 +167,7 @@
         override fun <T : ViewModel> create(modelClass: Class<T>): T {
             @Suppress("UNCHECKED_CAST")
             return BluetoothDeviceDetailsViewModel(
-                deviceSettingRepository, spatialAudioInteractor, cachedDevice)
+                application, deviceSettingRepository, spatialAudioInteractor, cachedDevice)
                 as T
         }
     }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
index 8396e48..a802132 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
@@ -43,6 +43,7 @@
 import androidx.lifecycle.DefaultLifecycleObserver;
 import androidx.lifecycle.LifecycleOwner;
 
+import com.android.settings.R;
 import com.android.settings.bluetooth.Utils;
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.overlay.FeatureFactory;
@@ -128,6 +129,8 @@
                                     + ", broadcastId = "
                                     + broadcastId);
                     updateSwitch();
+                    AudioSharingUtils.toastMessage(
+                            mContext, mContext.getString(R.string.audio_sharing_sharing_label));
                     mListener.onAudioSharingStateChanged();
                 }
 
@@ -161,6 +164,9 @@
                                     + ", broadcastId = "
                                     + broadcastId);
                     updateSwitch();
+                    AudioSharingUtils.toastMessage(
+                            mContext,
+                            mContext.getString(R.string.audio_sharing_sharing_stopped_label));
                     mListener.onAudioSharingStateChanged();
                 }
 
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamConfirmDialog.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamConfirmDialog.java
index 4c17a7c..958740b 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamConfirmDialog.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamConfirmDialog.java
@@ -129,6 +129,10 @@
     private Dialog getUnsupportedDialog() {
         return new AudioStreamsDialogFragment.DialogBuilder(getActivity())
                 .setTitle(getString(R.string.audio_streams_dialog_cannot_listen))
+                .setSubTitle1(
+                        mBroadcastMetadata != null
+                                ? AudioStreamsHelper.getBroadcastName(mBroadcastMetadata)
+                                : "")
                 .setSubTitle2(getString(R.string.audio_streams_dialog_unsupported_device_subtitle))
                 .setRightButtonText(getString(R.string.audio_streams_dialog_close))
                 .setRightButtonOnClickListener(
diff --git a/src/com/android/settings/development/TouchpadVisualizerPreferenceController.java b/src/com/android/settings/development/TouchpadVisualizerPreferenceController.java
index f918d26..04ae92b 100644
--- a/src/com/android/settings/development/TouchpadVisualizerPreferenceController.java
+++ b/src/com/android/settings/development/TouchpadVisualizerPreferenceController.java
@@ -17,8 +17,10 @@
 package com.android.settings.development;
 
 import android.content.Context;
-import android.provider.Settings;
+import android.hardware.input.InputSettings;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
 import androidx.preference.SwitchPreference;
@@ -26,23 +28,13 @@
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settingslib.development.DeveloperOptionsPreferenceController;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import android.hardware.input.InputSettings;
-
-/** PreferenceController that controls the "Touchpad visualizer" developer option. */
+/** PreferenceController that controls the "Show touchpad input" developer option. */
 public class TouchpadVisualizerPreferenceController extends
         DeveloperOptionsPreferenceController implements
         Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
 
     private static final String TOUCHPAD_VISUALIZER_KEY = "touchpad_visualizer";
 
-    @VisibleForTesting
-    static final int SETTING_VALUE_ON = 1;
-    @VisibleForTesting
-    static final int SETTING_VALUE_OFF = 0;
-
     public TouchpadVisualizerPreferenceController(@NonNull Context context) {
         super(context);
     }
@@ -60,24 +52,22 @@
     @Override
     public boolean onPreferenceChange(@NonNull Preference preference, @Nullable Object newValue) {
         final boolean isEnabled = newValue != null ? (Boolean) newValue : false;
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.TOUCHPAD_VISUALIZER,
-                isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF);
+        InputSettings.setTouchpadVisualizer(mContext, isEnabled);
+
         return true;
     }
 
     @Override
     public void updateState(@NonNull Preference preference) {
-        int touchpadVisualizer = Settings.System.getInt(mContext.getContentResolver(),
-                Settings.System.TOUCHPAD_VISUALIZER, SETTING_VALUE_OFF);
-        ((SwitchPreference) mPreference).setChecked(touchpadVisualizer != SETTING_VALUE_OFF);
+        boolean touchpadVisualizerEnabled = InputSettings.useTouchpadVisualizer(mContext);
+        ((SwitchPreference) mPreference).setChecked(touchpadVisualizerEnabled);
     }
 
     @Override
     protected void onDeveloperOptionsSwitchDisabled() {
         super.onDeveloperOptionsSwitchDisabled();
-        Settings.System.putInt(mContext.getContentResolver(), Settings.System.TOUCHPAD_VISUALIZER,
-                SETTING_VALUE_OFF);
+        InputSettings.setTouchpadVisualizer(mContext, false);
+
         ((SwitchPreference) mPreference).setChecked(false);
     }
 }
\ No newline at end of file
diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
index 7bbb06a..e922f70 100644
--- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
+++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
@@ -278,16 +278,17 @@
         super.onPause();
 
         final int currentOptimizeMode = mBatteryOptimizeUtils.getAppOptimizationMode();
+        final Context applicationContext = requireContext().getApplicationContext();
         mLogStringBuilder.append(", onPause mode = ").append(currentOptimizeMode);
         logMetricCategory(currentOptimizeMode);
         mExecutor.execute(
                 () -> {
                     if (currentOptimizeMode != mOptimizationMode) {
                         AppOptModeSharedPreferencesUtils.deleteAppOptimizationModeEventByUid(
-                                getContext(), mBatteryOptimizeUtils.getUid());
+                                applicationContext, mBatteryOptimizeUtils.getUid());
                     }
                     BatteryOptimizeLogUtils.writeLog(
-                            getContext().getApplicationContext(),
+                            applicationContext,
                             Action.LEAVE,
                             BatteryOptimizeLogUtils.getPackageNameWithUserId(
                                     mBatteryOptimizeUtils.getPackageName(), UserHandle.myUserId()),
diff --git a/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java b/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java
index 2d2c838..e59cc4ad 100644
--- a/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java
+++ b/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java
@@ -117,17 +117,17 @@
         super.onPause();
 
         final int currentOptimizeMode = mBatteryOptimizeUtils.getAppOptimizationMode();
+        final Context applicationContext = requireContext().getApplicationContext();
         mLogStringBuilder.append(", onPause mode = ").append(currentOptimizeMode);
         logMetricCategory(currentOptimizeMode);
-
         mExecutor.execute(
                 () -> {
                     if (currentOptimizeMode != mOptimizationMode) {
                         AppOptModeSharedPreferencesUtils.deleteAppOptimizationModeEventByUid(
-                                getContext(), mBatteryOptimizeUtils.getUid());
+                                applicationContext, mBatteryOptimizeUtils.getUid());
                     }
                     BatteryOptimizeLogUtils.writeLog(
-                            getContext().getApplicationContext(),
+                            applicationContext,
                             Action.LEAVE,
                             BatteryOptimizeLogUtils.getPackageNameWithUserId(
                                     mBatteryOptimizeUtils.getPackageName(), UserHandle.myUserId()),
diff --git a/src/com/android/settings/network/SimOnboardingService.kt b/src/com/android/settings/network/SimOnboardingService.kt
index 59dc35e..e4f17e2 100644
--- a/src/com/android/settings/network/SimOnboardingService.kt
+++ b/src/com/android/settings/network/SimOnboardingService.kt
@@ -24,10 +24,10 @@
 import android.telephony.SubscriptionManager
 import android.telephony.TelephonyManager
 import android.telephony.UiccCardInfo
-import android.telephony.UiccSlotInfo
 import android.util.Log
 import com.android.settings.network.SimOnboardingActivity.Companion.CallbackType
 import com.android.settings.network.telephony.MobileDataRepository
+import com.android.settings.network.telephony.UiccSlotRepository
 import com.android.settings.sim.SimActivationNotifier
 import com.android.settings.spa.network.setDefaultData
 import com.android.settings.spa.network.setDefaultSms
@@ -46,7 +46,6 @@
     var targetSubInfo: SubscriptionInfo? = null
     var availableSubInfoList: List<SubscriptionInfo> = listOf()
     var activeSubInfoList: List<SubscriptionInfo> = listOf()
-    var slotInfoList: List<UiccSlotInfo> = listOf()
     var uiccCardInfoList: List<UiccCardInfo> = listOf()
     var targetPrimarySimCalls: Int = INVALID_SUBSCRIPTION_ID
     var targetPrimarySimTexts: Int = INVALID_SUBSCRIPTION_ID
@@ -73,14 +72,6 @@
             }
             return  uiccCardInfoList.any { it.isMultipleEnabledProfilesSupported }
         }
-    var isRemovablePsimProfileEnabled: Boolean = false
-        get() {
-            if(slotInfoList.isEmpty()) {
-                Log.w(TAG, "UICC Slot info list is empty.")
-                return false
-            }
-            return UiccSlotUtil.isRemovableSimEnabled(slotInfoList)
-        }
     var isEsimProfileEnabled: Boolean = false
         get() {
             activeSubInfoList.stream().anyMatch { it.isEmbedded }
@@ -137,19 +128,11 @@
             return telephonyManager?.doesSwitchMultiSimConfigTriggerReboot() ?: false
         }
 
-    fun isValid(): Boolean {
-        return targetSubId != INVALID_SUBSCRIPTION_ID
-            && targetSubInfo != null
-            && activeSubInfoList.isNotEmpty()
-            && slotInfoList.isNotEmpty()
-    }
-
     fun clear() {
         targetSubId = -1
         targetSubInfo = null
         availableSubInfoList = listOf()
         activeSubInfoList = listOf()
-        slotInfoList = listOf()
         uiccCardInfoList = listOf()
         targetPrimarySimCalls = -1
         targetPrimarySimTexts = -1
@@ -181,8 +164,6 @@
                 availableSubInfoList.find { subInfo -> subInfo.subscriptionId == targetSubId }
             targetSubInfo?.let { userSelectedSubInfoList.add(it) }
             Log.d(TAG, "targetSubId: $targetSubId , targetSubInfo: $targetSubInfo")
-            slotInfoList = telephonyManager?.uiccSlotsInfo?.toList() ?: listOf()
-            Log.d(TAG, "slotInfoList: $slotInfoList.")
             uiccCardInfoList = telephonyManager?.uiccCardsInfo!!
             Log.d(TAG, "uiccCardInfoList: $uiccCardInfoList")
 
@@ -192,7 +173,6 @@
 
             Log.d(
                 TAG,"doesTargetSimHaveEsimOperation: $doesTargetSimHaveEsimOperation" +
-                    ", isRemovableSimEnabled: $isRemovablePsimProfileEnabled" +
                     ", isMultipleEnabledProfilesSupported: $isMultipleEnabledProfilesSupported" +
                     ", targetPrimarySimCalls: $targetPrimarySimCalls" +
                     ", targetPrimarySimTexts: $targetPrimarySimTexts" +
@@ -317,14 +297,15 @@
             return true
         }
 
-        if (doesTargetSimHaveEsimOperation && isRemovablePsimProfileEnabled) {
-            Log.d(TAG,
-                "eSIM operation and removable PSIM is enabled. DSDS condition satisfied."
-            )
-            return true
-        }
-
-        if (!doesTargetSimHaveEsimOperation && isEsimProfileEnabled) {
+        if (doesTargetSimHaveEsimOperation) {
+            if (UiccSlotRepository(telephonyManager).anyRemovablePhysicalSimEnabled()) {
+                Log.d(
+                    TAG,
+                    "eSIM operation and removable PSIM is enabled. DSDS condition satisfied."
+                )
+                return true
+            }
+        } else if (isEsimProfileEnabled) {
             Log.d(TAG,
                 "Removable SIM operation and eSIM profile is enabled. DSDS condition"
                         + " satisfied."
diff --git a/src/com/android/settings/network/SwitchToEuiccSubscriptionSidecar.java b/src/com/android/settings/network/SwitchToEuiccSubscriptionSidecar.java
index a26aa8a..8f6c32a 100644
--- a/src/com/android/settings/network/SwitchToEuiccSubscriptionSidecar.java
+++ b/src/com/android/settings/network/SwitchToEuiccSubscriptionSidecar.java
@@ -30,8 +30,6 @@
 import com.android.settings.SidecarFragment;
 import com.android.settings.network.telephony.EuiccOperationSidecar;
 
-import com.google.common.collect.ImmutableList;
-
 import java.util.Collection;
 import java.util.Comparator;
 import java.util.List;
@@ -205,10 +203,10 @@
     }
 
     private int getLogicalSlotIndex(int physicalSlotIndex, int portIndex) {
-        ImmutableList<UiccSlotInfo> slotInfos = UiccSlotUtil.getSlotInfos(mTelephonyManager);
-        if (slotInfos != null && physicalSlotIndex >= 0 && physicalSlotIndex < slotInfos.size()
-                && slotInfos.get(physicalSlotIndex) != null) {
-            for (UiccPortInfo portInfo : slotInfos.get(physicalSlotIndex).getPorts()) {
+        UiccSlotInfo[] slotInfos = mTelephonyManager.getUiccSlotsInfo();
+        if (slotInfos != null && physicalSlotIndex >= 0 && physicalSlotIndex < slotInfos.length
+                && slotInfos[physicalSlotIndex] != null) {
+            for (UiccPortInfo portInfo : slotInfos[physicalSlotIndex].getPorts()) {
                 if (portInfo.getPortIndex() == portIndex) {
                     return portInfo.getLogicalSlotIndex();
                 }
diff --git a/src/com/android/settings/network/UiccSlotUtil.java b/src/com/android/settings/network/UiccSlotUtil.java
index 5ae0a36..8a5a22c 100644
--- a/src/com/android/settings/network/UiccSlotUtil.java
+++ b/src/com/android/settings/network/UiccSlotUtil.java
@@ -33,8 +33,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settingslib.utils.ThreadUtils;
 
-import com.google.common.collect.ImmutableList;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -106,18 +104,6 @@
     }
 
     /**
-     * Returns an immutable list of all UICC slots. If TelephonyManager#getUiccSlotsInfo returns, it
-     * returns an empty list instead.
-     */
-    public static ImmutableList<UiccSlotInfo> getSlotInfos(TelephonyManager telMgr) {
-        UiccSlotInfo[] slotInfos = telMgr.getUiccSlotsInfo();
-        if (slotInfos == null) {
-            return ImmutableList.of();
-        }
-        return ImmutableList.copyOf(slotInfos);
-    }
-
-    /**
      * Switches to the removable slot. It waits for SIM_STATE_LOADED after switch. If slotId is
      * INVALID_PHYSICAL_SLOT_ID, the method will use the first detected inactive removable slot.
      *
@@ -219,14 +205,13 @@
      */
     public static int getEsimSlotId(Context context, int subId) {
         TelephonyManager telMgr = context.getSystemService(TelephonyManager.class);
-        List<UiccCardInfo> uiccCardInfos = telMgr.getUiccCardsInfo();
-        ImmutableList<UiccSlotInfo> slotInfos = UiccSlotUtil.getSlotInfos(telMgr);
         SubscriptionManager subscriptionManager = context.getSystemService(
                 SubscriptionManager.class).createForAllUserProfiles();
         SubscriptionInfo subInfo = SubscriptionUtil.getSubById(subscriptionManager, subId);
 
         // checking whether this is the removable esim. If it is, then return the removable slot id.
         if (subInfo != null && subInfo.isEmbedded()) {
+            List<UiccCardInfo> uiccCardInfos = telMgr.getUiccCardsInfo();
             for (UiccCardInfo uiccCardInfo : uiccCardInfos) {
                 if (uiccCardInfo.getCardId() == subInfo.getCardId()
                         && uiccCardInfo.getCardId() > TelephonyManager.UNSUPPORTED_CARD_ID
@@ -238,10 +223,12 @@
             }
         }
 
-        int firstEsimSlot = IntStream.range(0, slotInfos.size())
+        UiccSlotInfo[] slotInfos = telMgr.getUiccSlotsInfo();
+        if (slotInfos == null) return -1;
+        int firstEsimSlot = IntStream.range(0, slotInfos.length)
                 .filter(
                         index -> {
-                            UiccSlotInfo slotInfo = slotInfos.get(index);
+                            UiccSlotInfo slotInfo = slotInfos[index];
                             if (slotInfo == null) {
                                 return false;
                             }
@@ -421,41 +408,6 @@
                 .orElse(INVALID_LOGICAL_SLOT_ID);
     }
 
-    /**
-     * Return whether the removable psim is enabled.
-     *
-     * @param telMgr is a TelephonyManager.
-     * @return whether the removable psim is enabled.
-     */
-    public static boolean isRemovableSimEnabled(TelephonyManager telMgr) {
-        if (telMgr == null) {
-            return false;
-        }
-        List<UiccSlotInfo> slotInfos = UiccSlotUtil.getSlotInfos(telMgr);
-        return isRemovableSimEnabled(slotInfos);
-    }
-
-    /**
-     * Return whether the removable psim is enabled.
-     *
-     * @param slotInfos is a List of UiccSlotInfo.
-     * @return whether the removable psim is enabled.
-     */
-    public static boolean isRemovableSimEnabled(List<UiccSlotInfo> slotInfos) {
-        boolean isRemovableSimEnabled =
-                slotInfos.stream()
-                        .anyMatch(
-                                slot -> slot != null
-                                        && slot.isRemovable()
-                                        && !slot.getIsEuicc()
-                                        && slot.getPorts().stream()
-                                                .anyMatch(port -> port.isActive())
-                                        && slot.getCardStateInfo()
-                                        == UiccSlotInfo.CARD_STATE_INFO_PRESENT);
-        Log.i(TAG, "isRemovableSimEnabled: " + isRemovableSimEnabled);
-        return isRemovableSimEnabled;
-    }
-
     private static boolean isMultipleEnabledProfilesSupported(TelephonyManager telMgr) {
         List<UiccCardInfo> cardInfos = telMgr.getUiccCardsInfo();
         if (cardInfos == null) {
diff --git a/src/com/android/settings/network/ims/ImsQueryProvisioningStat.java b/src/com/android/settings/network/ims/ImsQueryProvisioningStat.java
index 44c4519..a43fda0 100644
--- a/src/com/android/settings/network/ims/ImsQueryProvisioningStat.java
+++ b/src/com/android/settings/network/ims/ImsQueryProvisioningStat.java
@@ -56,7 +56,7 @@
             final ProvisioningManager privisionManager =
                     ProvisioningManager.createForSubscriptionId(mSubId);
             return privisionManager.getProvisioningStatusForCapability(mCapability, mTech);
-        } catch (IllegalArgumentException exception) {
+        } catch (UnsupportedOperationException exception) {
             Log.w(LOG_TAG, "fail to get Provisioning stat. subId=" + mSubId, exception);
         }
         return false;
diff --git a/src/com/android/settings/network/telephony/CarrierConfigRepository.kt b/src/com/android/settings/network/telephony/CarrierConfigRepository.kt
index 3ec529d..3f5c06e 100644
--- a/src/com/android/settings/network/telephony/CarrierConfigRepository.kt
+++ b/src/com/android/settings/network/telephony/CarrierConfigRepository.kt
@@ -199,7 +199,7 @@
         }
 
         @VisibleForTesting
-        fun setStringForTest(subId: Int, key: String, value: String) {
+        fun setStringForTest(subId: Int, key: String, value: String?) {
             check(key.endsWith("_string")) { "String key should ends with _string" }
             getPerSubCache(subId)[key] = StringConfigValue(value)
         }
diff --git a/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceController.java b/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceController.java
deleted file mode 100644
index 575d19c..0000000
--- a/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceController.java
+++ /dev/null
@@ -1,56 +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.network.telephony;
-
-import android.content.Context;
-import android.os.PersistableBundle;
-import android.telephony.CarrierConfigManager;
-import android.telephony.SubscriptionManager;
-import android.text.TextUtils;
-
-import com.android.settings.core.BasePreferenceController;
-import com.android.settings.network.CarrierConfigCache;
-
-public class CarrierSettingsVersionPreferenceController extends BasePreferenceController {
-
-    private int mSubscriptionId;
-    private CarrierConfigCache mCarrierConfigCache;
-
-    public CarrierSettingsVersionPreferenceController(Context context, String preferenceKey) {
-        super(context, preferenceKey);
-        mCarrierConfigCache = CarrierConfigCache.getInstance(context);
-        mSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-    }
-
-    public void init(int subscriptionId) {
-        mSubscriptionId = subscriptionId;
-    }
-
-    @Override
-    public CharSequence getSummary() {
-        final PersistableBundle config = mCarrierConfigCache.getConfigForSubId(mSubscriptionId);
-        if (config == null) {
-            return null;
-        }
-        return config.getString(CarrierConfigManager.KEY_CARRIER_CONFIG_VERSION_STRING);
-    }
-
-    @Override
-    public int getAvailabilityStatus() {
-        return TextUtils.isEmpty(getSummary()) ? UNSUPPORTED_ON_DEVICE : AVAILABLE;
-    }
-}
diff --git a/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceController.kt b/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceController.kt
new file mode 100644
index 0000000..f949ab8
--- /dev/null
+++ b/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceController.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.telephony
+
+import android.content.Context
+import android.telephony.CarrierConfigManager
+import android.telephony.SubscriptionManager
+import com.android.settings.R
+import com.android.settings.core.BasePreferenceController
+import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem
+import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchResult
+
+class CarrierSettingsVersionPreferenceController(context: Context, preferenceKey: String) :
+    BasePreferenceController(context, preferenceKey) {
+
+    private var subId: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
+    private val searchItem = CarrierSettingsVersionSearchItem(context)
+
+    fun init(subId: Int) {
+        this.subId = subId
+    }
+
+    override fun getSummary() = searchItem.getSummary(subId)
+
+    override fun getAvailabilityStatus() =
+        if (searchItem.isAvailable(subId)) AVAILABLE else CONDITIONALLY_UNAVAILABLE
+
+    companion object {
+        class CarrierSettingsVersionSearchItem(private val context: Context) :
+            MobileNetworkSettingsSearchItem {
+            private val carrierConfigRepository = CarrierConfigRepository(context)
+
+            fun getSummary(subId: Int): String? =
+                carrierConfigRepository.getString(
+                    subId, CarrierConfigManager.KEY_CARRIER_CONFIG_VERSION_STRING)
+
+            fun isAvailable(subId: Int): Boolean = !getSummary(subId).isNullOrEmpty()
+
+            override fun getSearchResult(subId: Int): MobileNetworkSettingsSearchResult? {
+                if (!isAvailable(subId)) return null
+                return MobileNetworkSettingsSearchResult(
+                    key = "carrier_settings_version_key",
+                    title = context.getString(R.string.carrier_settings_version),
+                )
+            }
+        }
+    }
+}
diff --git a/src/com/android/settings/network/telephony/DataUsagePreferenceController.kt b/src/com/android/settings/network/telephony/DataUsagePreferenceController.kt
index d47a246..aa113b6 100644
--- a/src/com/android/settings/network/telephony/DataUsagePreferenceController.kt
+++ b/src/com/android/settings/network/telephony/DataUsagePreferenceController.kt
@@ -29,35 +29,32 @@
 import androidx.preference.Preference
 import androidx.preference.PreferenceScreen
 import com.android.settings.R
+import com.android.settings.core.BasePreferenceController
 import com.android.settings.datausage.DataUsageUtils
 import com.android.settings.datausage.lib.DataUsageFormatter.FormattedDataUsage
 import com.android.settings.datausage.lib.DataUsageLib
 import com.android.settings.datausage.lib.NetworkCycleDataRepository
 import com.android.settings.datausage.lib.NetworkStatsRepository.Companion.AllTimeRange
+import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem
+import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchResult
 import com.android.settingslib.spaprivileged.framework.compose.getPlaceholder
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 
-/**
- * Preference controller for "Data usage"
- */
+/** Preference controller for "Data usage" */
 class DataUsagePreferenceController(context: Context, key: String) :
-    TelephonyBasePreferenceController(context, key) {
+    BasePreferenceController(context, key) {
 
+    private var subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID
     private lateinit var preference: Preference
     private var networkTemplate: NetworkTemplate? = null
 
     fun init(subId: Int) {
-        mSubId = subId
+        this.subId = subId
     }
 
-    override fun getAvailabilityStatus(subId: Int): Int = when {
-        SubscriptionManager.isValidSubscriptionId(subId) &&
-            DataUsageUtils.hasMobileData(mContext) -> AVAILABLE
-
-        else -> AVAILABLE_UNSEARCHABLE
-    }
+    override fun getAvailabilityStatus() = AVAILABLE
 
     override fun displayPreference(screen: PreferenceScreen) {
         super.displayPreference(screen)
@@ -75,11 +72,12 @@
 
     override fun handlePreferenceTreeClick(preference: Preference): Boolean {
         if (preference.key != preferenceKey || networkTemplate == null) return false
-        val intent = Intent(Settings.ACTION_MOBILE_DATA_USAGE).apply {
-            setPackage(mContext.packageName)
-            putExtra(Settings.EXTRA_NETWORK_TEMPLATE, networkTemplate)
-            putExtra(Settings.EXTRA_SUB_ID, mSubId)
-        }
+        val intent =
+            Intent(Settings.ACTION_MOBILE_DATA_USAGE).apply {
+                setPackage(mContext.packageName)
+                putExtra(Settings.EXTRA_NETWORK_TEMPLATE, networkTemplate)
+                putExtra(Settings.EXTRA_SUB_ID, subId)
+            }
         mContext.startActivity(intent)
         return true
     }
@@ -93,13 +91,10 @@
         preference.summary = summary?.displayText
     }
 
-    private fun getNetworkTemplate(): NetworkTemplate? = when {
-        SubscriptionManager.isValidSubscriptionId(mSubId) -> {
-            DataUsageLib.getMobileTemplate(mContext, mSubId)
-        }
-
-        else -> null
-    }
+    private fun getNetworkTemplate(): NetworkTemplate? =
+        if (SubscriptionManager.isValidSubscriptionId(subId)) {
+            DataUsageLib.getMobileTemplate(mContext, subId)
+        } else null
 
     @VisibleForTesting
     fun createNetworkCycleDataRepository(): NetworkCycleDataRepository? =
@@ -118,4 +113,16 @@
         val allTimeUsage = repository.queryUsage(AllTimeRange)
         return allTimeUsage.getDataUsedString(mContext) to (allTimeUsage.usage > 0)
     }
+
+    companion object {
+        class DataUsageSearchItem(private val context: Context) : MobileNetworkSettingsSearchItem {
+            override fun getSearchResult(subId: Int): MobileNetworkSettingsSearchResult? {
+                if (!DataUsageUtils.hasMobileData(context)) return null
+                return MobileNetworkSettingsSearchResult(
+                    key = "data_usage_summary",
+                    title = context.getString(R.string.app_cellular_data_usage),
+                )
+            }
+        }
+    }
 }
diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt b/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt
index 58661f0..c63e7d2 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt
+++ b/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt
@@ -21,6 +21,8 @@
 import android.telephony.SubscriptionInfo
 import com.android.settings.R
 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
@@ -114,6 +116,8 @@
 
         fun createSearchItems(context: Context): List<MobileNetworkSettingsSearchItem> =
             listOf(
+                CarrierSettingsVersionSearchItem(context),
+                DataUsageSearchItem(context),
                 MmsMessageSearchItem(context),
                 NrAdvancedCallingSearchItem(context),
                 PreferredNetworkModeSearchItem(context),
diff --git a/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java b/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java
index 6f4d3c3..981e5bb 100644
--- a/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java
+++ b/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java
@@ -583,7 +583,7 @@
     }
 
     private boolean isRemovableSimEnabled() {
-        return UiccSlotUtil.isRemovableSimEnabled(mTelMgr);
+        return new UiccSlotRepository(mTelMgr).anyRemovablePhysicalSimEnabled();
     }
 
     private boolean isMultipleEnabledProfilesSupported() {
diff --git a/src/com/android/settings/network/telephony/UiccSlotRepository.kt b/src/com/android/settings/network/telephony/UiccSlotRepository.kt
new file mode 100644
index 0000000..3a83805
--- /dev/null
+++ b/src/com/android/settings/network/telephony/UiccSlotRepository.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.telephony.TelephonyManager
+import android.telephony.UiccSlotInfo
+import android.util.Log
+
+class UiccSlotRepository(private val telephonyManager: TelephonyManager?) {
+
+    /** Returns whether any removable physical sim is enabled. */
+    fun anyRemovablePhysicalSimEnabled(): Boolean {
+        val result =
+            telephonyManager?.uiccSlotsInfo?.any { uiccSlotInfo: UiccSlotInfo? ->
+                uiccSlotInfo.isRemovablePhysicalSimEnabled()
+            } ?: false
+        Log.i(TAG, "anyRemovablePhysicalSimEnabled: $result")
+        return result
+    }
+
+    private fun UiccSlotInfo?.isRemovablePhysicalSimEnabled(): Boolean {
+        return this != null &&
+            isRemovable &&
+            !isEuicc &&
+            ports.any { port -> port.isActive } &&
+            cardStateInfo == UiccSlotInfo.CARD_STATE_INFO_PRESENT
+    }
+
+    companion object {
+        private const val TAG = "UiccRepository"
+    }
+}
diff --git a/src/com/android/settings/network/telephony/ims/ImsFeatureRepository.kt b/src/com/android/settings/network/telephony/ims/ImsFeatureRepository.kt
new file mode 100644
index 0000000..ba33257
--- /dev/null
+++ b/src/com/android/settings/network/telephony/ims/ImsFeatureRepository.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.ims
+
+import android.content.Context
+import android.telephony.AccessNetworkConstants.TransportType
+import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability
+import android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech
+import com.android.settings.network.telephony.subscriptionsChangedFlow
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+
+/**
+ * A repository for the IMS feature.
+ *
+ * @throws IllegalArgumentException if the [subId] is invalid.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+class ImsFeatureRepository(
+    private val context: Context,
+    private val subId: Int,
+    private val provisioningRepository: ProvisioningRepository = ProvisioningRepository(context),
+    private val imsMmTelRepository: ImsMmTelRepository = ImsMmTelRepositoryImpl(context, subId)
+) {
+    /**
+     * A cold flow that determines the provisioning status for the specified IMS MmTel capability,
+     * and whether or not the requested MmTel capability is supported by the carrier on the
+     * specified network transport.
+     *
+     * @return true if the feature is provisioned and supported, false otherwise.
+     */
+    fun isReadyFlow(
+        @MmTelCapability capability: Int,
+        @ImsRegistrationTech tech: Int,
+        @TransportType transportType: Int,
+    ): Flow<Boolean> =
+        context.subscriptionsChangedFlow().flatMapLatest {
+            combine(
+                provisioningRepository.imsFeatureProvisionedFlow(subId, capability, tech),
+                imsMmTelRepository.isSupportedFlow(capability, transportType),
+            ) { imsFeatureProvisioned, isSupported ->
+                imsFeatureProvisioned && isSupported
+            }
+        }
+}
diff --git a/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt b/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt
index 9bc10e5..c5d1200 100644
--- a/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt
+++ b/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt
@@ -36,6 +36,7 @@
 import kotlinx.coroutines.flow.catch
 import kotlinx.coroutines.flow.conflate
 import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.suspendCancellableCoroutine
 import kotlinx.coroutines.withContext
 
@@ -47,6 +48,11 @@
 
     fun imsReadyFlow(): Flow<Boolean>
 
+    fun isSupportedFlow(
+        @MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int,
+        @AccessNetworkConstants.TransportType transportType: Int,
+    ): Flow<Boolean>
+
     suspend fun isSupported(
         @MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int,
         @AccessNetworkConstants.TransportType transportType: Int,
@@ -55,6 +61,11 @@
     suspend fun setCrossSimCallingEnabled(enabled: Boolean)
 }
 
+/**
+ * A repository for the IMS MMTel.
+ *
+ * @throws IllegalArgumentException if the [subId] is invalid.
+ */
 class ImsMmTelRepositoryImpl(
     context: Context,
     private val subId: Int,
@@ -126,8 +137,12 @@
         awaitClose { imsMmTelManager.unregisterImsStateCallback(callback) }
     }.catch { e ->
         Log.w(TAG, "[$subId] error while imsReadyFlow", e)
+        emit(false)
     }.conflate().flowOn(Dispatchers.Default)
 
+    override fun isSupportedFlow(capability: Int, transportType: Int): Flow<Boolean> =
+        imsReadyFlow().map { imsReady -> imsReady && isSupported(capability, transportType) }
+
     override suspend fun isSupported(
         @MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int,
         @AccessNetworkConstants.TransportType transportType: Int,
diff --git a/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt b/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt
index 04e687c..6af0559 100644
--- a/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt
+++ b/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt
@@ -20,24 +20,17 @@
 import android.telephony.AccessNetworkConstants
 import android.telephony.CarrierConfigManager
 import android.telephony.CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL
-import android.telephony.SubscriptionManager
 import android.telephony.ims.ImsMmTelManager.WiFiCallingMode
 import android.telephony.ims.feature.MmTelFeature
 import android.telephony.ims.stub.ImsRegistrationImplBase
 import androidx.lifecycle.LifecycleOwner
+import com.android.settings.network.telephony.ims.ImsFeatureRepository
 import com.android.settings.network.telephony.ims.ImsMmTelRepository
 import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl
-import com.android.settings.network.telephony.ims.ProvisioningRepository
-import com.android.settings.network.telephony.subscriptionsChangedFlow
 import com.android.settings.network.telephony.telephonyManager
 import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
 import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.withContext
 
 interface IWifiCallingRepository {
@@ -50,11 +43,11 @@
 constructor(
     private val context: Context,
     private val subId: Int,
-    private val imsMmTelRepository: ImsMmTelRepository = ImsMmTelRepositoryImpl(context, subId)
+    private val imsFeatureRepository: ImsFeatureRepository = ImsFeatureRepository(context, subId),
+    private val imsMmTelRepository: ImsMmTelRepository = ImsMmTelRepositoryImpl(context, subId),
 ) : IWifiCallingRepository {
     private val telephonyManager = context.telephonyManager(subId)
 
-    private val provisioningRepository = ProvisioningRepository(context)
     private val carrierConfigManager = context.getSystemService(CarrierConfigManager::class.java)!!
 
     @WiFiCallingMode
@@ -76,28 +69,12 @@
         wifiCallingReadyFlow().collectLatestWithLifecycle(lifecycleOwner, action = action)
     }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
-    fun wifiCallingReadyFlow(): Flow<Boolean> {
-        if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false)
-        return context.subscriptionsChangedFlow().flatMapLatest {
-            combine(
-                provisioningRepository.imsFeatureProvisionedFlow(
-                    subId = subId,
-                    capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
-                    tech = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
-                ),
-                isWifiCallingSupportedFlow(),
-            ) { imsFeatureProvisioned, isWifiCallingSupported ->
-                imsFeatureProvisioned && isWifiCallingSupported
-            }
-        }
-    }
-
-    private fun isWifiCallingSupportedFlow(): Flow<Boolean> {
-        return imsMmTelRepository.imsReadyFlow().map { imsReady ->
-            imsReady && isWifiCallingSupported()
-        }
-    }
+    fun wifiCallingReadyFlow(): Flow<Boolean> =
+        imsFeatureRepository.isReadyFlow(
+            capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+            tech = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
+            transportType = AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
+        )
 
     suspend fun isWifiCallingSupported(): Boolean = withContext(Dispatchers.Default) {
         imsMmTelRepository.isSupported(
diff --git a/src/com/android/settings/notification/modes/ZenModeFragment.java b/src/com/android/settings/notification/modes/ZenModeFragment.java
index 3777299..13aabd3 100644
--- a/src/com/android/settings/notification/modes/ZenModeFragment.java
+++ b/src/com/android/settings/notification/modes/ZenModeFragment.java
@@ -110,9 +110,10 @@
         if (mode == null || mode.getStatus() != DISABLED_BY_OTHER) {
             return false;
         }
+
+        mContext.startActivity(SetupInterstitialActivity.getIntent(mContext, mode));
         // don't come back here from the interstitial
         finish();
-        mContext.startActivity(SetupInterstitialActivity.getIntent(mContext, mode));
         return true;
     }
 
diff --git a/src/com/android/settings/notification/zen/ZenAccessSettings.java b/src/com/android/settings/notification/zen/ZenAccessSettings.java
index f765d6d..4b598db 100644
--- a/src/com/android/settings/notification/zen/ZenAccessSettings.java
+++ b/src/com/android/settings/notification/zen/ZenAccessSettings.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.notification.zen;
 
+import android.app.Flags;
 import android.app.NotificationManager;
 import android.app.settings.SettingsEnums;
 import android.content.Context;
@@ -68,6 +69,9 @@
         mContext = getActivity();
         mPkgMan = mContext.getPackageManager();
         mNoMan = mContext.getSystemService(NotificationManager.class);
+        requireActivity().setTitle(Flags.modesApi() && Flags.modesUi()
+                ? R.string.manage_zen_modes_access_title
+                : R.string.manage_zen_access_title);
         getSettingsLifecycle().addObserver(
                 new ZenAccessSettingObserverMixin(getContext(), this /* listener */));
     }
@@ -75,7 +79,9 @@
     @Override
     public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
-        setEmptyText(R.string.zen_access_empty_text);
+        setEmptyText(Flags.modesApi() && Flags.modesUi()
+                ? R.string.zen_modes_access_empty_text
+                : R.string.zen_access_empty_text);
     }
 
     @Override
@@ -139,7 +145,9 @@
             pref.setOnPreferenceClickListener(preference -> {
                 AppInfoBase.startAppInfoFragment(
                         ZenAccessDetails.class  /* fragment */,
-                        getString(R.string.manage_zen_access_title) /* titleRes */,
+                        getString(Flags.modesApi() && Flags.modesUi()
+                                ? R.string.manage_zen_modes_access_title
+                                : R.string.manage_zen_access_title),
                         pkg,
                         app.uid,
                         this /* source */,
@@ -154,7 +162,7 @@
 
     /**
      * @return the summary for the current state of whether the app associated with the given
-     * {@param packageName} is allowed to enter picture-in-picture.
+     * {@param packageName} is allowed to manage DND / Priority Modes.
      */
     private int getPreferenceSummary(String packageName) {
         final boolean enabled = ZenAccessController.hasAccess(getContext(), packageName);
diff --git a/src/com/android/settings/spa/search/SpaSearchLandingActivity.kt b/src/com/android/settings/spa/search/SpaSearchLandingActivity.kt
index cb5f745..2c0955b 100644
--- a/src/com/android/settings/spa/search/SpaSearchLandingActivity.kt
+++ b/src/com/android/settings/spa/search/SpaSearchLandingActivity.kt
@@ -27,9 +27,6 @@
 import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
 import com.android.settings.password.PasswordUtils
 import com.android.settings.spa.SpaDestination
-import com.android.settings.spa.SpaSearchLanding.SpaSearchLandingKey
-import com.google.protobuf.ByteString
-import com.google.protobuf.InvalidProtocolBufferException
 
 class SpaSearchLandingActivity : Activity() {
     override fun onCreate(savedInstanceState: Bundle?) {
@@ -48,22 +45,17 @@
     companion object {
         @VisibleForTesting
         fun tryLaunch(context: Context, keyString: String) {
-            val key =
-                try {
-                    SpaSearchLandingKey.parseFrom(ByteString.copyFromUtf8(keyString))
-                } catch (e: InvalidProtocolBufferException) {
-                    Log.w(TAG, "arg key ($keyString) invalid", e)
-                    return
-                }
-
+            val key = decodeToSpaSearchLandingKey(keyString) ?: return
             if (key.hasSpaPage()) {
                 val destination = key.spaPage.destination
                 if (destination.isNotEmpty()) {
+                    Log.d(TAG, "Launch SPA search result: ${key.spaPage}")
                     SpaDestination(destination = destination, highlightMenuKey = null)
                         .startFromExportedActivity(context)
                 }
             }
             if (key.hasFragment()) {
+                Log.d(TAG, "Launch fragment search result: ${key.fragment}")
                 val arguments =
                     Bundle().apply {
                         key.fragment.argumentsMap.forEach { (k, v) ->
diff --git a/src/com/android/settings/spa/search/SpaSearchLandingKeyExt.kt b/src/com/android/settings/spa/search/SpaSearchLandingKeyExt.kt
new file mode 100644
index 0000000..9540d9b
--- /dev/null
+++ b/src/com/android/settings/spa/search/SpaSearchLandingKeyExt.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.spa.search
+
+import android.util.Base64
+import android.util.Log
+import com.android.settings.spa.SpaSearchLanding.SpaSearchLandingKey
+
+private const val TAG = "SpaSearchLandingKeyExt"
+
+fun SpaSearchLandingKey.encodeToString(): String =
+    Base64.encodeToString(toByteArray(), Base64.DEFAULT)
+
+fun decodeToSpaSearchLandingKey(input: String): SpaSearchLandingKey? =
+    try {
+        SpaSearchLandingKey.parseFrom(Base64.decode(input, Base64.DEFAULT))
+    } catch (e: Exception) {
+        Log.w(TAG, "SpaSearchLandingKey ($input) invalid", e)
+        null
+    }
diff --git a/src/com/android/settings/spa/search/SpaSearchRepository.kt b/src/com/android/settings/spa/search/SpaSearchRepository.kt
index e5334dd..b1003ae 100644
--- a/src/com/android/settings/spa/search/SpaSearchRepository.kt
+++ b/src/com/android/settings/spa/search/SpaSearchRepository.kt
@@ -96,7 +96,7 @@
             keywords: String? = null,
         ) =
             SearchIndexableRaw(context).apply {
-                key = spaSearchLandingKey.toByteString().toStringUtf8()
+                key = spaSearchLandingKey.encodeToString()
                 title = itemTitle
                 this.keywords = keywords
                 intentAction = SEARCH_LANDING_ACTION
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragmentTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragmentTest.java
index 19d0edd..c84d42c 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragmentTest.java
@@ -50,6 +50,7 @@
 import androidx.preference.PreferenceScreen;
 
 import com.android.settings.R;
+import com.android.settings.bluetooth.ui.model.FragmentTypeModel;
 import com.android.settings.bluetooth.ui.view.DeviceDetailsFragmentFormatter;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -117,7 +118,9 @@
         FakeFeatureFactory fakeFeatureFactory = FakeFeatureFactory.setupForTest();
         when(fakeFeatureFactory.mBluetoothFeatureProvider.getDeviceDetailsFragmentFormatter(any(),
                 any(), any(), eq(mCachedDevice))).thenReturn(mFormatter);
-        when(mFormatter.getVisiblePreferenceKeysForMainPage()).thenReturn(null);
+        when(mFormatter.getVisiblePreferenceKeys(
+                        FragmentTypeModel.DeviceDetailsMainFragment.INSTANCE))
+                .thenReturn(null);
 
         mFragment = setupFragment();
         mFragment.onAttach(mContext);
diff --git a/tests/robotests/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatterTest.kt b/tests/robotests/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatterTest.kt
index 609d767..251b814 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatterTest.kt
+++ b/tests/robotests/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatterTest.kt
@@ -26,6 +26,7 @@
 import androidx.preference.PreferenceScreen
 import androidx.test.core.app.ApplicationProvider
 import com.android.settings.bluetooth.domain.interactor.SpatialAudioInteractor
+import com.android.settings.bluetooth.ui.model.FragmentTypeModel
 import com.android.settings.dashboard.DashboardFragment
 import com.android.settings.testutils.FakeFeatureFactory
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
@@ -45,7 +46,6 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
 import org.mockito.Mockito.any
@@ -111,10 +111,9 @@
                             DeviceSettingConfigItemModel.BuiltinItem(
                                 DeviceSettingId.DEVICE_SETTING_ID_ACTION_BUTTONS, "action_buttons"),
                         ),
-                        listOf(),
-                        "footer"))
+                        listOf()))
 
-            val keys = underTest.getVisiblePreferenceKeysForMainPage()
+            val keys = underTest.getVisiblePreferenceKeys(FragmentTypeModel.DeviceDetailsMainFragment)
 
             assertThat(keys).containsExactly("bluetooth_device_header", "action_buttons")
         }
@@ -125,7 +124,7 @@
         testScope.runTest {
             `when`(repository.getDeviceSettingsConfig(cachedDevice)).thenReturn(null)
 
-            val keys = underTest.getVisiblePreferenceKeysForMainPage()
+            val keys = underTest.getVisiblePreferenceKeys(FragmentTypeModel.DeviceDetailsMainFragment)
 
             assertThat(keys).isNull()
         }
@@ -136,9 +135,9 @@
         testScope.runTest {
             `when`(repository.getDeviceSettingsConfig(cachedDevice)).thenReturn(null)
 
-            underTest.updateLayout()
+            underTest.updateLayout(FragmentTypeModel.DeviceDetailsMainFragment)
 
-            assertThat(getDisplayedPreferences().map { it.key })
+            assertThat(getDisplayedPreferences().mapNotNull { it.key })
                 .containsExactly("bluetooth_device_header", "action_buttons", "keyboard_settings")
         }
     }
@@ -157,12 +156,11 @@
                                 DeviceSettingId.DEVICE_SETTING_ID_KEYBOARD_SETTINGS,
                                 "keyboard_settings"),
                         ),
-                        listOf(),
-                        "footer"))
+                        listOf()))
 
-            underTest.updateLayout()
+            underTest.updateLayout(FragmentTypeModel.DeviceDetailsMainFragment)
 
-            assertThat(getDisplayedPreferences().map { it.key })
+            assertThat(getDisplayedPreferences().mapNotNull { it.key })
                 .containsExactly("bluetooth_device_header", "keyboard_settings")
         }
     }
@@ -183,8 +181,7 @@
                                 DeviceSettingId.DEVICE_SETTING_ID_KEYBOARD_SETTINGS,
                                 "keyboard_settings"),
                         ),
-                        listOf(),
-                        "footer"))
+                        listOf()))
             `when`(repository.getDeviceSetting(cachedDevice, DeviceSettingId.DEVICE_SETTING_ID_ANC))
                 .thenReturn(
                     flowOf(
@@ -209,9 +206,9 @@
                             isAllowedChangingState = true,
                             updateState = {})))
 
-            underTest.updateLayout()
+            underTest.updateLayout(FragmentTypeModel.DeviceDetailsMainFragment)
 
-            assertThat(getDisplayedPreferences().map { it.key })
+            assertThat(getDisplayedPreferences().mapNotNull { it.key })
                 .containsExactly(
                     "bluetooth_device_header",
                     "DEVICE_SETTING_${DeviceSettingId.DEVICE_SETTING_ID_ANC}",
diff --git a/tests/robotests/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModelTest.kt b/tests/robotests/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModelTest.kt
index a1fadb8..378f363 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModelTest.kt
+++ b/tests/robotests/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModelTest.kt
@@ -16,12 +16,14 @@
 
 package com.android.settings.bluetooth.ui.viewmodel
 
+import android.app.Application
 import android.bluetooth.BluetoothAdapter
-import android.content.Context
 import android.graphics.Bitmap
 import androidx.test.core.app.ApplicationProvider
 import com.android.settings.bluetooth.domain.interactor.SpatialAudioInteractor
 import com.android.settings.bluetooth.ui.layout.DeviceSettingLayout
+import com.android.settings.bluetooth.ui.model.DeviceSettingPreferenceModel
+import com.android.settings.bluetooth.ui.model.FragmentTypeModel
 import com.android.settings.testutils.FakeFeatureFactory
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
 import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId
@@ -44,8 +46,6 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
@@ -73,26 +73,23 @@
 
     @Before
     fun setUp() {
-        val context = ApplicationProvider.getApplicationContext<Context>()
+        val application = ApplicationProvider.getApplicationContext<Application>()
         featureFactory = FakeFeatureFactory.setupForTest()
-        `when`(
-                featureFactory.bluetoothFeatureProvider.getDeviceSettingRepository(
-                    eq(context), eq(bluetoothAdapter), any()))
-            .thenReturn(repository)
 
         underTest =
-            BluetoothDeviceDetailsViewModel(repository, spatialAudioInteractor, cachedDevice)
+            BluetoothDeviceDetailsViewModel(
+                application, repository, spatialAudioInteractor, cachedDevice)
     }
 
     @Test
-    fun getItems_returnConfigMainItems() {
+    fun getItems_returnConfigMainMainItems() {
         testScope.runTest {
             `when`(repository.getDeviceSettingsConfig(cachedDevice))
                 .thenReturn(
                     DeviceSettingConfigModel(
-                        listOf(BUILTIN_SETTING_ITEM_1, BUILDIN_SETTING_ITEM_2), listOf(), "footer"))
+                        listOf(BUILTIN_SETTING_ITEM_1, BUILDIN_SETTING_ITEM_2), listOf()))
 
-            val keys = underTest.getItems()
+            val keys = underTest.getItems(FragmentTypeModel.DeviceDetailsMainFragment)
 
             assertThat(keys).containsExactly(BUILTIN_SETTING_ITEM_1, BUILDIN_SETTING_ITEM_2)
         }
@@ -110,19 +107,18 @@
                             BUILTIN_SETTING_ITEM_1,
                             buildRemoteSettingItem(remoteSettingId1),
                         ),
-                        listOf(),
-                        "footer"))
+                        listOf()))
             `when`(repository.getDeviceSetting(cachedDevice, remoteSettingId1))
                 .thenReturn(flowOf(pref))
 
-            var deviceSetting: DeviceSettingModel? = null
+            var deviceSettingPreference: DeviceSettingPreferenceModel? = null
             underTest
                 .getDeviceSetting(cachedDevice, remoteSettingId1)
-                .onEach { deviceSetting = it }
+                .onEach { deviceSettingPreference = it }
                 .launchIn(testScope.backgroundScope)
             runCurrent()
 
-            assertThat(deviceSetting).isSameInstanceAs(pref)
+            assertThat(deviceSettingPreference?.id).isEqualTo(pref.id)
             verify(repository, times(1)).getDeviceSetting(cachedDevice, remoteSettingId1)
         }
     }
@@ -141,19 +137,18 @@
                             buildRemoteSettingItem(
                                 DeviceSettingId.DEVICE_SETTING_ID_SPATIAL_AUDIO_MULTI_TOGGLE),
                         ),
-                        listOf(),
-                        "footer"))
+                        listOf()))
             `when`(spatialAudioInteractor.getDeviceSetting(cachedDevice)).thenReturn(flowOf(pref))
 
-            var deviceSetting: DeviceSettingModel? = null
+            var deviceSettingPreference: DeviceSettingPreferenceModel? = null
             underTest
                 .getDeviceSetting(
                     cachedDevice, DeviceSettingId.DEVICE_SETTING_ID_SPATIAL_AUDIO_MULTI_TOGGLE)
-                .onEach { deviceSetting = it }
+                .onEach { deviceSettingPreference = it }
                 .launchIn(testScope.backgroundScope)
             runCurrent()
 
-            assertThat(deviceSetting).isSameInstanceAs(pref)
+            assertThat(deviceSettingPreference?.id).isEqualTo(pref.id)
             verify(spatialAudioInteractor, times(1)).getDeviceSetting(cachedDevice)
         }
     }
@@ -164,9 +159,9 @@
             `when`(repository.getDeviceSettingsConfig(cachedDevice))
                 .thenReturn(
                     DeviceSettingConfigModel(
-                        listOf(BUILTIN_SETTING_ITEM_1, BUILDIN_SETTING_ITEM_2), listOf(), "footer"))
+                        listOf(BUILTIN_SETTING_ITEM_1, BUILDIN_SETTING_ITEM_2), listOf()))
 
-            val layout = underTest.getLayout()!!
+            val layout = underTest.getLayout(FragmentTypeModel.DeviceDetailsMainFragment)!!
 
             assertThat(getLatestLayout(layout))
                 .isEqualTo(
@@ -191,8 +186,7 @@
                             buildRemoteSettingItem(remoteSettingId2),
                             buildRemoteSettingItem(remoteSettingId3),
                         ),
-                        listOf(),
-                        "footer"))
+                        listOf()))
             `when`(repository.getDeviceSetting(cachedDevice, remoteSettingId1))
                 .thenReturn(flowOf(buildMultiTogglePreference(remoteSettingId1)))
             `when`(repository.getDeviceSetting(cachedDevice, remoteSettingId2))
@@ -200,7 +194,7 @@
             `when`(repository.getDeviceSetting(cachedDevice, remoteSettingId3))
                 .thenReturn(flowOf(buildActionSwitchPreference(remoteSettingId3)))
 
-            val layout = underTest.getLayout()!!
+            val layout = underTest.getLayout(FragmentTypeModel.DeviceDetailsMainFragment)!!
 
             assertThat(getLatestLayout(layout))
                 .isEqualTo(
diff --git a/tests/robotests/src/com/android/settings/development/TouchpadVisualizerPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/TouchpadVisualizerPreferenceControllerTest.java
index 98c774e..826bae9 100644
--- a/tests/robotests/src/com/android/settings/development/TouchpadVisualizerPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/TouchpadVisualizerPreferenceControllerTest.java
@@ -22,22 +22,35 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
-import android.provider.Settings;
+import android.hardware.input.InputSettings;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 
 import androidx.preference.PreferenceScreen;
 import androidx.preference.SwitchPreference;
 
+import com.android.hardware.input.Flags;
+import com.android.settings.testutils.shadow.ShadowSystemSettings;
+
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
 
 @RunWith(RobolectricTestRunner.class)
+@Config(shadows = {
+        ShadowSystemSettings.class,
+})
 public class TouchpadVisualizerPreferenceControllerTest {
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     @Mock
     private PreferenceScreen mScreen;
     @Mock
@@ -57,55 +70,52 @@
     }
 
     @Test
+    @EnableFlags({Flags.FLAG_TOUCHPAD_VISUALIZER})
     public void updateState_touchpadVisualizerEnabled_shouldCheckedPreference() {
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.TOUCHPAD_VISUALIZER, ShowTapsPreferenceController.SETTING_VALUE_ON);
-
+        InputSettings.setTouchpadVisualizer(mContext, true);
         mController.updateState(mPreference);
 
         verify(mPreference).setChecked(true);
     }
 
     @Test
+    @EnableFlags({Flags.FLAG_TOUCHPAD_VISUALIZER})
     public void updateState_touchpadVisualizerDisabled_shouldUncheckedPreference() {
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.TOUCHPAD_VISUALIZER,
-                ShowTapsPreferenceController.SETTING_VALUE_OFF);
-
+        InputSettings.setTouchpadVisualizer(mContext, false);
         mController.updateState(mPreference);
 
         verify(mPreference).setChecked(false);
     }
 
     @Test
+    @EnableFlags({Flags.FLAG_TOUCHPAD_VISUALIZER})
     public void onPreferenceChange_preferenceChecked_shouldEnableTouchpadVisualizer() {
         mController.onPreferenceChange(mPreference, true /* new value */);
 
-        final int touchpadVisualizer = Settings.System.getInt(mContext.getContentResolver(),
-                Settings.System.TOUCHPAD_VISUALIZER, -1 /* default */);
+        final boolean touchpadVisualizer = InputSettings.useTouchpadVisualizer(mContext);
 
-        assertThat(touchpadVisualizer).isEqualTo(ShowTapsPreferenceController.SETTING_VALUE_ON);
+        assertThat(touchpadVisualizer).isTrue();
     }
 
     @Test
+    @EnableFlags({Flags.FLAG_TOUCHPAD_VISUALIZER})
     public void onPreferenceChange_preferenceUnchecked_shouldDisableTouchpadVisualizer() {
         mController.onPreferenceChange(mPreference, false /* new value */);
 
-        final int showTapsMode = Settings.System.getInt(mContext.getContentResolver(),
-                Settings.System.TOUCHPAD_VISUALIZER, -1 /* default */);
+        final boolean touchpadVisualizer = InputSettings.useTouchpadVisualizer(mContext);
 
-        assertThat(showTapsMode).isEqualTo(ShowTapsPreferenceController.SETTING_VALUE_OFF);
+        assertThat(touchpadVisualizer).isFalse();
     }
 
     @Test
+    @EnableFlags({Flags.FLAG_TOUCHPAD_VISUALIZER})
     public void onDeveloperOptionsSwitchDisabled_preferenceShouldBeEnabled() {
         mController.onDeveloperOptionsSwitchDisabled();
 
-        final int showTapsMode = Settings.System.getInt(mContext.getContentResolver(),
-                Settings.System.TOUCHPAD_VISUALIZER, -1 /* default */);
+        final boolean touchpadVisualizer = InputSettings.useTouchpadVisualizer(mContext);
 
-        assertThat(showTapsMode).isEqualTo(ShowTapsPreferenceController.SETTING_VALUE_OFF);
+        assertThat(touchpadVisualizer).isFalse();
         verify(mPreference).setEnabled(false);
         verify(mPreference).setChecked(false);
     }
-}
\ No newline at end of file
+}
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceControllerTest.kt
new file mode 100644
index 0000000..ed6c027
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceControllerTest.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.telephony
+
+import android.content.Context
+import android.telephony.CarrierConfigManager
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class CarrierSettingsVersionPreferenceControllerTest {
+
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    private val controller =
+        CarrierSettingsVersionPreferenceController(context, TEST_KEY).apply { init(SUB_ID) }
+
+    @Before
+    fun setUp() {
+        CarrierConfigRepository.resetForTest()
+    }
+
+    @Test
+    fun getSummary_nullConfig_noCrash() {
+        controller.getSummary()
+    }
+
+    @Test
+    fun getSummary_nullVersionString_returnNull() {
+        CarrierConfigRepository.setStringForTest(
+            SUB_ID, CarrierConfigManager.KEY_CARRIER_CONFIG_VERSION_STRING, null)
+
+        val summary = controller.getSummary()
+
+        assertThat(summary).isNull()
+    }
+
+    @Test
+    fun getSummary_hasVersionString_returnCorrectSummary() {
+        CarrierConfigRepository.setStringForTest(
+            SUB_ID, CarrierConfigManager.KEY_CARRIER_CONFIG_VERSION_STRING, "test_version_123")
+
+        val summary = controller.getSummary()
+
+        assertThat(summary).isEqualTo("test_version_123")
+    }
+
+    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/DataUsagePreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/DataUsagePreferenceControllerTest.kt
index 7124b6a..f4974e9 100644
--- a/tests/spa_unit/src/com/android/settings/network/telephony/DataUsagePreferenceControllerTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/DataUsagePreferenceControllerTest.kt
@@ -29,7 +29,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.dx.mockito.inline.extended.ExtendedMockito
 import com.android.settings.core.BasePreferenceController.AVAILABLE
-import com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE
 import com.android.settings.datausage.DataUsageUtils
 import com.android.settings.datausage.lib.DataUsageLib
 import com.android.settings.datausage.lib.NetworkCycleDataRepository
@@ -77,7 +76,6 @@
     @Before
     fun setUp() {
         mockSession = ExtendedMockito.mockitoSession()
-            .initMocks(this)
             .spyStatic(DataUsageUtils::class.java)
             .spyStatic(DataUsageLib::class.java)
             .strictness(Strictness.LENIENT)
@@ -101,19 +99,11 @@
     }
 
     @Test
-    fun getAvailabilityStatus_validSubId_returnAvailable() {
+    fun getAvailabilityStatus_returnAvailable() {
         assertThat(controller.availabilityStatus).isEqualTo(AVAILABLE)
     }
 
     @Test
-    fun getAvailabilityStatus_invalidSubId_returnUnsearchable() {
-        val availabilityStatus =
-            controller.getAvailabilityStatus(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
-
-        assertThat(availabilityStatus).isEqualTo(AVAILABLE_UNSEARCHABLE)
-    }
-
-    @Test
     fun handlePreferenceTreeClick_startActivity() = runBlocking {
         val usageData = NetworkUsageData(START_TIME, END_TIME, 1L)
         repository.stub {
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndexTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndexTest.kt
index bf51208..ad50433 100644
--- a/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndexTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndexTest.kt
@@ -30,8 +30,8 @@
 import com.android.settings.spa.SpaSearchLanding.SpaSearchLandingFragment
 import com.android.settings.spa.SpaSearchLanding.SpaSearchLandingKey
 import com.android.settings.spa.search.SpaSearchLandingActivity
+import com.android.settings.spa.search.decodeToSpaSearchLandingKey
 import com.google.common.truth.Truth.assertThat
-import com.google.protobuf.ByteString
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -104,7 +104,7 @@
             searchIndexableData.searchIndexProvider.getDynamicRawDataToIndex(context, true)
         assertThat(dynamicRawDataToIndex).hasSize(1)
         val rawData = dynamicRawDataToIndex[0]
-        val key = SpaSearchLandingKey.parseFrom(ByteString.copyFromUtf8(rawData.key))
+        val key = decodeToSpaSearchLandingKey(rawData.key)
         assertThat(key)
             .isEqualTo(
                 SpaSearchLandingKey.newBuilder()
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/UiccSlotRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/UiccSlotRepositoryTest.kt
new file mode 100644
index 0000000..96aa151
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/UiccSlotRepositoryTest.kt
@@ -0,0 +1,262 @@
+/*
+ * 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.telephony.TelephonyManager
+import android.telephony.UiccPortInfo
+import android.telephony.UiccSlotInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+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 UiccSlotRepositoryTest {
+
+    private val mockTelephonyManager = mock<TelephonyManager>()
+
+    private val repository = UiccSlotRepository(mockTelephonyManager)
+
+    @Test
+    fun anyRemovablePhysicalSimEnabled_oneSimSlotDeviceActiveEsim_returnsFalse() {
+        mockTelephonyManager.stub {
+            on { uiccSlotsInfo } doReturn
+                arrayOf(
+                    createUiccSlotInfo(
+                        isEuicc = true, isRemovable = false, logicalSlotIdx = 1, isActive = true),
+                )
+        }
+
+        val result = repository.anyRemovablePhysicalSimEnabled()
+
+        assertThat(result).isFalse()
+    }
+
+    @Test
+    fun anyRemovablePhysicalSimEnabled_activeRemovableEsimAndInactivePsim_returnsFalse() {
+        mockTelephonyManager.stub {
+            on { uiccSlotsInfo } doReturn
+                arrayOf(
+                    createUiccSlotInfo(
+                        isEuicc = true, isRemovable = true, logicalSlotIdx = 0, isActive = true),
+                    createUiccSlotInfo(
+                        isEuicc = false, isRemovable = true, logicalSlotIdx = -1, isActive = false),
+                )
+        }
+
+        val result = repository.anyRemovablePhysicalSimEnabled()
+
+        assertThat(result).isFalse()
+    }
+
+    @Test
+    fun anyRemovablePhysicalSimEnabled_activeRemovableEsimAndActivePsim_returnsTrue() {
+        mockTelephonyManager.stub {
+            on { uiccSlotsInfo } doReturn
+                arrayOf(
+                    createUiccSlotInfo(
+                        isEuicc = false, isRemovable = true, logicalSlotIdx = 0, isActive = true),
+                    createUiccSlotInfo(
+                        isEuicc = true, isRemovable = true, logicalSlotIdx = 1, isActive = true),
+                )
+        }
+
+        val result = repository.anyRemovablePhysicalSimEnabled()
+
+        assertThat(result).isTrue()
+    }
+
+    @Test
+    fun anyRemovablePhysicalSimEnabled_inactiveRemovableEsimAndActivePsim_returnsTrue() {
+        mockTelephonyManager.stub {
+            on { uiccSlotsInfo } doReturn
+                arrayOf(
+                    createUiccSlotInfo(
+                        isEuicc = true, isRemovable = true, logicalSlotIdx = -1, isActive = false),
+                    createUiccSlotInfo(
+                        isEuicc = false, isRemovable = true, logicalSlotIdx = 0, isActive = true),
+                )
+        }
+
+        val result = repository.anyRemovablePhysicalSimEnabled()
+
+        assertThat(result).isTrue()
+    }
+
+    @Test
+    fun anyRemovablePhysicalSimEnabled_twoActiveRemovableEsimsAndInactivePsim_returnsFalse() {
+        mockTelephonyManager.stub {
+            on { uiccSlotsInfo } doReturn
+                arrayOf(
+                    createUiccSlotInfoForRemovableEsimMep(
+                        logicalSlotIdx1 = 0,
+                        isActiveEsim1 = true,
+                        logicalSlotIdx2 = 1,
+                        isActiveEsim2 = true,
+                    ),
+                    createUiccSlotInfo(
+                        isEuicc = false, isRemovable = true, logicalSlotIdx = -1, isActive = false),
+                )
+        }
+
+        val result = repository.anyRemovablePhysicalSimEnabled()
+
+        assertThat(result).isFalse()
+    }
+
+    @Test
+    fun anyRemovablePhysicalSimEnabled_oneActiveOneInactiveRemovableEsimActivePsim_returnsTrue() {
+        mockTelephonyManager.stub {
+            on { uiccSlotsInfo } doReturn
+                arrayOf(
+                    createUiccSlotInfoForRemovableEsimMep(
+                        logicalSlotIdx1 = 1,
+                        isActiveEsim1 = true,
+                        logicalSlotIdx2 = -1,
+                        isActiveEsim2 = false,
+                    ),
+                    createUiccSlotInfo(
+                        isEuicc = false, isRemovable = true, logicalSlotIdx = 0, isActive = true),
+                )
+        }
+
+        val result = repository.anyRemovablePhysicalSimEnabled()
+
+        assertThat(result).isTrue()
+    }
+
+    @Test
+    fun anyRemovablePhysicalSimEnabled_activePsim_returnsTrue() {
+        mockTelephonyManager.stub {
+            on { uiccSlotsInfo } doReturn
+                arrayOf(
+                    createUiccSlotInfo(
+                        isEuicc = false, isRemovable = true, logicalSlotIdx = 0, isActive = true),
+                )
+        }
+
+        val result = repository.anyRemovablePhysicalSimEnabled()
+
+        assertThat(result).isTrue()
+    }
+
+    @Test
+    fun anyRemovablePhysicalSimEnabled_inactivePsim_returnsFalse() {
+        mockTelephonyManager.stub {
+            on { uiccSlotsInfo } doReturn
+                arrayOf(
+                    createUiccSlotInfo(
+                        isEuicc = false, isRemovable = true, logicalSlotIdx = -1, isActive = false),
+                )
+        }
+
+        val result = repository.anyRemovablePhysicalSimEnabled()
+
+        assertThat(result).isFalse()
+    }
+
+    @Test
+    fun anyRemovablePhysicalSimEnabled_activeEsimAndActivePsim_returnsTrue() {
+        mockTelephonyManager.stub {
+            on { uiccSlotsInfo } doReturn
+                arrayOf(
+                    createUiccSlotInfo(
+                        isEuicc = false, isRemovable = true, logicalSlotIdx = 0, isActive = true),
+                    createUiccSlotInfo(
+                        isEuicc = true, isRemovable = false, logicalSlotIdx = 1, isActive = true),
+                )
+        }
+
+        val result = repository.anyRemovablePhysicalSimEnabled()
+
+        assertThat(result).isTrue()
+    }
+
+    @Test
+    fun anyRemovablePhysicalSimEnabled_activeEsimAndInactivePsim_returnsFalse() {
+        mockTelephonyManager.stub {
+            on { uiccSlotsInfo } doReturn
+                arrayOf(
+                    createUiccSlotInfo(
+                        isEuicc = false, isRemovable = true, logicalSlotIdx = 0, isActive = false),
+                    createUiccSlotInfo(
+                        isEuicc = true, isRemovable = false, logicalSlotIdx = 1, isActive = true),
+                )
+        }
+
+        val result = repository.anyRemovablePhysicalSimEnabled()
+
+        assertThat(result).isFalse()
+    }
+
+    @Test
+    fun anyRemovablePhysicalSimEnabled_uiccSlotInfoIsNull_returnsFalse() {
+        mockTelephonyManager.stub { on { uiccSlotsInfo } doReturn arrayOf(null) }
+
+        val result = repository.anyRemovablePhysicalSimEnabled()
+
+        assertThat(result).isFalse()
+    }
+
+    private companion object {
+        fun createUiccSlotInfo(
+            isEuicc: Boolean,
+            isRemovable: Boolean,
+            logicalSlotIdx: Int,
+            isActive: Boolean
+        ) =
+            UiccSlotInfo(
+                isEuicc,
+                /* cardId = */ "123",
+                /* cardStateInfo = */ UiccSlotInfo.CARD_STATE_INFO_PRESENT,
+                /* isExtendedApduSupported = */ true,
+                isRemovable,
+                /* portList = */ listOf(
+                    UiccPortInfo(/* iccId= */ "", /* portIndex= */ 0, logicalSlotIdx, isActive),
+                ),
+            )
+
+        fun createUiccSlotInfoForRemovableEsimMep(
+            logicalSlotIdx1: Int,
+            isActiveEsim1: Boolean,
+            logicalSlotIdx2: Int,
+            isActiveEsim2: Boolean,
+        ) =
+            UiccSlotInfo(
+                /* isEuicc = */ true,
+                /* cardId = */ "123",
+                /* cardStateInfo = */ UiccSlotInfo.CARD_STATE_INFO_PRESENT,
+                /* isExtendedApduSupported = */ true,
+                /* isRemovable = */ true,
+                /* portList = */ listOf(
+                    UiccPortInfo(
+                        /* iccId = */ "",
+                        /* portIndex = */ 0,
+                        /* logicalSlotIndex = */ logicalSlotIdx1,
+                        /* isActive = */ isActiveEsim1),
+                    UiccPortInfo(
+                        /* iccId = */ "",
+                        /* portIndex = */ 1,
+                        /* logicalSlotIndex = */ logicalSlotIdx2,
+                        /* isActive = */ isActiveEsim2),
+                ),
+            )
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsFeatureRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsFeatureRepositoryTest.kt
new file mode 100644
index 0000000..3f72b2c
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsFeatureRepositoryTest.kt
@@ -0,0 +1,108 @@
+/*
+ * 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.ims
+
+import android.content.Context
+import android.telephony.AccessNetworkConstants
+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.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
+
+@RunWith(AndroidJUnit4::class)
+class ImsFeatureRepositoryTest {
+
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    private val mockProvisioningRepository = mock<ProvisioningRepository>()
+    private val mockImsMmTelRepository = mock<ImsMmTelRepository>()
+
+    @Test
+    fun isReadyFlow_notProvisioned_returnFalse() = runBlocking {
+        mockProvisioningRepository.stub {
+            onBlocking { imsFeatureProvisionedFlow(SUB_ID, CAPABILITY, TECH) } doReturn
+                flowOf(false)
+        }
+
+        val repository =
+            ImsFeatureRepository(
+                context = context,
+                subId = SUB_ID,
+                provisioningRepository = mockProvisioningRepository,
+            )
+
+        val isReady = repository.isReadyFlow(CAPABILITY, TECH, TRANSPORT_TYPE).first()
+
+        assertThat(isReady).isFalse()
+    }
+
+    @Test
+    fun isReadyFlow_notSupported_returnFalse() = runBlocking {
+        mockImsMmTelRepository.stub {
+            onBlocking { isSupportedFlow(CAPABILITY, TRANSPORT_TYPE) } doReturn flowOf(false)
+        }
+
+        val repository =
+            ImsFeatureRepository(
+                context = context,
+                subId = SUB_ID,
+                imsMmTelRepository = mockImsMmTelRepository,
+            )
+
+        val isReady = repository.isReadyFlow(CAPABILITY, TECH, TRANSPORT_TYPE).first()
+
+        assertThat(isReady).isFalse()
+    }
+
+    @Test
+    fun isReadyFlow_provisionedAndSupported_returnFalse() = runBlocking {
+        mockProvisioningRepository.stub {
+            onBlocking { imsFeatureProvisionedFlow(SUB_ID, CAPABILITY, TECH) } doReturn flowOf(true)
+        }
+        mockImsMmTelRepository.stub {
+            onBlocking { isSupportedFlow(CAPABILITY, TRANSPORT_TYPE) } doReturn flowOf(true)
+        }
+
+        val repository =
+            ImsFeatureRepository(
+                context = context,
+                subId = SUB_ID,
+                provisioningRepository = mockProvisioningRepository,
+                imsMmTelRepository = mockImsMmTelRepository,
+            )
+
+        val isReady = repository.isReadyFlow(CAPABILITY, TECH, TRANSPORT_TYPE).first()
+
+        assertThat(isReady).isTrue()
+    }
+
+    private companion object {
+        const val SUB_ID = 10
+        const val CAPABILITY = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE
+        const val TECH = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN
+        const val TRANSPORT_TYPE = AccessNetworkConstants.TRANSPORT_TYPE_WLAN
+    }
+}
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 0144f66..f0a23eb 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
@@ -55,7 +55,8 @@
         on { getWiFiCallingMode(any()) } doReturn ImsMmTelManager.WIFI_MODE_UNKNOWN
     }
 
-    private val repository = WifiCallingRepository(context, SUB_ID, mockImsMmTelRepository)
+    private val repository =
+        WifiCallingRepository(context, SUB_ID, imsMmTelRepository = mockImsMmTelRepository)
 
     @Test
     fun getWiFiCallingMode_roamingAndNotUseWfcHomeModeForRoaming_returnRoamingSetting() {
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/InstantAppDomainsPreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/InstantAppDomainsPreferenceTest.kt
index e5f8fb0..a952763 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/InstantAppDomainsPreferenceTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/InstantAppDomainsPreferenceTest.kt
@@ -22,9 +22,9 @@
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.assertIsEnabled
 import androidx.compose.ui.test.assertIsNotDisplayed
 import androidx.compose.ui.test.hasText
+import androidx.compose.ui.test.isEnabled
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithText
 import androidx.compose.ui.test.onRoot
@@ -96,10 +96,9 @@
     fun title_displayed() {
         setContent()
 
-        composeTestRule
-            .onNodeWithText(context.getString(R.string.app_launch_supported_domain_urls_title))
-            .assertIsDisplayed()
-            .assertIsEnabled()
+        composeTestRule.waitUntilExists(
+            hasText(context.getString(R.string.app_launch_supported_domain_urls_title)) and
+                isEnabled())
     }
 
     @Test
diff --git a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt
index ad2ba55..1910153 100644
--- a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt
@@ -19,23 +19,17 @@
 import android.content.Context
 import android.telephony.SubscriptionInfo
 import android.telephony.SubscriptionManager
-import android.view.KeyEvent.ACTION_DOWN
-import android.view.KeyEvent.KEYCODE_FORWARD_DEL
 import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.ui.input.key.KeyEvent
-import androidx.compose.ui.input.key.NativeKeyEvent
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.platform.LocalLifecycleOwner
 import androidx.compose.ui.semantics.SemanticsProperties
 import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.assertIsEnabled
 import androidx.compose.ui.test.assertIsNotEnabled
 import androidx.compose.ui.test.hasText
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.onNodeWithText
 import androidx.compose.ui.test.performClick
-import androidx.compose.ui.test.performKeyPress
 import androidx.compose.ui.test.performTextClearance
 import androidx.compose.ui.test.performTextInput
 import androidx.lifecycle.testing.TestLifecycleOwner
@@ -80,7 +74,6 @@
         on { targetSubInfo }.doReturn(null)
         on { availableSubInfoList }.doReturn(listOf())
         on { activeSubInfoList }.doReturn(listOf())
-        on { slotInfoList }.doReturn(listOf())
         on { uiccCardInfoList }.doReturn(listOf())
 
         on { targetPrimarySimCalls }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
diff --git a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPageProviderTest.kt
index 1395ed4..fcd3e24 100644
--- a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPageProviderTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPageProviderTest.kt
@@ -45,7 +45,6 @@
         on { targetSubInfo }.doReturn(null)
         on { availableSubInfoList }.doReturn(listOf())
         on { activeSubInfoList }.doReturn(listOf())
-        on { slotInfoList }.doReturn(listOf())
         on { uiccCardInfoList }.doReturn(listOf())
 
         on { targetPrimarySimCalls }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
diff --git a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPrimarySimTest.kt b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPrimarySimTest.kt
index d9c762d..ef86ac5 100644
--- a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPrimarySimTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPrimarySimTest.kt
@@ -46,7 +46,6 @@
         on { targetSubInfo }.doReturn(null)
         on { availableSubInfoList }.doReturn(listOf())
         on { activeSubInfoList }.doReturn(listOf())
-        on { slotInfoList }.doReturn(listOf())
         on { uiccCardInfoList }.doReturn(listOf())
 
         on { targetPrimarySimCalls }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
diff --git a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingSelectSimTest.kt b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingSelectSimTest.kt
index 5b7778e..385bc42 100644
--- a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingSelectSimTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingSelectSimTest.kt
@@ -69,7 +69,6 @@
         on { targetSubInfo }.doReturn(null)
         on { availableSubInfoList }.doReturn(listOf())
         on { activeSubInfoList }.doReturn(listOf())
-        on { slotInfoList }.doReturn(listOf())
         on { uiccCardInfoList }.doReturn(listOf())
 
         on { targetPrimarySimCalls }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
diff --git a/tests/spa_unit/src/com/android/settings/spa/search/SpaSearchLandingActivityTest.kt b/tests/spa_unit/src/com/android/settings/spa/search/SpaSearchLandingActivityTest.kt
index 7410bb4..6fb4b84 100644
--- a/tests/spa_unit/src/com/android/settings/spa/search/SpaSearchLandingActivityTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/search/SpaSearchLandingActivityTest.kt
@@ -52,7 +52,7 @@
                 .setSpaPage(SpaSearchLandingSpaPage.newBuilder().setDestination(DESTINATION))
                 .build()
 
-        SpaSearchLandingActivity.tryLaunch(context, key.toByteString().toStringUtf8())
+        SpaSearchLandingActivity.tryLaunch(context, key.encodeToString())
 
         verify(context).startActivity(argThat { getStringExtra(KEY_DESTINATION) == DESTINATION })
     }
@@ -70,7 +70,7 @@
                             BundleValue.newBuilder().setIntValue(ARGUMENT_VALUE).build()))
                 .build()
 
-        SpaSearchLandingActivity.tryLaunch(context, key.toByteString().toStringUtf8())
+        SpaSearchLandingActivity.tryLaunch(context, key.encodeToString())
 
         val intent = argumentCaptor<Intent> { verify(context).startActivity(capture()) }.firstValue
         assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
diff --git a/tests/spa_unit/src/com/android/settings/spa/search/SpaSearchLandingKeyExtTest.kt b/tests/spa_unit/src/com/android/settings/spa/search/SpaSearchLandingKeyExtTest.kt
new file mode 100644
index 0000000..f4a9ce6
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/search/SpaSearchLandingKeyExtTest.kt
@@ -0,0 +1,57 @@
+/*
+ * 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.spa.search
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.spa.SpaSearchLanding.BundleValue
+import com.android.settings.spa.SpaSearchLanding.SpaSearchLandingFragment
+import com.android.settings.spa.SpaSearchLanding.SpaSearchLandingKey
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SpaSearchLandingKeyExtTest {
+
+    @Test
+    fun encodeToString_thenDecode_shouldDecodeCorrectly() {
+        val encoded = KEY.encodeToString()
+
+        val decoded = decodeToSpaSearchLandingKey(encoded)
+
+        assertThat(decoded).isEqualTo(KEY)
+    }
+
+    @Test
+    fun decodeToSpaSearchLandingKey_badString_shouldReturnNull() {
+        val decoded = decodeToSpaSearchLandingKey("bad")
+
+        assertThat(decoded).isNull()
+    }
+
+    private companion object {
+        val KEY: SpaSearchLandingKey =
+            SpaSearchLandingKey.newBuilder()
+                .setFragment(
+                    SpaSearchLandingFragment.newBuilder()
+                        .setFragmentName("Destination")
+                        .setPreferenceKey("preference_key")
+                        .putArguments(
+                            "argument_key", BundleValue.newBuilder().setIntValue(123).build()))
+                .build()
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/spa/search/SpaSearchRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/spa/search/SpaSearchRepositoryTest.kt
index c38f22f..f97abe4 100644
--- a/tests/spa_unit/src/com/android/settings/spa/search/SpaSearchRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/search/SpaSearchRepositoryTest.kt
@@ -23,7 +23,6 @@
 import com.android.settings.spa.search.SpaSearchRepository.Companion.createSearchIndexableData
 import com.android.settingslib.spa.framework.common.SettingsPageProvider
 import com.google.common.truth.Truth.assertThat
-import com.google.protobuf.ByteString
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.kotlin.mock
@@ -46,7 +45,7 @@
         assertThat(searchIndexableData.targetClass).isEqualTo(pageProvider::class.java)
         assertThat(dynamicRawDataToIndex).hasSize(1)
         val rawData = dynamicRawDataToIndex[0]
-        val key = SpaSearchLandingKey.parseFrom(ByteString.copyFromUtf8(rawData.key))
+        val key = decodeToSpaSearchLandingKey(rawData.key)
         assertThat(key)
             .isEqualTo(
                 SpaSearchLandingKey.newBuilder()
diff --git a/tests/unit/src/com/android/settings/network/UiccSlotUtilTest.java b/tests/unit/src/com/android/settings/network/UiccSlotUtilTest.java
index 2aa1573..83a6fed 100644
--- a/tests/unit/src/com/android/settings/network/UiccSlotUtilTest.java
+++ b/tests/unit/src/com/android/settings/network/UiccSlotUtilTest.java
@@ -38,8 +38,6 @@
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
-import com.google.common.collect.ImmutableList;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -47,7 +45,6 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
@@ -82,25 +79,6 @@
     }
 
     @Test
-    public void getSlotInfos_oneSimSlotDevice_returnTheCorrectSlotInfoList() {
-        when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(oneSimSlotDeviceActivePsim());
-        ImmutableList<UiccSlotInfo> testUiccSlotInfos =
-                UiccSlotUtil.getSlotInfos(mTelephonyManager);
-
-        assertThat(testUiccSlotInfos.size()).isEqualTo(1);
-    }
-
-    @Test
-    public void getSlotInfos_twoSimSlotsDevice_returnTheCorrectSlotInfoList() {
-        when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
-                twoSimSlotsDeviceActivePsimActiveEsim());
-        ImmutableList<UiccSlotInfo> testUiccSlotInfos =
-                UiccSlotUtil.getSlotInfos(mTelephonyManager);
-
-        assertThat(testUiccSlotInfos.size()).isEqualTo(2);
-    }
-
-    @Test
     public void getEsimSlotId_twoSimSlotsDeviceAndEsimIsSlot0_returnTheCorrectEsimSlot() {
         when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
                 twoSimSlotsDeviceActiveEsimActivePsim());
@@ -643,105 +621,7 @@
         assertThat(testExcludedLogicalSlotIndex).isEqualTo(verifyExcludedLogicalSlotIndex);
     }
 
-    @Test
-    public void isRemovableSimEnabled_noPsim_returnsFalse() {
-        when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
-                oneSimSlotDeviceActiveEsim());
 
-        boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
-
-        assertThat(testSlot).isFalse();
-    }
-
-    @Test
-    public void isRemovableSimEnabled_activeRemovableEsimAndInactivePsim_returnsFalse() {
-        when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
-                twoSimSlotsDeviceActiveRemovableEsimInactivePsim());
-
-        boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
-
-        assertThat(testSlot).isFalse();
-    }
-
-    @Test
-    public void isRemovableSimEnabled_activeRemovableEsimAndActivePsim_returnsTrue() {
-        when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
-                twoSimSlotsDeviceActivePsimActiveRemovableEsim());
-
-        boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
-
-        assertThat(testSlot).isTrue();
-    }
-
-    @Test
-    public void isRemovableSimEnabled_inactiveRemovableEsimAndActivePsim_returnsTrue() {
-        when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
-                twoSimSlotsDeviceInactiveRemovableEsimActivePsim());
-
-        boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
-
-        assertThat(testSlot).isTrue();
-    }
-
-    @Test
-    public void isRemovableSimEnabled_twoActiveRemovableEsimsAndInactivePsim_returnsFalse() {
-        when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
-                twoSimSlotsDeviceTwoActiveRemovableEsimsInactivePsim());
-
-        boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
-
-        assertThat(testSlot).isFalse();
-    }
-
-    @Test
-    public void isRemovableSimEnabled_oneActiveOneInactiveRemovableEsimActivePsim_returnsTrue() {
-        when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
-                twoSimSlotsDeviceOneActiveOneInactiveRemovableEsimsActivePsim());
-
-        boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
-
-        assertThat(testSlot).isTrue();
-    }
-
-    @Test
-    public void isRemovableSimEnabled_activePsim_returnsTrue() {
-        when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
-                oneSimSlotDeviceActivePsim());
-
-        boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
-
-        assertThat(testSlot).isTrue();
-    }
-
-    @Test
-    public void isRemovableSimEnabled_inactivePsim_returnsFalse() {
-        when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
-                oneSimSlotDeviceinactivePsim());
-
-        boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
-
-        assertThat(testSlot).isFalse();
-    }
-
-    @Test
-    public void isRemovableSimEnabled_activeEsimAndActivePsim_returnsTrue() {
-        when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
-                twoSimSlotsDeviceActivePsimActiveEsim());
-
-        boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
-
-        assertThat(testSlot).isTrue();
-    }
-
-    @Test
-    public void isRemovableSimEnabled_activeEsimAndInactivePsim_returnsFalse() {
-        when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(
-                twoSimSlotsDeviceInactivePsimActiveEsim());
-
-        boolean testSlot = UiccSlotUtil.isRemovableSimEnabled(mTelephonyManager);
-
-        assertThat(testSlot).isFalse();
-    }
 
     @Test
     public void performSwitchToSlot_setSimSlotMapping() throws UiccSlotsException {
@@ -856,13 +736,6 @@
         return slotMap;
     }
 
-    private List<UiccSlotMapping> createUiccSlotMappingSsModeEsimPort1Active() {
-        List<UiccSlotMapping> slotMap = new ArrayList<>();
-        slotMap.add(new UiccSlotMapping(1, ESIM_PHYSICAL_SLOT, 0));
-
-        return slotMap;
-    }
-
     private List<UiccSlotMapping> createUiccSlotMappingPsimAndPort0() {
         List<UiccSlotMapping> slotMap = new ArrayList<>();
         slotMap.add(new UiccSlotMapping(0, PSIM_PHYSICAL_SLOT, 0));
@@ -915,14 +788,6 @@
         return new UiccSlotInfo[]{createUiccSlotInfo(false, true, 0, true)};
     }
 
-    private UiccSlotInfo[] oneSimSlotDeviceActiveEsim() {
-        return new UiccSlotInfo[]{createUiccSlotInfo(true, false, 1, true)};
-    }
-
-    private UiccSlotInfo[] oneSimSlotDeviceinactivePsim() {
-        return new UiccSlotInfo[]{createUiccSlotInfo(false, true, -1, false)};
-    }
-
     private UiccSlotInfo[] twoSimSlotsDeviceActivePsimActiveEsim() {
         return new UiccSlotInfo[]{
                 createUiccSlotInfo(false, true, 0, true),
@@ -941,61 +806,12 @@
                 createUiccSlotInfo(true, true, 1, true)};
     }
 
-    private UiccSlotInfo[] twoSimSlotsDeviceActiveRemovableEsimInactivePsim() {
-        return new UiccSlotInfo[]{
-                createUiccSlotInfo(true, true, 0, true),
-                createUiccSlotInfo(false, true, -1, false)};
-    }
-
-    private UiccSlotInfo[] twoSimSlotsDeviceInactiveRemovableEsimActivePsim() {
-        return new UiccSlotInfo[]{
-                createUiccSlotInfo(true, true, -1, false),
-                createUiccSlotInfo(false, true, 0, true)};
-    }
-
-    private UiccSlotInfo[] twoSimSlotsDeviceTwoActiveRemovableEsimsInactivePsim() {
-        return new UiccSlotInfo[]{
-                createUiccSlotInfoForRemovableEsimMep(0, true, 1, true),
-                createUiccSlotInfo(false, true, -1, false)};
-    }
-
-    private UiccSlotInfo[] twoSimSlotsDeviceOneActiveOneInactiveRemovableEsimsActivePsim() {
-        return new UiccSlotInfo[]{
-                createUiccSlotInfoForRemovableEsimMep(1, true, -1, false),
-                createUiccSlotInfo(false, true, 0, true)};
-    }
-
     private UiccSlotInfo[] twoSimSlotsDeviceActiveEsimActivePsim() {
         return new UiccSlotInfo[]{
                 createUiccSlotInfo(true, false, 0, true),
                 createUiccSlotInfo(false, true, 1, true)};
     }
 
-    private UiccSlotInfo[] twoSimSlotsDeviceTwoActiveEsims() {
-        // device supports MEP, so device can enable two esims.
-        // If device has psim slot, the UiccSlotInfo of psim always be in UiccSlotInfo[].
-        return new UiccSlotInfo[]{
-                createUiccSlotInfo(false, true, -1, true),
-                createUiccSlotInfoForEsimMep(0, true, 1, true)};
-    }
-
-    private UiccSlotInfo[] twoSimSlotsDeviceActivePsimInactiveEsim() {
-        return new UiccSlotInfo[]{
-                createUiccSlotInfo(false, true, 0, true),
-                createUiccSlotInfo(true, false, -1, false)};
-    }
-
-    private UiccSlotInfo[] twoSimSlotsDeviceInactivePsimActiveEsim() {
-        return new UiccSlotInfo[]{
-                createUiccSlotInfo(false, true, 0, false),
-                createUiccSlotInfo(true, false, 1, true)};
-    }
-
-    private UiccSlotInfo[] twoSimSlotsDeviceNoInsertPsimActiveEsim() {
-        return new UiccSlotInfo[]{
-                createUiccSlotInfo(false, true, -1, false),
-                createUiccSlotInfo(true, false, 1, true)};
-    }
     //ToDo: add more cases.
 
     private UiccSlotInfo createUiccSlotInfo(boolean isEuicc, boolean isRemovable,
@@ -1011,36 +827,4 @@
                                 logicalSlotIdx /* logicalSlotIdx */, isActive /* isActive */))
         );
     }
-
-    private UiccSlotInfo createUiccSlotInfoForEsimMep(int logicalSlotIdx1, boolean isActiveEsim1,
-            int logicalSlotIdx2, boolean isActiveEsim2) {
-        return new UiccSlotInfo(
-                true, /* isEuicc */
-                "123", /* cardId */
-                CARD_STATE_INFO_PRESENT, /* cardStateInfo */
-                true, /* isExtendApduSupported */
-                false, /* isRemovable */
-                Arrays.asList(
-                        new UiccPortInfo("" /* iccId */, 0 /* portIdx */,
-                                logicalSlotIdx1 /* logicalSlotIdx */, isActiveEsim1 /* isActive */),
-                        new UiccPortInfo("" /* iccId */, 1 /* portIdx */,
-                                logicalSlotIdx2 /* logicalSlotIdx */,
-                                isActiveEsim2 /* isActive */)));
-    }
-
-    private UiccSlotInfo createUiccSlotInfoForRemovableEsimMep(int logicalSlotIdx1,
-            boolean isActiveEsim1, int logicalSlotIdx2, boolean isActiveEsim2) {
-        return new UiccSlotInfo(
-                true, /* isEuicc */
-                "123", /* cardId */
-                CARD_STATE_INFO_PRESENT, /* cardStateInfo */
-                true, /* isExtendApduSupported */
-                true, /* isRemovable */
-                Arrays.asList(
-                        new UiccPortInfo("" /* iccId */, 0 /* portIdx */,
-                                logicalSlotIdx1 /* logicalSlotIdx */, isActiveEsim1 /* isActive */),
-                        new UiccPortInfo("" /* iccId */, 1 /* portIdx */,
-                                logicalSlotIdx2 /* logicalSlotIdx */,
-                                isActiveEsim2 /* isActive */)));
-    }
 }
diff --git a/tests/unit/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceControllerTest.java
deleted file mode 100644
index 40be07f..0000000
--- a/tests/unit/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceControllerTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.network.telephony;
-
-import static com.google.common.truth.Truth.assertThat;
-
-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 androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.android.settings.network.CarrierConfigCache;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@RunWith(AndroidJUnit4.class)
-public class CarrierSettingsVersionPreferenceControllerTest {
-    @Mock
-    private CarrierConfigCache mCarrierConfigCache;
-
-    private CarrierSettingsVersionPreferenceController mController;
-    private int mSubscriptionId = 1234;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        Context context = spy(ApplicationProvider.getApplicationContext());
-        CarrierConfigCache.setTestInstance(context, mCarrierConfigCache);
-        mController = new CarrierSettingsVersionPreferenceController(context, "mock_key");
-        mController.init(mSubscriptionId);
-    }
-
-    @Test
-    public void getSummary_nullConfig_noCrash() {
-        doReturn(null).when(mCarrierConfigCache).getConfigForSubId(mSubscriptionId);
-
-        assertThat(mController.getSummary()).isNull();
-    }
-
-    @Test
-    public void getSummary_nullVersionString_noCrash() {
-        doReturn(new PersistableBundle()).when(mCarrierConfigCache)
-                .getConfigForSubId(mSubscriptionId);
-        assertThat(mController.getSummary()).isNull();
-    }
-
-    @Test
-    public void getSummary_hasVersionString_correctSummary() {
-        final PersistableBundle bundle = new PersistableBundle();
-        bundle.putString(CarrierConfigManager.KEY_CARRIER_CONFIG_VERSION_STRING,
-                "test_version_123");
-        doReturn(bundle).when(mCarrierConfigCache).getConfigForSubId(mSubscriptionId);
-
-        assertThat(mController.getSummary()).isEqualTo("test_version_123");
-    }
-}