Merge "Replace HashMap with ArrayMap for better performance" into main
diff --git a/aconfig/Android.bp b/aconfig/Android.bp
index 78fba34..f485869 100644
--- a/aconfig/Android.bp
+++ b/aconfig/Android.bp
@@ -8,8 +8,10 @@
     srcs: [
         "settings_accessibility_flag_declarations.aconfig",
         "settings_connecteddevice_flag_declarations.aconfig",
+        "settings_development_flag_declarations.aconfig",
         "settings_globalintl_flag_declarations.aconfig",
         "settings_experience_flag_declarations.aconfig",
+	"settings_notification_flag_declarations.aconfig",
         "settings_onboarding_experience_flag_declarations.aconfig",
         "settings_telephony_flag_declarations.aconfig",
         "settings_biometrics_integration_declarations.aconfig",
diff --git a/aconfig/settings_development_flag_declarations.aconfig b/aconfig/settings_development_flag_declarations.aconfig
new file mode 100644
index 0000000..c23a38f
--- /dev/null
+++ b/aconfig/settings_development_flag_declarations.aconfig
@@ -0,0 +1,13 @@
+package: "com.android.settings.flags"
+
+# NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
+
+# NOTE: All Settings flags share the same Flags class, so prefix our
+# flags with 'development' to prevent naming collision.
+
+flag {
+  name: "development_hdr_sdr_ratio"
+  namespace: "core_graphics"
+  description: "Shows hdr/sdr dev opton on the development options page from aconfig"
+  bug: "291863102"
+}
diff --git a/aconfig/settings_notification_flag_declarations.aconfig b/aconfig/settings_notification_flag_declarations.aconfig
new file mode 100644
index 0000000..f2bf1c8
--- /dev/null
+++ b/aconfig/settings_notification_flag_declarations.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.settings.flags"
+
+flag {
+  name: "dedupe_dnd_settings_channels"
+  namespace: "systemui"
+  description: "Controls adding group names to channel names in the DND>Apps settings page"
+  bug: "294333850"
+}
+
diff --git a/protos/fuelgauge_log.proto b/protos/fuelgauge_log.proto
index e75ca48..36126a5 100644
--- a/protos/fuelgauge_log.proto
+++ b/protos/fuelgauge_log.proto
@@ -43,6 +43,7 @@
     RECHECK_JOB = 3;
     FETCH_USAGE_DATA = 4;
     INSERT_USAGE_DATA = 5;
+    TIME_UPDATED = 6;
   }
 
   optional int64 timestamp = 1;
diff --git a/res/drawable/ic_error_red.xml b/res/drawable/ic_error_red.xml
new file mode 100644
index 0000000..d17c85b
--- /dev/null
+++ b/res/drawable/ic_error_red.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="960"
+        android:viewportHeight="960"
+        android:tint="?android:attr/colorError">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M480,680Q497,680 508.5,668.5Q520,657 520,640Q520,623 508.5,611.5Q497,600 480,600Q463,600 451.5,611.5Q440,623 440,640Q440,657 451.5,668.5Q463,680 480,680ZM440,520L520,520L520,280L440,280L440,520ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z"/>
+</vector>
diff --git a/res/layout/dialog_audio_sharing.xml b/res/layout/dialog_audio_sharing.xml
index 9624c90..5f26f74 100644
--- a/res/layout/dialog_audio_sharing.xml
+++ b/res/layout/dialog_audio_sharing.xml
@@ -23,7 +23,7 @@
     android:orientation="vertical">
 
     <TextView
-        style="@style/device_info_dialog_value"
+        style="@style/DeviceAudioSharingText"
         android:id="@+id/share_audio_subtitle1"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
@@ -31,7 +31,7 @@
         android:layout_gravity="center"/>
 
     <TextView
-        style="@style/device_info_dialog_value"
+        style="@style/DeviceAudioSharingText"
         android:id="@+id/share_audio_subtitle2"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
diff --git a/res/layout/dialog_eid_status.xml b/res/layout/dialog_eid_status.xml
index 77d6494..3297dac 100644
--- a/res/layout/dialog_eid_status.xml
+++ b/res/layout/dialog_eid_status.xml
@@ -27,8 +27,7 @@
         style="@style/device_info_dialog_value"
         android:id="@+id/esim_id_value"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:textIsSelectable="true" />
+        android:layout_height="wrap_content" />
 
     <ImageView
         android:id="@+id/esim_id_qrcode"
diff --git a/res/layout/preference_widget_qrcode.xml b/res/layout/preference_widget_qrcode.xml
new file mode 100644
index 0000000..7994fe1
--- /dev/null
+++ b/res/layout/preference_widget_qrcode.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/button_icon"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:minWidth="@dimen/two_target_min_width"
+    android:minHeight="@dimen/min_tap_target_size"
+    android:layout_gravity="center"
+    android:background="?android:attr/selectableItemBackground"/>
\ No newline at end of file
diff --git a/res/layout/privatespace_account_login_error.xml b/res/layout/privatespace_account_login_error.xml
new file mode 100644
index 0000000..a38dd50
--- /dev/null
+++ b/res/layout/privatespace_account_login_error.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<com.google.android.setupdesign.GlifLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/ps_error_page_layout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:filterTouchesWhenObscured="true"
+    app:sucHeaderText="@string/privatespace_retry_signin_title"
+    app:sudDescriptionText="@string/privatespace_retry_summary"
+    android:icon="@drawable/ic_error_red">
+</com.google.android.setupdesign.GlifLayout>
diff --git a/res/navigation/privatespace_main_context_nav.xml b/res/navigation/privatespace_main_context_nav.xml
index 5b2552a..ffc63ec 100644
--- a/res/navigation/privatespace_main_context_nav.xml
+++ b/res/navigation/privatespace_main_context_nav.xml
@@ -33,8 +33,8 @@
             android:id="@+id/action_advance_profile_error"
             app:destination="@id/ps_profile_error_fragment"/>
         <action
-            android:id="@+id/action_advance_to_success"
-            app:destination="@id/ps_profile_success_fragment"/>
+            android:id="@+id/action_advance_login_error"
+            app:destination="@id/ps_account_error_fragment"/>
     </fragment>
     <fragment android:id="@+id/ps_profile_error_fragment"
               android:name="com.android.settings.privatespace.PrivateProfileCreationError"
@@ -46,4 +46,18 @@
     <fragment android:id="@+id/ps_profile_success_fragment"
               android:name="com.android.settings.privatespace.SetupSuccessFragment"
               android:label="fragment_ps_success"/>
-</navigation>
\ No newline at end of file
+    <fragment android:id="@+id/ps_account_error_fragment"
+              android:name="com.android.settings.privatespace.PrivateSpaceAccountLoginError"
+              android:label="fragment_account_error">
+        <action
+            android:id="@+id/action_advance_login_error"
+            app:destination="@id/ps_account_error_fragment"/>
+    </fragment>
+    <fragment android:id="@+id/ps_profile_lock_fragment"
+              android:name="com.android.settings.privatespace.PrivateSpaceSetLockFragment"
+              android:label="fragment_ps_lock"/>
+    <action android:id="@+id/action_success_fragment"
+            app:destination="@id/ps_profile_success_fragment"/>
+    <action android:id="@+id/action_set_lock_fragment"
+            app:destination="@id/ps_profile_lock_fragment"/>
+</navigation>
diff --git a/res/navigation/privatespace_private_context_nav.xml b/res/navigation/privatespace_private_context_nav.xml
deleted file mode 100644
index 3df8fa5..0000000
--- a/res/navigation/privatespace_private_context_nav.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
-  ~ Copyright (C) 2023 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<navigation xmlns:android="http://schemas.android.com/apk/res/android"
-            xmlns:app="http://schemas.android.com/apk/res-auto"
-            android:id="@+id/private_space_navigation"
-            app:startDestination="@id/ps_profile_lock_fragment">
-    <fragment android:id="@+id/ps_profile_lock_fragment"
-              android:name="com.android.settings.privatespace.PrivateSpaceSetLockFragment"
-              android:label="fragment_ps_lock"/>
-</navigation>
diff --git a/res/values/config.xml b/res/values/config.xml
index 99052ca..f50e918 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -753,6 +753,9 @@
     <!-- Whether to display Cloned Apps page in Settings (Settings > Apps > Cloned Apps).-->
     <bool name="config_cloned_apps_page_enabled">false</bool>
 
+    <!-- Whether to initiate Account login during Private Space setup.-->
+    <bool name="config_privatespace_account_login_enabled">false</bool>
+
     <!-- Certificates of apps which are allowed to use activity embedding with Settings.-->
     <string-array name="config_known_host_certs" translatable="false">
         <item></item>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a3760c5..7ed9ade 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1298,6 +1298,10 @@
     <string name="privatespace_done_label">Done</string>
     <!-- Toast to show on private space setup completion informing user to scroll down All apps to access private space. [CHAR LIMIT=60] -->
     <string name="scrolldown_to_access">Scroll down to access Private Space</string>
+    <!-- Title for Private Space account login error screen. [CHAR LIMIT=60] -->
+    <string name="privatespace_retry_signin_title">Sign in to set up Private Space</string>
+    <!-- Summary for the Private Space account login error screen. [CHAR LIMIT=NONE] -->
+    <string name="privatespace_retry_summary">You need to sign in to a Account to set up Private Space</string>
 
     <!-- Text shown when "Add fingerprint" button is disabled -->
     <string name="fingerprint_add_max">You can add up to <xliff:g id="count" example="5">%d</xliff:g> fingerprints</string>
@@ -1853,6 +1857,11 @@
     <!-- Debugging developer settings: show refresh rate summary [CHAR LIMIT=58] -->
     <string name="show_refresh_rate_summary">Show the current display refresh rate</string>
 
+    <!-- Debugging developer settings: show HDR/SDR ratio? [CHAR LIMIT=36] -->
+    <string name="show_hdr_sdr_ratio">Show HDR/SDR ratio</string>
+    <!-- Debugging developer settings: show HDR/SDR ratio summary [CHAR LIMIT=58] -->
+    <string name="show_hdr_sdr_ratio_summary">Show the current HDR/SDR ratio</string>
+
     <!-- NFC settings -->
     <!-- Used in the 1st-level settings screen to turn on NFC -->
     <string name="nfc_quick_toggle_title">NFC</string>
@@ -9520,6 +9529,13 @@
     <!-- Label for showing apps that can manage external storage[CHAR LIMIT=45] -->
     <string name="filter_manage_external_storage">Can access all files</string>
 
+    <!-- Voice Activation apps settings title [CHAR LIMIT=40] -->
+    <string name="voice_activation_apps_title">Voice activation apps</string>
+    <!-- Label for a setting which controls whether an app can be voice activated [CHAR LIMIT=NONE] -->
+    <string name="permit_voice_activation_apps">Allow voice activation</string>
+    <!-- Description for a setting which controls whether an app can be voice activated [CHAR LIMIT=NONE] -->
+    <string name ="allow_voice_activation_apps_description">Voice activation turns-on approved apps, hands-free, using voice command. Built-in adaptive sensing ensures data stays private only to you.\n\n<a href="">More about protected adaptive sensing</a></string>
+
     <!-- Manage full screen intent permission title [CHAR LIMIT=40] -->
     <string name="full_screen_intent_title">Full screen notifications</string>
 
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 3a2f9ad..8736689 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -433,6 +433,15 @@
         <item name="android:textSize">14sp</item>
         <item name="android:textColor">?android:attr/textColorPrimary</item>
         <item name="android:paddingBottom">24dp</item>
+        <item name="android:textIsSelectable">true</item>
+    </style>
+
+    <style name="DeviceAudioSharingText">
+        <item name="android:textAlignment">viewStart</item>
+        <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:paddingBottom">24dp</item>
     </style>
 
     <style name="ContextualCardStyle">
diff --git a/res/xml/bluetooth_audio_sharing.xml b/res/xml/bluetooth_audio_sharing.xml
index 86bb062..ca7137a 100644
--- a/res/xml/bluetooth_audio_sharing.xml
+++ b/res/xml/bluetooth_audio_sharing.xml
@@ -26,6 +26,12 @@
         settings:controller="com.android.settings.connecteddevice.audiosharing.CallsAndAlarmsPreferenceController"
         android:summary=""/>
 
+    <com.android.settings.connecteddevice.audiosharing.AudioSharingNamePreference
+        android:key="audio_sharing_stream_name"
+        android:title="Stream name"
+        android:summary="********"
+        settings:controller="com.android.settings.connecteddevice.audiosharing.AudioSharingNamePreferenceController"/>
+
     <PreferenceCategory
         android:key="audio_streams_settings_category"
         android:title="@string/audio_sharing_streams_category_title"
@@ -38,5 +44,4 @@
             android:icon="@drawable/ic_chevron_right_24dp" />
 
     </PreferenceCategory>
-
 </PreferenceScreen>
\ No newline at end of file
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index b053424..f890984 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -274,6 +274,11 @@
             android:summary="@string/show_refresh_rate_summary" />
 
         <SwitchPreferenceCompat
+            android:key="show_hdr_sdr_ratio"
+            android:title="@string/show_hdr_sdr_ratio"
+            android:summary="@string/show_hdr_sdr_ratio_summary" />
+
+        <SwitchPreferenceCompat
             android:key="overlay_settings"
             android:title="@string/overlay_settings_title"
             android:summary="@string/overlay_settings_summary" />
diff --git a/res/xml/special_access.xml b/res/xml/special_access.xml
index b3f3f7d..3f3d75d 100644
--- a/res/xml/special_access.xml
+++ b/res/xml/special_access.xml
@@ -100,6 +100,11 @@
         settings:controller="com.android.settings.spa.app.specialaccess.UseFullScreenIntentPreferenceController" />
 
     <Preference
+        android:key="voice_activation_apps"
+        android:title="@string/voice_activation_apps_title"
+        settings:controller="com.android.settings.spa.app.specialaccess.VoiceActivationAppsPreferenceController" />
+
+    <Preference
         android:key="picture_in_picture"
         android:title="@string/picture_in_picture_title"
         android:order="-1100"
diff --git a/src/com/android/settings/SettingsActivityUtil.kt b/src/com/android/settings/SettingsActivityUtil.kt
index 65d26de..c23bc18 100644
--- a/src/com/android/settings/SettingsActivityUtil.kt
+++ b/src/com/android/settings/SettingsActivityUtil.kt
@@ -37,6 +37,7 @@
 import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider
 import com.android.settings.spa.app.specialaccess.NfcTagAppsSettingsProvider
 import com.android.settings.spa.app.specialaccess.PictureInPictureListProvider
+import com.android.settings.spa.app.specialaccess.VoiceActivationAppsListProvider
 import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider
 import com.android.settings.wifi.ChangeWifiStateDetails
 
@@ -65,6 +66,8 @@
             WifiControlAppListProvider.getAppInfoRoutePrefix(),
         NfcTagAppsSettingsProvider::class.qualifiedName to
             NfcTagAppsSettingsProvider.getAppInfoRoutePrefix(),
+        VoiceActivationAppsListProvider::class.qualifiedName to
+            VoiceActivationAppsListProvider.getAppInfoRoutePrefix(),
     )
 
     @JvmStatic
diff --git a/src/com/android/settings/applications/appcompat/UserAspectRatioDetails.java b/src/com/android/settings/applications/appcompat/UserAspectRatioDetails.java
index 076d37c..02d5c27 100644
--- a/src/com/android/settings/applications/appcompat/UserAspectRatioDetails.java
+++ b/src/com/android/settings/applications/appcompat/UserAspectRatioDetails.java
@@ -28,6 +28,7 @@
 
 import android.app.ActivityManager;
 import android.app.IActivityManager;
+import android.app.settings.SettingsEnums;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.os.Build;
@@ -44,8 +45,10 @@
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settings.applications.AppInfoBase;
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.widget.EntityHeaderController;
 import com.android.settingslib.applications.AppUtils;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.widget.ActionButtonsPreference;
 
 import java.util.ArrayList;
@@ -104,6 +107,7 @@
             Log.e(TAG, "Unable to set user min aspect ratio");
             return;
         }
+        logActionMetrics(selectedKey, mSelectedKey);
         // Only update to selected aspect ratio if nothing goes wrong
         mSelectedKey = selectedKey;
         updateAllPreferences(mSelectedKey);
@@ -118,8 +122,7 @@
 
     @Override
     public int getMetricsCategory() {
-        // TODO(b/292566895): add metrics for logging
-        return 0;
+        return SettingsEnums.USER_ASPECT_RATIO_APP_INFO_SETTINGS;
     }
 
     @Override
@@ -244,6 +247,68 @@
         }
     }
 
+    private void logActionMetrics(@NonNull String selectedKey, @NonNull String unselectedKey) {
+        final MetricsFeatureProvider metricsFeatureProvider =
+                FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
+        final int attribution = metricsFeatureProvider.getAttribution(getActivity());
+        metricsFeatureProvider.action(
+                attribution,
+                getUnselectedAspectRatioAction(unselectedKey),
+                getMetricsCategory(),
+                mPackageName,
+                mUserId
+        );
+        metricsFeatureProvider.action(
+                attribution,
+                getSelectedAspectRatioAction(selectedKey),
+                getMetricsCategory(),
+                mPackageName,
+                mUserId
+        );
+    }
+
+    private static int getSelectedAspectRatioAction(@NonNull String selectedKey) {
+        switch (selectedKey) {
+            case KEY_PREF_DEFAULT:
+                return SettingsEnums.ACTION_USER_ASPECT_RATIO_APP_DEFAULT_SELECTED;
+            case KEY_PREF_FULLSCREEN:
+                return SettingsEnums.ACTION_USER_ASPECT_RATIO_FULL_SCREEN_SELECTED;
+            case KEY_PREF_HALF_SCREEN:
+                return SettingsEnums.ACTION_USER_ASPECT_RATIO_HALF_SCREEN_SELECTED;
+            case KEY_PREF_4_3:
+                return SettingsEnums.ACTION_USER_ASPECT_RATIO_4_3_SELECTED;
+            case KEY_PREF_16_9:
+                return SettingsEnums.ACTION_USER_ASPECT_RATIO_16_9_SELECTED;
+            case KEY_PREF_3_2:
+                return SettingsEnums.ACTION_USER_ASPECT_RATIO_3_2_SELECTED;
+            case KEY_PREF_DISPLAY_SIZE:
+                return SettingsEnums.ACTION_USER_ASPECT_RATIO_DISPLAY_SIZE_SELECTED;
+            default:
+                return SettingsEnums.ACTION_UNKNOWN;
+        }
+    }
+
+    private static int getUnselectedAspectRatioAction(@NonNull String unselectedKey) {
+        switch (unselectedKey) {
+            case KEY_PREF_DEFAULT:
+                return SettingsEnums.ACTION_USER_ASPECT_RATIO_APP_DEFAULT_UNSELECTED;
+            case KEY_PREF_FULLSCREEN:
+                return SettingsEnums.ACTION_USER_ASPECT_RATIO_FULL_SCREEN_UNSELECTED;
+            case KEY_PREF_HALF_SCREEN:
+                return SettingsEnums.ACTION_USER_ASPECT_RATIO_HALF_SCREEN_UNSELECTED;
+            case KEY_PREF_4_3:
+                return SettingsEnums.ACTION_USER_ASPECT_RATIO_4_3_UNSELECTED;
+            case KEY_PREF_16_9:
+                return SettingsEnums.ACTION_USER_ASPECT_RATIO_16_9_UNSELECTED;
+            case KEY_PREF_3_2:
+                return SettingsEnums.ACTION_USER_ASPECT_RATIO_3_2_UNSELECTED;
+            case KEY_PREF_DISPLAY_SIZE:
+                return SettingsEnums.ACTION_USER_ASPECT_RATIO_DISPLAY_SIZE_UNSELECTED;
+            default:
+                return SettingsEnums.ACTION_UNKNOWN;
+        }
+    }
+
     @VisibleForTesting
     UserAspectRatioManager getAspectRatioManager() {
         return mUserAspectRatioManager;
diff --git a/src/com/android/settings/applications/manageapplications/ApplicationViewHolder.java b/src/com/android/settings/applications/manageapplications/ApplicationViewHolder.java
index c94edc6..1d96688 100644
--- a/src/com/android/settings/applications/manageapplications/ApplicationViewHolder.java
+++ b/src/com/android/settings/applications/manageapplications/ApplicationViewHolder.java
@@ -46,14 +46,14 @@
 import com.android.settingslib.applications.ApplicationsState;
 import com.android.settingslib.applications.ApplicationsState.AppEntry;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-import com.android.settingslib.spaprivileged.template.app.AppListItemKt;
+import com.android.settingslib.spaprivileged.template.app.AppListItemModelKt;
 import com.android.settingslib.spaprivileged.template.app.AppListPageKt;
 import com.android.settingslib.widget.LottieColorUtils;
 
 import com.airbnb.lottie.LottieAnimationView;
 
 /**
- * @deprecated Will be removed, use {@link AppListItemKt} {@link AppListPageKt} instead.
+ * @deprecated Will be removed, use {@link AppListItemModelKt} {@link AppListPageKt} instead.
  */
 @Deprecated(forRemoval = true)
 public class ApplicationViewHolder extends RecyclerView.ViewHolder {
diff --git a/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt b/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt
index 216ce47..82e987e 100644
--- a/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt
+++ b/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt
@@ -64,9 +64,11 @@
 import com.android.settings.spa.app.specialaccess.AllFilesAccessAppListProvider
 import com.android.settings.spa.app.specialaccess.DisplayOverOtherAppsAppListProvider
 import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider
+import com.android.settings.spa.app.specialaccess.LongBackgroundTasksAppListProvider
 import com.android.settings.spa.app.specialaccess.MediaManagementAppsAppListProvider
 import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider
 import com.android.settings.spa.app.specialaccess.NfcTagAppsSettingsProvider
+import com.android.settings.spa.app.specialaccess.TurnScreenOnAppsAppListProvider
 import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider
 import com.android.settings.spa.app.storage.StorageAppListPageProvider
 import com.android.settings.spa.notification.AppListNotificationsPageProvider
@@ -120,6 +122,8 @@
             LIST_TYPE_MAIN -> AllAppListPageProvider.name
             LIST_TYPE_NFC_TAG_APPS -> NfcTagAppsSettingsProvider.getAppListRoute()
             LIST_TYPE_USER_ASPECT_RATIO_APPS -> UserAspectRatioAppsPageProvider.name
+            LIST_TYPE_LONG_BACKGROUND_TASKS -> LongBackgroundTasksAppListProvider.getAppListRoute()
+            LIST_TYPE_TURN_SCREEN_ON -> TurnScreenOnAppsAppListProvider.getAppListRoute()
             // TODO(b/292165031) enable once sorting is supported
             //LIST_TYPE_STORAGE -> StorageAppListPageProvider.Apps.name
             //LIST_TYPE_GAMES -> StorageAppListPageProvider.Games.name
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java
index 5b99907..1fd0b87 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java
@@ -38,6 +38,26 @@
 public class AudioSharingDialogFragment extends InstrumentedDialogFragment {
     private static final String TAG = "AudioSharingDialog";
 
+    private static final String BUNDLE_KEY_DEVICE_NAMES = "bundle_key_device_names";
+
+    // The host creates an instance of this dialog fragment must implement this interface to receive
+    // event callbacks.
+    public interface DialogEventListener {
+        /**
+         * Called when users click the device item for sharing in the dialog.
+         *
+         * @param position The position of the item clicked.
+         */
+        void onItemClick(int position);
+
+        /**
+         * Called when users click the cancel button in the dialog.
+         */
+        void onCancelClick();
+    }
+
+    private static DialogEventListener sListener;
+
     private View mRootView;
 
     @Override
@@ -50,40 +70,59 @@
      *
      * @param host The Fragment this dialog will be hosted.
      */
-    public static void show(Fragment host) {
+    public static void show(
+            Fragment host, ArrayList<String> deviceNames, DialogEventListener listener) {
         if (!Flags.enableLeAudioSharing()) return;
         final FragmentManager manager = host.getChildFragmentManager();
+        sListener = listener;
         if (manager.findFragmentByTag(TAG) == null) {
-            final AudioSharingDialogFragment dialog = new AudioSharingDialogFragment();
+            final Bundle bundle = new Bundle();
+            bundle.putStringArrayList(BUNDLE_KEY_DEVICE_NAMES, deviceNames);
+            AudioSharingDialogFragment dialog = new AudioSharingDialogFragment();
+            dialog.setArguments(bundle);
             dialog.show(manager, TAG);
         }
     }
 
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
+        Bundle arguments = requireArguments();
+        ArrayList<String> deviceNames = arguments.getStringArrayList(BUNDLE_KEY_DEVICE_NAMES);
         final AlertDialog.Builder builder =
-                new AlertDialog.Builder(getActivity()).setTitle("Share audio");
+                new AlertDialog.Builder(getActivity()).setTitle("Share audio").setCancelable(false);
         mRootView =
                 LayoutInflater.from(builder.getContext())
                         .inflate(R.layout.dialog_audio_sharing, /* parent= */ null);
-        // TODO: use real subtitle according to device count.
         TextView subTitle1 = mRootView.findViewById(R.id.share_audio_subtitle1);
         TextView subTitle2 = mRootView.findViewById(R.id.share_audio_subtitle2);
-        subTitle1.setText("2 devices connected");
-        subTitle2.setText("placeholder");
+        if (deviceNames.isEmpty()) {
+            subTitle1.setVisibility(View.INVISIBLE);
+            subTitle2.setText("To start sharing audio, connect headphones that support LE audio");
+            builder.setNegativeButton(
+                    "Close",
+                    (dialog, which) -> {
+                        sListener.onCancelClick();
+                    });
+        } else if (deviceNames.size() == 1) {
+            // TODO: add real impl
+            subTitle1.setText("1 devices connected");
+            subTitle2.setText("placeholder");
+        } else {
+            // TODO: add real impl
+            subTitle1.setText("2 devices connected");
+            subTitle2.setText("placeholder");
+        }
         RecyclerView recyclerView = mRootView.findViewById(R.id.btn_list);
-        // TODO: use real audio sharing device list.
-        ArrayList<String> devices = new ArrayList<>();
-        devices.add("Buds 1");
-        devices.add("Buds 2");
         recyclerView.setAdapter(
                 new AudioSharingDeviceAdapter(
-                        devices,
+                        deviceNames,
                         (int position) -> {
-                            // TODO: add on click callback.
+                            sListener.onItemClick(position);
                         }));
         recyclerView.setLayoutManager(
                 new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
-        return builder.setView(mRootView).create();
+        AlertDialog dialog = builder.setView(mRootView).create();
+        dialog.setCanceledOnTouchOutside(false);
+        return dialog;
     }
 }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreference.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreference.java
new file mode 100644
index 0000000..387ab7e
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreference.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.connecteddevice.audiosharing;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ImageButton;
+
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.settings.R;
+import com.android.settings.widget.ValidatedEditTextPreference;
+
+public class AudioSharingNamePreference extends ValidatedEditTextPreference {
+    private static final String TAG = "AudioSharingNamePreference";
+
+    public AudioSharingNamePreference(
+            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        initialize();
+    }
+
+    public AudioSharingNamePreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        initialize();
+    }
+
+    public AudioSharingNamePreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        initialize();
+    }
+
+    public AudioSharingNamePreference(Context context) {
+        super(context);
+        initialize();
+    }
+
+    private void initialize() {
+        setLayoutResource(
+                com.android.settingslib.widget.preference.twotarget.R.layout.preference_two_target);
+        setWidgetLayoutResource(R.layout.preference_widget_qrcode);
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+        final ImageButton shareButton = (ImageButton) holder.findViewById(R.id.button_icon);
+        shareButton.setImageDrawable(getContext().getDrawable(R.drawable.ic_qrcode_24dp));
+    }
+}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreferenceController.java
new file mode 100644
index 0000000..18c9bfd
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreferenceController.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.connecteddevice.audiosharing;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.flags.Flags;
+import com.android.settings.widget.ValidatedEditTextPreference;
+
+public class AudioSharingNamePreferenceController extends BasePreferenceController
+        implements ValidatedEditTextPreference.Validator,
+                Preference.OnPreferenceChangeListener,
+                DefaultLifecycleObserver {
+
+    private static final String TAG = "AudioSharingNamePreferenceController";
+
+    private static final String PREF_KEY = "audio_sharing_stream_name";
+
+    protected Preference mPreference;
+
+    private AudioSharingNameTextValidator mAudioSharingNameTextValidator;
+
+    public AudioSharingNamePreferenceController(Context context) {
+        super(context, PREF_KEY);
+        mAudioSharingNameTextValidator = new AudioSharingNameTextValidator();
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return Flags.enableLeAudioSharing() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return PREF_KEY;
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        // TODO: update broadcast when name is changed.
+        return true;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mPreference = screen.findPreference(getPreferenceKey());
+    }
+
+    @Override
+    public boolean isTextValid(String value) {
+        return mAudioSharingNameTextValidator.isTextValid(value);
+    }
+
+    @Override
+    public void onStart(@NonNull LifecycleOwner owner) {
+        // TODO
+    }
+
+    @Override
+    public void onStop(@NonNull LifecycleOwner owner) {
+        // TODO
+    }
+}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNameTextValidator.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNameTextValidator.java
new file mode 100644
index 0000000..9492961
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNameTextValidator.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.connecteddevice.audiosharing;
+
+import com.android.settings.widget.ValidatedEditTextPreference;
+
+public class AudioSharingNameTextValidator implements ValidatedEditTextPreference.Validator {
+    @Override
+    public boolean isTextValid(String value) {
+        // TODO: Add validate rule if applicable.
+        return true;
+    }
+}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
index a375a3c..bd8027c 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.connecteddevice.audiosharing;
 
+import android.bluetooth.BluetoothLeBroadcast;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
 import android.content.Context;
 import android.util.Log;
 import android.widget.Switch;
@@ -24,36 +26,116 @@
 import androidx.lifecycle.DefaultLifecycleObserver;
 import androidx.lifecycle.LifecycleOwner;
 
+import com.android.settings.bluetooth.Utils;
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.flags.Flags;
 import com.android.settings.widget.SettingsMainSwitchBar;
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.utils.ThreadUtils;
 import com.android.settingslib.widget.OnMainSwitchChangeListener;
 
+import java.util.ArrayList;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
 public class AudioSharingSwitchBarController extends BasePreferenceController
         implements DefaultLifecycleObserver, OnMainSwitchChangeListener {
     private static final String TAG = "AudioSharingSwitchBarCtl";
     private static final String PREF_KEY = "audio_sharing_main_switch";
-
-    private final Context mContext;
     private final SettingsMainSwitchBar mSwitchBar;
+    private final LocalBluetoothManager mBtManager;
+    private final LocalBluetoothLeBroadcast mBroadcast;
+    private final Executor mExecutor;
     private DashboardFragment mFragment;
 
+    private final BluetoothLeBroadcast.Callback mBroadcastCallback =
+            new BluetoothLeBroadcast.Callback() {
+                @Override
+                public void onBroadcastStarted(int reason, int broadcastId) {
+                    Log.d(
+                            TAG,
+                            "onBroadcastStarted(), reason = "
+                                    + reason
+                                    + ", broadcastId = "
+                                    + broadcastId);
+                    updateSwitch();
+                }
+
+                @Override
+                public void onBroadcastStartFailed(int reason) {
+                    Log.d(TAG, "onBroadcastStartFailed(), reason = " + reason);
+                    // TODO: handle broadcast start fail
+                    updateSwitch();
+                }
+
+                @Override
+                public void onBroadcastMetadataChanged(
+                        int broadcastId, @NonNull BluetoothLeBroadcastMetadata metadata) {
+                    Log.d(
+                            TAG,
+                            "onBroadcastMetadataChanged(), broadcastId = "
+                                    + broadcastId
+                                    + ", metadata = "
+                                    + metadata);
+                    // TODO: handle add sink if there are connected lea devices.
+                }
+
+                @Override
+                public void onBroadcastStopped(int reason, int broadcastId) {
+                    Log.d(
+                            TAG,
+                            "onBroadcastStopped(), reason = "
+                                    + reason
+                                    + ", broadcastId = "
+                                    + broadcastId);
+                    updateSwitch();
+                }
+
+                @Override
+                public void onBroadcastStopFailed(int reason) {
+                    Log.d(TAG, "onBroadcastStopFailed(), reason = " + reason);
+                    // TODO: handle broadcast stop fail
+                    updateSwitch();
+                }
+
+                @Override
+                public void onBroadcastUpdated(int reason, int broadcastId) {}
+
+                @Override
+                public void onBroadcastUpdateFailed(int reason, int broadcastId) {}
+
+                @Override
+                public void onPlaybackStarted(int reason, int broadcastId) {}
+
+                @Override
+                public void onPlaybackStopped(int reason, int broadcastId) {}
+            };
+
     AudioSharingSwitchBarController(Context context, SettingsMainSwitchBar switchBar) {
         super(context, PREF_KEY);
-        mContext = context;
         mSwitchBar = switchBar;
-        mSwitchBar.setChecked(false);
+        mBtManager = Utils.getLocalBtManager(context);
+        mBroadcast = mBtManager.getProfileManager().getLeAudioBroadcastProfile();
+        mExecutor = Executors.newSingleThreadExecutor();
+        mSwitchBar.setChecked(isBroadcasting());
     }
 
     @Override
     public void onStart(@NonNull LifecycleOwner owner) {
         mSwitchBar.addOnSwitchChangeListener(this);
+        if (mBroadcast != null) {
+            mBroadcast.registerServiceCallBack(mExecutor, mBroadcastCallback);
+        }
     }
 
     @Override
     public void onStop(@NonNull LifecycleOwner owner) {
         mSwitchBar.removeOnSwitchChangeListener(this);
+        if (mBroadcast != null) {
+            mBroadcast.unregisterServiceCallBack(mBroadcastCallback);
+        }
     }
 
     @Override
@@ -63,7 +145,7 @@
         if (isChecked) {
             startAudioSharing();
         } else {
-            // TODO: stop sharing
+            stopAudioSharing();
         }
     }
 
@@ -82,10 +164,53 @@
     }
 
     private void startAudioSharing() {
-        if (mFragment != null) {
-            AudioSharingDialogFragment.show(mFragment);
-        } else {
-            Log.w(TAG, "Dialog fail to show due to null fragment.");
+        mSwitchBar.setEnabled(false);
+        if (mBroadcast == null || isBroadcasting()) {
+            Log.d(TAG, "Already in broadcasting or broadcast not support, ignore!");
+            mSwitchBar.setEnabled(true);
+            return;
         }
+        if (mFragment == null) {
+            Log.w(TAG, "Dialog fail to show due to null fragment.");
+            mSwitchBar.setEnabled(true);
+            return;
+        }
+        ArrayList<String> deviceNames = new ArrayList<>();
+        AudioSharingDialogFragment.show(
+                mFragment,
+                deviceNames,
+                new AudioSharingDialogFragment.DialogEventListener() {
+                    @Override
+                    public void onItemClick(int position) {
+                        // TODO: handle broadcast based on the dialog device item clicked
+                    }
+
+                    @Override
+                    public void onCancelClick() {
+                        mBroadcast.startBroadcast("test", /* language= */ null);
+                    }
+                });
+    }
+
+    private void stopAudioSharing() {
+        mSwitchBar.setEnabled(false);
+        if (mBroadcast == null || !isBroadcasting()) {
+            Log.d(TAG, "Already not broadcasting or broadcast not support, ignore!");
+            mSwitchBar.setEnabled(true);
+            return;
+        }
+        mBroadcast.stopBroadcast(mBroadcast.getLatestBroadcastId());
+    }
+
+    private void updateSwitch() {
+        ThreadUtils.postOnMainThread(
+                () -> {
+                    mSwitchBar.setChecked(isBroadcasting());
+                    mSwitchBar.setEnabled(true);
+                });
+    }
+
+    private boolean isBroadcasting() {
+        return mBroadcast != null && mBroadcast.isEnabled(null);
     }
 }
