Merge "[Audiosharing] Impl audio sharing main switch." into main
diff --git a/aconfig/Android.bp b/aconfig/Android.bp
index 78fba34..6b7869e 100644
--- a/aconfig/Android.bp
+++ b/aconfig/Android.bp
@@ -10,6 +10,7 @@
         "settings_connecteddevice_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_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/values/strings.xml b/res/values/strings.xml
index a3760c5..02b7a4c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -9520,6 +9520,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.\n\nUntill activated, none of these apps can directly access your microphone.Instead, this device uses built-in proteced adaptive sensing to turn-on aprroved apps for 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/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/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 1a226fd..c298afa 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
@@ -72,8 +72,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
@@ -575,7 +573,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/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..5000afd
--- /dev/null
+++ b/src/com/android/settings/network/MobileNetworkListFragment.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.settings.utils.observeSettingsGlobalBoolean
+import com.android.settingslib.search.SearchIndexable
+import com.android.settingslib.spaprivileged.framework.common.userManager
+
+@SearchIndexable(forTarget = SearchIndexable.ALL and SearchIndexable.ARC.inv())
+class MobileNetworkListFragment : DashboardFragment() {
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        observeAirplaneModeAndFinishIfOn()
+    }
+
+    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.observeAirplaneModeAndFinishIfOn() {
+            requireContext().observeSettingsGlobalBoolean(
+                name = Settings.Global.AIRPLANE_MODE_ON,
+                lifecycle = viewLifecycleOwner.lifecycle,
+            ) { isAirplaneModeOn: Boolean ->
+                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/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
index afc1b7e..7e290b8 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.observeAirplaneModeAndFinishIfOn;
+
 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);
+        observeAirplaneModeAndFinishIfOn(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/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..77d68b5 100644
--- a/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/HibernationSwitchPreference.kt
@@ -31,12 +31,11 @@
 import androidx.compose.runtime.derivedStateOf
 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) {
@@ -62,7 +61,7 @@
     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 summary = { context.getString(R.string.unused_apps_switch_summary) }
             override val changeable = isEligibleState
 
             override val checked = derivedStateOf {
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/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/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/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..692ffcb 100644
--- a/src/com/android/settings/spa/notification/AppNotificationsListModel.kt
+++ b/src/com/android/settings/spa/notification/AppNotificationsListModel.kt
@@ -27,7 +27,6 @@
 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.widget.ui.SpinnerOption
@@ -91,16 +90,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),
@@ -145,6 +145,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/src/com/android/settings/utils/SettingsGlobalBooleanDelegate.kt b/src/com/android/settings/utils/SettingsGlobalBooleanDelegate.kt
new file mode 100644
index 0000000..fdfbdb4
--- /dev/null
+++ b/src/com/android/settings/utils/SettingsGlobalBooleanDelegate.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.utils
+
+import android.content.ContentResolver
+import android.content.Context
+import android.database.ContentObserver
+import android.os.Handler
+import android.provider.Settings
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import kotlin.properties.ReadWriteProperty
+import kotlin.reflect.KProperty
+
+fun Context.observeSettingsGlobalBoolean(
+    name: String,
+    lifecycle: Lifecycle,
+    onChange: (newValue: Boolean) -> Unit,
+) {
+    val field by settingsGlobalBoolean(name)
+    val contentObserver = object : ContentObserver(Handler.getMain()) {
+        override fun onChange(selfChange: Boolean) {
+            onChange(field)
+        }
+    }
+    val uri = Settings.Global.getUriFor(name)
+    lifecycle.addObserver(object : DefaultLifecycleObserver {
+        override fun onStart(owner: LifecycleOwner) {
+            contentResolver.registerContentObserver(uri, false, contentObserver)
+            onChange(field)
+        }
+
+        override fun onStop(owner: LifecycleOwner) {
+            contentResolver.unregisterContentObserver(contentObserver)
+        }
+    })
+}
+
+fun Context.settingsGlobalBoolean(name: String): ReadWriteProperty<Any?, Boolean> =
+    SettingsGlobalBooleanDelegate(this, name)
+
+private class SettingsGlobalBooleanDelegate(context: Context, private val name: String) :
+    ReadWriteProperty<Any?, Boolean> {
+
+    private val contentResolver: ContentResolver = context.contentResolver
+
+    override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean =
+        Settings.Global.getInt(contentResolver, name, 0) != 0
+
+    override fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) {
+        Settings.Global.putInt(contentResolver, name, if (value) 1 else 0)
+    }
+}
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/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/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/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/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/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/TurnScreenOnAppsTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/TurnScreenOnAppsTest.kt
new file mode 100644
index 0000000..54ae6c6
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/TurnScreenOnAppsTest.kt
@@ -0,0 +1,62 @@
+/*
+ * 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 pageTitleResId() {
+        assertThat(listModel.pageTitleResId).isEqualTo(com.android.settingslib.R.string.turn_screen_on_title)
+    }
+
+    @Test
+    fun switchTitleResId() {
+        assertThat(listModel.switchTitleResId).isEqualTo(com.android.settingslib.R.string.allow_turn_screen_on)
+    }
+
+    @Test
+    fun footerResId() {
+        assertThat(listModel.footerResId).isEqualTo(com.android.settingslib.R.string.allow_turn_screen_on_description)
+    }
+
+    @Test
+    fun appOp() {
+        assertThat(listModel.appOp).isEqualTo(AppOpsManager.OP_TURN_SCREEN_ON)
+    }
+
+    @Test
+    fun permission() {
+        assertThat(listModel.permission).isEqualTo(Manifest.permission.TURN_SCREEN_ON)
+    }
+
+    @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/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..7d636b3
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/VoiceActivationAppsTest.kt
@@ -0,0 +1,50 @@
+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 pageTitleResId() {
+        assertThat(listModel.pageTitleResId).isEqualTo(R.string.voice_activation_apps_title)
+    }
+
+    @Test
+    fun switchTitleResId() {
+        assertThat(listModel.switchTitleResId).isEqualTo(R.string.permit_voice_activation_apps)
+    }
+
+    @Test
+    fun footerResId() {
+        assertThat(listModel.footerResId)
+            .isEqualTo(R.string.allow_voice_activation_apps_description)
+    }
+
+    @Test
+    fun appOp() {
+        assertThat(listModel.appOp).isEqualTo(AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO)
+    }
+
+    @Test
+    fun permission() {
+        assertThat(listModel.permission).isEqualTo(
+            Manifest.permission.RECEIVE_SANDBOX_TRIGGER_AUDIO)
+    }
+
+    @Test
+    fun setModeByUid() {
+        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/utils/SettingsGlobalBooleanDelegateTest.kt b/tests/spa_unit/src/com/android/settings/utils/SettingsGlobalBooleanDelegateTest.kt
new file mode 100644
index 0000000..75c3685
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/utils/SettingsGlobalBooleanDelegateTest.kt
@@ -0,0 +1,99 @@
+/*
+ * 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.utils
+
+import android.content.Context
+import android.provider.Settings
+import androidx.lifecycle.testing.TestLifecycleOwner
+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 SettingsGlobalBooleanDelegateTest {
+
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    @Test
+    fun getValue_setTrue_returnTrue() {
+        Settings.Global.putInt(context.contentResolver, TEST_NAME, 1)
+
+        val value by context.settingsGlobalBoolean(TEST_NAME)
+
+        assertThat(value).isTrue()
+    }
+
+    @Test
+    fun getValue_setFalse_returnFalse() {
+        Settings.Global.putInt(context.contentResolver, TEST_NAME, 0)
+
+        val value by context.settingsGlobalBoolean(TEST_NAME)
+
+        assertThat(value).isFalse()
+    }
+
+    @Test
+    fun setValue_setTrue_returnTrue() {
+        var value by context.settingsGlobalBoolean(TEST_NAME)
+
+        value = true
+
+        assertThat(Settings.Global.getInt(context.contentResolver, TEST_NAME, 0)).isEqualTo(1)
+    }
+
+    @Test
+    fun setValue_setFalse_returnFalse() {
+        var value by context.settingsGlobalBoolean(TEST_NAME)
+
+        value = false
+
+        assertThat(Settings.Global.getInt(context.contentResolver, TEST_NAME, 1)).isEqualTo(0)
+    }
+
+    @Test
+    fun observeSettingsGlobalBoolean_valueNotChanged() {
+        var value by context.settingsGlobalBoolean(TEST_NAME)
+        value = false
+        var newValue: Boolean? = null
+
+        context.observeSettingsGlobalBoolean(TEST_NAME, TestLifecycleOwner().lifecycle) {
+            newValue = it
+        }
+
+        assertThat(newValue).isFalse()
+    }
+
+    @Test
+    fun observeSettingsGlobalBoolean_valueChanged() {
+        var value by context.settingsGlobalBoolean(TEST_NAME)
+        value = false
+        var newValue: Boolean? = null
+
+        context.observeSettingsGlobalBoolean(TEST_NAME, TestLifecycleOwner().lifecycle) {
+            newValue = it
+        }
+        value = true
+
+        assertThat(newValue).isFalse()
+    }
+
+    private companion object {
+        const val TEST_NAME = "test_boolean_delegate"
+    }
+}
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