diff --git a/src/com/android/settings/datausage/AppDataUsageCycleController.kt b/src/com/android/settings/datausage/AppDataUsageCycleController.kt
index b1a0e76..a3b7499 100644
--- a/src/com/android/settings/datausage/AppDataUsageCycleController.kt
+++ b/src/com/android/settings/datausage/AppDataUsageCycleController.kt
@@ -25,7 +25,6 @@
 import androidx.lifecycle.repeatOnLifecycle
 import androidx.preference.PreferenceScreen
 import com.android.settings.core.BasePreferenceController
-import com.android.settings.datausage.lib.AppDataUsageDetailsRepository
 import com.android.settings.datausage.lib.IAppDataUsageDetailsRepository
 import com.android.settings.datausage.lib.NetworkUsageDetailsData
 import kotlinx.coroutines.Dispatchers
@@ -40,11 +39,18 @@
     private lateinit var preference: SpinnerPreference
     private var cycleAdapter: CycleAdapter? = null
 
-    private var initialCycles: List<Long> = emptyList()
-    private var initialSelectedEndTime: Long = -1
-
     private var usageDetailsDataList: List<NetworkUsageDetailsData> = emptyList()
 
+    override fun getAvailabilityStatus() = AVAILABLE
+
+    override fun displayPreference(screen: PreferenceScreen) {
+        super.displayPreference(screen)
+        preference = screen.findPreference(preferenceKey)!!
+        if (cycleAdapter == null) {
+            cycleAdapter = CycleAdapter(mContext, preference)
+        }
+    }
+
     fun init(
         repository: IAppDataUsageDetailsRepository,
         onUsageDataUpdated: (NetworkUsageDetailsData) -> Unit,
@@ -60,22 +66,9 @@
      * before loading to reduce flicker.
      */
     fun setInitialCycles(initialCycles: List<Long>, initialSelectedEndTime: Long) {
-        this.initialCycles = initialCycles
-        this.initialSelectedEndTime = initialSelectedEndTime
-    }
-
-    override fun getAvailabilityStatus() = AVAILABLE
-
-    override fun displayPreference(screen: PreferenceScreen) {
-        super.displayPreference(screen)
-        preference = screen.findPreference(preferenceKey)!!
-        if (cycleAdapter == null) {
-            cycleAdapter = CycleAdapter(mContext, preference).apply {
-                if (initialCycles.isNotEmpty()) {
-                    setInitialCycleList(initialCycles, initialSelectedEndTime)
-                    preference.setHasCycles(true)
-                }
-            }
+        if (initialCycles.isNotEmpty()) {
+            cycleAdapter?.setInitialCycleList(initialCycles, initialSelectedEndTime)
+            preference.setHasCycles(true)
         }
     }
 
diff --git a/src/com/android/settings/datausage/CellDataPreference.java b/src/com/android/settings/datausage/CellDataPreference.java
index 9374217..3bd3ecc 100644
--- a/src/com/android/settings/datausage/CellDataPreference.java
+++ b/src/com/android/settings/datausage/CellDataPreference.java
@@ -26,11 +26,10 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
-import android.widget.Checkable;
+import android.widget.CompoundButton;
 
 import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.app.AlertDialog.Builder;
-import androidx.core.content.res.TypedArrayUtils;
 import androidx.preference.PreferenceViewHolder;
 
 import com.android.settings.R;
@@ -51,12 +50,10 @@
     public int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     public boolean mChecked;
     public boolean mMultiSimDialog;
-    private MobileDataEnabledListener mDataStateListener;
+    private final MobileDataEnabledListener mDataStateListener;
 
     public CellDataPreference(Context context, AttributeSet attrs) {
-        super(context, attrs, TypedArrayUtils.getAttr(context,
-                androidx.preference.R.attr.switchPreferenceStyle,
-                android.R.attr.switchPreferenceStyle));
+        super(context, attrs, androidx.preference.R.attr.switchPreferenceCompatStyle);
         mDataStateListener = new MobileDataEnabledListener(context, this);
     }
 
@@ -170,9 +167,10 @@
     @Override
     public void onBindViewHolder(PreferenceViewHolder holder) {
         super.onBindViewHolder(holder);
-        final View switchView = holder.findViewById(android.R.id.switch_widget);
+        final CompoundButton switchView =
+                (CompoundButton) holder.findViewById(androidx.preference.R.id.switchWidget);
         switchView.setClickable(false);
-        ((Checkable) switchView).setChecked(mChecked);
+        switchView.setChecked(mChecked);
     }
 
     @Override
diff --git a/src/com/android/settings/datausage/ChartDataUsagePreferenceController.kt b/src/com/android/settings/datausage/ChartDataUsagePreferenceController.kt
index 5149af0..780978f 100644
--- a/src/com/android/settings/datausage/ChartDataUsagePreferenceController.kt
+++ b/src/com/android/settings/datausage/ChartDataUsagePreferenceController.kt
@@ -39,6 +39,8 @@
     private lateinit var repository: INetworkCycleDataRepository
     private lateinit var preference: ChartDataUsagePreference
     private lateinit var lifecycleScope: LifecycleCoroutineScope
+    private var lastStartTime: Long? = null
+    private var lastEndTime: Long? = null
 
     open fun init(template: NetworkTemplate) {
         this.repository = NetworkCycleDataRepository(mContext, template)
@@ -72,6 +74,10 @@
     }
 
     fun update(startTime: Long, endTime: Long) {
+        if (lastStartTime == startTime && lastEndTime == endTime) return
+        lastStartTime = startTime
+        lastEndTime = endTime
+
         preference.setTime(startTime, endTime)
         preference.setNetworkCycleData(NetworkCycleChartData.AllZero)
         lifecycleScope.launch {
diff --git a/src/com/android/settings/datausage/DataUsageList.kt b/src/com/android/settings/datausage/DataUsageList.kt
index 7240150..30e8db3 100644
--- a/src/com/android/settings/datausage/DataUsageList.kt
+++ b/src/com/android/settings/datausage/DataUsageList.kt
@@ -31,9 +31,10 @@
 import com.android.settings.R
 import com.android.settings.datausage.lib.BillingCycleRepository
 import com.android.settings.datausage.lib.NetworkUsageData
-import com.android.settings.network.MobileDataEnabledListener
 import com.android.settings.network.MobileNetworkRepository
+import com.android.settings.network.mobileDataEnabledFlow
 import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity
+import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
 import com.android.settingslib.spaprivileged.framework.common.userManager
 import com.android.settingslib.utils.ThreadUtils
 import kotlin.jvm.optionals.getOrNull
@@ -43,10 +44,7 @@
  * to inspect based on usage cycle and control through [NetworkPolicy].
  */
 @OpenForTesting
-open class DataUsageList : DataUsageBaseFragment(), MobileDataEnabledListener.Client {
-    @VisibleForTesting
-    lateinit var dataStateListener: MobileDataEnabledListener
-
+open class DataUsageList : DataUsageBaseFragment() {
     @JvmField
     @VisibleForTesting
     var template: NetworkTemplate? = null
@@ -89,7 +87,6 @@
             return
         }
         updateSubscriptionInfoEntity()
-        dataStateListener = MobileDataEnabledListener(activity, this)
         dataUsageListAppsController = use(DataUsageListAppsController::class.java).apply {
             init(template)
         }
@@ -103,6 +100,9 @@
     override fun onViewCreated(v: View, savedInstanceState: Bundle?) {
         super.onViewCreated(v, savedInstanceState)
 
+        requireContext().mobileDataEnabledFlow(subId)
+            .collectLatestWithLifecycle(viewLifecycleOwner) { updatePolicy() }
+
         val template = template ?: return
         dataUsageListHeaderController = DataUsageListHeaderController(
             setPinnedHeaderView(R.layout.apps_filter_spinner),
@@ -114,17 +114,6 @@
         )
     }
 
-    override fun onResume() {
-        super.onResume()
-        dataStateListener.start(subId)
-        updatePolicy()
-    }
-
-    override fun onPause() {
-        super.onPause()
-        dataStateListener.stop()
-    }
-
     override fun getPreferenceScreenResId() = R.xml.data_usage_list
 
     override fun getLogTag() = TAG
@@ -154,13 +143,6 @@
         }
     }
 
-    /**
-     * Implementation of `MobileDataEnabledListener.Client`
-     */
-    override fun onMobileDataEnabledChange() {
-        updatePolicy()
-    }
-
     /** Update chart sweeps and cycle list to reflect [NetworkPolicy] for current [template]. */
     @VisibleForTesting
     fun updatePolicy() {
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index b88c345..a995ac3 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -666,6 +666,7 @@
         controllers.add(new ShowKeyPressesPreferenceController(context));
         controllers.add(new ShowSurfaceUpdatesPreferenceController(context));
         controllers.add(new ShowLayoutBoundsPreferenceController(context));
+        controllers.add(new ShowHdrSdrRatioPreferenceController(context));
         controllers.add(new ShowRefreshRatePreferenceController(context));
         controllers.add(new RtlLayoutPreferenceController(context));
         controllers.add(new WindowAnimationScalePreferenceController(context));
diff --git a/src/com/android/settings/development/OWNERS b/src/com/android/settings/development/OWNERS
new file mode 100644
index 0000000..6443afe
--- /dev/null
+++ b/src/com/android/settings/development/OWNERS
@@ -0,0 +1,5 @@
+# ShowHdrSdrRatioPreferenceController
+per-file ShowHdrSdrRatioPreferenceController.java=file:platform/frameworks/native:/services/surfaceflinger/OWNERS
+
+# ShowRefreshRatePreferenceController
+per-file ShowRefreshRatePreferenceController.java=file:platform/frameworks/native:/services/surfaceflinger/OWNERS
diff --git a/src/com/android/settings/development/ShowHdrSdrRatioPreferenceController.java b/src/com/android/settings/development/ShowHdrSdrRatioPreferenceController.java
new file mode 100644
index 0000000..2e7807e
--- /dev/null
+++ b/src/com/android/settings/development/ShowHdrSdrRatioPreferenceController.java
@@ -0,0 +1,144 @@
+/**
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.development;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.view.Display;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.TwoStatePreference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.flags.Flags;
+import com.android.settingslib.development.DeveloperOptionsPreferenceController;
+
+/**
+ * Controller class for controlling the hdr/sdr ratio on SurfaceFlinger
+ */
+public class ShowHdrSdrRatioPreferenceController extends DeveloperOptionsPreferenceController
+        implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
+
+    private static final String SHOW_REFRESH_RATE_KEY = "show_hdr_sdr_ratio";
+
+    private static final int SETTING_VALUE_QUERY = 2;
+    private static final int SETTING_VALUE_ON = 1;
+    private static final int SETTING_VALUE_OFF = 0;
+
+    private static final String SURFACE_FLINGER_SERVICE_KEY = "SurfaceFlinger";
+
+    private static final int SURFACE_FLINGER_CODE = 1043;
+
+    private static final String SURFACE_COMPOSER_INTERFACE_KEY = "android.ui.ISurfaceComposer";
+
+    private final IBinder mSurfaceFlinger;
+
+    private final boolean mIsHdrSdrRatioAvailable;
+
+    public ShowHdrSdrRatioPreferenceController(Context context) {
+        super(context);
+        mSurfaceFlinger = ServiceManager.getService(SURFACE_FLINGER_SERVICE_KEY);
+        DisplayManager displayManager = context.getSystemService(DisplayManager.class);
+        Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
+        mIsHdrSdrRatioAvailable = display != null && display.isHdrSdrRatioAvailable();
+    }
+
+    @VisibleForTesting
+    ShowHdrSdrRatioPreferenceController(Context context, IBinder surfaceFlinger,
+                                        boolean isHdrSdrRatioAvailable) {
+        super(context);
+        mSurfaceFlinger = surfaceFlinger;
+        mIsHdrSdrRatioAvailable = isHdrSdrRatioAvailable;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return SHOW_REFRESH_RATE_KEY;
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        final boolean isEnabled = (Boolean) newValue;
+        writeShowHdrSdrRatioSetting(isEnabled);
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+        updateShowHdrSdrRatioSetting();
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return Flags.developmentHdrSdrRatio() && mIsHdrSdrRatioAvailable;
+    }
+
+    @Override
+    protected void onDeveloperOptionsSwitchDisabled() {
+        super.onDeveloperOptionsSwitchDisabled();
+        final TwoStatePreference preference = (TwoStatePreference) mPreference;
+        if (preference.isChecked()) {
+            // Writing false to the preference when the setting is already off will have a
+            // side effect of turning on the preference that we wish to avoid
+            writeShowHdrSdrRatioSetting(false);
+            preference.setChecked(false);
+        }
+    }
+
+    private void updateShowHdrSdrRatioSetting() {
+        // magic communication with surface flinger.
+        try {
+            if (mSurfaceFlinger != null) {
+                final Parcel data = Parcel.obtain();
+                final Parcel reply = Parcel.obtain();
+                data.writeInterfaceToken(SURFACE_COMPOSER_INTERFACE_KEY);
+                data.writeInt(SETTING_VALUE_QUERY);
+                mSurfaceFlinger.transact(SURFACE_FLINGER_CODE, data, reply, 0 /* flags */);
+                final boolean enabled = reply.readBoolean();
+                ((TwoStatePreference) mPreference).setChecked(enabled);
+                reply.recycle();
+                data.recycle();
+            }
+        } catch (RemoteException ex) {
+            // intentional no-op
+        }
+    }
+
+    @VisibleForTesting
+    void writeShowHdrSdrRatioSetting(boolean isEnabled) {
+        try {
+            if (mSurfaceFlinger != null) {
+                final Parcel data = Parcel.obtain();
+                data.writeInterfaceToken(SURFACE_COMPOSER_INTERFACE_KEY);
+                final int showHdrSdrRatio = isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF;
+                data.writeInt(showHdrSdrRatio);
+                mSurfaceFlinger.transact(SURFACE_FLINGER_CODE, data,
+                        null /* reply */, 0 /* flags */);
+                data.recycle();
+            }
+        } catch (RemoteException ex) {
+            // intentional no-op
+        }
+        updateShowHdrSdrRatioSetting();
+    }
+}
diff --git a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogFragment.java b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogFragment.java
index e82d541..f212eea 100644
--- a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogFragment.java
+++ b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogFragment.java
@@ -111,10 +111,7 @@
             Log.d(TAG, "Fragment not attached yet.");
             return;
         }
-        setText(viewId, text, true);
-    }
 
-    public void setText(int viewId, CharSequence text, boolean enableCopy) {
         final TextView textView = mRootView.findViewById(viewId);
         if (textView == null) {
             return;
@@ -125,6 +122,5 @@
             text = PhoneNumberUtil.expandByTts(text);
         }
         textView.setText(text);
-        textView.setTextIsSelectable(enableCopy);
     }
 }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java b/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java
index ebf1543..dc8dfb6 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java
@@ -70,8 +70,7 @@
                 break;
             case Intent.ACTION_TIME_CHANGED:
                 Log.d(TAG, "refresh job and clear all data from action=" + action);
-                DatabaseUtils.clearAll(context);
-                PeriodicJobManager.getInstance(context).refreshJob(/*fromBoot=*/ false);
+                DatabaseUtils.clearDataAfterTimeChangedIfNeeded(context);
                 break;
             default:
                 Log.w(TAG, "receive unsupported action=" + action);
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
index 549a0bf..99cf354 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
@@ -71,8 +71,6 @@
     private static final String TAG = "DataProcessManager";
     private static final List<BatteryEventType> POWER_CONNECTION_EVENTS =
             List.of(BatteryEventType.POWER_CONNECTED, BatteryEventType.POWER_DISCONNECTED);
-    private static final List<BatteryEventType> BATTERY_LEVEL_RECORD_EVENTS =
-            List.of(BatteryEventType.FULL_CHARGED, BatteryEventType.EVEN_HOUR);
 
     // For testing only.
     @VisibleForTesting
@@ -574,7 +572,7 @@
         final List<BatteryEvent> batteryLevelRecordEvents =
                 DatabaseUtils.getBatteryEvents(
                         context, Calendar.getInstance(), lastFullChargeTime,
-                        BATTERY_LEVEL_RECORD_EVENTS);
+                        DatabaseUtils.BATTERY_LEVEL_RECORD_EVENTS);
         final long startTimestamp = batteryLevelRecordEvents.isEmpty()
                 ? lastFullChargeTime : batteryLevelRecordEvents.get(0).getTimestamp();
         final BatteryLevelData batteryLevelData = getPeriodBatteryLevelData(context, handler,
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
index e78d25c..d099843 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
@@ -53,6 +53,7 @@
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 import java.util.function.Function;
@@ -133,6 +134,9 @@
                     .authority(AUTHORITY)
                     .appendPath(BATTERY_USAGE_SLOT_TABLE)
                     .build();
+    /** A list of level record event types to access battery usage data. */
+    public static final List<BatteryEventType> BATTERY_LEVEL_RECORD_EVENTS =
+            List.of(BatteryEventType.FULL_CHARGED, BatteryEventType.EVEN_HOUR);
 
     // For testing only.
     @VisibleForTesting
@@ -408,6 +412,35 @@
         });
     }
 
+    /** Clears all data and jobs if current timestamp is out of the range of last recorded job. */
+    public static void clearDataAfterTimeChangedIfNeeded(Context context) {
+        AsyncTask.execute(() -> {
+            try {
+                final List<BatteryEvent> batteryLevelRecordEvents =
+                        DatabaseUtils.getBatteryEvents(context, Calendar.getInstance(),
+                                getLastFullChargeTime(context), BATTERY_LEVEL_RECORD_EVENTS);
+                final long lastRecordTimestamp = batteryLevelRecordEvents.isEmpty()
+                        ? INVALID_TIMESTAMP : batteryLevelRecordEvents.get(0).getTimestamp();
+                final long nextRecordTimestamp =
+                        TimestampUtils.getNextEvenHourTimestamp(lastRecordTimestamp);
+                final long currentTime = System.currentTimeMillis();
+                final boolean isOutOfTimeRange = lastRecordTimestamp == INVALID_TIMESTAMP
+                        || currentTime < lastRecordTimestamp || currentTime > nextRecordTimestamp;
+                final String logInfo = String.format(Locale.ENGLISH,
+                        "clear database = %b, current time = %d, last record time = %d",
+                        isOutOfTimeRange, currentTime, lastRecordTimestamp);
+                Log.d(TAG, logInfo);
+                BatteryUsageLogUtils.writeLog(context, Action.TIME_UPDATED, logInfo);
+                if (isOutOfTimeRange) {
+                    DatabaseUtils.clearAll(context);
+                    PeriodicJobManager.getInstance(context).refreshJob(/* fromBoot= */ false);
+                }
+            } catch (RuntimeException e) {
+                Log.e(TAG, "refreshDataAndJobIfNeededAfterTimeChanged() failed", e);
+            }
+        });
+    }
+
     /** Returns the timestamp for 00:00 6 days before the calendar date. */
     public static long getTimestampSixDaysAgo(Calendar calendar) {
         Calendar startCalendar =
diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDao.java b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDao.java
index 0a6de71..91ff4e8 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDao.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDao.java
@@ -43,7 +43,7 @@
 
     /** Gets the {@link Cursor} of all recorded data after a specific timestamp. */
     @Query("SELECT * FROM BatteryEventEntity"
-            + " WHERE timestamp > :timestamp AND batteryEventType IN (:batteryEventTypes)"
+            + " WHERE timestamp >= :timestamp AND batteryEventType IN (:batteryEventTypes)"
             + " ORDER BY timestamp DESC")
     Cursor getAllAfter(long timestamp, List<Integer> batteryEventTypes);
 
diff --git a/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java b/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java
new file mode 100644
index 0000000..be72e56
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.datasaver;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.net.NetworkPolicyManager;
+
+import androidx.annotation.VisibleForTesting;
+
+/** A class to dynamically manage per apps {@link NetworkPolicyManager} POLICY_ flags. */
+public final class DynamicDenylistManager {
+
+    private static final String TAG = "DynamicDenylistManager";
+    private static final String PREF_KEY_MANUAL_DENY = "manual_denylist_preference";
+    private static final String PREF_KEY_DYNAMIC_DENY = "dynamic_denylist_preference";
+
+    private final Context mContext;
+    private final NetworkPolicyManager mNetworkPolicyManager;
+
+    private static DynamicDenylistManager sInstance;
+
+    /** @return a DynamicDenylistManager object */
+    public static DynamicDenylistManager getInstance(Context context) {
+        synchronized (DynamicDenylistManager.class) {
+            if (sInstance == null) {
+                sInstance = new DynamicDenylistManager(context);
+            }
+            return sInstance;
+        }
+    }
+
+    DynamicDenylistManager(Context context) {
+        mContext = context.getApplicationContext();
+        mNetworkPolicyManager = NetworkPolicyManager.from(mContext);
+    }
+
+    /** Update the target uid policy in {@link #getManualDenylistPref()}. */
+    public void updateManualDenylist(String uid, int policy) {
+        if (policy != NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND) {
+            getManualDenylistPref().edit().remove(uid).apply();
+        } else {
+            getManualDenylistPref().edit().putInt(uid, policy).apply();
+        }
+    }
+
+    /** Return true if the target uid is in {@link #getManualDenylistPref()}. */
+    public boolean isInManualDenylist(String uid) {
+        return getManualDenylistPref().contains(uid);
+    }
+
+    /** Clear all data in {@link #getManualDenylistPref()} */
+    public void clearManualDenylistPref() {
+        getManualDenylistPref().edit().clear().apply();
+    }
+
+    /** Clear all data in {@link #getDynamicDenylistPref()} */
+    public void clearDynamicDenylistPref() {
+        getDynamicDenylistPref().edit().clear().apply();
+    }
+
+    @VisibleForTesting
+    SharedPreferences getManualDenylistPref() {
+        return mContext.getSharedPreferences(PREF_KEY_MANUAL_DENY, Context.MODE_PRIVATE);
+    }
+
+    @VisibleForTesting
+    SharedPreferences getDynamicDenylistPref() {
+        return mContext.getSharedPreferences(PREF_KEY_DYNAMIC_DENY, Context.MODE_PRIVATE);
+    }
+}
diff --git a/src/com/android/settings/localepicker/AppLocalePickerActivity.java b/src/com/android/settings/localepicker/AppLocalePickerActivity.java
index 194a08f..6706c6d 100644
--- a/src/com/android/settings/localepicker/AppLocalePickerActivity.java
+++ b/src/com/android/settings/localepicker/AppLocalePickerActivity.java
@@ -23,6 +23,7 @@
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.app.settings.SettingsEnums;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.net.Uri;
@@ -45,12 +46,18 @@
 import com.android.settings.applications.AppLocaleUtil;
 import com.android.settings.applications.appinfo.AppLocaleDetails;
 import com.android.settings.core.SettingsBaseActivity;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 public class AppLocalePickerActivity extends SettingsBaseActivity
         implements LocalePickerWithRegion.LocaleSelectedListener, MenuItem.OnActionExpandListener {
     private static final String TAG = AppLocalePickerActivity.class.getSimpleName();
     private static final String CHANNEL_ID_SUGGESTION = "suggestion";
     private static final String CHANNEL_ID_SUGGESTION_TO_USER = "Locale suggestion";
+    private static final int SIM_LOCALE = 1 << 0;
+    private static final int SYSTEM_LOCALE = 1 << 1;
+    private static final int APP_LOCALE = 1 << 2;
+    private static final int IME_LOCALE = 1 << 3;
     static final String EXTRA_APP_LOCALE = "app_locale";
     static final String EXTRA_NOTIFICATION_ID = "notification_id";
 
@@ -59,6 +66,7 @@
     private AppLocaleDetails mAppLocaleDetails;
     private View mAppLocaleDetailContainer;
     private NotificationController mNotificationController;
+    private MetricsFeatureProvider mMetricsFeatureProvider;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -84,6 +92,7 @@
 
         setTitle(R.string.app_locale_picker_title);
         getActionBar().setDisplayHomeAsUpEnabled(true);
+        mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
         mNotificationController = NotificationController.getInstance(this);
 
         mLocalePickerWithRegion = LocalePickerWithRegion.createLanguagePicker(
@@ -113,6 +122,7 @@
         if (localeInfo == null || localeInfo.getLocale() == null || localeInfo.isSystemLocale()) {
             setAppDefaultLocale("");
         } else {
+            logLocaleSource(localeInfo);
             setAppDefaultLocale(localeInfo.getLocale().toLanguageTag());
             broadcastAppLocaleChange(localeInfo);
         }
@@ -268,4 +278,32 @@
 
         return false;
     }
+
+    private void logLocaleSource(LocaleStore.LocaleInfo localeInfo) {
+        if (!localeInfo.isSuggested() || localeInfo.isAppCurrentLocale()) {
+            return;
+        }
+        int localeSource = 0;
+        if (hasSuggestionType(localeInfo,
+                LocaleStore.LocaleInfo.SUGGESTION_TYPE_SYSTEM_AVAILABLE_LANGUAGE)) {
+            localeSource |= SYSTEM_LOCALE;
+        }
+        if (hasSuggestionType(localeInfo,
+                LocaleStore.LocaleInfo.SUGGESTION_TYPE_OTHER_APP_LANGUAGE)) {
+            localeSource |= APP_LOCALE;
+        }
+        if (hasSuggestionType(localeInfo, LocaleStore.LocaleInfo.SUGGESTION_TYPE_IME_LANGUAGE)) {
+            localeSource |= IME_LOCALE;
+        }
+        if (hasSuggestionType(localeInfo, LocaleStore.LocaleInfo.SUGGESTION_TYPE_SIM)) {
+            localeSource |= SIM_LOCALE;
+        }
+        mMetricsFeatureProvider.action(this,
+                SettingsEnums.ACTION_CHANGE_APP_LANGUAGE_FROM_SUGGESTED, localeSource);
+    }
+
+    private static boolean hasSuggestionType(LocaleStore.LocaleInfo localeInfo,
+            int suggestionType) {
+        return localeInfo.isSuggestionOfType(suggestionType);
+    }
 }
diff --git a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
index bfe0749..43fc9cf 100644
--- a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
+++ b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.localepicker;
 
+import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.os.Bundle;
@@ -38,6 +39,7 @@
 import com.android.internal.app.LocalePicker;
 import com.android.internal.app.LocaleStore;
 import com.android.settings.R;
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.shortcut.ShortcutsUpdateTask;
 
 import java.text.NumberFormat;
@@ -225,6 +227,12 @@
                     "Negative position in onItemMove %d -> %d", fromPosition, toPosition));
         }
 
+        if (fromPosition != toPosition) {
+            FeatureFactory.getFeatureFactory().getMetricsFeatureProvider()
+                    .action(mContext, SettingsEnums.ACTION_REORDER_LANGUAGE,
+                            mDragLocale.getLocale().toLanguageTag() + " move to " + toPosition);
+        }
+
         notifyItemChanged(fromPosition); // to update the numbers
         notifyItemChanged(toPosition);
         notifyItemMoved(fromPosition, toPosition);
@@ -263,6 +271,9 @@
         for (int i = itemCount - 1; i >= 0; i--) {
             localeInfo = mFeedItemList.get(i);
             if (localeInfo.getChecked()) {
+                FeatureFactory.getFeatureFactory().getMetricsFeatureProvider()
+                        .action(mContext, SettingsEnums.ACTION_REMOVE_LANGUAGE,
+                                localeInfo.getLocale().toLanguageTag());
                 mFeedItemList.remove(i);
             }
         }
diff --git a/src/com/android/settings/localepicker/LocaleHelperPreferenceController.java b/src/com/android/settings/localepicker/LocaleHelperPreferenceController.java
index a639c9d..b962b9e 100644
--- a/src/com/android/settings/localepicker/LocaleHelperPreferenceController.java
+++ b/src/com/android/settings/localepicker/LocaleHelperPreferenceController.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.localepicker;
 
+import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.content.Intent;
 import android.util.Log;
@@ -24,8 +25,10 @@
 import androidx.preference.PreferenceScreen;
 
 import com.android.settings.R;
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.HelpUtils;
 import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.widget.FooterPreference;
 
 /**
@@ -36,8 +39,11 @@
 
     private static final String KEY_FOOTER_LANGUAGE_PICKER = "footer_languages_picker";
 
+    private final MetricsFeatureProvider mMetricsFeatureProvider;
+
     public LocaleHelperPreferenceController(Context context) {
         super(context);
+        mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
     }
 
     @Override
@@ -72,6 +78,7 @@
                 mContext.getString(R.string.link_locale_picker_footer_learn_more),
                 mContext.getClass().getName());
         if (intent != null) {
+            mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_LANGUAGES_LEARN_MORE);
             mContext.startActivity(intent);
         } else {
             Log.w(TAG, "HelpIntent is null");
diff --git a/src/com/android/settings/localepicker/LocaleListEditor.java b/src/com/android/settings/localepicker/LocaleListEditor.java
index bdda549..28f066a 100644
--- a/src/com/android/settings/localepicker/LocaleListEditor.java
+++ b/src/com/android/settings/localepicker/LocaleListEditor.java
@@ -224,6 +224,8 @@
             localeInfo = mayAppendUnicodeTags(localeInfo, preferencesTags);
             mAdapter.addLocale(localeInfo);
             updateVisibilityOfRemoveMenu();
+            mMetricsFeatureProvider.action(getContext(), SettingsEnums.ACTION_ADD_LANGUAGE,
+                    localeInfo.getLocale().toLanguageTag());
         } else if (requestCode == DIALOG_CONFIRM_SYSTEM_DEFAULT) {
             localeInfo = mAdapter.getFeedItemList().get(0);
             if (resultCode == Activity.RESULT_OK) {
diff --git a/src/com/android/settings/network/MobileDataEnabledFlow.kt b/src/com/android/settings/network/MobileDataEnabledFlow.kt
new file mode 100644
index 0000000..2342377
--- /dev/null
+++ b/src/com/android/settings/network/MobileDataEnabledFlow.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network
+
+import android.content.Context
+import android.provider.Settings
+import android.telephony.SubscriptionManager
+import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalChangeFlow
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.merge
+
+/**
+ * Flow for mobile data enabled changed event.
+ *
+ * Note: This flow can only notify enabled status changes, cannot provide the latest status.
+ */
+fun Context.mobileDataEnabledFlow(subId: Int): Flow<Unit> {
+    val flow = settingsGlobalChangeFlow(Settings.Global.MOBILE_DATA)
+    return when (subId) {
+        SubscriptionManager.INVALID_SUBSCRIPTION_ID -> flow
+        else -> {
+            val subIdFlow = settingsGlobalChangeFlow(
+                name = Settings.Global.MOBILE_DATA + subId,
+                sendInitialValue = false,
+            )
+            merge(flow, subIdFlow)
+        }
+    }
+}
diff --git a/src/com/android/settings/network/MobileDataEnabledListener.java b/src/com/android/settings/network/MobileDataEnabledListener.java
index b030823..f2d55ab 100644
--- a/src/com/android/settings/network/MobileDataEnabledListener.java
+++ b/src/com/android/settings/network/MobileDataEnabledListener.java
@@ -20,7 +20,12 @@
 import android.provider.Settings;
 import android.telephony.SubscriptionManager;
 
-/** Helper class to listen for changes in the enabled state of mobile data. */
+/**
+ *  Helper class to listen for changes in the enabled state of mobile data.
+ *
+ * @deprecated use {@link MobileDataEnabledFlowKt} instead
+ */
+@Deprecated
 public class MobileDataEnabledListener {
     private Context mContext;
     private Client mClient;
diff --git a/src/com/android/settings/network/MobileNetworkListFragment.java b/src/com/android/settings/network/MobileNetworkListFragment.java
deleted file mode 100644
index 3de05af..0000000
--- a/src/com/android/settings/network/MobileNetworkListFragment.java
+++ /dev/null
@@ -1,73 +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;
-
-import android.app.settings.SettingsEnums;
-import android.content.Context;
-import android.os.UserManager;
-
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.settings.R;
-import com.android.settings.dashboard.DashboardFragment;
-import com.android.settings.network.telephony.MobileNetworkUtils;
-import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settingslib.search.SearchIndexable;
-
-@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
-public class MobileNetworkListFragment extends DashboardFragment {
-    private static final String LOG_TAG = "NetworkListFragment";
-
-    private static final String KEY_ADD_SIM = "add_sim";
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        // Disable the animation of the preference list
-        final RecyclerView prefListView = getListView();
-        if (prefListView != null) {
-            prefListView.setItemAnimator(null);
-        }
-
-        findPreference(KEY_ADD_SIM).setVisible(MobileNetworkUtils.showEuiccSettings(getContext()));
-    }
-
-    @Override
-    protected int getPreferenceScreenResId() {
-        return R.xml.network_provider_sims_list;
-    }
-
-    @Override
-    protected String getLogTag() {
-        return LOG_TAG;
-    }
-
-    @Override
-    public int getMetricsCategory() {
-        return SettingsEnums.MOBILE_NETWORK_LIST;
-    }
-
-    public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
-            new BaseSearchIndexProvider(R.xml.network_provider_sims_list) {
-
-                @Override
-                protected boolean isPageSearchEnabled(Context context) {
-                    return SubscriptionUtil.isSimHardwareVisible(context) &&
-                            context.getSystemService(UserManager.class).isAdminUser();
-                }
-            };
-}
diff --git a/src/com/android/settings/network/MobileNetworkListFragment.kt b/src/com/android/settings/network/MobileNetworkListFragment.kt
new file mode 100644
index 0000000..09b1150
--- /dev/null
+++ b/src/com/android/settings/network/MobileNetworkListFragment.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network
+
+import android.app.settings.SettingsEnums
+import android.content.Context
+import android.os.Bundle
+import android.provider.Settings
+import android.view.View
+import androidx.annotation.VisibleForTesting
+import androidx.preference.Preference
+import com.android.settings.R
+import com.android.settings.SettingsPreferenceFragment
+import com.android.settings.dashboard.DashboardFragment
+import com.android.settings.network.telephony.MobileNetworkUtils
+import com.android.settings.search.BaseSearchIndexProvider
+import com.android.settingslib.search.SearchIndexable
+import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
+import com.android.settingslib.spaprivileged.framework.common.userManager
+import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalBooleanFlow
+
+@SearchIndexable(forTarget = SearchIndexable.ALL and SearchIndexable.ARC.inv())
+class MobileNetworkListFragment : DashboardFragment() {
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        collectAirplaneModeAndFinishIfOn()
+    }
+
+    override fun onResume() {
+        super.onResume()
+        // Disable the animation of the preference list
+        listView.itemAnimator = null
+
+        findPreference<Preference>(KEY_ADD_SIM)!!.isVisible =
+            MobileNetworkUtils.showEuiccSettings(context)
+    }
+
+    override fun getPreferenceScreenResId() = R.xml.network_provider_sims_list
+
+    override fun getLogTag() = LOG_TAG
+
+    override fun getMetricsCategory() = SettingsEnums.MOBILE_NETWORK_LIST
+
+    companion object {
+        private const val LOG_TAG = "NetworkListFragment"
+        private const val KEY_ADD_SIM = "add_sim"
+
+        @JvmStatic
+        fun SettingsPreferenceFragment.collectAirplaneModeAndFinishIfOn() {
+            requireContext().settingsGlobalBooleanFlow(Settings.Global.AIRPLANE_MODE_ON)
+                .collectLatestWithLifecycle(viewLifecycleOwner) { isAirplaneModeOn ->
+                    if (isAirplaneModeOn) {
+                        finish()
+                    }
+                }
+        }
+
+        @JvmField
+        val SEARCH_INDEX_DATA_PROVIDER = SearchIndexProvider()
+
+        @VisibleForTesting
+        class SearchIndexProvider : BaseSearchIndexProvider(R.xml.network_provider_sims_list) {
+            public override fun isPageSearchEnabled(context: Context): Boolean =
+                SubscriptionUtil.isSimHardwareVisible(context) &&
+                    context.userManager.isAdminUser
+        }
+    }
+}
diff --git a/src/com/android/settings/network/apn/ApnEditPageProvider.kt b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
index 81da0bf..0ed54e7 100644
--- a/src/com/android/settings/network/apn/ApnEditPageProvider.kt
+++ b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
@@ -38,7 +38,6 @@
 import com.android.settings.network.apn.ApnNetworkTypes.getNetworkTypeDisplayNames
 import com.android.settings.network.apn.ApnNetworkTypes.getNetworkTypeSelectedOptionsState
 import com.android.settingslib.spa.framework.common.SettingsPageProvider
-import com.android.settingslib.spa.framework.compose.stateOf
 import com.android.settingslib.spa.widget.editor.SettingsExposedDropdownMenuBox
 import com.android.settingslib.spa.widget.editor.SettingsExposedDropdownMenuCheckBox
 import com.android.settingslib.spa.widget.editor.SettingsOutlinedTextField
@@ -186,10 +185,8 @@
             SwitchPreference(
                 object : SwitchPreferenceModel {
                     override val title = context.resources.getString(R.string.carrier_enabled)
-                    override val changeable =
-                        stateOf(apnData.apnEnableEnabled)
-                    override val checked =
-                        stateOf(apnData.apnEnable)
+                    override val changeable = { apnData.apnEnableEnabled }
+                    override val checked = { apnData.apnEnable }
                     override val onCheckedChange = { newChecked: Boolean ->
                         apnData = apnData.copy(apnEnable = newChecked)
                     }
diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
index afc1b7e..a514414 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.network.telephony;
 
+import static com.android.settings.network.MobileNetworkListFragment.collectAirplaneModeAndFinishIfOn;
+
 import android.app.Activity;
 import android.app.settings.SettingsEnums;
 import android.content.Context;
@@ -31,7 +33,10 @@
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
+import android.view.View;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
 
@@ -327,6 +332,12 @@
     }
 
     @Override
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        collectAirplaneModeAndFinishIfOn(this);
+    }
+
+    @Override
     public void onResume() {
         super.onResume();
         mMobileNetworkRepository.addRegister(this, this, mSubId);
@@ -361,11 +372,6 @@
         super.onPause();
     }
 
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-    }
-
     @VisibleForTesting
     void onRestoreInstance(Bundle icicle) {
         if (icicle != null) {
diff --git a/src/com/android/settings/notification/app/AppChannelsBypassingDndPreferenceController.java b/src/com/android/settings/notification/app/AppChannelsBypassingDndPreferenceController.java
index 2830024..200a47b 100644
--- a/src/com/android/settings/notification/app/AppChannelsBypassingDndPreferenceController.java
+++ b/src/com/android/settings/notification/app/AppChannelsBypassingDndPreferenceController.java
@@ -39,13 +39,18 @@
 import com.android.settings.applications.AppInfoBase;
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.flags.Flags;
 import com.android.settings.notification.NotificationBackend;
 import com.android.settingslib.PrimarySwitchPreference;
 import com.android.settingslib.RestrictedSwitchPreference;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * Populates the PreferenceCategory with notification channels associated with the given app.
@@ -61,6 +66,9 @@
     private RestrictedSwitchPreference mAllNotificationsToggle;
     private PreferenceCategory mPreferenceCategory;
     private List<NotificationChannel> mChannels = new ArrayList<>();
+    private Set<String> mDuplicateChannelNames = new HashSet<>();
+    private Map<NotificationChannel, String> mChannelGroupNames =
+            new HashMap<NotificationChannel, String>();
 
     public AppChannelsBypassingDndPreferenceController(
             Context context,
@@ -135,10 +143,20 @@
                 List<NotificationChannel> newChannelList = new ArrayList<>();
                 List<NotificationChannelGroup> mChannelGroupList = mBackend.getGroups(mAppRow.pkg,
                         mAppRow.uid).getList();
+                Set<String> allChannelNames = new HashSet<>();
                 for (NotificationChannelGroup channelGroup : mChannelGroupList) {
                     for (NotificationChannel channel : channelGroup.getChannels()) {
                         if (!isConversation(channel)) {
                             newChannelList.add(channel);
+                            if (Flags.dedupeDndSettingsChannels()) {
+                                mChannelGroupNames.put(channel, channelGroup.getName().toString());
+                                // Check if channel name is unique on this page; if not, save it.
+                                if (allChannelNames.contains(channel.getName())) {
+                                    mDuplicateChannelNames.add(channel.getName().toString());
+                                } else {
+                                    allChannelNames.add(channel.getName().toString());
+                                }
+                            }
                         }
                     }
                 }
@@ -172,6 +190,17 @@
                             && isChannelConfigurable(channel)
                             && showNotification(channel));
             channelPreference.setTitle(BidiFormatter.getInstance().unicodeWrap(channel.getName()));
+            if (Flags.dedupeDndSettingsChannels()) {
+                // If the channel shares its name with another channel, set group name as summary
+                // to disambiguate in the list.
+                if (mDuplicateChannelNames.contains(channel.getName().toString())
+                        && mChannelGroupNames.containsKey(channel)
+                        && mChannelGroupNames.get(channel) != null
+                        && !mChannelGroupNames.get(channel).isEmpty()) {
+                    channelPreference.setSummary(BidiFormatter.getInstance().unicodeWrap(
+                            mChannelGroupNames.get(channel)));
+                }
+            }
             channelPreference.setChecked(showNotificationInDnd(channel));
             channelPreference.setOnPreferenceChangeListener(
                     new Preference.OnPreferenceChangeListener() {
diff --git a/src/com/android/settings/overlay/FeatureFactory.kt b/src/com/android/settings/overlay/FeatureFactory.kt
index ac689d9..bc0cf1f 100644
--- a/src/com/android/settings/overlay/FeatureFactory.kt
+++ b/src/com/android/settings/overlay/FeatureFactory.kt
@@ -39,6 +39,7 @@
 import com.android.settings.onboarding.OnboardingFeatureProvider
 import com.android.settings.overlay.FeatureFactory.Companion.setFactory
 import com.android.settings.panel.PanelFeatureProvider
+import com.android.settings.privatespace.PrivateSpaceLoginFeatureProvider
 import com.android.settings.search.SearchFeatureProvider
 import com.android.settings.security.SecurityFeatureProvider
 import com.android.settings.security.SecuritySettingsFeatureProvider
@@ -170,6 +171,11 @@
      */
     abstract val fastPairFeatureProvider: FastPairFeatureProvider
 
+    /**
+     * Gets implementation for Private Space account login feature.
+     */
+    abstract val privateSpaceLoginFeatureProvider: PrivateSpaceLoginFeatureProvider
+
     companion object {
         private var _factory: FeatureFactory? = null
 
diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.kt b/src/com/android/settings/overlay/FeatureFactoryImpl.kt
index 7f991b7..28dbb23 100644
--- a/src/com/android/settings/overlay/FeatureFactoryImpl.kt
+++ b/src/com/android/settings/overlay/FeatureFactoryImpl.kt
@@ -59,6 +59,8 @@
 import com.android.settings.security.SecurityFeatureProviderImpl
 import com.android.settings.security.SecuritySettingsFeatureProvider
 import com.android.settings.security.SecuritySettingsFeatureProviderImpl
+import com.android.settings.privatespace.PrivateSpaceLoginFeatureProvider
+import com.android.settings.privatespace.PrivateSpaceLoginFeatureProviderImpl
 import com.android.settings.slices.SlicesFeatureProviderImpl
 import com.android.settings.users.UserFeatureProviderImpl
 import com.android.settings.vpn2.AdvancedVpnFeatureProviderImpl
@@ -184,4 +186,8 @@
     override val fastPairFeatureProvider: FastPairFeatureProvider by lazy {
         FastPairFeatureProviderImpl()
     }
+
+    override val privateSpaceLoginFeatureProvider: PrivateSpaceLoginFeatureProvider by lazy {
+        PrivateSpaceLoginFeatureProviderImpl()
+    }
 }
diff --git a/src/com/android/settings/privatespace/AutoAdvanceSetupFragment.java b/src/com/android/settings/privatespace/AutoAdvanceSetupFragment.java
index 5456c01..3b59166 100644
--- a/src/com/android/settings/privatespace/AutoAdvanceSetupFragment.java
+++ b/src/com/android/settings/privatespace/AutoAdvanceSetupFragment.java
@@ -16,7 +16,8 @@
 
 package com.android.settings.privatespace;
 
-import static com.android.settings.privatespace.PrivateSpaceSetupActivity.SET_LOCK_ACTION;
+import static com.android.settings.privatespace.PrivateSpaceSetupActivity.ACCOUNT_LOGIN_ACTION;
+import static com.android.settings.privatespace.PrivateSpaceSetupActivity.EXTRA_ACTION_TYPE;
 
 import android.annotation.SuppressLint;
 import android.content.Intent;
@@ -134,7 +135,8 @@
     private void startActivityInPrivateUser(UserHandle userHandle) {
         /* Start new activity in private profile which is needed to set private profile lock */
         Intent intent = new Intent(getContext(), PrivateProfileContextHelperActivity.class);
-        getActivity().startActivityForResultAsUser(intent, SET_LOCK_ACTION, userHandle);
+        intent.putExtra(EXTRA_ACTION_TYPE, ACCOUNT_LOGIN_ACTION);
+        getActivity().startActivityForResultAsUser(intent, ACCOUNT_LOGIN_ACTION, userHandle);
     }
 
     private void showPrivateSpaceErrorScreen() {
diff --git a/src/com/android/settings/privatespace/PrivateProfileContextHelperActivity.java b/src/com/android/settings/privatespace/PrivateProfileContextHelperActivity.java
index c0d762a..0539f60 100644
--- a/src/com/android/settings/privatespace/PrivateProfileContextHelperActivity.java
+++ b/src/com/android/settings/privatespace/PrivateProfileContextHelperActivity.java
@@ -16,27 +16,84 @@
 
 package com.android.settings.privatespace;
 
+import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
+import static android.app.admin.DevicePolicyManager.EXTRA_PASSWORD_COMPLEXITY;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
+
+import static com.android.settings.privatespace.PrivateSpaceSetupActivity.ACCOUNT_LOGIN_ACTION;
+import static com.android.settings.privatespace.PrivateSpaceSetupActivity.EXTRA_ACTION_TYPE;
+import static com.android.settings.privatespace.PrivateSpaceSetupActivity.SET_LOCK_ACTION;
+
+import android.app.KeyguardManager;
+import android.content.Intent;
 import android.os.Bundle;
 
+import androidx.activity.result.ActivityResult;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.annotation.Nullable;
 import androidx.fragment.app.FragmentActivity;
-import androidx.navigation.fragment.NavHostFragment;
 
-import com.android.settings.R;
 import com.android.settings.SetupWizardUtils;
+import com.android.settings.overlay.FeatureFactory;
 
 import com.google.android.setupdesign.util.ThemeHelper;
 
-/** Activity that is started as private profile user that helps to set private profile lock. */
+/** Activity that is started as private profile user that helps to set private profile lock or
+ * add an account on the private profile. */
 public class PrivateProfileContextHelperActivity extends FragmentActivity {
     private static final String TAG = "PrivateProfileHelper";
+    private final ActivityResultLauncher<Intent> mAddAccountToPrivateProfile =
+            registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
+                    this::onAccountAdded);
+    private final ActivityResultLauncher<Intent> mVerifyDeviceLock =
+            registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
+                    this::onSetDeviceNewLock);
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         setTheme(SetupWizardUtils.getTheme(this, getIntent()));
         ThemeHelper.trySetDynamicColor(this);
         super.onCreate(savedInstanceState);
-        setContentView(R.layout.privatespace_setup_root);
-        NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager()
-                .findFragmentById(R.id.ps_nav_host_fragment);
-        navHostFragment.getNavController().setGraph(R.navigation.privatespace_private_context_nav);
+        if (savedInstanceState == null) {
+            int action = getIntent().getIntExtra(EXTRA_ACTION_TYPE, -1);
+            if (action == ACCOUNT_LOGIN_ACTION) {
+                PrivateSpaceLoginFeatureProvider privateSpaceLoginFeatureProvider =
+                        FeatureFactory.getFeatureFactory().getPrivateSpaceLoginFeatureProvider();
+                if (!privateSpaceLoginFeatureProvider.initiateAccountLogin(this,
+                        mAddAccountToPrivateProfile)) {
+                    setResult(RESULT_OK);
+                    finish();
+                }
+            } else if (action == SET_LOCK_ACTION) {
+                createPrivateSpaceLock();
+            }
+        }
+    }
+
+    private void createPrivateSpaceLock() {
+        final Intent intent = new Intent(ACTION_SET_NEW_PASSWORD);
+        intent.putExtra(EXTRA_PASSWORD_COMPLEXITY, PASSWORD_COMPLEXITY_LOW);
+        mVerifyDeviceLock.launch(intent);
+    }
+
+    private void onAccountAdded(@Nullable ActivityResult result) {
+        if (result != null && result.getResultCode() == RESULT_OK) {
+            setResult(RESULT_OK);
+        } else {
+            setResult(RESULT_CANCELED);
+        }
+        finish();
+    }
+
+    private void onSetDeviceNewLock(@Nullable ActivityResult result) {
+        // TODO(b/307281644) : Verify this for biometrics and check result code after new
+        //  Authentication changes are merged.
+        if (result != null && getSystemService(KeyguardManager.class).isDeviceSecure()) {
+            setResult(RESULT_OK);
+        } else {
+            setResult(RESULT_CANCELED);
+        }
+        finish();
     }
 }
diff --git a/src/com/android/settings/privatespace/PrivateSpaceAccountLoginError.java b/src/com/android/settings/privatespace/PrivateSpaceAccountLoginError.java
new file mode 100644
index 0000000..2d263d7
--- /dev/null
+++ b/src/com/android/settings/privatespace/PrivateSpaceAccountLoginError.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.privatespace;
+
+import static com.android.settings.privatespace.PrivateSpaceSetupActivity.ACCOUNT_LOGIN_ACTION;
+import static com.android.settings.privatespace.PrivateSpaceSetupActivity.EXTRA_ACTION_TYPE;
+
+import android.annotation.SuppressLint;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.activity.OnBackPressedCallback;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+
+import com.android.settings.R;
+
+import com.google.android.setupcompat.template.FooterBarMixin;
+import com.google.android.setupcompat.template.FooterButton;
+import com.google.android.setupdesign.GlifLayout;
+
+/** Fragment to display error screen if the profile is not signed in with a Google account. */
+public class PrivateSpaceAccountLoginError extends Fragment {
+    @Override
+    public View onCreateView(
+            LayoutInflater inflater,
+            @Nullable ViewGroup container,
+            @Nullable Bundle savedInstanceState) {
+        GlifLayout rootView =
+                (GlifLayout) inflater
+                        .inflate(R.layout.privatespace_account_login_error, container, false);
+        final FooterBarMixin mixin = rootView.getMixin(FooterBarMixin.class);
+        mixin.setPrimaryButton(
+                new FooterButton.Builder(getContext())
+                        .setText(R.string.privatespace_tryagain_label)
+                        .setListener(nextScreen())
+                        .setButtonType(FooterButton.ButtonType.NEXT)
+                        .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
+                        .build());
+        OnBackPressedCallback callback =
+                new OnBackPressedCallback(true /* enabled by default */) {
+                    @Override
+                    public void handleOnBackPressed() {
+                        // Handle the back button event
+                    }
+                };
+        requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
+
+        return rootView;
+    }
+
+    @SuppressLint("MissingPermission")
+    private View.OnClickListener nextScreen() {
+        return v -> {
+            PrivateSpaceMaintainer privateSpaceMaintainer = PrivateSpaceMaintainer
+                    .getInstance(getActivity());
+            UserHandle userHandle;
+            if (privateSpaceMaintainer.doesPrivateSpaceExist() && (userHandle =
+                    privateSpaceMaintainer.getPrivateProfileHandle()) != null) {
+                Intent intent = new Intent(getContext(), PrivateProfileContextHelperActivity.class);
+                intent.putExtra(EXTRA_ACTION_TYPE, ACCOUNT_LOGIN_ACTION);
+                getActivity().startActivityForResultAsUser(intent, ACCOUNT_LOGIN_ACTION,
+                        userHandle);
+            }
+        };
+    }
+}
diff --git a/src/com/android/settings/privatespace/PrivateSpaceLoginFeatureProvider.java b/src/com/android/settings/privatespace/PrivateSpaceLoginFeatureProvider.java
new file mode 100644
index 0000000..76ea9ac
--- /dev/null
+++ b/src/com/android/settings/privatespace/PrivateSpaceLoginFeatureProvider.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.privatespace;
+
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.annotation.NonNull;
+
+/** Feature provider for account login during Private Space setup. */
+public interface PrivateSpaceLoginFeatureProvider {
+    /** Returns true if login to an account is enabled during Private Space setup. */
+    boolean initiateAccountLogin(@NonNull Context context,
+            @NonNull ActivityResultLauncher<Intent> resultLauncher);
+}
diff --git a/src/com/android/settings/privatespace/PrivateSpaceLoginFeatureProviderImpl.java b/src/com/android/settings/privatespace/PrivateSpaceLoginFeatureProviderImpl.java
new file mode 100644
index 0000000..7fca2a4
--- /dev/null
+++ b/src/com/android/settings/privatespace/PrivateSpaceLoginFeatureProviderImpl.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.privatespace;
+
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.annotation.NonNull;
+
+/** Stub class for Private space to not initiate account login during setup */
+public class PrivateSpaceLoginFeatureProviderImpl implements PrivateSpaceLoginFeatureProvider {
+    @Override
+    public boolean initiateAccountLogin(@NonNull Context context,
+            @NonNull ActivityResultLauncher<Intent> resultLauncher) {
+        return false;
+    }
+}
diff --git a/src/com/android/settings/privatespace/PrivateSpaceSetLockFragment.java b/src/com/android/settings/privatespace/PrivateSpaceSetLockFragment.java
index 3d17638..93dc43b 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceSetLockFragment.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceSetLockFragment.java
@@ -16,25 +16,21 @@
 
 package com.android.settings.privatespace;
 
-import static android.app.Activity.RESULT_OK;
-import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
-import static android.app.admin.DevicePolicyManager.EXTRA_PASSWORD_COMPLEXITY;
-import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
+import static com.android.settings.privatespace.PrivateSpaceSetupActivity.EXTRA_ACTION_TYPE;
+import static com.android.settings.privatespace.PrivateSpaceSetupActivity.SET_LOCK_ACTION;
 
-import android.app.Activity;
-import android.app.KeyguardManager;
+import android.annotation.SuppressLint;
 import android.content.Intent;
 import android.os.Bundle;
+import android.os.UserHandle;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 
 import androidx.activity.OnBackPressedCallback;
-import androidx.activity.result.ActivityResult;
-import androidx.activity.result.ActivityResultLauncher;
-import androidx.activity.result.contract.ActivityResultContracts;
 import androidx.annotation.Nullable;
 import androidx.fragment.app.Fragment;
+import androidx.navigation.fragment.NavHostFragment;
 
 import com.android.settings.R;
 
@@ -45,9 +41,6 @@
 /** Fragment that provides an option to user to choose between the existing screen lock or set a
  * separate private profile lock. */
 public class PrivateSpaceSetLockFragment extends Fragment {
-    private final ActivityResultLauncher<Intent> mVerifyDeviceLock =
-            registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
-                    this::onSetDeviceNewLock);
 
     @Override
     public View onCreateView(
@@ -90,11 +83,8 @@
     private View.OnClickListener onClickUse() {
         return v -> {
             // Simply Use default screen lock. No need to handle
-            Activity activity = getActivity();
-            if (activity != null) {
-                activity.setResult(RESULT_OK);
-                activity.finish();
-            }
+            NavHostFragment.findNavController(PrivateSpaceSetLockFragment.this)
+                    .navigate(R.id.action_success_fragment);
         };
     }
 
@@ -104,22 +94,17 @@
         };
     }
 
+    @SuppressLint("MissingPermission")
     private void createPrivateSpaceLock() {
-        final Intent intent = new Intent(ACTION_SET_NEW_PASSWORD);
-        intent.putExtra(EXTRA_PASSWORD_COMPLEXITY, PASSWORD_COMPLEXITY_LOW);
-        mVerifyDeviceLock.launch(intent);
-    }
-
-    private void onSetDeviceNewLock(@Nullable ActivityResult result) {
-        // TODO(b/307281644) : Verify this for biometrics and check result code after new
-        //  Authentication changes are merged.
-        if (result != null) {
-            Activity profileContextHelperActivity = getActivity();
-            if (profileContextHelperActivity != null && profileContextHelperActivity
-                    .getSystemService(KeyguardManager.class).isDeviceSecure()) {
-                profileContextHelperActivity.setResult(RESULT_OK);
-                profileContextHelperActivity.finish();
-            }
+        PrivateSpaceMaintainer privateSpaceMaintainer = PrivateSpaceMaintainer
+                .getInstance(getActivity());
+        UserHandle userHandle;
+        if (privateSpaceMaintainer.doesPrivateSpaceExist() && (userHandle =
+                privateSpaceMaintainer.getPrivateProfileHandle()) != null) {
+            Intent intent = new Intent(getContext(), PrivateProfileContextHelperActivity.class);
+            intent.putExtra(EXTRA_ACTION_TYPE, SET_LOCK_ACTION);
+            getActivity().startActivityForResultAsUser(intent, SET_LOCK_ACTION,
+                    userHandle);
         }
     }
 }
diff --git a/src/com/android/settings/privatespace/PrivateSpaceSetupActivity.java b/src/com/android/settings/privatespace/PrivateSpaceSetupActivity.java
index 3a58e9e..a5628c8 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceSetupActivity.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceSetupActivity.java
@@ -31,6 +31,8 @@
 /** Activity class that helps in setting up of private space */
 public class PrivateSpaceSetupActivity extends FragmentActivity {
     public static final int SET_LOCK_ACTION = 1;
+    public static final int ACCOUNT_LOGIN_ACTION = 2;
+    public static final String EXTRA_ACTION_TYPE = "action_type";
     private NavHostFragment mNavHostFragment;
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -46,7 +48,13 @@
     @Override
     protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
         if (requestCode == SET_LOCK_ACTION && resultCode == RESULT_OK) {
-            mNavHostFragment.getNavController().navigate(R.id.action_advance_to_success);
+            mNavHostFragment.getNavController().navigate(R.id.action_success_fragment);
+        } else if (requestCode == ACCOUNT_LOGIN_ACTION) {
+            if (resultCode == RESULT_OK) {
+                mNavHostFragment.getNavController().navigate(R.id.action_set_lock_fragment);
+            } else {
+                mNavHostFragment.getNavController().navigate(R.id.action_advance_login_error);
+            }
         }
         super.onActivityResult(requestCode, resultCode, data);
     }
diff --git a/src/com/android/settings/privatespace/SetupSuccessFragment.java b/src/com/android/settings/privatespace/SetupSuccessFragment.java
index a8ca3f1..b761da7 100644
--- a/src/com/android/settings/privatespace/SetupSuccessFragment.java
+++ b/src/com/android/settings/privatespace/SetupSuccessFragment.java
@@ -70,7 +70,7 @@
     private View.OnClickListener onClickNext() {
         return v -> {
             accessPrivateSpaceToast();
-            // TODO: Replace with the intent to launch PS/PS Launch Settings
+            // TODO(b/306228087): Replace with the intent to launch All Apps once it is working.
             Intent startMain = new Intent(Intent.ACTION_MAIN);
             startMain.addCategory(Intent.CATEGORY_HOME);
             startActivity(startMain);
diff --git a/src/com/android/settings/regionalpreferences/FirstDayOfWeekItemListController.java b/src/com/android/settings/regionalpreferences/FirstDayOfWeekItemListController.java
index 03a59de..d509d2e 100644
--- a/src/com/android/settings/regionalpreferences/FirstDayOfWeekItemListController.java
+++ b/src/com/android/settings/regionalpreferences/FirstDayOfWeekItemListController.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.regionalpreferences;
 
+import android.app.settings.SettingsEnums;
 import android.content.Context;
 
 import com.android.settings.R;
@@ -57,4 +58,9 @@
     protected String[] getUnitValues() {
         return mContext.getResources().getStringArray(R.array.first_day_of_week);
     }
+
+    @Override
+    protected int getMetricsActionKey() {
+        return SettingsEnums.ACTION_SET_FIRST_DAY_OF_WEEK;
+    }
 }
diff --git a/src/com/android/settings/regionalpreferences/RegionalPreferenceListBasePreferenceController.java b/src/com/android/settings/regionalpreferences/RegionalPreferenceListBasePreferenceController.java
index 2f2bf76..ac0e7ee 100644
--- a/src/com/android/settings/regionalpreferences/RegionalPreferenceListBasePreferenceController.java
+++ b/src/com/android/settings/regionalpreferences/RegionalPreferenceListBasePreferenceController.java
@@ -22,16 +22,20 @@
 import androidx.preference.PreferenceScreen;
 
 import com.android.settings.core.BasePreferenceController;
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.widget.TickButtonPreference;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 /** A base controller for handling all regional preferences controllers. */
 public abstract class RegionalPreferenceListBasePreferenceController extends
         BasePreferenceController {
 
+    private final MetricsFeatureProvider mMetricsFeatureProvider;
     private PreferenceCategory mPreferenceCategory;
 
     public RegionalPreferenceListBasePreferenceController(Context context, String preferenceKey) {
         super(context, preferenceKey);
+        mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
     }
 
     @Override
@@ -61,6 +65,8 @@
                 RegionalPreferencesDataUtils.savePreference(mContext, getExtensionTypes(),
                         item.equals(RegionalPreferencesDataUtils.DEFAULT_VALUE)
                                 ? null : item);
+                mMetricsFeatureProvider.action(mContext, getMetricsActionKey(),
+                        getPreferenceTitle(value) + " > " +  getPreferenceTitle(item));
                 return true;
             });
             pref.setSelected(!value.isEmpty() && item.equals(value));
@@ -90,4 +96,8 @@
     protected abstract String getExtensionTypes();
 
     protected abstract String[] getUnitValues();
+
+    protected abstract int getMetricsActionKey();
+
+
 }
diff --git a/src/com/android/settings/regionalpreferences/TemperatureUnitListController.java b/src/com/android/settings/regionalpreferences/TemperatureUnitListController.java
index c51ca71..91ab1a2 100644
--- a/src/com/android/settings/regionalpreferences/TemperatureUnitListController.java
+++ b/src/com/android/settings/regionalpreferences/TemperatureUnitListController.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.regionalpreferences;
 
+import android.app.settings.SettingsEnums;
 import android.content.Context;
 
 import com.android.settings.R;
@@ -55,4 +56,9 @@
     protected String[] getUnitValues() {
         return mContext.getResources().getStringArray(R.array.temperature_units);
     }
+
+    @Override
+    protected int getMetricsActionKey() {
+        return SettingsEnums.ACTION_SET_TEMPERATURE_UNIT;
+    }
 }
diff --git a/src/com/android/settings/spa/SettingsSpaEnvironment.kt b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
index 40cc9a2..6b96460 100644
--- a/src/com/android/settings/spa/SettingsSpaEnvironment.kt
+++ b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
@@ -30,12 +30,15 @@
 import com.android.settings.spa.app.specialaccess.AllFilesAccessAppListProvider
 import com.android.settings.spa.app.specialaccess.DisplayOverOtherAppsAppListProvider
 import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider
+import com.android.settings.spa.app.specialaccess.LongBackgroundTasksAppListProvider
 import com.android.settings.spa.app.specialaccess.MediaManagementAppsAppListProvider
 import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider
 import com.android.settings.spa.app.specialaccess.NfcTagAppsSettingsProvider
 import com.android.settings.spa.app.specialaccess.PictureInPictureListProvider
 import com.android.settings.spa.app.specialaccess.SpecialAppAccessPageProvider
+import com.android.settings.spa.app.specialaccess.TurnScreenOnAppsAppListProvider
 import com.android.settings.spa.app.specialaccess.UseFullScreenIntentAppListProvider
+import com.android.settings.spa.app.specialaccess.VoiceActivationAppsListProvider
 import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider
 import com.android.settings.spa.app.storage.StorageAppListPageProvider
 import com.android.settings.spa.core.instrumentation.SpaLogProvider
@@ -66,8 +69,11 @@
             PictureInPictureListProvider,
             InstallUnknownAppsListProvider,
             AlarmsAndRemindersAppListProvider,
+            VoiceActivationAppsListProvider,
             WifiControlAppListProvider,
             NfcTagAppsSettingsProvider,
+            LongBackgroundTasksAppListProvider,
+            TurnScreenOnAppsAppListProvider,
         )
     }
 
diff --git a/src/com/android/settings/spa/about/AboutPhone.kt b/src/com/android/settings/spa/about/AboutPhone.kt
index 7343da0..5f9aa97 100644
--- a/src/com/android/settings/spa/about/AboutPhone.kt
+++ b/src/com/android/settings/spa/about/AboutPhone.kt
@@ -29,7 +29,6 @@
 import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
 import com.android.settingslib.spa.framework.common.createSettingsPage
 import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.framework.compose.toState
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spa.widget.scaffold.RegularScaffold
@@ -56,7 +55,7 @@
                 val deviceNamePresenter = remember { DeviceNamePresenter(context) }
                 Preference(object : PreferenceModel {
                     override val title = stringResource(R.string.about_settings)
-                    override val summary = deviceNamePresenter.deviceName.toState()
+                    override val summary = { deviceNamePresenter.deviceName }
                     override val onClick = navigator(name)
                     override val icon = @Composable {
                         SettingsIcon(imageVector = Icons.Outlined.PermDeviceInformation)
diff --git a/src/com/android/settings/spa/about/DeviceName.kt b/src/com/android/settings/spa/about/DeviceName.kt
index c481e32..86a9512 100644
--- a/src/com/android/settings/spa/about/DeviceName.kt
+++ b/src/com/android/settings/spa/about/DeviceName.kt
@@ -24,7 +24,6 @@
 import androidx.compose.ui.res.stringResource
 import com.android.settings.R
 import com.android.settings.deviceinfo.DeviceNamePreferenceController
-import com.android.settingslib.spa.framework.compose.toState
 import com.android.settingslib.spa.widget.dialog.AlertDialogButton
 import com.android.settingslib.spa.widget.dialog.rememberAlertDialogPresenter
 import com.android.settingslib.spa.widget.preference.Preference
@@ -48,7 +47,7 @@
         Preference(object : PreferenceModel {
             override val title =
                 stringResource(R.string.my_device_info_device_name_preference_title)
-            override val summary = deviceNamePresenter.deviceName.toState()
+            override val summary = { deviceNamePresenter.deviceName }
             override val onClick = dialogPresenter::open
         })
 
diff --git a/src/com/android/settings/spa/app/AllAppList.kt b/src/com/android/settings/spa/app/AllAppList.kt
index 383a0e8..5b13211 100644
--- a/src/com/android/settings/spa/app/AllAppList.kt
+++ b/src/com/android/settings/spa/app/AllAppList.kt
@@ -21,8 +21,6 @@
 import android.os.Bundle
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.State
-import androidx.compose.runtime.derivedStateOf
-import androidx.compose.runtime.remember
 import androidx.compose.ui.res.stringResource
 import com.android.settings.R
 import com.android.settings.spa.app.appinfo.AppInfoSettingsProvider
@@ -116,7 +114,7 @@
         option: Int,
         recordListFlow: Flow<List<AppRecordWithSize>>,
     ): Flow<List<AppRecordWithSize>> = recordListFlow.filterItem(
-        when (SpinnerItem.values().getOrNull(option)) {
+        when (SpinnerItem.entries.getOrNull(option)) {
             SpinnerItem.Enabled -> ({ it.app.enabled && !it.app.isInstantApp })
             SpinnerItem.Disabled -> isDisabled
             SpinnerItem.Instant -> isInstant
@@ -130,22 +128,20 @@
     private val isInstant: (AppRecordWithSize) -> Boolean = { it.app.isInstantApp }
 
     @Composable
-    override fun getSummary(option: Int, record: AppRecordWithSize): State<String> {
+    override fun getSummary(option: Int, record: AppRecordWithSize): () -> String {
         val storageSummary = record.app.getStorageSummary()
-        return remember {
-            derivedStateOf {
-                storageSummary.value +
-                    when {
-                        !record.app.installed && !record.app.isArchived -> {
-                            System.lineSeparator() + context.getString(R.string.not_installed)
-                        }
-                        isDisabled(record) -> {
-                            System.lineSeparator() +
-                                context.getString(com.android.settingslib.R.string.disabled)
-                        }
-                        else -> ""
-                    }
+        return {
+            val summaryList = mutableListOf(storageSummary.value)
+            when {
+                !record.app.installed && !record.app.isArchived -> {
+                    summaryList += context.getString(R.string.not_installed)
+                }
+
+                isDisabled(record) -> {
+                    summaryList += context.getString(com.android.settingslib.R.string.disabled)
+                }
             }
+            summaryList.joinToString(separator = System.lineSeparator())
         }
     }
 
diff --git a/src/com/android/settings/spa/app/AppsMain.kt b/src/com/android/settings/spa/app/AppsMain.kt
index 2dea9c5..83b3080 100644
--- a/src/com/android/settings/spa/app/AppsMain.kt
+++ b/src/com/android/settings/spa/app/AppsMain.kt
@@ -30,7 +30,6 @@
 import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
 import com.android.settingslib.spa.framework.common.createSettingsPage
 import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.framework.compose.toState
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spa.widget.scaffold.RegularScaffold
@@ -54,10 +53,10 @@
     fun buildInjectEntry() =
         SettingsEntryBuilder.createInject(owner = owner)
             .setUiLayoutFn {
+                val summary = stringResource(R.string.app_and_notification_dashboard_summary)
                 Preference(object : PreferenceModel {
                     override val title = stringResource(R.string.apps_dashboard_title)
-                    override val summary =
-                        stringResource(R.string.app_and_notification_dashboard_summary).toState()
+                    override val summary = { summary }
                     override val onClick = navigator(name)
                     override val icon = @Composable {
                         SettingsIcon(imageVector = Icons.Outlined.Apps)
diff --git a/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppPreference.kt b/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppPreference.kt
index 61098e8..96884be 100644
--- a/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppPreference.kt
+++ b/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppPreference.kt
@@ -19,6 +19,7 @@
 import android.content.Context
 import android.content.pm.ApplicationInfo
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.stringResource
@@ -40,11 +41,12 @@
     val presenter = remember { UserAspectRatioAppPresenter(context, app) }
     if (!presenter.isAvailableFlow.collectAsStateWithLifecycle(initialValue = false).value) return
 
+    val summary by presenter.summaryFlow.collectAsStateWithLifecycle(
+        initialValue = stringResource(R.string.summary_placeholder),
+    )
     Preference(object : PreferenceModel {
         override val title = stringResource(R.string.aspect_ratio_experimental_title)
-        override val summary = presenter.summaryFlow.collectAsStateWithLifecycle(
-            initialValue = stringResource(R.string.summary_placeholder),
-        )
+        override val summary = { summary }
         override val onClick = presenter::startActivity
     })
 }
@@ -75,4 +77,4 @@
         context,
         AppInfoSettingsProvider.METRICS_CATEGORY,
     )
-}
\ No newline at end of file
+}
diff --git a/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt b/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt
index deea745..5af29ef 100644
--- a/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt
+++ b/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt
@@ -28,7 +28,7 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
@@ -41,15 +41,14 @@
 import com.android.settingslib.spa.framework.common.createSettingsPage
 import com.android.settingslib.spa.framework.compose.navigator
 import com.android.settingslib.spa.framework.compose.rememberContext
-import com.android.settingslib.spa.framework.compose.toState
 import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.framework.util.asyncMap
 import com.android.settingslib.spa.framework.util.filterItem
-import com.android.settingslib.spa.widget.preference.Preference
-import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spa.widget.illustration.Illustration
 import com.android.settingslib.spa.widget.illustration.IllustrationModel
 import com.android.settingslib.spa.widget.illustration.ResourceType
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spa.widget.ui.SettingsBody
 import com.android.settingslib.spa.widget.ui.SpinnerOption
 import com.android.settingslib.spaprivileged.model.app.AppListModel
@@ -80,12 +79,14 @@
 
     @Composable
     @VisibleForTesting
-    fun EntryItem() =
+    fun EntryItem() {
+        val summary = getSummary()
         Preference(object : PreferenceModel {
             override val title = stringResource(R.string.aspect_ratio_experimental_title)
-            override val summary = getSummary().toState()
+            override val summary = { summary }
             override val onClick = navigator(name)
         })
+    }
 
     @VisibleForTesting
     fun buildInjectEntry() = SettingsEntryBuilder
@@ -177,7 +178,7 @@
         option: Int,
         recordListFlow: Flow<List<UserAspectRatioAppListItemModel>>
     ): Flow<List<UserAspectRatioAppListItemModel>> = recordListFlow.filterItem(
-        when (SpinnerItem.values().getOrNull(option)) {
+        when (SpinnerItem.entries.getOrNull(option)) {
             SpinnerItem.Suggested -> ({ it.canDisplay && it.suggested })
             SpinnerItem.Overridden -> ({ it.userOverride != USER_MIN_ASPECT_RATIO_UNSET })
             else -> ({ it.canDisplay })
@@ -185,13 +186,15 @@
     )
 
     @Composable
-    override fun getSummary(option: Int, record: UserAspectRatioAppListItemModel) : State<String> =
-        remember(record.userOverride) {
+    override fun getSummary(option: Int, record: UserAspectRatioAppListItemModel): () -> String {
+        val summary by remember(record.userOverride) {
             flow {
                 emit(userAspectRatioManager.getUserMinAspectRatioEntry(record.userOverride,
                     record.app.packageName))
             }.flowOn(Dispatchers.IO)
         }.collectAsStateWithLifecycle(initialValue = stringResource(R.string.summary_placeholder))
+        return { summary }
+    }
 
     private fun getPackageAndActivityInfo(app: ApplicationInfo): PackageInfo? = try {
         packageManager.getPackageInfoAsUser(app.packageName, GET_ACTIVITIES_FLAGS, app.userId)
diff --git a/src/com/android/settings/spa/app/appinfo/AppAllServicesPreference.kt b/src/com/android/settings/spa/app/appinfo/AppAllServicesPreference.kt
index 34272d4..31e068c 100644
--- a/src/com/android/settings/spa/app/appinfo/AppAllServicesPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppAllServicesPreference.kt
@@ -24,6 +24,7 @@
 import android.os.Bundle
 import android.util.Log
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.platform.LocalContext
@@ -52,11 +53,12 @@
     val presenter = remember { AppAllServicesPresenter(context, app, coroutineScope) }
     if (!presenter.isAvailableFlow.collectAsStateWithLifecycle(initialValue = false).value) return
 
+    val summary by presenter.summaryFlow.collectAsStateWithLifecycle(
+        initialValue = stringResource(R.string.summary_placeholder),
+    )
     Preference(object : PreferenceModel {
         override val title = stringResource(R.string.app_info_all_services_label)
-        override val summary = presenter.summaryFlow.collectAsStateWithLifecycle(
-            initialValue = stringResource(R.string.summary_placeholder),
-        )
+        override val summary = { summary }
         override val onClick = presenter::startActivity
     })
 }
diff --git a/src/com/android/settings/spa/app/appinfo/AppBatteryPreference.kt b/src/com/android/settings/spa/app/appinfo/AppBatteryPreference.kt
index 7dd78a9..c707b44b 100644
--- a/src/com/android/settings/spa/app/appinfo/AppBatteryPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppBatteryPreference.kt
@@ -21,7 +21,6 @@
 import android.util.Log
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -91,16 +90,17 @@
         }
     }
 
-    val enabled = derivedStateOf { batteryDiffEntryState is LoadingState.Done }
+    val enabled = { batteryDiffEntryState is LoadingState.Done }
 
-    val summary = derivedStateOf<String> {
-        if (!app.installed) return@derivedStateOf ""
-        batteryDiffEntryState.let { batteryDiffEntryState ->
-            when (batteryDiffEntryState) {
-                is LoadingState.Loading -> context.getString(R.string.summary_placeholder)
-                is LoadingState.Done -> batteryDiffEntryState.result.getSummary()
+    val summary = {
+        if (app.installed) {
+            batteryDiffEntryState.let { batteryDiffEntryState ->
+                when (batteryDiffEntryState) {
+                    is LoadingState.Loading -> context.getString(R.string.summary_placeholder)
+                    is LoadingState.Done -> batteryDiffEntryState.result.getSummary()
+                }
             }
-        }
+        } else ""
     }
 
     private fun BatteryDiffEntry?.getSummary(): String =
@@ -155,7 +155,7 @@
 }
 
 private sealed class LoadingState<out T> {
-    object Loading : LoadingState<Nothing>()
+    data object Loading : LoadingState<Nothing>()
 
     data class Done<T>(val result: T) : LoadingState<T>()
 
diff --git a/src/com/android/settings/spa/app/appinfo/AppDataUsagePreference.kt b/src/com/android/settings/spa/app/appinfo/AppDataUsagePreference.kt
index ceb3986..057f911 100644
--- a/src/com/android/settings/spa/app/appinfo/AppDataUsagePreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppDataUsagePreference.kt
@@ -20,6 +20,7 @@
 import android.content.pm.ApplicationInfo
 import android.net.NetworkTemplate
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.platform.LocalContext
@@ -34,7 +35,6 @@
 import com.android.settings.datausage.lib.INetworkTemplates
 import com.android.settings.datausage.lib.NetworkTemplates
 import com.android.settings.datausage.lib.NetworkTemplates.getTitleResId
-import com.android.settingslib.spa.framework.compose.toState
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spaprivileged.model.app.hasFlag
@@ -64,16 +64,17 @@
     }
     if (!presenter.isAvailableFlow.collectAsStateWithLifecycle(initialValue = false).value) return
 
+    val summary by presenter.summaryFlow.collectAsStateWithLifecycle(
+        initialValue = stringResource(R.string.computing_size),
+    )
     Preference(object : PreferenceModel {
         override val title = stringResource(
             presenter.titleResIdFlow.collectAsStateWithLifecycle(
                 initialValue = R.string.summary_placeholder,
             ).value
         )
-        override val summary = presenter.summaryFlow.collectAsStateWithLifecycle(
-            initialValue = stringResource(R.string.computing_size),
-        )
-        override val enabled = presenter.isEnabled().toState()
+        override val summary = { summary }
+        override val enabled = { presenter.isEnabled() }
         override val onClick = presenter::startActivity
     })
 }
diff --git a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
index ed912c3..3b7f579 100644
--- a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
@@ -32,6 +32,7 @@
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import androidx.navigation.NavType
 import androidx.navigation.navArgument
+import com.android.settings.flags.Flags
 import com.android.settings.R
 import com.android.settings.applications.AppInfoBase
 import com.android.settings.applications.appinfo.AppInfoDashboardFragment
@@ -42,6 +43,7 @@
 import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider
 import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider
 import com.android.settings.spa.app.specialaccess.PictureInPictureListProvider
+import com.android.settings.spa.app.specialaccess.VoiceActivationAppsListProvider
 import com.android.settingslib.spa.framework.common.SettingsPageProvider
 import com.android.settingslib.spa.framework.compose.LifecycleEffect
 import com.android.settingslib.spa.framework.compose.navigator
@@ -160,6 +162,9 @@
             InstallUnknownAppsListProvider.InfoPageEntryItem(app)
             InteractAcrossProfilesDetailsPreference(app)
             AlarmsAndRemindersAppListProvider.InfoPageEntryItem(app)
+            if (Flags.enableVoiceActivationAppsInSettings()) {
+                VoiceActivationAppsListProvider.InfoPageEntryItem(app)
+            }
         }
 
         Category(title = stringResource(R.string.app_install_details_group_title)) {
diff --git a/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreference.kt b/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreference.kt
index 5a348f7..62e714a 100644
--- a/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreference.kt
@@ -19,15 +19,16 @@
 import android.content.Context
 import android.content.pm.ApplicationInfo
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.stringResource
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.settings.R
 import com.android.settings.Utils
 import com.android.settings.applications.AppStoreUtil
 import com.android.settingslib.applications.AppUtils
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spaprivileged.framework.common.asUser
@@ -49,13 +50,14 @@
     val presenter = remember { AppInstallerInfoPresenter(context, app, coroutineScope) }
     if (!presenter.isAvailableFlow.collectAsStateWithLifecycle(initialValue = false).value) return
 
+    val summary by presenter.summaryFlow.collectAsStateWithLifecycle(
+        initialValue = stringResource(R.string.summary_placeholder),
+    )
+    val enabled by presenter.enabledFlow.collectAsStateWithLifecycle(initialValue = false)
     Preference(object : PreferenceModel {
         override val title = stringResource(R.string.app_install_details_title)
-        override val summary = presenter.summaryFlow.collectAsStateWithLifecycle(
-            initialValue = stringResource(R.string.summary_placeholder),
-        )
-        override val enabled =
-            presenter.enabledFlow.collectAsStateWithLifecycle(initialValue = false)
+        override val summary = { summary }
+        override val enabled = { enabled }
         override val onClick = presenter::startActivity
     })
 }
diff --git a/src/com/android/settings/spa/app/appinfo/AppLocalePreference.kt b/src/com/android/settings/spa/app/appinfo/AppLocalePreference.kt
index 2d6fbb6..3b2aace 100644
--- a/src/com/android/settings/spa/app/appinfo/AppLocalePreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppLocalePreference.kt
@@ -23,15 +23,16 @@
 import android.content.pm.PackageManager.ResolveInfoFlags
 import android.net.Uri
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.stringResource
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.settings.R
 import com.android.settings.applications.AppInfoBase
 import com.android.settings.applications.AppLocaleUtil
 import com.android.settings.applications.appinfo.AppLocaleDetails
 import com.android.settings.localepicker.AppLocalePickerActivity
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spaprivileged.model.app.userHandle
@@ -46,11 +47,12 @@
     val presenter = remember { AppLocalePresenter(context, app) }
     if (!presenter.isAvailableFlow.collectAsStateWithLifecycle(initialValue = false).value) return
 
+    val summary by presenter.summaryFlow.collectAsStateWithLifecycle(
+        initialValue = stringResource(R.string.summary_placeholder),
+    )
     Preference(object : PreferenceModel {
         override val title = stringResource(R.string.app_locale_preference_title)
-        override val summary = presenter.summaryFlow.collectAsStateWithLifecycle(
-            initialValue = stringResource(R.string.summary_placeholder),
-        )
+        override val summary = { summary }
         override val onClick = presenter::startActivity
     })
 }
diff --git a/src/com/android/settings/spa/app/appinfo/AppNotificationPreference.kt b/src/com/android/settings/spa/app/appinfo/AppNotificationPreference.kt
index 45033e7..28527c1 100644
--- a/src/com/android/settings/spa/app/appinfo/AppNotificationPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppNotificationPreference.kt
@@ -19,6 +19,7 @@
 import android.content.Context
 import android.content.pm.ApplicationInfo
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.stringResource
@@ -29,7 +30,6 @@
 import com.android.settings.spa.notification.AppNotificationRepository
 import com.android.settings.spa.notification.IAppNotificationRepository
 import com.android.settingslib.spa.framework.compose.rememberContext
-import com.android.settingslib.spa.framework.compose.stateOf
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spaprivileged.model.app.installed
@@ -43,17 +43,17 @@
     repository: IAppNotificationRepository = rememberContext(::AppNotificationRepository),
 ) {
     val context = LocalContext.current
-    val summaryFlow = remember(app) {
+    val summary by remember(app) {
         flow {
             emit(repository.getNotificationSummary(app))
-        }.flowOn(Dispatchers.IO)
-    }
+        }.flowOn(Dispatchers.Default)
+    }.collectAsStateWithLifecycle(
+        initialValue = stringResource(R.string.summary_placeholder)
+    )
     Preference(object : PreferenceModel {
         override val title = stringResource(R.string.notifications_label)
-        override val summary = summaryFlow.collectAsStateWithLifecycle(
-            initialValue = stringResource(R.string.summary_placeholder)
-        )
-        override val enabled = stateOf(app.installed)
+        override val summary = { summary }
+        override val enabled = { app.installed }
         override val onClick = { navigateToAppNotificationSettings(context, app) }
     })
 }
@@ -65,4 +65,4 @@
         context,
         AppInfoSettingsProvider.METRICS_CATEGORY,
     )
-}
\ No newline at end of file
+}
diff --git a/src/com/android/settings/spa/app/appinfo/AppOpenByDefaultPreference.kt b/src/com/android/settings/spa/app/appinfo/AppOpenByDefaultPreference.kt
index 757ddc2..aae9569 100644
--- a/src/com/android/settings/spa/app/appinfo/AppOpenByDefaultPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppOpenByDefaultPreference.kt
@@ -19,6 +19,7 @@
 import android.content.Context
 import android.content.pm.ApplicationInfo
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.stringResource
@@ -28,7 +29,6 @@
 import com.android.settings.applications.intentpicker.AppLaunchSettings
 import com.android.settings.applications.intentpicker.IntentPickerUtils
 import com.android.settingslib.applications.AppUtils
-import com.android.settingslib.spa.framework.compose.stateOf
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spaprivileged.framework.common.asUser
@@ -46,12 +46,13 @@
     val presenter = remember(app) { AppOpenByDefaultPresenter(context, app) }
     if (remember(presenter) { !presenter.isAvailable() }) return
 
+    val summary by presenter.summaryFlow.collectAsStateWithLifecycle(
+        initialValue = stringResource(R.string.summary_placeholder),
+    )
     Preference(object : PreferenceModel {
         override val title = stringResource(R.string.launch_by_default)
-        override val summary = presenter.summaryFlow.collectAsStateWithLifecycle(
-            initialValue = stringResource(R.string.summary_placeholder),
-        )
-        override val enabled = stateOf(presenter.isEnabled())
+        override val summary = { summary }
+        override val enabled = { presenter.isEnabled() }
         override val onClick = presenter::startActivity
     })
 }
diff --git a/src/com/android/settings/spa/app/appinfo/AppPermissionPreference.kt b/src/com/android/settings/spa/app/appinfo/AppPermissionPreference.kt
index ad666dc..ec1780f 100644
--- a/src/com/android/settings/spa/app/appinfo/AppPermissionPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppPermissionPreference.kt
@@ -22,7 +22,6 @@
 import android.content.pm.ApplicationInfo
 import android.util.Log
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.livedata.observeAsState
 import androidx.compose.runtime.remember
 import androidx.compose.ui.platform.LocalContext
@@ -52,8 +51,8 @@
         model = remember {
             object : PreferenceModel {
                 override val title = context.getString(R.string.permissions_label)
-                override val summary = derivedStateOf { summaryState.value.summary }
-                override val enabled = derivedStateOf { summaryState.value.enabled }
+                override val summary = { summaryState.value.summary }
+                override val enabled = { summaryState.value.enabled }
                 override val onClick = { startManagePermissionsActivity(context, app) }
             }
         },
diff --git a/src/com/android/settings/spa/app/appinfo/AppStoragePreference.kt b/src/com/android/settings/spa/app/appinfo/AppStoragePreference.kt
index e8b1018..2b96454 100644
--- a/src/com/android/settings/spa/app/appinfo/AppStoragePreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppStoragePreference.kt
@@ -19,9 +19,6 @@
 import android.content.Context
 import android.content.pm.ApplicationInfo
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
-import androidx.compose.runtime.derivedStateOf
-import androidx.compose.runtime.remember
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.stringResource
 import com.android.settings.R
@@ -47,12 +44,13 @@
 }
 
 @Composable
-private fun getSummary(context: Context, app: ApplicationInfo): State<String> {
+private fun getSummary(context: Context, app: ApplicationInfo): () -> String {
     val sizeState = app.getStorageSize()
-    return remember {
-        derivedStateOf {
-            val size = sizeState.value
-            if (size.isBlank()) return@derivedStateOf context.getString(R.string.computing_size)
+    return {
+        val size = sizeState.value
+        if (size.isBlank()) {
+            context.getString(R.string.computing_size)
+        } else {
             val storageType = context.getString(
                 when (app.hasFlag(ApplicationInfo.FLAG_EXTERNAL_STORAGE)) {
                     true -> R.string.storage_type_external
diff --git a/src/com/android/settings/spa/app/appinfo/AppTimeSpentPreference.kt b/src/com/android/settings/spa/app/appinfo/AppTimeSpentPreference.kt
index 21b3d73..7ba61dc 100644
--- a/src/com/android/settings/spa/app/appinfo/AppTimeSpentPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppTimeSpentPreference.kt
@@ -22,6 +22,7 @@
 import android.content.pm.PackageManager.ResolveInfoFlags
 import android.provider.Settings
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.livedata.observeAsState
 import androidx.compose.runtime.remember
 import androidx.compose.ui.platform.LocalContext
@@ -29,7 +30,6 @@
 import androidx.lifecycle.liveData
 import com.android.settings.R
 import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
-import com.android.settingslib.spa.framework.compose.stateOf
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spaprivileged.model.app.hasFlag
@@ -43,12 +43,13 @@
     val presenter = remember { AppTimeSpentPresenter(context, app) }
     if (!presenter.isAvailable()) return
 
+    val summary by presenter.summaryLiveData.observeAsState(
+        initial = stringResource(R.string.summary_placeholder),
+    )
     Preference(object : PreferenceModel {
         override val title = stringResource(R.string.time_spent_in_app_pref_title)
-        override val summary = presenter.summaryLiveData.observeAsState(
-            initial = stringResource(R.string.summary_placeholder),
-        )
-        override val enabled = stateOf(presenter.isEnabled())
+        override val summary = { summary }
+        override val enabled = { presenter.isEnabled() }
         override val onClick = presenter::startActivity
     })
 }
diff --git a/src/com/android/settings/spa/app/appinfo/DefaultAppShortcutPreference.kt b/src/com/android/settings/spa/app/appinfo/DefaultAppShortcutPreference.kt
index 74c0aa4..51f6845 100644
--- a/src/com/android/settings/spa/app/appinfo/DefaultAppShortcutPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/DefaultAppShortcutPreference.kt
@@ -22,6 +22,7 @@
 import android.content.pm.ApplicationInfo
 import androidx.annotation.StringRes
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.livedata.observeAsState
 import androidx.compose.runtime.remember
 import androidx.compose.ui.platform.LocalContext
@@ -57,11 +58,12 @@
     if (remember(presenter) { !presenter.isAvailable() }) return
     if (presenter.isVisible().observeAsState().value != true) return
 
+    val summary by presenter.summaryFlow.collectAsStateWithLifecycle(
+        initialValue = stringResource(R.string.summary_placeholder),
+    )
     Preference(object : PreferenceModel {
         override val title = stringResource(shortcut.titleResId)
-        override val summary = presenter.summaryFlow.collectAsStateWithLifecycle(
-            initialValue = stringResource(R.string.summary_placeholder),
-        )
+        override val summary = { summary }
         override val onClick = presenter::startActivity
     })
 }
diff --git a/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreference.kt b/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreference.kt
index f62a3be..78ca15b 100644
--- a/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreference.kt
@@ -28,15 +28,14 @@
 import android.provider.DeviceConfig
 import android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.ui.platform.LocalContext
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.settings.R
 import com.android.settings.Utils.PROPERTY_APP_HIBERNATION_ENABLED
 import com.android.settings.Utils.PROPERTY_HIBERNATION_TARGETS_PRE_S_APPS
 import com.android.settingslib.spa.framework.compose.OverridableFlow
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.settingslib.spa.framework.compose.stateOf
 import com.android.settingslib.spa.widget.preference.SwitchPreference
 import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
 import com.android.settingslib.spaprivileged.framework.common.appHibernationManager
@@ -44,12 +43,12 @@
 import com.android.settingslib.spaprivileged.framework.common.asUser
 import com.android.settingslib.spaprivileged.framework.common.permissionControllerManager
 import com.android.settingslib.spaprivileged.model.app.userHandle
+import kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.asExecutor
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.withContext
-import kotlin.coroutines.resume
-import kotlin.coroutines.suspendCoroutine
 
 @Composable
 fun HibernationSwitchPreference(app: ApplicationInfo) {
@@ -57,18 +56,14 @@
     val presenter = remember { HibernationSwitchPresenter(context, app) }
     if (!presenter.isAvailable()) return
 
-    val isEligibleState = presenter.isEligibleFlow.collectAsStateWithLifecycle(initialValue = false)
+    val isEligibleState by presenter.isEligibleFlow.collectAsStateWithLifecycle(initialValue = false)
     val isCheckedState = presenter.isCheckedFlow.collectAsStateWithLifecycle(initialValue = null)
     SwitchPreference(remember {
         object : SwitchPreferenceModel {
             override val title = context.getString(R.string.unused_apps_switch)
-            override val summary = stateOf(context.getString(R.string.unused_apps_switch_summary))
-            override val changeable = isEligibleState
-
-            override val checked = derivedStateOf {
-                if (!changeable.value) false else isCheckedState.value
-            }
-
+            override val summary = { context.getString(R.string.unused_apps_switch_summary) }
+            override val changeable = { isEligibleState }
+            override val checked = { if (changeable()) isCheckedState.value else false }
             override val onCheckedChange = presenter::onCheckedChange
         }
     })
diff --git a/src/com/android/settings/spa/app/appinfo/InstantAppDomainsPreference.kt b/src/com/android/settings/spa/app/appinfo/InstantAppDomainsPreference.kt
index 7b9480d..9c3ec97 100644
--- a/src/com/android/settings/spa/app/appinfo/InstantAppDomainsPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/InstantAppDomainsPreference.kt
@@ -32,9 +32,9 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.stringResource
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.settings.R
 import com.android.settings.Utils
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
@@ -53,11 +53,12 @@
     val presenter = remember { InstantAppDomainsPresenter(context, app) }
     var openDialog by rememberSaveable { mutableStateOf(false) }
 
+    val summary by presenter.summaryFlow.collectAsStateWithLifecycle(
+        initialValue = stringResource(R.string.summary_placeholder),
+    )
     Preference(object : PreferenceModel {
         override val title = stringResource(R.string.app_launch_supported_domain_urls_title)
-        override val summary = presenter.summaryFlow.collectAsStateWithLifecycle(
-            initialValue = stringResource(R.string.summary_placeholder),
-        )
+        override val summary = { summary }
         override val onClick = { openDialog = true }
     })
 
diff --git a/src/com/android/settings/spa/app/appinfo/InteractAcrossProfilesDetailsPreference.kt b/src/com/android/settings/spa/app/appinfo/InteractAcrossProfilesDetailsPreference.kt
index 12f6907..905e057 100644
--- a/src/com/android/settings/spa/app/appinfo/InteractAcrossProfilesDetailsPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/InteractAcrossProfilesDetailsPreference.kt
@@ -19,13 +19,14 @@
 import android.content.Context
 import android.content.pm.ApplicationInfo
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.stringResource
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.settings.R
 import com.android.settings.applications.appinfo.AppInfoDashboardFragment
 import com.android.settings.applications.specialaccess.interactacrossprofiles.InteractAcrossProfilesDetails
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spaprivileged.framework.common.crossProfileApps
@@ -39,11 +40,12 @@
     val presenter = remember { InteractAcrossProfilesDetailsPresenter(context, app) }
     if (!presenter.isAvailableFlow.collectAsStateWithLifecycle(initialValue = false).value) return
 
+    val summary by presenter.summaryFlow.collectAsStateWithLifecycle(
+        initialValue = stringResource(R.string.summary_placeholder),
+    )
     Preference(object : PreferenceModel {
         override val title = stringResource(R.string.interact_across_profiles_title)
-        override val summary = presenter.summaryFlow.collectAsStateWithLifecycle(
-            initialValue = stringResource(R.string.summary_placeholder),
-        )
+        override val summary = { summary }
         override val onClick = presenter::startActivity
     })
 }
diff --git a/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProvider.kt b/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProvider.kt
index 6e0643b..89f473b 100644
--- a/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProvider.kt
+++ b/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProvider.kt
@@ -32,6 +32,7 @@
 import androidx.compose.material.icons.outlined.Delete
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.State
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.produceState
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalContext
@@ -88,9 +89,10 @@
     @Composable
     fun EntryItem() {
         if(featureIsDisabled) return
+        val summary by generatePreferenceSummary()
         Preference(object : PreferenceModel {
             override val title = stringResource(R.string.background_install_title)
-            override val summary = generatePreferenceSummary()
+            override val summary = { summary }
             override val onClick = navigator(name)
         })
     }
diff --git a/src/com/android/settings/spa/app/specialaccess/AlarmsAndRemindersAppList.kt b/src/com/android/settings/spa/app/specialaccess/AlarmsAndRemindersAppList.kt
index c31eb7a..c990927 100644
--- a/src/com/android/settings/spa/app/specialaccess/AlarmsAndRemindersAppList.kt
+++ b/src/com/android/settings/spa/app/specialaccess/AlarmsAndRemindersAppList.kt
@@ -24,10 +24,9 @@
 import android.content.pm.ApplicationInfo
 import android.os.PowerExemptionManager
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.livedata.observeAsState
 import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
 import com.android.settingslib.R
-import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.livedata.observeAsCallback
 import com.android.settingslib.spaprivileged.model.app.AppRecord
 import com.android.settingslib.spaprivileged.model.app.IPackageManagers
 import com.android.settingslib.spaprivileged.model.app.PackageManagers
@@ -79,9 +78,10 @@
     }
 
     @Composable
-    override fun isAllowed(record: AlarmsAndRemindersAppRecord) =
-        if (record.isTrumped) stateOf(true)
-        else record.controller.isAllowed.observeAsState()
+    override fun isAllowed(record: AlarmsAndRemindersAppRecord): () -> Boolean? = when {
+        record.isTrumped -> ({ true })
+        else -> record.controller.isAllowed.observeAsCallback()
+    }
 
     override fun isChangeable(record: AlarmsAndRemindersAppRecord) = record.isChangeable
 
diff --git a/src/com/android/settings/spa/app/specialaccess/InstallUnknownApps.kt b/src/com/android/settings/spa/app/specialaccess/InstallUnknownApps.kt
index c98b2ee..7f63e38 100644
--- a/src/com/android/settings/spa/app/specialaccess/InstallUnknownApps.kt
+++ b/src/com/android/settings/spa/app/specialaccess/InstallUnknownApps.kt
@@ -24,8 +24,8 @@
 import android.content.pm.ApplicationInfo
 import android.os.UserManager
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.livedata.observeAsState
 import com.android.settings.R
+import com.android.settingslib.spa.livedata.observeAsCallback
 import com.android.settingslib.spaprivileged.model.app.AppOpsController
 import com.android.settingslib.spaprivileged.model.app.AppRecord
 import com.android.settingslib.spaprivileged.model.app.userId
@@ -79,7 +79,7 @@
 
     @Composable
     override fun isAllowed(record: InstallUnknownAppsRecord) =
-        record.appOpsController.isAllowed.observeAsState()
+        record.appOpsController.isAllowed.observeAsCallback()
 
     override fun isChangeable(record: InstallUnknownAppsRecord) =
         isChangeable(record, getPotentialPackageNames(record.app.userId))
diff --git a/src/com/android/settings/spa/app/specialaccess/LongBackgroundTasksApps.kt b/src/com/android/settings/spa/app/specialaccess/LongBackgroundTasksApps.kt
new file mode 100644
index 0000000..3ba9b08
--- /dev/null
+++ b/src/com/android/settings/spa/app/specialaccess/LongBackgroundTasksApps.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.specialaccess
+
+import android.Manifest
+import android.app.AppOpsManager
+import android.app.settings.SettingsEnums
+import android.content.Context
+import com.android.settings.R
+import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
+import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
+import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
+import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
+
+object LongBackgroundTasksAppListProvider : TogglePermissionAppListProvider {
+    override val permissionType = "LongBackgroundTasksApps"
+    override fun createModel(context: Context) = LongBackgroundTasksAppsListModel(context)
+}
+
+class LongBackgroundTasksAppsListModel(context: Context) : AppOpPermissionListModel(context) {
+    override val pageTitleResId = R.string.long_background_tasks_title
+    override val switchTitleResId = R.string.long_background_tasks_switch_title
+    override val footerResId = R.string.long_background_tasks_footer_title
+    override val appOp = AppOpsManager.OP_RUN_USER_INITIATED_JOBS
+    override val permission = Manifest.permission.RUN_USER_INITIATED_JOBS
+    override val setModeByUid = true
+
+    override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
+        super.setAllowed(record, newAllowed)
+        logPermissionChange(newAllowed)
+    }
+
+    private fun logPermissionChange(newAllowed: Boolean) {
+        featureFactory.metricsFeatureProvider.action(
+            context,
+            SettingsEnums.ACTION_LONG_BACKGROUND_TASKS_TOGGLE,
+            if (newAllowed) 1 else 0
+        )
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/spa/app/specialaccess/NfcTagAppsSettings.kt b/src/com/android/settings/spa/app/specialaccess/NfcTagAppsSettings.kt
index 3dede42..f02a6a1 100644
--- a/src/com/android/settings/spa/app/specialaccess/NfcTagAppsSettings.kt
+++ b/src/com/android/settings/spa/app/specialaccess/NfcTagAppsSettings.kt
@@ -23,8 +23,8 @@
 import android.nfc.NfcAdapter
 import android.util.Log
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.livedata.observeAsState
 import com.android.settings.R
+import com.android.settingslib.spa.livedata.observeAsCallback
 import com.android.settingslib.spaprivileged.model.app.AppRecord
 import com.android.settingslib.spaprivileged.model.app.userId
 import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListModel
@@ -100,7 +100,7 @@
 
     @Composable
     override fun isAllowed(record: NfcTagAppsSettingsRecord) =
-        record.controller.isAllowed.observeAsState()
+        record.controller.isAllowed.observeAsCallback()
 
     override fun isChangeable(record: NfcTagAppsSettingsRecord) = true
 
diff --git a/src/com/android/settings/spa/app/specialaccess/PictureInPicture.kt b/src/com/android/settings/spa/app/specialaccess/PictureInPicture.kt
index 5ed3615..cd615919 100644
--- a/src/com/android/settings/spa/app/specialaccess/PictureInPicture.kt
+++ b/src/com/android/settings/spa/app/specialaccess/PictureInPicture.kt
@@ -25,8 +25,8 @@
 import android.content.pm.PackageManager.PackageInfoFlags
 import android.util.Log
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.livedata.observeAsState
 import com.android.settings.R
+import com.android.settingslib.spa.livedata.observeAsCallback
 import com.android.settingslib.spaprivileged.model.app.AppOpsController
 import com.android.settingslib.spaprivileged.model.app.AppRecord
 import com.android.settingslib.spaprivileged.model.app.installed
@@ -90,7 +90,7 @@
 
     @Composable
     override fun isAllowed(record: PictureInPictureRecord) =
-        record.appOpsController.isAllowed.observeAsState()
+        record.appOpsController.isAllowed.observeAsCallback()
 
     override fun isChangeable(record: PictureInPictureRecord) = record.isSupport
 
diff --git a/src/com/android/settings/spa/app/specialaccess/SpecialAppAccess.kt b/src/com/android/settings/spa/app/specialaccess/SpecialAppAccess.kt
index b40e32b..fb05a38 100644
--- a/src/com/android/settings/spa/app/specialaccess/SpecialAppAccess.kt
+++ b/src/com/android/settings/spa/app/specialaccess/SpecialAppAccess.kt
@@ -66,7 +66,10 @@
                 PictureInPictureListProvider,
                 InstallUnknownAppsListProvider,
                 AlarmsAndRemindersAppListProvider,
+                VoiceActivationAppsListProvider,
                 WifiControlAppListProvider,
+                LongBackgroundTasksAppListProvider,
+                TurnScreenOnAppsAppListProvider,
             )
             .map { it.buildAppListInjectEntry().setLink(fromPage = owner).build() }
     }
diff --git a/src/com/android/settings/spa/app/specialaccess/TurnScreenOnApps.kt b/src/com/android/settings/spa/app/specialaccess/TurnScreenOnApps.kt
new file mode 100644
index 0000000..262acb7
--- /dev/null
+++ b/src/com/android/settings/spa/app/specialaccess/TurnScreenOnApps.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.specialaccess
+
+import android.Manifest
+import android.app.AppOpsManager
+import android.app.settings.SettingsEnums
+import android.content.Context
+import com.android.settings.R
+import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
+import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
+import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
+import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
+
+object TurnScreenOnAppsAppListProvider : TogglePermissionAppListProvider {
+    override val permissionType = "TurnScreenOnApps"
+    override fun createModel(context: Context) = TurnScreenOnAppsListModel(context)
+}
+
+class TurnScreenOnAppsListModel(context: Context) : AppOpPermissionListModel(context) {
+    override val pageTitleResId = com.android.settingslib.R.string.turn_screen_on_title
+    override val switchTitleResId = com.android.settingslib.R.string.allow_turn_screen_on
+    override val footerResId = com.android.settingslib.R.string.allow_turn_screen_on_description
+    override val appOp = AppOpsManager.OP_TURN_SCREEN_ON
+    override val permission = Manifest.permission.TURN_SCREEN_ON
+    override val setModeByUid = true
+
+    override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
+        super.setAllowed(record, newAllowed)
+        logPermissionChange(newAllowed)
+    }
+
+    private fun logPermissionChange(newAllowed: Boolean) {
+        featureFactory.metricsFeatureProvider.action(
+            context,
+            SettingsEnums.SETTINGS_MANAGE_TURN_SCREEN_ON,
+            if (newAllowed) 1 else 0
+        )
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/spa/app/specialaccess/VoiceActivationApps.kt b/src/com/android/settings/spa/app/specialaccess/VoiceActivationApps.kt
new file mode 100644
index 0000000..de5f3b7
--- /dev/null
+++ b/src/com/android/settings/spa/app/specialaccess/VoiceActivationApps.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.specialaccess
+
+import android.Manifest
+import android.app.AppOpsManager
+import android.app.settings.SettingsEnums
+import android.content.Context
+import android.content.res.Resources
+import com.android.settings.R
+import com.android.settings.overlay.FeatureFactory
+import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
+import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
+import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
+
+
+object VoiceActivationAppsListProvider : TogglePermissionAppListProvider {
+    override val permissionType = "VoiceActivationApps"
+    override fun createModel(context: Context) = VoiceActivationAppsListModel(context)
+}
+
+class VoiceActivationAppsListModel(context: Context) : AppOpPermissionListModel(context) {
+    override val pageTitleResId = R.string.voice_activation_apps_title
+    override val switchTitleResId = R.string.permit_voice_activation_apps
+    override val footerResId = R.string.allow_voice_activation_apps_description
+    override val appOp = AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO
+    override val permission = Manifest.permission.RECEIVE_SANDBOX_TRIGGER_AUDIO
+    override val setModeByUid = true
+
+    override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
+        super.setAllowed(record, newAllowed)
+        logPermissionChange(newAllowed)
+    }
+
+    private fun logPermissionChange(newAllowed: Boolean) {
+        val category = when {
+            newAllowed -> SettingsEnums.APP_SPECIAL_PERMISSION_RECEIVE_SANDBOX_TRIGGER_AUDIO_ALLOW
+            else -> SettingsEnums.APP_SPECIAL_PERMISSION_RECEIVE_SANDBOX_TRIGGER_AUDIO_DENY
+        }
+        /**
+         * Leave the package string empty as we should not log the package names for the collected
+         * metrics.
+         */
+        FeatureFactory.featureFactory.metricsFeatureProvider.action(context, category, "")
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/spa/app/specialaccess/VoiceActivationAppsPreferenceController.kt b/src/com/android/settings/spa/app/specialaccess/VoiceActivationAppsPreferenceController.kt
new file mode 100644
index 0000000..27d4b4b
--- /dev/null
+++ b/src/com/android/settings/spa/app/specialaccess/VoiceActivationAppsPreferenceController.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.specialaccess
+
+import android.content.Context
+import androidx.preference.Preference
+import com.android.settings.core.BasePreferenceController
+import com.android.settings.flags.Flags
+import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
+
+class VoiceActivationAppsPreferenceController(context: Context, preferenceKey: String) :
+        BasePreferenceController(context, preferenceKey) {
+    override fun getAvailabilityStatus() =
+        if (Flags.enableVoiceActivationAppsInSettings()) AVAILABLE
+        else CONDITIONALLY_UNAVAILABLE
+
+    override fun handlePreferenceTreeClick(preference: Preference): Boolean {
+        if (preference.key == mPreferenceKey) {
+            mContext.startSpaActivity(VoiceActivationAppsListProvider.getAppListRoute())
+            return true
+        }
+        return false
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/spa/app/storage/StorageAppList.kt b/src/com/android/settings/spa/app/storage/StorageAppList.kt
index 8fc3eb5..c33de33 100644
--- a/src/com/android/settings/spa/app/storage/StorageAppList.kt
+++ b/src/com/android/settings/spa/app/storage/StorageAppList.kt
@@ -22,7 +22,7 @@
 import androidx.annotation.StringRes
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.State
-import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.stringResource
@@ -121,13 +121,9 @@
     ): Flow<List<AppRecordWithSize>> = recordListFlow.filterItem { type.filter(it) }
 
     @Composable
-    override fun getSummary(option: Int, record: AppRecordWithSize): State<String> {
-        val storageSummary = record.app.getStorageSummary()
-        return remember {
-            derivedStateOf {
-                storageSummary.value
-            }
-        }
+    override fun getSummary(option: Int, record: AppRecordWithSize): () -> String {
+        val storageSummary by record.app.getStorageSummary()
+        return { storageSummary }
     }
 
     @Composable
diff --git a/src/com/android/settings/spa/development/UsageStatsListModel.kt b/src/com/android/settings/spa/development/UsageStatsListModel.kt
index 61c24ac..d27796d 100644
--- a/src/com/android/settings/spa/development/UsageStatsListModel.kt
+++ b/src/com/android/settings/spa/development/UsageStatsListModel.kt
@@ -22,10 +22,8 @@
 import android.content.pm.ApplicationInfo
 import android.text.format.DateUtils
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
 import com.android.settings.R
 import com.android.settings.spa.development.UsageStatsListModel.SpinnerItem.Companion.toSpinnerItem
-import com.android.settingslib.spa.framework.compose.stateOf
 import com.android.settingslib.spa.widget.ui.SpinnerOption
 import com.android.settingslib.spaprivileged.model.app.AppEntry
 import com.android.settingslib.spaprivileged.model.app.AppListModel
@@ -55,7 +53,7 @@
         }
 
     override fun getSpinnerOptions(recordList: List<UsageStatsAppRecord>): List<SpinnerOption> =
-        SpinnerItem.values().map {
+        SpinnerItem.entries.map {
             SpinnerOption(
                 id = it.ordinal,
                 text = context.getString(it.stringResId),
@@ -77,7 +75,7 @@
     }.then(super.getComparator(option))
 
     @Composable
-    override fun getSummary(option: Int, record: UsageStatsAppRecord): State<String>? {
+    override fun getSummary(option: Int, record: UsageStatsAppRecord): (() -> String)? {
         val usageStats = record.usageStats ?: return null
         val lastTimeUsed = DateUtils.formatSameDayTime(
             usageStats.lastTimeUsed, now, DateFormat.MEDIUM, DateFormat.MEDIUM
@@ -85,7 +83,7 @@
         val lastTimeUsedLine = "${context.getString(R.string.last_time_used_label)}: $lastTimeUsed"
         val usageTime = DateUtils.formatElapsedTime(usageStats.totalTimeInForeground / 1000)
         val usageTimeLine = "${context.getString(R.string.usage_time_label)}: $usageTime"
-        return stateOf("$lastTimeUsedLine\n$usageTimeLine")
+        return { "$lastTimeUsedLine\n$usageTimeLine" }
     }
 
     private fun getUsageStats(): Map<String, UsageStats> {
@@ -101,7 +99,7 @@
         AppName(R.string.usage_stats_sort_by_app_name);
 
         companion object {
-            fun Int.toSpinnerItem(): SpinnerItem = values()[this]
+            fun Int.toSpinnerItem(): SpinnerItem = entries[this]
         }
     }
 }
diff --git a/src/com/android/settings/spa/development/compat/PlatformCompatAppListModel.kt b/src/com/android/settings/spa/development/compat/PlatformCompatAppListModel.kt
index c6752b9..8f53698 100644
--- a/src/com/android/settings/spa/development/compat/PlatformCompatAppListModel.kt
+++ b/src/com/android/settings/spa/development/compat/PlatformCompatAppListModel.kt
@@ -24,7 +24,6 @@
 import androidx.core.os.bundleOf
 import com.android.settings.core.SubSettingLauncher
 import com.android.settings.development.compat.PlatformCompatDashboard
-import com.android.settingslib.spa.framework.compose.stateOf
 import com.android.settingslib.spa.framework.util.filterItem
 import com.android.settingslib.spa.framework.util.mapItem
 import com.android.settingslib.spaprivileged.model.app.AppListModel
@@ -53,8 +52,9 @@
     }
 
     @Composable
-    override fun getSummary(option: Int, record: PlatformCompatAppRecord) =
-        stateOf(record.app.packageName)
+    override fun getSummary(option: Int, record: PlatformCompatAppRecord): () -> String = {
+        record.app.packageName
+    }
 
     @Composable
     override fun AppListItemModel<PlatformCompatAppRecord>.AppItem() {
diff --git a/src/com/android/settings/spa/network/AirplaneModePreference.kt b/src/com/android/settings/spa/network/AirplaneModePreference.kt
index 462c121..27261b6 100644
--- a/src/com/android/settings/spa/network/AirplaneModePreference.kt
+++ b/src/com/android/settings/spa/network/AirplaneModePreference.kt
@@ -20,8 +20,9 @@
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.outlined.AirplanemodeActive
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.runtime.remember
 import androidx.compose.ui.platform.LocalContext
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
@@ -37,11 +38,12 @@
     val context = LocalContext.current
     val controller = remember { AirplaneModeController(context) }
     if (!controller.isAvailable()) return
+    val checked by controller.airplaneModeState.observeAsState(
+        initial = controller.isAirplaneModeOn()
+    )
     SwitchPreference(object : SwitchPreferenceModel {
         override val title = context.getString(R.string.airplane_mode)
-        override val checked = controller.airplaneModeState.observeAsState(
-            initial = controller.isAirplaneModeOn()
-        )
+        override val checked = { checked }
         override val onCheckedChange = { newChecked: Boolean ->
             controller.setChecked(newChecked)
         }
diff --git a/src/com/android/settings/spa/network/NetworkAndInternet.kt b/src/com/android/settings/spa/network/NetworkAndInternet.kt
index 777133e..f985237 100644
--- a/src/com/android/settings/spa/network/NetworkAndInternet.kt
+++ b/src/com/android/settings/spa/network/NetworkAndInternet.kt
@@ -34,7 +34,6 @@
 import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
 import com.android.settingslib.spa.framework.common.createSettingsPage
 import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.framework.compose.toState
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spa.widget.scaffold.RegularScaffold
@@ -60,9 +59,10 @@
     fun buildInjectEntry(): SettingsEntryBuilder {
         return SettingsEntryBuilder.createInject(owner = owner)
             .setUiLayoutFn {
+                val summary = stringResource(getSummaryResId())
                 Preference(object : PreferenceModel {
                     override val title = stringResource(R.string.network_dashboard_title)
-                    override val summary = stringResource(getSummaryResId()).toState()
+                    override val summary = { summary }
                     override val onClick = navigator(name)
                     override val icon = @Composable {
                         SettingsIcon(imageVector = Icons.Outlined.Wifi)
diff --git a/src/com/android/settings/spa/notification/AppListNotifications.kt b/src/com/android/settings/spa/notification/AppListNotifications.kt
index c1e5d64..00e4394 100644
--- a/src/com/android/settings/spa/notification/AppListNotifications.kt
+++ b/src/com/android/settings/spa/notification/AppListNotifications.kt
@@ -23,7 +23,6 @@
 import com.android.settingslib.spa.framework.common.SettingsPageProvider
 import com.android.settingslib.spa.framework.compose.navigator
 import com.android.settingslib.spa.framework.compose.rememberContext
-import com.android.settingslib.spa.framework.compose.toState
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spaprivileged.template.app.AppListPage
@@ -41,9 +40,10 @@
 
     @Composable
     fun EntryItem() {
+        val summary = stringResource(R.string.app_notification_field_summary)
         Preference(object : PreferenceModel {
             override val title = stringResource(R.string.app_notifications_title)
-            override val summary = stringResource(R.string.app_notification_field_summary).toState()
+            override val summary = { summary }
             override val onClick = navigator(name)
         })
     }
diff --git a/src/com/android/settings/spa/notification/AppNotificationsListModel.kt b/src/com/android/settings/spa/notification/AppNotificationsListModel.kt
index 0b9b676..2f3de3a 100644
--- a/src/com/android/settings/spa/notification/AppNotificationsListModel.kt
+++ b/src/com/android/settings/spa/notification/AppNotificationsListModel.kt
@@ -21,15 +21,15 @@
 import android.content.pm.ApplicationInfo
 import android.icu.text.RelativeDateTimeFormatter
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.produceState
 import com.android.settings.R
 import com.android.settings.applications.AppInfoBase
 import com.android.settings.notification.app.AppNotificationSettings
 import com.android.settings.spa.notification.SpinnerItem.Companion.toSpinnerItem
-import com.android.settingslib.spa.framework.compose.stateOf
 import com.android.settingslib.spa.framework.util.asyncFilter
 import com.android.settingslib.spa.framework.util.asyncForEach
+import com.android.settingslib.spa.livedata.observeAsCallback
 import com.android.settingslib.spa.widget.ui.SpinnerOption
 import com.android.settingslib.spaprivileged.model.app.AppEntry
 import com.android.settingslib.spaprivileged.model.app.AppListModel
@@ -37,9 +37,11 @@
 import com.android.settingslib.spaprivileged.template.app.AppListItemModel
 import com.android.settingslib.spaprivileged.template.app.AppListTwoTargetSwitchItem
 import com.android.settingslib.utils.StringUtil
+import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.withContext
 
 data class AppNotificationsRecord(
     override val app: ApplicationInfo,
@@ -91,16 +93,17 @@
     }.then(super.getComparator(option))
 
     @Composable
-    override fun getSummary(option: Int, record: AppNotificationsRecord) = record.sentState?.let {
-        when (option.toSpinnerItem()) {
-            SpinnerItem.MostRecent -> stateOf(formatLastSent(it.lastSent))
-            SpinnerItem.MostFrequent -> stateOf(repository.calculateFrequencySummary(it.sentCount))
-            else -> null
+    override fun getSummary(option: Int, record: AppNotificationsRecord): (() -> String)? =
+        record.sentState?.let {
+            when (option.toSpinnerItem()) {
+                SpinnerItem.MostRecent -> ({ formatLastSent(it.lastSent) })
+                SpinnerItem.MostFrequent -> ({ repository.calculateFrequencySummary(it.sentCount) })
+                else -> null
+            }
         }
-    }
 
     override fun getSpinnerOptions(recordList: List<AppNotificationsRecord>): List<SpinnerOption> =
-        SpinnerItem.values().map {
+        SpinnerItem.entries.map {
             SpinnerOption(
                 id = it.ordinal,
                 text = context.getString(it.stringResId),
@@ -117,12 +120,15 @@
 
     @Composable
     override fun AppListItemModel<AppNotificationsRecord>.AppItem() {
+        val changeable by produceState(initialValue = false) {
+            withContext(Dispatchers.Default) {
+                value = repository.isChangeable(record.app)
+            }
+        }
         AppListTwoTargetSwitchItem(
             onClick = { navigateToAppNotificationSettings(app = record.app) },
-            checked = record.controller.isEnabled.observeAsState(),
-            changeable = produceState(initialValue = false) {
-                value = repository.isChangeable(record.app)
-            },
+            checked = record.controller.isEnabled.observeAsCallback(),
+            changeable = { changeable },
             onCheckedChange = record.controller::setEnabled,
         )
     }
@@ -145,6 +151,6 @@
     TurnedOff(R.string.filter_notif_blocked_apps);
 
     companion object {
-        fun Int.toSpinnerItem(): SpinnerItem = values()[this]
+        fun Int.toSpinnerItem(): SpinnerItem = entries[this]
     }
 }
diff --git a/src/com/android/settings/spa/notification/NotificationMain.kt b/src/com/android/settings/spa/notification/NotificationMain.kt
index 305f201..b3c7a55 100644
--- a/src/com/android/settings/spa/notification/NotificationMain.kt
+++ b/src/com/android/settings/spa/notification/NotificationMain.kt
@@ -25,13 +25,12 @@
 import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
 import com.android.settingslib.spa.framework.common.SettingsPageProvider
 import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
+import com.android.settingslib.spa.framework.common.createSettingsPage
 import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.framework.compose.toState
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spa.widget.scaffold.RegularScaffold
 import com.android.settingslib.spa.widget.ui.SettingsIcon
-import com.android.settingslib.spa.framework.common.createSettingsPage
 
 object NotificationMainPageProvider : SettingsPageProvider {
     override val name = "NotificationMain"
@@ -53,9 +52,10 @@
     fun buildInjectEntry(): SettingsEntryBuilder {
         return SettingsEntryBuilder.createInject(owner = owner)
             .setUiLayoutFn {
+                val summary = stringResource(R.string.notification_dashboard_summary)
                 Preference(object : PreferenceModel {
                     override val title = stringResource(R.string.configure_notification_settings)
-                    override val summary = stringResource(R.string.notification_dashboard_summary).toState()
+                    override val summary = { summary }
                     override val onClick = navigator(name)
                     override val icon = @Composable {
                         SettingsIcon(imageVector = Icons.Outlined.Notifications)
diff --git a/src/com/android/settings/spa/system/AppLanguages.kt b/src/com/android/settings/spa/system/AppLanguages.kt
index b878aa7..d836a32 100644
--- a/src/com/android/settings/spa/system/AppLanguages.kt
+++ b/src/com/android/settings/spa/system/AppLanguages.kt
@@ -26,7 +26,6 @@
 import com.android.settingslib.spa.framework.common.SettingsPageProvider
 import com.android.settingslib.spa.framework.compose.navigator
 import com.android.settingslib.spa.framework.compose.rememberContext
-import com.android.settingslib.spa.framework.compose.toState
 import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
@@ -52,9 +51,10 @@
 
     @Composable
     fun EntryItem() {
+        val summary = stringResource(R.string.app_locale_picker_summary)
         Preference(object : PreferenceModel {
             override val title = stringResource(R.string.app_locales_picker_menu_title)
-            override val summary = stringResource(R.string.app_locale_picker_summary).toState()
+            override val summary = { summary }
             override val onClick = navigator(name)
         })
     }
diff --git a/src/com/android/settings/spa/system/AppLanguagesListModel.kt b/src/com/android/settings/spa/system/AppLanguagesListModel.kt
index 3413ff0..3573e25 100644
--- a/src/com/android/settings/spa/system/AppLanguagesListModel.kt
+++ b/src/com/android/settings/spa/system/AppLanguagesListModel.kt
@@ -23,7 +23,7 @@
 import android.net.Uri
 import android.os.UserHandle
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.ui.res.stringResource
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -79,12 +79,14 @@
     ) = recordListFlow.filterItem { it.isAppLocaleSupported }
 
     @Composable
-    override fun getSummary(option: Int, record: AppLanguagesRecord): State<String> =
-        remember(record.app) {
+    override fun getSummary(option: Int, record: AppLanguagesRecord): () -> String {
+        val summary by remember(record.app) {
             flow {
                 emit(getSummary(record.app))
             }.flowOn(Dispatchers.IO)
         }.collectAsStateWithLifecycle(initialValue = stringResource(R.string.summary_placeholder))
+        return { summary }
+    }
 
     private fun getSummary(app: ApplicationInfo): String =
         AppLocaleDetails.getSummary(context, app).toString()
diff --git a/src/com/android/settings/spa/system/LanguageAndInputPageProvider.kt b/src/com/android/settings/spa/system/LanguageAndInputPageProvider.kt
index b5cd299..5c1038d 100644
--- a/src/com/android/settings/spa/system/LanguageAndInputPageProvider.kt
+++ b/src/com/android/settings/spa/system/LanguageAndInputPageProvider.kt
@@ -24,7 +24,6 @@
 import com.android.settings.R
 import com.android.settingslib.spa.framework.common.SettingsPageProvider
 import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.framework.compose.toState
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spa.widget.scaffold.RegularScaffold
@@ -42,9 +41,10 @@
 
     @Composable
     fun EntryItem() {
+        val summary = stringResource(R.string.language_settings)
         Preference(object : PreferenceModel {
             override val title = stringResource(R.string.language_settings)
-            override val summary = stringResource(R.string.language_settings).toState()
+            override val summary = { summary }
             override val onClick = navigator(name)
             override val icon = @Composable {
                 SettingsIcon(imageVector = Icons.Outlined.Language)
diff --git a/src/com/android/settings/spa/system/SystemMain.kt b/src/com/android/settings/spa/system/SystemMain.kt
index 04ae512..c9aa8cc 100644
--- a/src/com/android/settings/spa/system/SystemMain.kt
+++ b/src/com/android/settings/spa/system/SystemMain.kt
@@ -27,7 +27,6 @@
 import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
 import com.android.settingslib.spa.framework.common.createSettingsPage
 import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.framework.compose.toState
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spa.widget.scaffold.RegularScaffold
@@ -53,9 +52,10 @@
     fun buildInjectEntry(): SettingsEntryBuilder {
         return SettingsEntryBuilder.createInject(owner = owner)
             .setUiLayoutFn {
+                val summary = stringResource(R.string.system_dashboard_summary)
                 Preference(object : PreferenceModel {
                     override val title = stringResource(R.string.header_category_system)
-                    override val summary = stringResource(R.string.system_dashboard_summary).toState()
+                    override val summary = { summary }
                     override val onClick = navigator(name)
                     override val icon = @Composable {
                         SettingsIcon(imageVector = Icons.Outlined.Info)
diff --git a/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java b/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java
index d98b0e7..b615163 100644
--- a/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java
@@ -21,21 +21,27 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.IActivityManager;
+import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.os.RemoteException;
 
 import androidx.test.core.app.ApplicationProvider;
 
+import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.shadow.ShadowActivityManager;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
@@ -56,6 +62,7 @@
     private RadioWithImagePreference mRadioButtonPref;
     private Context mContext;
     private UserAspectRatioDetails mFragment;
+    private MetricsFeatureProvider mMetricsFeatureProvider;
 
     @Before
     public void setUp() {
@@ -67,6 +74,8 @@
         when(mFragment.getAspectRatioManager()).thenReturn(mUserAspectRatioManager);
         ShadowActivityManager.setService(mAm);
         mRadioButtonPref = new RadioWithImagePreference(mContext);
+        final FakeFeatureFactory featureFactory = FakeFeatureFactory.setupForTest();
+        mMetricsFeatureProvider = featureFactory.metricsFeatureProvider;
     }
 
     @Test
@@ -93,4 +102,31 @@
         verify(mUserAspectRatioManager).setUserMinAspectRatio(
                 any(), anyInt(), anyInt());
     }
+
+    @Test
+    public void onRadioButtonClicked_prefChange_logMetrics() throws NullPointerException {
+        // Default was already selected
+        mRadioButtonPref.setKey(KEY_PREF_DEFAULT);
+        mFragment.onRadioButtonClicked(mRadioButtonPref);
+        // Preference changed
+        mRadioButtonPref.setKey(KEY_PREF_3_2);
+        mFragment.onRadioButtonClicked(mRadioButtonPref);
+        InOrder inOrder = inOrder(mMetricsFeatureProvider);
+        // Check the old aspect ratio value is logged as having been unselected
+        inOrder.verify(mMetricsFeatureProvider)
+                .action(
+                        eq(SettingsEnums.PAGE_UNKNOWN),
+                        eq(SettingsEnums.ACTION_USER_ASPECT_RATIO_APP_DEFAULT_UNSELECTED),
+                        eq(SettingsEnums.USER_ASPECT_RATIO_APP_INFO_SETTINGS),
+                        any(),
+                        anyInt());
+        // Check the new aspect ratio value is logged as having been selected
+        inOrder.verify(mMetricsFeatureProvider)
+                .action(
+                        eq(SettingsEnums.PAGE_UNKNOWN),
+                        eq(SettingsEnums.ACTION_USER_ASPECT_RATIO_3_2_SELECTED),
+                        eq(SettingsEnums.USER_ASPECT_RATIO_APP_INFO_SETTINGS),
+                        any(),
+                        anyInt());
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.kt b/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.kt
index 90bb048..39b8446 100644
--- a/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.kt
+++ b/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.kt
@@ -23,22 +23,18 @@
 import android.os.UserManager
 import android.provider.Settings
 import androidx.preference.Preference
-import androidx.preference.PreferenceManager
 import androidx.test.core.app.ApplicationProvider
 import com.android.settings.datausage.DataUsageListTest.ShadowDataUsageBaseFragment
 import com.android.settings.datausage.TemplatePreference.NetworkServices
 import com.android.settings.datausage.lib.BillingCycleRepository
-import com.android.settings.network.MobileDataEnabledListener
 import com.android.settings.testutils.FakeFeatureFactory
 import com.android.settingslib.NetworkPolicyEditor
 import com.android.settingslib.core.AbstractPreferenceController
-import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers
 import org.mockito.Mock
 import org.mockito.Mockito.doNothing
 import org.mockito.Mockito.doReturn
@@ -62,9 +58,6 @@
     val mockito: MockitoRule = MockitoJUnit.rule()
 
     @Mock
-    private lateinit var mobileDataEnabledListener: MobileDataEnabledListener
-
-    @Mock
     private lateinit var networkServices: NetworkServices
 
     @Mock
@@ -86,7 +79,6 @@
     fun setUp() {
         FakeFeatureFactory.setupForTest()
         networkServices.mPolicyEditor = mock(NetworkPolicyEditor::class.java)
-        dataUsageList.dataStateListener = mobileDataEnabledListener
         doReturn(context).`when`(dataUsageList).context
         doReturn(userManager).`when`(context).getSystemService(UserManager::class.java)
         doReturn(false).`when`(userManager).isGuestUser
@@ -113,46 +105,6 @@
     }
 
     @Test
-    fun resume_shouldListenDataStateChange() {
-        dataUsageList.template = mock(NetworkTemplate::class.java)
-        dataUsageList.onCreate(null)
-        dataUsageList.dataStateListener = mobileDataEnabledListener
-        ReflectionHelpers.setField(
-            dataUsageList,
-            "mVisibilityLoggerMixin",
-            mock(VisibilityLoggerMixin::class.java),
-        )
-        ReflectionHelpers.setField(
-            dataUsageList,
-            "mPreferenceManager",
-            mock(PreferenceManager::class.java),
-        )
-        dataUsageList.onResume()
-        verify(mobileDataEnabledListener).start(ArgumentMatchers.anyInt())
-        dataUsageList.onPause()
-    }
-
-    @Test
-    fun pause_shouldUnlistenDataStateChange() {
-        dataUsageList.template = mock(NetworkTemplate::class.java)
-        dataUsageList.onCreate(null)
-        dataUsageList.dataStateListener = mobileDataEnabledListener
-        ReflectionHelpers.setField(
-            dataUsageList, "mVisibilityLoggerMixin", mock(
-                VisibilityLoggerMixin::class.java
-            )
-        )
-        ReflectionHelpers.setField(
-            dataUsageList, "mPreferenceManager", mock(
-                PreferenceManager::class.java
-            )
-        )
-        dataUsageList.onResume()
-        dataUsageList.onPause()
-        verify(mobileDataEnabledListener).stop()
-    }
-
-    @Test
     fun processArgument_shouldGetTemplateFromArgument() {
         val args = Bundle()
         args.putParcelable(
diff --git a/tests/robotests/src/com/android/settings/development/ShowHdrSdrRatioPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/ShowHdrSdrRatioPreferenceControllerTest.java
new file mode 100644
index 0000000..2234754
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/ShowHdrSdrRatioPreferenceControllerTest.java
@@ -0,0 +1,171 @@
+/**
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.development;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import androidx.preference.PreferenceScreen;
+import androidx.preference.TwoStatePreference;
+
+import com.android.settings.flags.Flags;
+import com.android.settings.testutils.shadow.ShadowParcel;
+
+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)
+public class ShowHdrSdrRatioPreferenceControllerTest {
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private TwoStatePreference mPreference;
+    @Mock
+    private IBinder mSurfaceFlinger;
+
+    private ShowHdrSdrRatioPreferenceController mController;
+
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mController = new ShowHdrSdrRatioPreferenceController(mContext, mSurfaceFlinger, true);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
+        mController.displayPreference(mScreen);
+    }
+
+    @Test
+    @Config(shadows = ShadowParcel.class)
+    public void onPreferenceChange_settingEnabled_shouldChecked() throws RemoteException {
+        mSetFlagsRule.enableFlags(Flags.FLAG_DEVELOPMENT_HDR_SDR_RATIO);
+        assertTrue(mController.isAvailable());
+        ShadowParcel.sReadBoolResult = true;
+        doReturn(true).when(mSurfaceFlinger)
+            .transact(anyInt(), any(), any(), eq(0 /* flags */));
+        mController.onPreferenceChange(mPreference, true /* new value */);
+        verify(mPreference).setChecked(true);
+    }
+
+    @Test
+    @Config(shadows = ShadowParcel.class)
+    public void onPreferenceChange_settingDisabled_shouldUnchecked() throws RemoteException {
+        mSetFlagsRule.enableFlags(Flags.FLAG_DEVELOPMENT_HDR_SDR_RATIO);
+        assertTrue(mController.isAvailable());
+        ShadowParcel.sReadBoolResult = false;
+        doReturn(true).when(mSurfaceFlinger)
+            .transact(anyInt(), any(), any(), eq(0 /* flags */));
+        mController.onPreferenceChange(mPreference, false /* new value */);
+        verify(mPreference).setChecked(false);
+    }
+
+    @Test
+    @Config(shadows = ShadowParcel.class)
+    public void updateState_settingEnabled_shouldChecked() throws RemoteException {
+        mSetFlagsRule.enableFlags(Flags.FLAG_DEVELOPMENT_HDR_SDR_RATIO);
+        assertTrue(mController.isAvailable());
+        ShadowParcel.sReadBoolResult = true;
+        doReturn(true).when(mSurfaceFlinger)
+            .transact(anyInt(), any(), any(), eq(0 /* flags */));
+        mController.updateState(mPreference);
+        verify(mPreference).setChecked(true);
+    }
+
+    @Test
+    @Config(shadows = ShadowParcel.class)
+    public void updateState_settingDisabled_shouldUnchecked() throws RemoteException {
+        mSetFlagsRule.enableFlags(Flags.FLAG_DEVELOPMENT_HDR_SDR_RATIO);
+        assertTrue(mController.isAvailable());
+        ShadowParcel.sReadBoolResult = false;
+        doReturn(true).when(mSurfaceFlinger)
+            .transact(anyInt(), any(), any(), eq(0 /* flags */));
+        mController.updateState(mPreference);
+        verify(mPreference).setChecked(false);
+    }
+
+    @Test
+    public void settingNotAvailable_isHdrSdrRatioAvailableFalse_flagsOff() {
+        mSetFlagsRule.disableFlags(Flags.FLAG_DEVELOPMENT_HDR_SDR_RATIO);
+        mController = new ShowHdrSdrRatioPreferenceController(mContext, mSurfaceFlinger, true);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void settingNotAvailable_isHdrSdrRatioAvailableTrue_flagsOn() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_DEVELOPMENT_HDR_SDR_RATIO);
+        mController = new ShowHdrSdrRatioPreferenceController(mContext, mSurfaceFlinger, false);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    @Config(shadows = ShadowParcel.class)
+    public void onDeveloperOptionsSwitchDisabled_preferenceUnchecked_shouldNotTurnOffPreference()
+            throws RemoteException {
+        mSetFlagsRule.enableFlags(Flags.FLAG_DEVELOPMENT_HDR_SDR_RATIO);
+        assertTrue(mController.isAvailable());
+        ShadowParcel.sReadBoolResult = false;
+        doReturn(true).when(mSurfaceFlinger)
+            .transact(anyInt(), any(), any(), eq(0 /* flags */));
+        when(mPreference.isChecked()).thenReturn(false);
+        mController.onDeveloperOptionsSwitchDisabled();
+
+        mController.writeShowHdrSdrRatioSetting(true);
+        verify(mPreference).setChecked(false);
+        verify(mPreference).setEnabled(false);
+    }
+
+    @Test
+    @Config(shadows = ShadowParcel.class)
+    public void onDeveloperOptionsSwitchDisabled_preferenceChecked_shouldTurnOffPreference()
+            throws RemoteException {
+        mSetFlagsRule.enableFlags(Flags.FLAG_DEVELOPMENT_HDR_SDR_RATIO);
+        assertTrue(mController.isAvailable());
+        ShadowParcel.sReadBoolResult = true;
+        doReturn(true).when(mSurfaceFlinger)
+            .transact(anyInt(), any(), any(), eq(0 /* flags */));
+        when(mPreference.isChecked()).thenReturn(true);
+        mController.onDeveloperOptionsSwitchDisabled();
+
+        mController.writeShowHdrSdrRatioSetting(false);
+        verify(mPreference).setChecked(false);
+        verify(mPreference).setEnabled(false);
+    }
+}
+
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDaoTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDaoTest.java
index 8462867..3c3e3c3 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDaoTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDaoTest.java
@@ -149,4 +149,31 @@
         mBatteryEventDao.clearAll();
         assertThat(mBatteryEventDao.getAll()).isEmpty();
     }
+
+    @Test
+    public void getAllAfter_filterTimestamp_returnExpectedResult() {
+        mBatteryEventDao.insert(BatteryEventEntity.newBuilder()
+                .setTimestamp(100L)
+                .setBatteryEventType(1)
+                .setBatteryLevel(66)
+                .build());
+        mBatteryEventDao.insert(BatteryEventEntity.newBuilder()
+                .setTimestamp(200L)
+                .setBatteryEventType(1)
+                .setBatteryLevel(88)
+                .build());
+
+        final Cursor cursor = mBatteryEventDao.getAllAfter(200L, List.of(1));
+        assertThat(cursor.getCount()).isEqualTo(1);
+        cursor.moveToFirst();
+        assertThat(cursor.getLong(cursor.getColumnIndex(KEY_TIMESTAMP)))
+                .isEqualTo(200L);
+        assertThat(cursor.getInt(cursor.getColumnIndex(KEY_BATTERY_EVENT_TYPE)))
+                .isEqualTo(1);
+        assertThat(cursor.getInt(cursor.getColumnIndex(KEY_BATTERY_LEVEL)))
+                .isEqualTo(88);
+
+        mBatteryEventDao.clearAll();
+        assertThat(mBatteryEventDao.getAll()).isEmpty();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManagerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManagerTest.java
new file mode 100644
index 0000000..cdf1514
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManagerTest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.datasaver;
+
+import static android.net.NetworkPolicyManager.POLICY_NONE;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class DynamicDenylistManagerTest {
+
+    private static final String FAKE_UID_1 = "package_uid_1";
+    private static final String FAKE_UID_2 = "package_uid_2";
+
+    private SharedPreferences mManualDenyListPref;
+    private SharedPreferences mDynamicDenyListPref;
+    private DynamicDenylistManager mDynamicDenylistManager;
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application.getApplicationContext();
+        mDynamicDenylistManager = new DynamicDenylistManager(mContext);
+        mManualDenyListPref = mDynamicDenylistManager.getManualDenylistPref();
+        mDynamicDenyListPref = mDynamicDenylistManager.getDynamicDenylistPref();
+    }
+
+    @After
+    public void tearDown() {
+        mDynamicDenylistManager.clearManualDenylistPref();
+        mDynamicDenylistManager.clearDynamicDenylistPref();
+    }
+
+    @Test
+    public void getManualDenylistPref_isEmpty() {
+        assertThat(mManualDenyListPref.getAll()).isEmpty();
+    }
+
+    @Test
+    public void getDynamicDenylistPref_isEmpty() {
+        assertThat(mDynamicDenyListPref.getAll()).isEmpty();
+    }
+
+    @Test
+    public void getManualDenylistPref_initiated_containsExpectedValue() {
+        mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
+
+        assertThat(mManualDenyListPref.getAll().size()).isEqualTo(1);
+        assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
+    }
+
+    @Test
+    public void getDynamicDenylistPref_initiated_containsExpectedValue() {
+        mDynamicDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
+
+        assertThat(mDynamicDenyListPref.getAll()).hasSize(1);
+        assertTrue(mDynamicDenyListPref.contains(FAKE_UID_1));
+    }
+
+    @Test
+    public void updateManualDenylist_policyReject_addsUid() {
+        mDynamicDenylistManager.updateManualDenylist(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND);
+
+        assertThat(mManualDenyListPref.getAll()).hasSize(1);
+        assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
+    }
+
+    @Test
+    public void updateManualDenylist_policyNone_removesUid() {
+        mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
+        assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
+
+        mDynamicDenylistManager.updateManualDenylist(FAKE_UID_1, POLICY_NONE);
+
+        assertThat(mManualDenyListPref.getAll()).isEmpty();
+    }
+
+    @Test
+    public void updateManualDenylist_samePolicy_doNothing() {
+        mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
+        assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
+
+        mDynamicDenylistManager.updateManualDenylist(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND);
+
+        assertThat(mManualDenyListPref.getAll()).hasSize(1);
+    }
+
+    @Test
+    public void isManualDenylist_returnsFalse() {
+        assertFalse(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1));
+    }
+
+    @Test
+    public void isManualDenylist_incorrectUid_returnsFalse() {
+        mManualDenyListPref.edit().putInt(FAKE_UID_2, POLICY_REJECT_METERED_BACKGROUND).apply();
+
+        assertFalse(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1));
+    }
+
+    @Test
+    public void isManualDenylist_initiated_returnsTrue() {
+        mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
+
+        assertTrue(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1));
+    }
+
+    @Test
+    public void clearManualDenylistPref_isEmpty() {
+        mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
+        assertThat(mManualDenyListPref.getAll()).hasSize(1);
+        assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
+
+        mDynamicDenylistManager.clearManualDenylistPref();
+
+        assertThat(mManualDenyListPref.getAll()).isEmpty();
+    }
+
+    @Test
+    public void clearDynamicDenylistPref_isEmpty() {
+        mDynamicDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
+        assertThat(mDynamicDenyListPref.getAll()).hasSize(1);
+        assertTrue(mDynamicDenyListPref.contains(FAKE_UID_1));
+
+        mDynamicDenylistManager.clearDynamicDenylistPref();
+
+        assertThat(mDynamicDenyListPref.getAll()).isEmpty();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/localepicker/AppLocalePickerActivityTest.java b/tests/robotests/src/com/android/settings/localepicker/AppLocalePickerActivityTest.java
index 72b01f8..817df4c 100644
--- a/tests/robotests/src/com/android/settings/localepicker/AppLocalePickerActivityTest.java
+++ b/tests/robotests/src/com/android/settings/localepicker/AppLocalePickerActivityTest.java
@@ -18,6 +18,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
@@ -27,6 +29,7 @@
 import android.app.Activity;
 import android.app.ApplicationPackageManager;
 import android.app.LocaleConfig;
+import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -51,6 +54,7 @@
 import com.android.settings.applications.AppInfoBase;
 import com.android.settings.applications.AppLocaleUtil;
 import com.android.settings.flags.Flags;
+import com.android.settings.testutils.FakeFeatureFactory;
 
 import org.junit.After;
 import org.junit.Before;
@@ -91,6 +95,7 @@
     private static final String EN_US = "en-US";
     private static int sUid;
 
+    private FakeFeatureFactory mFeatureFactory;
     private LocaleNotificationDataManager mDataManager;
     private AppLocalePickerActivity mActivity;
 
@@ -117,6 +122,7 @@
         when(mLocaleConfig.getSupportedLocales()).thenReturn(LocaleList.forLanguageTags("en-US"));
         ReflectionHelpers.setStaticField(AppLocaleUtil.class, "sLocaleConfig", mLocaleConfig);
         sUid = Process.myUid();
+        mFeatureFactory = FakeFeatureFactory.setupForTest();
     }
 
     @After
@@ -229,6 +235,37 @@
     }
 
     @Test
+    public void onLocaleSelected_logLocaleSource() {
+        ActivityController<TestAppLocalePickerActivity> controller =
+                initActivityController(true);
+        LocaleList.setDefault(LocaleList.forLanguageTags("ja-JP,en-CA,en-US"));
+        Locale locale = new Locale("en", "US");
+        when(mLocaleInfo.getLocale()).thenReturn(locale);
+        when(mLocaleInfo.isSystemLocale()).thenReturn(false);
+        when(mLocaleInfo.isSuggested()).thenReturn(true);
+        when(mLocaleInfo.isSuggestionOfType(LocaleStore.LocaleInfo.SUGGESTION_TYPE_SIM)).thenReturn(
+                true);
+        when(mLocaleInfo.isSuggestionOfType(
+                LocaleStore.LocaleInfo.SUGGESTION_TYPE_SYSTEM_AVAILABLE_LANGUAGE)).thenReturn(
+                true);
+        when(mLocaleInfo.isSuggestionOfType(
+                LocaleStore.LocaleInfo.SUGGESTION_TYPE_OTHER_APP_LANGUAGE)).thenReturn(
+                true);
+        when(mLocaleInfo.isSuggestionOfType(
+                LocaleStore.LocaleInfo.SUGGESTION_TYPE_IME_LANGUAGE)).thenReturn(
+                true);
+
+        controller.create();
+        AppLocalePickerActivity mActivity = controller.get();
+        mActivity.onLocaleSelected(mLocaleInfo);
+
+        int localeSource = 15; // SIM_LOCALE | SYSTEM_LOCALE |IME_LOCALE|APP_LOCALE
+        verify(mFeatureFactory.metricsFeatureProvider).action(
+                any(), eq(SettingsEnums.ACTION_CHANGE_APP_LANGUAGE_FROM_SUGGESTED),
+                eq(localeSource));
+    }
+
+    @Test
     @RequiresFlagsEnabled(Flags.FLAG_LOCALE_NOTIFICATION_ENABLED)
     public void onLocaleSelected_evaluateNotification_simpleLocaleUpdate_localeCreatedWithUid()
             throws Exception {
diff --git a/tests/robotests/src/com/android/settings/network/MobileNetworkListFragmentTest.java b/tests/robotests/src/com/android/settings/network/MobileNetworkListFragmentTest.java
deleted file mode 100644
index 2e04ea7..0000000
--- a/tests/robotests/src/com/android/settings/network/MobileNetworkListFragmentTest.java
+++ /dev/null
@@ -1,88 +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;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.os.UserManager;
-
-import com.android.settings.R;
-import com.android.settings.search.BaseSearchIndexProvider;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.util.ReflectionHelpers;
-
-@RunWith(RobolectricTestRunner.class)
-public class MobileNetworkListFragmentTest {
-    @Mock
-    private Context mContext;
-    @Mock
-    private Resources mResources;
-    @Mock
-    private UserManager mUserManager;
-
-    private MobileNetworkListFragment mFragment;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mFragment = new MobileNetworkListFragment();
-    }
-
-    @Test
-    public void isPageSearchEnabled_adminUser_shouldReturnTrue() {
-        when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
-        when(mUserManager.isAdminUser()).thenReturn(true);
-        final BaseSearchIndexProvider provider =
-                (BaseSearchIndexProvider) mFragment.SEARCH_INDEX_DATA_PROVIDER;
-
-        when(mContext.getResources()).thenReturn(mResources);
-        when(mResources.getBoolean(R.bool.config_show_sim_info)).thenReturn(true);
-
-        final Object obj = ReflectionHelpers.callInstanceMethod(provider, "isPageSearchEnabled",
-                ReflectionHelpers.ClassParameter.from(Context.class, mContext));
-        final boolean isEnabled = (Boolean) obj;
-
-        assertThat(isEnabled).isTrue();
-    }
-
-    @Test
-    public void isPageSearchEnabled_nonAdminUser_shouldReturnFalse() {
-        when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
-        when(mUserManager.isAdminUser()).thenReturn(false);
-        final BaseSearchIndexProvider provider =
-                (BaseSearchIndexProvider) mFragment.SEARCH_INDEX_DATA_PROVIDER;
-
-        when(mContext.getResources()).thenReturn(mResources);
-        when(mResources.getBoolean(R.bool.config_show_sim_info)).thenReturn(true);
-
-        final Object obj = ReflectionHelpers.callInstanceMethod(provider, "isPageSearchEnabled",
-                ReflectionHelpers.ClassParameter.from(Context.class, mContext));
-        final boolean isEnabled = (Boolean) obj;
-
-        assertThat(isEnabled).isFalse();
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/notification/app/AppChannelsBypassingDndPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/app/AppChannelsBypassingDndPreferenceControllerTest.java
index 13528b4..053b352 100644
--- a/tests/robotests/src/com/android/settings/notification/app/AppChannelsBypassingDndPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/app/AppChannelsBypassingDndPreferenceControllerTest.java
@@ -26,16 +26,19 @@
 import android.app.NotificationManager;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
+import android.platform.test.flag.junit.SetFlagsRule;
 
 import androidx.preference.PreferenceCategory;
 import androidx.preference.PreferenceManager;
 import androidx.preference.PreferenceScreen;
 import androidx.test.core.app.ApplicationProvider;
 
+import com.android.settings.flags.Flags;
 import com.android.settings.notification.NotificationBackend;
 import com.android.settingslib.PrimarySwitchPreference;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -51,6 +54,9 @@
 @LooperMode(LooperMode.Mode.LEGACY)
 public class AppChannelsBypassingDndPreferenceControllerTest {
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     @Mock
     private NotificationBackend mBackend;
 
@@ -150,4 +156,44 @@
         }
         return new ParceledListSlice<>(Collections.singletonList(group));
     }
+
+    @Test
+    public void displayPreference_duplicateChannelName_AddsGroupNameAsSummary() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_DEDUPE_DND_SETTINGS_CHANNELS);
+        NotificationChannelGroup group1 = new NotificationChannelGroup("group1_id", "Group1");
+        NotificationChannelGroup group2 = new NotificationChannelGroup("group2_id", "Group2");
+
+        group1.addChannel(new NotificationChannel("mail_group1_id", "Mail",
+                NotificationManager.IMPORTANCE_DEFAULT));
+        group1.addChannel(new NotificationChannel("other_group1_id", "Other",
+                NotificationManager.IMPORTANCE_DEFAULT));
+
+        group2.addChannel(new NotificationChannel("music_group2_id", "Music",
+                NotificationManager.IMPORTANCE_DEFAULT));
+        // This channel has the same name as a channel in group1.
+        group2.addChannel(new NotificationChannel("mail_group2_id", "Mail",
+                NotificationManager.IMPORTANCE_DEFAULT));
+
+        ParceledListSlice<NotificationChannelGroup> groups = new ParceledListSlice<>(
+                new ArrayList<NotificationChannelGroup>() {
+                    {
+                        add(group1);
+                        add(group2);
+                    }
+                }
+        );
+
+        when(mBackend.getGroups(eq(mAppRow.pkg), eq(mAppRow.uid))).thenReturn(groups);
+        mController.displayPreference(mPreferenceScreen);
+        ShadowApplication.runBackgroundTasks();
+        // Check that we've added the group name as a summary to channels that have identical names.
+        // Channels are also alphabetized.
+        assertThat(mCategory.getPreference(1).getTitle().toString()).isEqualTo("Mail");
+        assertThat(mCategory.getPreference(1).getSummary().toString()).isEqualTo("Group1");
+        assertThat(mCategory.getPreference(2).getTitle().toString()).isEqualTo("Mail");
+        assertThat(mCategory.getPreference(2).getSummary().toString()).isEqualTo("Group2");
+        assertThat(mCategory.getPreference(3).getTitle().toString()).isEqualTo("Music");
+        assertThat(mCategory.getPreference(4).getTitle().toString()).isEqualTo("Other");
+
+    }
 }
diff --git a/tests/robotests/testutils/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/testutils/com/android/settings/testutils/FakeFeatureFactory.java
index 9156cae..5a5008c 100644
--- a/tests/robotests/testutils/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/robotests/testutils/com/android/settings/testutils/FakeFeatureFactory.java
@@ -46,6 +46,7 @@
 import com.android.settings.overlay.SupportFeatureProvider;
 import com.android.settings.overlay.SurveyFeatureProvider;
 import com.android.settings.panel.PanelFeatureProvider;
+import com.android.settings.privatespace.PrivateSpaceLoginFeatureProvider;
 import com.android.settings.search.SearchFeatureProvider;
 import com.android.settings.security.SecurityFeatureProvider;
 import com.android.settings.security.SecuritySettingsFeatureProvider;
@@ -99,6 +100,7 @@
     public StylusFeatureProvider mStylusFeatureProvider;
     public OnboardingFeatureProvider mOnboardingFeatureProvider;
     public FastPairFeatureProvider mFastPairFeatureProvider;
+    public PrivateSpaceLoginFeatureProvider mPrivateSpaceLoginFeatureProvider;
 
     /**
      * Call this in {@code @Before} method of the test class to use fake factory.
@@ -146,6 +148,7 @@
         mStylusFeatureProvider = mock(StylusFeatureProvider.class);
         mOnboardingFeatureProvider = mock(OnboardingFeatureProvider.class);
         mFastPairFeatureProvider = mock(FastPairFeatureProvider.class);
+        mPrivateSpaceLoginFeatureProvider =  mock(PrivateSpaceLoginFeatureProvider.class);
     }
 
     @Override
@@ -323,5 +326,10 @@
     public FastPairFeatureProvider getFastPairFeatureProvider() {
         return mFastPairFeatureProvider;
     }
+
+    @Override
+    public PrivateSpaceLoginFeatureProvider getPrivateSpaceLoginFeatureProvider() {
+        return mPrivateSpaceLoginFeatureProvider;
+    }
 }
 
diff --git a/tests/spa_unit/Android.bp b/tests/spa_unit/Android.bp
index 28a2667..c3e99f7 100644
--- a/tests/spa_unit/Android.bp
+++ b/tests/spa_unit/Android.bp
@@ -34,6 +34,7 @@
         "androidx.compose.runtime_runtime",
         "androidx.test.ext.junit",
         "androidx.test.runner",
+        "flag-junit",
         "mockito-target-extended-minus-junit4",
     ],
     jni_libs: [
diff --git a/tests/spa_unit/src/com/android/settings/datausage/AppDataUsageCycleControllerTest.kt b/tests/spa_unit/src/com/android/settings/datausage/AppDataUsageCycleControllerTest.kt
index ea51f01..fe35259 100644
--- a/tests/spa_unit/src/com/android/settings/datausage/AppDataUsageCycleControllerTest.kt
+++ b/tests/spa_unit/src/com/android/settings/datausage/AppDataUsageCycleControllerTest.kt
@@ -22,7 +22,6 @@
 import androidx.preference.PreferenceManager
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.settings.datausage.lib.AppDataUsageDetailsRepository
 import com.android.settings.datausage.lib.IAppDataUsageDetailsRepository
 import com.android.settings.datausage.lib.NetworkUsageDetailsData
 import com.google.common.truth.Truth.assertThat
@@ -31,23 +30,18 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.kotlin.doReturn
-import org.mockito.kotlin.mock
 import org.mockito.kotlin.spy
-import org.mockito.kotlin.stub
 import org.mockito.kotlin.verify
 
 @RunWith(AndroidJUnit4::class)
 class AppDataUsageCycleControllerTest {
     private val context: Context = ApplicationProvider.getApplicationContext()
 
-    private val controller = AppDataUsageCycleController(context, KEY)
-
     private val preference = spy(SpinnerPreference(context, null).apply { key = KEY })
 
     private val preferenceScreen = PreferenceManager(context).createPreferenceScreen(context)
 
-    private val onUsageDataUpdated: (NetworkUsageDetailsData) -> Unit = {}
+    private val controller = AppDataUsageCycleController(context, KEY)
 
     @Before
     fun setUp() {
@@ -59,8 +53,8 @@
         val repository = object : IAppDataUsageDetailsRepository {
             override suspend fun queryDetailsForCycles() = emptyList<NetworkUsageDetailsData>()
         }
-        controller.init(repository, onUsageDataUpdated)
         controller.displayPreference(preferenceScreen)
+        controller.init(repository) {}
 
         controller.onViewCreated(TestLifecycleOwner())
         delay(100)
@@ -79,8 +73,8 @@
         val repository = object : IAppDataUsageDetailsRepository {
             override suspend fun queryDetailsForCycles() = listOf(detailsData)
         }
-        controller.init(repository, onUsageDataUpdated)
         controller.displayPreference(preferenceScreen)
+        controller.init(repository) {}
 
         controller.onViewCreated(TestLifecycleOwner())
         delay(100)
@@ -93,14 +87,14 @@
         val repository = object : IAppDataUsageDetailsRepository {
             override suspend fun queryDetailsForCycles() = emptyList<NetworkUsageDetailsData>()
         }
-        controller.init(repository, onUsageDataUpdated)
+        controller.displayPreference(preferenceScreen)
+        controller.init(repository) {}
+
         controller.setInitialCycles(
             initialCycles = listOf(CYCLE2_END_TIME, CYCLE1_END_TIME, CYCLE1_START_TIME),
             initialSelectedEndTime = CYCLE1_END_TIME,
         )
 
-        controller.displayPreference(preferenceScreen)
-
         verify(preference).setSelection(1)
     }
 
diff --git a/tests/spa_unit/src/com/android/settings/network/MobileDataEnabledFlowTest.kt b/tests/spa_unit/src/com/android/settings/network/MobileDataEnabledFlowTest.kt
new file mode 100644
index 0000000..4862309
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/MobileDataEnabledFlowTest.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network
+
+import android.content.Context
+import android.provider.Settings
+import android.telephony.SubscriptionManager
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
+import com.android.settingslib.spa.testutils.toListWithTimeout
+import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalBoolean
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.async
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class MobileDataEnabledFlowTest {
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    @Test
+    fun mobileDataEnabledFlow_notified(): Unit = runBlocking {
+        val flow = context.mobileDataEnabledFlow(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+
+        assertThat(flow.firstWithTimeoutOrNull()).isNotNull()
+    }
+
+    @Test
+    fun mobileDataEnabledFlow_changed_notified(): Unit = runBlocking {
+        var mobileDataEnabled by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA)
+        mobileDataEnabled = false
+
+        val flow = context.mobileDataEnabledFlow(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+        mobileDataEnabled = true
+
+        assertThat(flow.firstWithTimeoutOrNull()).isNotNull()
+    }
+
+    @Test
+    fun mobileDataEnabledFlow_forSubIdNotChanged(): Unit = runBlocking {
+        var mobileDataEnabled by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA)
+        mobileDataEnabled = false
+        var mobileDataEnabledForSubId
+            by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA + SUB_ID)
+        mobileDataEnabledForSubId = false
+
+        val listDeferred = async {
+            context.mobileDataEnabledFlow(SUB_ID).toListWithTimeout()
+        }
+
+        assertThat(listDeferred.await()).hasSize(1)
+    }
+
+    @Test
+    fun mobileDataEnabledFlow_forSubIdChanged(): Unit = runBlocking {
+        var mobileDataEnabled by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA)
+        mobileDataEnabled = false
+        var mobileDataEnabledForSubId
+            by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA + SUB_ID)
+        mobileDataEnabledForSubId = false
+
+        val listDeferred = async {
+            context.mobileDataEnabledFlow(SUB_ID).toListWithTimeout()
+        }
+        delay(100)
+        mobileDataEnabledForSubId = true
+
+        assertThat(listDeferred.await()).hasSize(2)
+    }
+
+    private companion object {
+        const val SUB_ID = 123
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/network/MobileNetworkListFragmentTest.kt b/tests/spa_unit/src/com/android/settings/network/MobileNetworkListFragmentTest.kt
new file mode 100644
index 0000000..3ba4bac
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/MobileNetworkListFragmentTest.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network
+
+import android.content.Context
+import android.content.res.Resources
+import android.os.UserManager
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.android.settingslib.spaprivileged.framework.common.userManager
+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.spy
+import org.mockito.kotlin.stub
+
+@RunWith(AndroidJUnit4::class)
+class MobileNetworkListFragmentTest {
+    private val mockUserManager = mock<UserManager>()
+
+    private val mockResources = mock<Resources>()
+
+    private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+        on { userManager } doReturn mockUserManager
+        on { resources } doReturn mockResources
+    }
+
+    @Test
+    fun isPageSearchEnabled_adminUser_shouldReturnTrue() {
+        mockUserManager.stub {
+            on { isAdminUser } doReturn true
+        }
+        mockResources.stub {
+            on { getBoolean(R.bool.config_show_sim_info) } doReturn true
+        }
+
+        val isEnabled =
+            MobileNetworkListFragment.SEARCH_INDEX_DATA_PROVIDER.isPageSearchEnabled(context)
+
+        assertThat(isEnabled).isTrue()
+    }
+
+    @Test
+    fun isPageSearchEnabled_nonAdminUser_shouldReturnFalse() {
+        mockUserManager.stub {
+            on { isAdminUser } doReturn false
+        }
+        mockResources.stub {
+            on { getBoolean(R.bool.config_show_sim_info) } doReturn true
+        }
+
+        val isEnabled =
+            MobileNetworkListFragment.SEARCH_INDEX_DATA_PROVIDER.isPageSearchEnabled(context)
+
+        assertThat(isEnabled).isFalse()
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/AllAppListTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/AllAppListTest.kt
index 53ed4f0..1a05479 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/AllAppListTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/AllAppListTest.kt
@@ -21,7 +21,6 @@
 import android.content.pm.PackageManager
 import android.graphics.drawable.Drawable
 import androidx.compose.runtime.SideEffect
-import androidx.compose.runtime.State
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithText
@@ -146,12 +145,12 @@
     fun allAppListModel_getSummary() {
         val listModel = AllAppListModel(context) { stateOf(SUMMARY) }
 
-        lateinit var summaryState: State<String>
+        lateinit var summary: () -> String
         composeTestRule.setContent {
-            summaryState = listModel.getSummary(option = 0, record = AppRecordWithSize(app = APP))
+            summary = listModel.getSummary(option = 0, record = AppRecordWithSize(app = APP))
         }
 
-        assertThat(summaryState.value).isEqualTo(SUMMARY)
+        assertThat(summary()).isEqualTo(SUMMARY)
     }
 
     @Test
@@ -163,13 +162,13 @@
             enabled = false
         }
 
-        lateinit var summaryState: State<String>
+        lateinit var summary: () -> String
         composeTestRule.setContent {
-            summaryState =
+            summary =
                 listModel.getSummary(option = 0, record = AppRecordWithSize(app = disabledApp))
         }
 
-        assertThat(summaryState.value).isEqualTo("$SUMMARY${System.lineSeparator()}Disabled")
+        assertThat(summary()).isEqualTo("$SUMMARY${System.lineSeparator()}Disabled")
     }
 
     @Test
@@ -179,13 +178,13 @@
             packageName = PACKAGE_NAME
         }
 
-        lateinit var summaryState: State<String>
+        lateinit var summary: () -> String
         composeTestRule.setContent {
-            summaryState =
+            summary =
                 listModel.getSummary(option = 0, record = AppRecordWithSize(app = notInstalledApp))
         }
 
-        assertThat(summaryState.value)
+        assertThat(summary())
             .isEqualTo("$SUMMARY${System.lineSeparator()}Not installed for this user")
     }
 
@@ -207,7 +206,7 @@
                     AppListItemModel(
                         record = AppRecordWithSize(app = app),
                         label = LABEL,
-                        summary = stateOf(SUMMARY),
+                        summary = { SUMMARY },
                     ).AppItem()
                 }
             }
@@ -224,13 +223,13 @@
             isArchived = true
         }
 
-        lateinit var summaryState: State<String>
+        lateinit var summary: () -> String
         composeTestRule.setContent {
-            summaryState =
+            summary =
                 listModel.getSummary(option = 0, record = AppRecordWithSize(app = archivedApp))
         }
 
-        assertThat(summaryState.value).isEqualTo(SUMMARY)
+        assertThat(summary()).isEqualTo(SUMMARY)
     }
 
     private fun getAppListInput(): AppListInput<AppRecordWithSize> {
@@ -252,7 +251,7 @@
                     AppListItemModel(
                         record = AppRecordWithSize(app = APP),
                         label = LABEL,
-                        summary = stateOf(SUMMARY),
+                        summary = { SUMMARY },
                     ).AppItem()
                 }
             }
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/WifiControlAppListModelTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/WifiControlAppListModelTest.kt
index c5c48f5..74aa861 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/WifiControlAppListModelTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/WifiControlAppListModelTest.kt
@@ -18,11 +18,8 @@
 
 import android.Manifest
 import android.app.AppOpsManager
-import android.app.AppOpsManager.MODE_ALLOWED
-import android.app.AppOpsManager.MODE_DEFAULT
 import android.content.Context
 import android.content.pm.ApplicationInfo
-import androidx.compose.runtime.State
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.lifecycle.MutableLiveData
 import androidx.test.core.app.ApplicationProvider
@@ -40,9 +37,9 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.junit.MockitoJUnit
 import org.mockito.junit.MockitoRule
+import org.mockito.Mockito.`when` as whenever
 
 @ExperimentalCoroutinesApi
 @RunWith(AndroidJUnit4::class)
@@ -248,9 +245,9 @@
     }
 
     private fun getIsAllowed(record: AppOpPermissionRecord): Boolean? {
-        lateinit var isAllowedState: State<Boolean?>
+        lateinit var isAllowedState: () -> Boolean?
         composeTestRule.setContent { isAllowedState = listModel.isAllowed(record) }
-        return isAllowedState.value
+        return isAllowedState()
     }
 
     private companion object {
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProviderTest.kt
index 4f372e2..dfacca8 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProviderTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProviderTest.kt
@@ -21,7 +21,6 @@
 import android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN
 import android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET
 import android.os.Build
-import androidx.compose.runtime.State
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithText
@@ -29,7 +28,6 @@
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.settings.R
-import com.android.settingslib.spa.framework.compose.stateOf
 import com.android.settingslib.spa.testutils.FakeNavControllerWrapper
 import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
 import com.android.settingslib.spaprivileged.template.app.AppListItemModel
@@ -132,7 +130,7 @@
                     AppListItemModel(
                         record = APP_RECORD_SUGGESTED,
                         label = LABEL,
-                        summary = stateOf(SUMMARY)
+                        summary = { SUMMARY }
                     ).AppItem()
                 }
             }
@@ -141,23 +139,23 @@
 
     @Test
     fun aspectRatioAppListModel_getSummaryDefault() {
-        val summaryState = setSummaryState(USER_MIN_ASPECT_RATIO_UNSET)
-        assertThat(summaryState.value)
-            .isEqualTo(context.getString(R.string.user_aspect_ratio_app_default))
+        val summary = getSummary(USER_MIN_ASPECT_RATIO_UNSET)
+
+        assertThat(summary).isEqualTo(context.getString(R.string.user_aspect_ratio_app_default))
     }
 
     @Test
     fun aspectRatioAppListModel_getSummaryWhenSplitScreen() {
-        val summaryState = setSummaryState(USER_MIN_ASPECT_RATIO_SPLIT_SCREEN)
-        assertThat(summaryState.value)
-            .isEqualTo(context.getString(R.string.user_aspect_ratio_half_screen))
+        val summary = getSummary(USER_MIN_ASPECT_RATIO_SPLIT_SCREEN)
+
+        assertThat(summary).isEqualTo(context.getString(R.string.user_aspect_ratio_half_screen))
     }
 
-    private fun setSummaryState(userOverride: Int): State<String> {
+    private fun getSummary(userOverride: Int): String {
         val listModel = UserAspectRatioAppListModel(context)
-        lateinit var summaryState: State<String>
+        lateinit var summary: () -> String
         composeTestRule.setContent {
-            summaryState = listModel.getSummary(option = 0,
+            summary = listModel.getSummary(option = 0,
                 record = UserAspectRatioAppListItemModel(
                     app = APP,
                     userOverride = userOverride,
@@ -165,7 +163,7 @@
                     canDisplay = true,
                 ))
         }
-        return summaryState
+        return summary()
     }
 
 
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProviderTest.kt
index ccd385f..5c65da1 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProviderTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProviderTest.kt
@@ -29,7 +29,6 @@
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.settings.R
-import com.android.settingslib.spa.framework.compose.stateOf
 import com.android.settingslib.spa.testutils.FakeNavControllerWrapper
 import com.android.settingslib.spaprivileged.template.app.AppListItemModel
 import com.google.common.truth.Truth.assertThat
@@ -244,7 +243,7 @@
                                 app = APP,
                                 dateOfInstall = TEST_FIRST_INSTALL_TIME),
                             label = TEST_LABEL,
-                            summary = stateOf(TEST_SUMMARY),
+                            summary = { TEST_SUMMARY },
                         ).AppItem()
                     }
                 }
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/AllFilesAccessTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/AllFilesAccessTest.kt
index f5d422d..4c65d90 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/AllFilesAccessTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/AllFilesAccessTest.kt
@@ -33,33 +33,12 @@
     private val listModel = AllFilesAccessListModel(context)
 
     @Test
-    fun pageTitleResId() {
+    fun modelResourceIdAndProperties() {
         assertThat(listModel.pageTitleResId).isEqualTo(R.string.manage_external_storage_title)
-    }
-
-    @Test
-    fun switchTitleResId() {
         assertThat(listModel.switchTitleResId).isEqualTo(R.string.permit_manage_external_storage)
-    }
-
-    @Test
-    fun footerResId() {
-        assertThat(listModel.footerResId)
-            .isEqualTo(R.string.allow_manage_external_storage_description)
-    }
-
-    @Test
-    fun appOp() {
+        assertThat(listModel.footerResId).isEqualTo(R.string.allow_manage_external_storage_description)
         assertThat(listModel.appOp).isEqualTo(AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE)
-    }
-
-    @Test
-    fun permission() {
         assertThat(listModel.permission).isEqualTo(Manifest.permission.MANAGE_EXTERNAL_STORAGE)
-    }
-
-    @Test
-    fun setModeByUid() {
         assertThat(listModel.setModeByUid).isTrue()
     }
 }
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/LongBackgroundTasksAppsTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/LongBackgroundTasksAppsTest.kt
new file mode 100644
index 0000000..579c6c9
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/LongBackgroundTasksAppsTest.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.specialaccess
+
+import android.Manifest
+import android.app.AppOpsManager
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import com.android.settings.R
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class LongBackgroundTasksAppsTest {
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    private val listModel = LongBackgroundTasksAppsListModel(context)
+
+    @Test
+    fun modelResourceIdAndProperties() {
+        assertThat(listModel.pageTitleResId).isEqualTo(R.string.long_background_tasks_title)
+        assertThat(listModel.switchTitleResId).isEqualTo(R.string.long_background_tasks_switch_title)
+        assertThat(listModel.footerResId).isEqualTo(R.string.long_background_tasks_footer_title)
+        assertThat(listModel.appOp).isEqualTo(AppOpsManager.OP_RUN_USER_INITIATED_JOBS)
+        assertThat(listModel.permission).isEqualTo(Manifest.permission.RUN_USER_INITIATED_JOBS)
+        assertThat(listModel.setModeByUid).isTrue()
+    }
+}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/MediaManagementAppsTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/MediaManagementAppsTest.kt
index b56d997..b901043 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/MediaManagementAppsTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/MediaManagementAppsTest.kt
@@ -33,33 +33,12 @@
     private val listModel = MediaManagementAppsListModel(context)
 
     @Test
-    fun pageTitleResId() {
+    fun modelResourceIdAndProperties() {
         assertThat(listModel.pageTitleResId).isEqualTo(R.string.media_management_apps_title)
-    }
-
-    @Test
-    fun switchTitleResId() {
-        assertThat(listModel.switchTitleResId)
-            .isEqualTo(R.string.media_management_apps_toggle_label)
-    }
-
-    @Test
-    fun footerResId() {
+        assertThat(listModel.switchTitleResId).isEqualTo(R.string.media_management_apps_toggle_label)
         assertThat(listModel.footerResId).isEqualTo(R.string.media_management_apps_description)
-    }
-
-    @Test
-    fun appOp() {
         assertThat(listModel.appOp).isEqualTo(AppOpsManager.OP_MANAGE_MEDIA)
-    }
-
-    @Test
-    fun permission() {
         assertThat(listModel.permission).isEqualTo(Manifest.permission.MANAGE_MEDIA)
-    }
-
-    @Test
-    fun setModeByUid() {
         assertThat(listModel.setModeByUid).isTrue()
     }
 }
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/PictureInPictureTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/PictureInPictureTest.kt
index 6054bb5..4229247 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/PictureInPictureTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/PictureInPictureTest.kt
@@ -67,18 +67,9 @@
     }
 
     @Test
-    fun pageTitleResId() {
+    fun modelResourceId() {
         assertThat(listModel.pageTitleResId).isEqualTo(R.string.picture_in_picture_title)
-    }
-
-    @Test
-    fun switchTitleResId() {
-        assertThat(listModel.switchTitleResId)
-            .isEqualTo(R.string.picture_in_picture_app_detail_switch)
-    }
-
-    @Test
-    fun footerResId() {
+        assertThat(listModel.switchTitleResId).isEqualTo(R.string.picture_in_picture_app_detail_switch)
         assertThat(listModel.footerResId).isEqualTo(R.string.picture_in_picture_app_detail_summary)
     }
 
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/TurnScreenOnAppsTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/TurnScreenOnAppsTest.kt
new file mode 100644
index 0000000..9c6079d
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/TurnScreenOnAppsTest.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.spa.app.specialaccess
+
+import android.Manifest
+import android.app.AppOpsManager
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class TurnScreenOnAppsTest {
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    private val listModel = TurnScreenOnAppsListModel(context)
+
+    @Test
+    fun modelResourceIdAndProperties() {
+        assertThat(listModel.pageTitleResId).isEqualTo(com.android.settingslib.R.string.turn_screen_on_title)
+        assertThat(listModel.switchTitleResId).isEqualTo(com.android.settingslib.R.string.allow_turn_screen_on)
+        assertThat(listModel.footerResId).isEqualTo(com.android.settingslib.R.string.allow_turn_screen_on_description)
+        assertThat(listModel.appOp).isEqualTo(AppOpsManager.OP_TURN_SCREEN_ON)
+        assertThat(listModel.permission).isEqualTo(Manifest.permission.TURN_SCREEN_ON)
+        assertThat(listModel.setModeByUid).isTrue()
+    }
+}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/VoiceActivationAppsPreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/VoiceActivationAppsPreferenceControllerTest.kt
new file mode 100644
index 0000000..2127497
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/VoiceActivationAppsPreferenceControllerTest.kt
@@ -0,0 +1,65 @@
+package com.android.settings.spa.app.specialaccess
+
+import android.content.Context
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.CheckFlagsRule
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
+import androidx.preference.Preference
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import com.android.settings.flags.Flags
+import com.google.common.truth.Truth.assertThat
+
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doNothing
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.whenever
+
+@RunWith(AndroidJUnit4::class)
+class VoiceActivationAppsPreferenceControllerTest {
+
+    @get:Rule
+    val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
+    private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+        doNothing().whenever(mock).startActivity(any())
+    }
+
+    private val matchedPreference = Preference(context).apply { key = preferenceKey }
+
+    private val misMatchedPreference = Preference(context).apply { key = testPreferenceKey }
+
+    private val controller = VoiceActivationAppsPreferenceController(context, preferenceKey)
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_VOICE_ACTIVATION_APPS_IN_SETTINGS)
+    fun getAvailabilityStatus_enableVoiceActivationApps_returnAvailable() {
+        assertThat(controller.isAvailable).isTrue()
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_VOICE_ACTIVATION_APPS_IN_SETTINGS)
+    fun getAvailableStatus_disableVoiceActivationApps_returnConditionallyUnavailable() {
+        assertThat(controller.isAvailable).isFalse()
+    }
+
+    @Test
+    fun handlePreferenceTreeClick_keyMatched_returnTrue() {
+        assertThat(controller.handlePreferenceTreeClick(matchedPreference)).isTrue()
+    }
+
+    @Test
+    fun handlePreferenceTreeClick_keyMisMatched_returnFalse() {
+        assertThat(controller.handlePreferenceTreeClick(misMatchedPreference)).isFalse()
+    }
+
+    companion object {
+        private const val preferenceKey: String = "voice_activation_apps"
+        private const val testPreferenceKey: String = "test_key"
+    }
+}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/VoiceActivationAppsTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/VoiceActivationAppsTest.kt
new file mode 100644
index 0000000..a2aa293
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/VoiceActivationAppsTest.kt
@@ -0,0 +1,30 @@
+package com.android.settings.spa.app.specialaccess
+
+import android.Manifest
+import android.app.AppOpsManager
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class VoiceActivationAppsTest {
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    private val listModel = VoiceActivationAppsListModel(context)
+
+    @Test
+    fun modelResourceIdAndProperties() {
+        assertThat(listModel.pageTitleResId).isEqualTo(R.string.voice_activation_apps_title)
+        assertThat(listModel.switchTitleResId).isEqualTo(R.string.permit_voice_activation_apps)
+        assertThat(listModel.footerResId).isEqualTo(R.string.allow_voice_activation_apps_description)
+        assertThat(listModel.appOp).isEqualTo(AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO)
+        assertThat(listModel.permission).isEqualTo(
+            Manifest.permission.RECEIVE_SANDBOX_TRIGGER_AUDIO
+        )
+        assertThat(listModel.setModeByUid).isTrue()
+    }
+}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/spa/development/compat/PlatformCompatAppListModelTest.kt b/tests/spa_unit/src/com/android/settings/spa/development/compat/PlatformCompatAppListModelTest.kt
index 0cfdc7d..6aee4ce 100644
--- a/tests/spa_unit/src/com/android/settings/spa/development/compat/PlatformCompatAppListModelTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/development/compat/PlatformCompatAppListModelTest.kt
@@ -20,7 +20,6 @@
 import android.content.pm.ApplicationInfo
 import android.content.pm.PackageManager
 import android.content.pm.PackageManager.PackageInfoFlags
-import androidx.compose.runtime.State
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -79,20 +78,20 @@
 
     @Test
     fun getSummary() = runTest {
-        val summaryState = getSummaryState(APP)
+        val summary = getSummary(APP)
 
-        assertThat(summaryState.value).isEqualTo(PACKAGE_NAME)
+        assertThat(summary).isEqualTo(PACKAGE_NAME)
     }
 
-    private fun getSummaryState(app: ApplicationInfo): State<String> {
-        lateinit var summary: State<String>
+    private fun getSummary(app: ApplicationInfo): String {
+        lateinit var summary: () -> String
         composeTestRule.setContent {
             summary = listModel.getSummary(
                 option = 0,
                 record = PlatformCompatAppRecord(app),
             )
         }
-        return summary
+        return summary()
     }
 
     private companion object {
diff --git a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
index 54299eb..9b098a7 100644
--- a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
+++ b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
@@ -41,6 +41,7 @@
 import com.android.settings.overlay.FeatureFactory
 import com.android.settings.overlay.SurveyFeatureProvider
 import com.android.settings.panel.PanelFeatureProvider
+import com.android.settings.privatespace.PrivateSpaceLoginFeatureProvider
 import com.android.settings.search.SearchFeatureProvider
 import com.android.settings.security.SecurityFeatureProvider
 import com.android.settings.security.SecuritySettingsFeatureProvider
@@ -143,4 +144,6 @@
         get() = TODO("Not yet implemented")
     override val fastPairFeatureProvider: FastPairFeatureProvider
         get() = TODO("Not yet implemented")
+    override val privateSpaceLoginFeatureProvider: PrivateSpaceLoginFeatureProvider
+        get() = TODO("Not yet implemented")
 }
diff --git a/tests/unit/src/com/android/settings/localepicker/LocaleHelperPreferenceControllerTest.java b/tests/unit/src/com/android/settings/localepicker/LocaleHelperPreferenceControllerTest.java
index 31b8e79..5ac367e 100644
--- a/tests/unit/src/com/android/settings/localepicker/LocaleHelperPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/localepicker/LocaleHelperPreferenceControllerTest.java
@@ -19,12 +19,14 @@
 import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.verify;
 
+import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.os.Looper;
 
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
+import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settingslib.widget.FooterPreference;
 
 import org.junit.Before;
@@ -37,6 +39,7 @@
 public class LocaleHelperPreferenceControllerTest {
     private Context mContext;
     private LocaleHelperPreferenceController mLocaleHelperPreferenceController;
+    private FakeFeatureFactory mFeatureFactory;
 
     @Mock
     private FooterPreference mMockFooterPreference;
@@ -49,11 +52,16 @@
         }
         mContext = ApplicationProvider.getApplicationContext();
         mLocaleHelperPreferenceController = new LocaleHelperPreferenceController(mContext);
+        mFeatureFactory = FakeFeatureFactory.setupForTest();
     }
 
     @Test
     public void updateFooterPreference_setFooterPreference_hasClickAction() {
         mLocaleHelperPreferenceController.updateFooterPreference(mMockFooterPreference);
         verify(mMockFooterPreference).setLearnMoreText(anyString());
+        mMockFooterPreference.setLearnMoreAction(v -> {
+            verify(mFeatureFactory.metricsFeatureProvider).action(
+                    mContext, SettingsEnums.ACTION_LANGUAGES_LEARN_MORE);
+        });
     }
 }
diff --git a/tests/unit/src/com/android/settings/regionalpreferences/NumberingSystemItemControllerTest.java b/tests/unit/src/com/android/settings/regionalpreferences/NumberingSystemItemControllerTest.java
index 0a67824..5c42ad9 100644
--- a/tests/unit/src/com/android/settings/regionalpreferences/NumberingSystemItemControllerTest.java
+++ b/tests/unit/src/com/android/settings/regionalpreferences/NumberingSystemItemControllerTest.java
@@ -24,6 +24,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
+import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.os.Bundle;
 import android.os.LocaleList;
@@ -51,6 +52,7 @@
     private NumberingPreferencesFragment mFragment;
     private PreferenceScreen mPreferenceScreen;
     private LocaleList mCacheLocale;
+    private FakeFeatureFactory mFeatureFactory;
 
     @Before
     @UiThreadTest
@@ -59,6 +61,7 @@
             Looper.prepare();
         }
         mApplicationContext = ApplicationProvider.getApplicationContext();
+        mFeatureFactory = FakeFeatureFactory.setupForTest();
         mFragment = spy(new NumberingPreferencesFragment());
         PreferenceManager preferenceManager = new PreferenceManager(mApplicationContext);
         mPreferenceScreen = preferenceManager.createPreferenceScreen(mApplicationContext);
@@ -94,6 +97,10 @@
         }
 
         assertTrue(isCallingStartActivity);
+        verify(mFeatureFactory.metricsFeatureProvider).action(
+                mApplicationContext,
+                SettingsEnums.ACTION_CHOOSE_LANGUAGE_FOR_NUMBERS_PREFERENCES,
+                "I_am_the_key");
     }
 
     @Test
@@ -114,6 +121,9 @@
         mController.handlePreferenceTreeClick(preference);
 
         verify(mFragment).setArguments(any());
+        verify(mFeatureFactory.metricsFeatureProvider).action(
+                mApplicationContext, SettingsEnums.ACTION_SET_NUMBERS_PREFERENCES,
+                "test_key");
     }
 
     @Test
diff --git a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
index b5062a0..bf2c84a 100644
--- a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -46,6 +46,7 @@
 import com.android.settings.overlay.SupportFeatureProvider;
 import com.android.settings.overlay.SurveyFeatureProvider;
 import com.android.settings.panel.PanelFeatureProvider;
+import com.android.settings.privatespace.PrivateSpaceLoginFeatureProvider;
 import com.android.settings.search.SearchFeatureProvider;
 import com.android.settings.security.SecurityFeatureProvider;
 import com.android.settings.security.SecuritySettingsFeatureProvider;
@@ -98,6 +99,7 @@
     public StylusFeatureProvider mStylusFeatureProvider;
     public OnboardingFeatureProvider mOnboardingFeatureProvider;
     public FastPairFeatureProvider mFastPairFeatureProvider;
+    public PrivateSpaceLoginFeatureProvider mPrivateSpaceLoginFeatureProvider;
 
     /**
      * Call this in {@code @Before} method of the test class to use fake factory.
@@ -145,6 +147,7 @@
         mStylusFeatureProvider = mock(StylusFeatureProvider.class);
         mOnboardingFeatureProvider = mock(OnboardingFeatureProvider.class);
         mFastPairFeatureProvider = mock(FastPairFeatureProvider.class);
+        mPrivateSpaceLoginFeatureProvider = mock(PrivateSpaceLoginFeatureProvider.class);
     }
 
     @Override
@@ -322,4 +325,9 @@
     public FastPairFeatureProvider getFastPairFeatureProvider() {
         return mFastPairFeatureProvider;
     }
+
+    @Override
+    public PrivateSpaceLoginFeatureProvider getPrivateSpaceLoginFeatureProvider() {
+        return mPrivateSpaceLoginFeatureProvider;
+    }
 }