Merge "Quick fix: fix bug when using binary search," into main
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index c64187f..998ecc5 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -645,6 +645,7 @@
         <activity android:name="Settings$FaceSettingsActivity"
             android:label="@string/security_settings_face_preference_title"
             android:exported="true"
+            android:theme="@style/Theme.Settings.NoActionBar"
             android:icon="@drawable/ic_face_header">
             <intent-filter>
                 <action android:name="android.settings.FACE_SETTINGS" />
@@ -659,6 +660,7 @@
         <activity android:name="Settings$FaceSettingsInternalActivity"
                   android:label="@string/security_settings_face_preference_title"
                   android:exported="false"
+                  android:theme="@style/Theme.Settings.NoActionBar"
                   android:icon="@drawable/ic_face_header"
                   android:taskAffinity="com.android.settings.root">
             <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
diff --git a/aconfig/Android.bp b/aconfig/Android.bp
index d4960c2..d0a8afe 100644
--- a/aconfig/Android.bp
+++ b/aconfig/Android.bp
@@ -9,6 +9,7 @@
         "settings_connecteddevice_flag_declarations.aconfig",
         "settings_globalintl_flag_declarations.aconfig",
         "settings_apn_flag_declarations.aconfig",
+        "settings_onboarding_experience_flag_declarations.aconfig"
     ],
 }
 
diff --git a/aconfig/settings_onboarding_experience_flag_declarations.aconfig b/aconfig/settings_onboarding_experience_flag_declarations.aconfig
new file mode 100644
index 0000000..6fb5377
--- /dev/null
+++ b/aconfig/settings_onboarding_experience_flag_declarations.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.settings.flags"
+
+flag {
+  name: "enable_sound_backup"
+  namespace: "onboarding_experience"
+  description: "Feature flag for B&R sound settings."
+  bug: "278975761"
+}
diff --git a/res-product/values/strings.xml b/res-product/values/strings.xml
index 63ab6ac..0e1713c 100644
--- a/res-product/values/strings.xml
+++ b/res-product/values/strings.xml
@@ -738,4 +738,11 @@
     <string name="daltonizer_feature_summary" product="default">Adjust how colors display on your phone</string>
     <!-- The daltonizer feature summary display as a subtext as an item in a list. -->
     <string name="daltonizer_feature_summary" product="tablet">Adjust how colors display on your tablet</string>
+
+    <!-- Output device type for the phone speaker that is available for spatializer effect. [CHAR LIMIT=NONE]-->
+    <string name="spatial_audio_speaker" product="default">Phone speakers</string>
+    <!-- Output device type for the phone speaker that is available for spatializer effect. [CHAR LIMIT=NONE]-->
+    <string name="spatial_audio_speaker" product="tablet">Tablet speakers</string>
+    <!-- Output device type for the phone speaker that is available for spatializer effect. [CHAR LIMIT=NONE]-->
+    <string name="spatial_audio_speaker" product="device">Device speakers</string>
 </resources>
diff --git a/res/drawable/battery_hints_chip_bg.xml b/res/drawable/battery_hints_chip_bg.xml
new file mode 100644
index 0000000..e7d1d0f
--- /dev/null
+++ b/res/drawable/battery_hints_chip_bg.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="@color/settingslib_dialog_background" />
+    <corners android:radius="@dimen/battery_hints_chip_corner_radius" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/battery_hints_chip_bg_ripple.xml b/res/drawable/battery_hints_chip_bg_ripple.xml
new file mode 100644
index 0000000..a8bd0b37
--- /dev/null
+++ b/res/drawable/battery_hints_chip_bg_ripple.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:attr/colorControlHighlight">
+    <item android:drawable="@drawable/battery_hints_chip_bg"/>
+</ripple>
\ No newline at end of file
diff --git a/res/layout/anomaly_app_item_preference.xml b/res/layout/anomaly_app_item_preference.xml
new file mode 100644
index 0000000..0a19849
--- /dev/null
+++ b/res/layout/anomaly_app_item_preference.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <include layout="@layout/preference_app"/>
+
+    <LinearLayout
+        android:id="@+id/warning_chip"
+        android:visibility="gone"
+        android:clickable="false"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="?android:attr/selectableItemBackground"
+        android:gravity="center_vertical"
+        android:orientation="horizontal"
+        android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+        <Space
+            android:layout_width="@dimen/secondary_app_icon_size"
+            android:layout_height="wrap_content" />
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:padding="8dp"
+            android:layout_marginStart="16dp"
+            android:background="@drawable/battery_hints_chip_bg_ripple">
+
+            <ImageView
+                android:layout_width="16dp"
+                android:layout_height="16dp"
+                android:layout_gravity="center_vertical|start"
+                android:contentDescription="@string/battery_hints_warning_icon_a11y"
+                android:src="@drawable/ic_battery_tips_warning_icon" />
+
+            <TextView
+                android:id="@+id/warning_info"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:paddingHorizontal="8dp"
+                android:layout_gravity="center_vertical|start"
+                android:textAlignment="viewStart"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textColor="?android:attr/textColorPrimary"/>
+        </LinearLayout>
+
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 1723d17..f454954 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -1420,20 +1420,49 @@
         <item>color_battery_anomaly_yellow_selector</item>
     </string-array>
 
-    <!-- The following 3 arrays are for power anomaly tips card. Please keep them the same size. -->
-    <string-array name="power_anomaly_titles">
-        <item>Turn on adaptive brightness to extend battery life</item>
-        <item>Reduce screen timeout to extend battery life</item>
+    <!-- The following 4 arrays are for power anomaly tips card. Please keep them the same size. -->
+    <string-array name="power_anomaly_title_ids" translatable="false">
+        <item>battery_tips_settings_summary_brightness</item>
+        <item>battery_tips_settings_summary_screen_timeout</item>
+        <item>battery_tips_apps_summary_always_high</item>
+        <item>battery_tips_apps_summary_higher_than_usual</item>
+        <item>battery_tips_apps_summary_always_high_in_background</item>
+        <item>battery_tips_apps_summary_higher_than_usual_in_background</item>
+        <item>battery_tips_apps_summary_always_high_in_foreground</item>
+        <item>battery_tips_apps_summary_higher_than_usual_in_foreground</item>
     </string-array>
 
     <string-array name="power_anomaly_main_btn_strings" translatable="false">
         <item>@string/battery_tips_card_action_button</item>
         <item>@string/battery_tips_card_action_button</item>
+        <item>@string/battery_tips_card_action_button_check</item>
+        <item>@string/battery_tips_card_action_button_check</item>
+        <item>@string/battery_tips_card_action_button_check</item>
+        <item>@string/battery_tips_card_action_button_check</item>
+        <item>@string/battery_tips_card_action_button_check</item>
+        <item>@string/battery_tips_card_action_button_check</item>
     </string-array>
 
     <string-array name="power_anomaly_dismiss_btn_strings" translatable="false">
         <item>@string/battery_tips_card_dismiss_button</item>
         <item>@string/battery_tips_card_dismiss_button</item>
+        <item>@string/battery_tips_card_dismiss_button</item>
+        <item>@string/battery_tips_card_dismiss_button</item>
+        <item>@string/battery_tips_card_dismiss_button</item>
+        <item>@string/battery_tips_card_dismiss_button</item>
+        <item>@string/battery_tips_card_dismiss_button</item>
+        <item>@string/battery_tips_card_dismiss_button</item>
+    </string-array>
+
+    <string-array name="power_anomaly_hint_messages" translatable="false">
+        <item></item>
+        <item></item>
+        <item>@string/battery_app_item_hint</item>
+        <item>@string/battery_app_item_hint</item>
+        <item>@string/battery_app_item_hint_in_bg</item>
+        <item>@string/battery_app_item_hint_in_bg</item>
+        <item>@string/battery_app_item_hint_in_fg</item>
+        <item>@string/battery_app_item_hint_in_fg</item>
     </string-array>
 
     <!-- A list of not supporting Terms of Address. [DO NOT TRANSLATE] -->
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index a8ad434..205e2a3 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -403,6 +403,7 @@
     <!-- Battery tips card view component -->
     <dimen name="battery_tips_card_corner_radius_small">4dp</dimen>
     <dimen name="battery_tips_card_corner_radius_normal">24dp</dimen>
+    <dimen name="battery_hints_chip_corner_radius">8dp</dimen>
 
     <!-- Dimensions for Dream settings cards -->
     <dimen name="dream_item_min_column_width">174dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index e4b68d0..89f3bd4 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -7387,7 +7387,7 @@
     <string name="vibrate_when_ringing_option_ramping_ringer">Vibrate first then ring gradually</string>
 
     <!-- Sound: Title for the option enabling spatializer effect. [CHAR LIMIT=30] -->
-    <string name="spatial_audio_title">Spatial audio</string>
+    <string name="spatial_audio_title">Spatial Audio</string>
 
     <!-- Sound: Other sounds: Title for the option enabling touch sounds for dial pad tones. [CHAR LIMIT=30] -->
     <string name="dial_pad_tones_title">Dial pad tones</string>
@@ -7434,9 +7434,6 @@
     <!-- Setting summary for controlling how caption text display in real time [CHAR LIMIT=NONE]-->
     <string name="live_caption_summary">Automatically caption media</string>
 
-    <!-- Output device type for the phone speaker that is available for spatializer effect. [CHAR LIMIT=NONE]-->
-    <string name="spatial_audio_speaker">Phone speaker</string>
-
     <!-- Output device type for the wired headphones that is available for spatializer effect. [CHAR LIMIT=NONE]-->
     <string name="spatial_audio_wired_headphones">Wired headphones</string>
 
@@ -9821,12 +9818,51 @@
     <!-- Label of action button in battery tips card [CHAR LIMIT=50] -->
     <string name="battery_tips_card_action_button">View Settings</string>
 
+    <!-- Label of action button in battery tips card [CHAR LIMIT=50] -->
+    <string name="battery_tips_card_action_button_check">Check</string>
+
     <!-- Label of dismiss button in battery tips card [CHAR LIMIT=50] -->
     <string name="battery_tips_card_dismiss_button">Got it</string>
 
     <!-- Feedback card message in battery tips card [CHAR LIMIT=NONE] -->
     <string name="battery_tips_card_feedback_info">Is this message helpful?</string>
 
+    <!-- Content description for battery hints warning icon of app anomaly [CHAR LIMIT=NONE] -->
+    <string name="battery_hints_warning_icon_a11y">Battery tips warning icon</string>
+
+    <!-- Summary of settings anomaly for adaptive brightness [CHAR LIMIT=NONE] -->
+    <string name="battery_tips_settings_summary_brightness">Turn on adaptive brightness to extend battery life</string>
+
+    <!-- Summary of settings anomaly for screen timeout [CHAR LIMIT=NONE] -->
+    <string name="battery_tips_settings_summary_screen_timeout">Reduce screen timeout to extend battery life</string>
+
+    <!-- Summary of apps anomaly for always high [CHAR LIMIT=NONE] -->
+    <string name="battery_tips_apps_summary_always_high"><xliff:g id="app_label" example="Pokemon Go">%1$s</xliff:g> used more battery</string>
+
+    <!-- Summary of apps anomaly for higher than usual [CHAR LIMIT=NONE] -->
+    <string name="battery_tips_apps_summary_higher_than_usual"><xliff:g id="app_label" example="Pokemon Go">%1$s</xliff:g> used more battery than usual</string>
+
+    <!-- Summary of apps anomaly for always high in background [CHAR LIMIT=NONE] -->
+    <string name="battery_tips_apps_summary_always_high_in_background"><xliff:g id="app_label" example="Pokemon Go">%1$s</xliff:g> used more battery while in the background</string>
+
+    <!-- Summary of apps anomaly for higher than usual in background [CHAR LIMIT=NONE] -->
+    <string name="battery_tips_apps_summary_higher_than_usual_in_background"><xliff:g id="app_label" example="Pokemon Go">%1$s</xliff:g> used more battery than usual while in the background</string>
+
+    <!-- Summary of apps anomaly for always high in foreground [CHAR LIMIT=NONE] -->
+    <string name="battery_tips_apps_summary_always_high_in_foreground"><xliff:g id="app_label" example="Pokemon Go">%1$s</xliff:g> used more battery while in the foreground</string>
+
+    <!-- Summary of apps anomaly for higher than usual in foreground [CHAR LIMIT=NONE] -->
+    <string name="battery_tips_apps_summary_higher_than_usual_in_foreground"><xliff:g id="app_label" example="Pokemon Go">%1$s</xliff:g> used more battery than usual while in the foreground</string>
+
+    <!-- Label of hint for apps anomaly in battery usage [CHAR LIMIT=NONE] -->
+    <string name="battery_app_item_hint">High battery usage</string>
+
+    <!-- Label of hint for apps background anomaly in battery usage [CHAR LIMIT=NONE] -->
+    <string name="battery_app_item_hint_in_bg">High battery usage in the background</string>
+
+    <!-- Label of hint for apps foreground anomaly in battery usage [CHAR LIMIT=NONE] -->
+    <string name="battery_app_item_hint_in_fg">High battery usage in the foreground</string>
+
     <!-- Filter title for battery unrestricted[CHAR_LIMIT=50]-->
     <string name="filter_battery_unrestricted_title">Unrestricted</string>
 
diff --git a/res/xml/data_usage_list.xml b/res/xml/data_usage_list.xml
index 28f09c6..791fc86 100644
--- a/res/xml/data_usage_list.xml
+++ b/res/xml/data_usage_list.xml
@@ -14,7 +14,8 @@
      limitations under the License.
 -->
 
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto">
 
     <PreferenceCategory
         android:key="usage_amount"
@@ -32,6 +33,7 @@
 
     <PreferenceCategory
         android:key="apps_group"
-        android:layout="@layout/preference_category_no_label" />
+        android:layout="@layout/preference_category_no_label"
+        settings:controller="com.android.settings.datausage.DataUsageListAppsController" />
 
 </PreferenceScreen>
diff --git a/src/com/android/settings/accessibility/HearingAidUtils.java b/src/com/android/settings/accessibility/HearingAidUtils.java
index 4734c55..edf2d9f 100644
--- a/src/com/android/settings/accessibility/HearingAidUtils.java
+++ b/src/com/android/settings/accessibility/HearingAidUtils.java
@@ -42,9 +42,10 @@
      */
     public static void launchHearingAidPairingDialog(FragmentManager fragmentManager,
             @NonNull CachedBluetoothDevice device, int launchPage) {
-        // No need to show the pair another ear dialog if the device supports and enables CSIP.
+        // No need to show the pair another ear dialog if the device supports CSIP.
         // CSIP will pair other devices in the same set automatically.
-        if (isCsipSupportedAndEnabled(device)) {
+        if (device.getProfiles().stream().anyMatch(
+                profile -> profile instanceof CsipSetCoordinatorProfile)) {
             return;
         }
         if (device.isConnectedAshaHearingAidDevice()
@@ -63,10 +64,4 @@
         HearingAidPairingDialogFragment.newInstance(device.getAddress(), launchPage)
                 .show(fragmentManager, HearingAidPairingDialogFragment.TAG);
     }
-
-    private static boolean isCsipSupportedAndEnabled(@NonNull CachedBluetoothDevice device) {
-        return device.getProfiles().stream().anyMatch(
-                profile -> (profile instanceof CsipSetCoordinatorProfile)
-                        && (profile.isEnabled(device.getDevice())));
-    }
 }
diff --git a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
index 182e5ae..98d56cc 100644
--- a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
+++ b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
@@ -159,6 +159,16 @@
             return CONDITIONALLY_UNAVAILABLE;
         }
 
+        // If we are in work profile mode and there is no user then we
+        // should hide for now. We use CONDITIONALLY_UNAVAILABLE
+        // because it is possible for the user to be set later.
+        if (mIsWorkProfile) {
+            UserHandle workProfile = getWorkProfileUserHandle();
+            if (workProfile == null) {
+                return CONDITIONALLY_UNAVAILABLE;
+            }
+        }
+
         return AVAILABLE;
     }
 
@@ -186,12 +196,17 @@
         fragment.getSettingsLifecycle().addObserver(this);
         mFragmentManager = fragmentManager;
         mIsWorkProfile = isWorkProfile;
+
         setDelegate(delegate);
         verifyReceivedIntent(launchIntent);
 
         // Recreate the content observers because the user might have changed.
         mSettingsContentObserver.unregister();
         mSettingsContentObserver.register();
+
+        // When we set the mIsWorkProfile above we should try and force a refresh
+        // so we can get the correct data.
+        delegate.forceDelegateRefresh();
     }
 
     /**
@@ -302,10 +317,15 @@
                 null);
     }
 
-    private Set<ComponentName> buildComponentNameSet(List<CredentialProviderInfo> providers) {
+    private Set<ComponentName> buildComponentNameSet(
+            List<CredentialProviderInfo> providers, boolean removeNonPrimary) {
         Set<ComponentName> output = new HashSet<>();
 
         for (CredentialProviderInfo cpi : providers) {
+            if (removeNonPrimary && !cpi.isPrimary()) {
+                continue;
+            }
+
             output.add(cpi.getComponentName());
         }
 
@@ -321,13 +341,16 @@
         List<CredentialProviderInfo> newProviders =
                 mCredentialManager.getCredentialProviderServices(
                         getUser(), CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY);
-        Set<ComponentName> newComponents = buildComponentNameSet(newProviders);
+        Set<ComponentName> newComponents = buildComponentNameSet(newProviders, false);
+        Set<ComponentName> newPrimaryComponents = buildComponentNameSet(newProviders, true);
 
         // Get the list of old components
-        Set<ComponentName> oldComponents = buildComponentNameSet(mServices);
+        Set<ComponentName> oldComponents = buildComponentNameSet(mServices, false);
+        Set<ComponentName> oldPrimaryComponents = buildComponentNameSet(mServices, true);
 
         // If the sets are equal then don't update the UI.
-        if (oldComponents.equals(newComponents)) {
+        if (oldComponents.equals(newComponents)
+                && oldPrimaryComponents.equals(newPrimaryComponents)) {
             return;
         }
 
@@ -698,12 +721,22 @@
 
     protected int getUser() {
         if (mIsWorkProfile) {
-            UserHandle workProfile = Utils.getManagedProfile(UserManager.get(mContext));
-            return workProfile.getIdentifier();
+            UserHandle workProfile = getWorkProfileUserHandle();
+            if (workProfile != null) {
+                return workProfile.getIdentifier();
+            }
         }
         return UserHandle.myUserId();
     }
 
+    private @Nullable UserHandle getWorkProfileUserHandle() {
+        if (mIsWorkProfile) {
+            return Utils.getManagedProfile(UserManager.get(mContext));
+        }
+
+        return null;
+    }
+
     /** Called when the dialog button is clicked. */
     private static interface DialogHost {
         void onDialogClick(int whichButton);
diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractor.kt
index 1f57198..2c8ee8f 100644
--- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractor.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractor.kt
@@ -27,12 +27,18 @@
 import com.android.settings.biometrics.GatekeeperPasswordProvider
 import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintAuthAttemptViewModel
 import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.EnrollReason
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerEnrollStateViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.toOriginalReason
 import com.android.settings.password.ChooseLockSettingsHelper
 import kotlin.coroutines.resume
 import kotlin.coroutines.suspendCoroutine
 import kotlinx.coroutines.CancellableContinuation
 import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.channels.onFailure
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.suspendCancellableCoroutine
 import kotlinx.coroutines.withContext
@@ -67,6 +73,16 @@
   suspend fun generateChallenge(gateKeeperPasswordHandle: Long): Pair<Long, ByteArray>
 
   /**
+   * Runs [FingerprintManager.enroll] with the [hardwareAuthToken] and [EnrollReason] for this
+   * enrollment. Returning the [FingerEnrollStateViewModel] that represents this fingerprint
+   * enrollment state.
+   */
+  suspend fun enroll(
+    hardwareAuthToken: ByteArray?,
+    enrollReason: EnrollReason,
+  ): Flow<FingerEnrollStateViewModel>
+
+  /**
    * Removes the given fingerprint, returning true if it was successfully removed and false
    * otherwise
    */
@@ -133,6 +149,51 @@
 
   override val maxEnrollableFingerprints = flow { emit(maxFingerprints) }
 
+  override suspend fun enroll(
+    hardwareAuthToken: ByteArray?,
+    enrollReason: EnrollReason,
+  ): Flow<FingerEnrollStateViewModel> = callbackFlow {
+    var streamEnded = false
+    val enrollmentCallback =
+      object : FingerprintManager.EnrollmentCallback() {
+        override fun onEnrollmentProgress(remaining: Int) {
+          trySend(FingerEnrollStateViewModel.EnrollProgress(remaining)).onFailure { error ->
+            Log.d(TAG, "onEnrollmentProgress($remaining) failed to send, due to $error")
+          }
+          if (remaining == 0) {
+            streamEnded = true
+          }
+        }
+
+        override fun onEnrollmentHelp(helpMsgId: Int, helpString: CharSequence?) {
+          trySend(FingerEnrollStateViewModel.EnrollHelp(helpMsgId, helpString.toString()))
+            .onFailure { error -> Log.d(TAG, "onEnrollmentHelp failed to send, due to $error") }
+        }
+
+        override fun onEnrollmentError(errMsgId: Int, errString: CharSequence?) {
+          trySend(FingerEnrollStateViewModel.EnrollError(errMsgId, errString.toString()))
+            .onFailure { error -> Log.d(TAG, "onEnrollmentError failed to send, due to $error") }
+          streamEnded = true
+        }
+      }
+
+    val cancellationSignal = CancellationSignal()
+    fingerprintManager.enroll(
+      hardwareAuthToken,
+      cancellationSignal,
+      applicationContext.userId,
+      enrollmentCallback,
+      enrollReason.toOriginalReason()
+    )
+    awaitClose {
+      // If the stream has not been ended, and the user has stopped collecting the flow
+      // before it was over, send cancel.
+      if (!streamEnded) {
+        cancellationSignal.cancel()
+      }
+    }
+  }
+
   override suspend fun removeFingerprint(fp: FingerprintViewModel): Boolean = suspendCoroutine {
     val callback =
       object : RemovalCallback() {
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt
index f6d20ae..d43aeba 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt
@@ -43,12 +43,12 @@
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollConfirmationV2Fragment
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollEnrollingV2Fragment
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollFindSensorV2Fragment
-import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollmentIntroV2Fragment
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollIntroV2Fragment
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Confirmation
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Education
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Enrollment
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollNavigationViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollViewModel
-import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollmentNavigationViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintGatekeeperViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintScrollViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Finish
@@ -70,7 +70,7 @@
  * children fragments.
  */
 class FingerprintEnrollmentV2Activity : FragmentActivity() {
-  private lateinit var navigationViewModel: FingerprintEnrollmentNavigationViewModel
+  private lateinit var navigationViewModel: FingerprintEnrollNavigationViewModel
   private lateinit var gatekeeperViewModel: FingerprintGatekeeperViewModel
   private val coroutineDispatcher = Dispatchers.Default
 
@@ -170,18 +170,18 @@
     navigationViewModel =
       ViewModelProvider(
         this,
-        FingerprintEnrollmentNavigationViewModel.FingerprintEnrollmentNavigationViewModelFactory(
+        FingerprintEnrollNavigationViewModel.FingerprintEnrollNavigationViewModelFactory(
           backgroundDispatcher,
           interactor,
           gatekeeperViewModel,
           gatekeeperInfo is GatekeeperInfo.GatekeeperPasswordInfo, /* canSkipConfirm */
         )
-      )[FingerprintEnrollmentNavigationViewModel::class.java]
+      )[FingerprintEnrollNavigationViewModel::class.java]
 
     // Initialize FingerprintViewModel
     ViewModelProvider(
       this,
-      FingerprintEnrollViewModel.FingerprintEnrollViewModelFactory(interactor)
+      FingerprintEnrollViewModel.FingerprintEnrollViewModelFactory(interactor, backgroundDispatcher)
     )[FingerprintEnrollViewModel::class.java]
 
     // Initialize scroll view model
@@ -198,7 +198,7 @@
             Confirmation -> FingerprintEnrollConfirmationV2Fragment::class.java as Class<Fragment>
             Education -> FingerprintEnrollFindSensorV2Fragment::class.java as Class<Fragment>
             Enrollment -> FingerprintEnrollEnrollingV2Fragment::class.java as Class<Fragment>
-            Intro -> FingerprintEnrollmentIntroV2Fragment::class.java as Class<Fragment>
+            Intro -> FingerprintEnrollIntroV2Fragment::class.java as Class<Fragment>
             else -> null
           }
 
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollConfirmationV2Fragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollConfirmationV2Fragment.kt
index df4cf72..b12491f 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollConfirmationV2Fragment.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollConfirmationV2Fragment.kt
@@ -19,7 +19,7 @@
 import android.os.Bundle
 import androidx.fragment.app.Fragment
 import androidx.lifecycle.ViewModelProvider
-import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollmentNavigationViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollNavigationViewModel
 
 /**
  * A fragment to indicate that fingerprint enrollment has been completed.
@@ -33,7 +33,7 @@
     super.onCreate(savedInstanceState)
     if (savedInstanceState == null) {
       val navigationViewModel =
-        ViewModelProvider(requireActivity())[FingerprintEnrollmentNavigationViewModel::class.java]
+        ViewModelProvider(requireActivity())[FingerprintEnrollNavigationViewModel::class.java]
     }
   }
 }
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollEnrollingV2Fragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollEnrollingV2Fragment.kt
index 915aa1f..3f615ce 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollEnrollingV2Fragment.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollEnrollingV2Fragment.kt
@@ -19,7 +19,7 @@
 import android.os.Bundle
 import androidx.fragment.app.Fragment
 import androidx.lifecycle.ViewModelProvider
-import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollmentNavigationViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollNavigationViewModel
 
 /** A fragment that is responsible for enrolling a users fingerprint. */
 class FingerprintEnrollEnrollingV2Fragment : Fragment() {
@@ -28,7 +28,7 @@
     super.onCreate(savedInstanceState)
     if (savedInstanceState == null) {
       val navigationViewModel =
-        ViewModelProvider(requireActivity())[FingerprintEnrollmentNavigationViewModel::class.java]
+        ViewModelProvider(requireActivity())[FingerprintEnrollNavigationViewModel::class.java]
     }
   }
 }
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollFindSensorV2Fragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollFindSensorV2Fragment.kt
index e9e1db2..beb84e9 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollFindSensorV2Fragment.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollFindSensorV2Fragment.kt
@@ -20,7 +20,7 @@
 import androidx.fragment.app.Fragment
 import androidx.lifecycle.ViewModelProvider
 import com.android.settings.R
-import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollmentNavigationViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollNavigationViewModel
 
 /**
  * A fragment that is used to educate the user about the fingerprint sensor on this device.
@@ -36,7 +36,7 @@
     super.onCreate(savedInstanceState)
     if (savedInstanceState == null) {
       val navigationViewModel =
-        ViewModelProvider(requireActivity())[FingerprintEnrollmentNavigationViewModel::class.java]
+        ViewModelProvider(requireActivity())[FingerprintEnrollNavigationViewModel::class.java]
     }
   }
 }
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollmentIntroV2Fragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollIntroV2Fragment.kt
similarity index 97%
rename from src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollmentIntroV2Fragment.kt
rename to src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollIntroV2Fragment.kt
index f2f925b..03c7a5f 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollmentIntroV2Fragment.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollIntroV2Fragment.kt
@@ -33,8 +33,8 @@
 import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.lifecycleScope
 import com.android.settings.R
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollNavigationViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollViewModel
-import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollmentNavigationViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintGatekeeperViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintScrollViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Unicorn
@@ -72,10 +72,10 @@
  * 2. How the data will be stored
  * 3. How the user can access and remove their data
  */
-class FingerprintEnrollmentIntroV2Fragment : Fragment(R.layout.fingerprint_v2_enroll_introduction) {
+class FingerprintEnrollIntroV2Fragment : Fragment(R.layout.fingerprint_v2_enroll_introduction) {
   private lateinit var footerBarMixin: FooterBarMixin
   private lateinit var textModel: TextModel
-  private lateinit var navigationViewModel: FingerprintEnrollmentNavigationViewModel
+  private lateinit var navigationViewModel: FingerprintEnrollNavigationViewModel
   private lateinit var fingerprintEnrollViewModel: FingerprintEnrollViewModel
   private lateinit var fingerprintScrollViewModel: FingerprintScrollViewModel
   private lateinit var gateKeeperViewModel: FingerprintGatekeeperViewModel
@@ -83,7 +83,7 @@
   override fun onCreate(savedInstanceState: Bundle?) {
     super.onCreate(savedInstanceState)
     navigationViewModel =
-      ViewModelProvider(requireActivity())[FingerprintEnrollmentNavigationViewModel::class.java]
+      ViewModelProvider(requireActivity())[FingerprintEnrollNavigationViewModel::class.java]
     fingerprintEnrollViewModel =
       ViewModelProvider(requireActivity())[FingerprintEnrollViewModel::class.java]
     fingerprintScrollViewModel =
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/EnrollReason.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/EnrollReason.kt
new file mode 100644
index 0000000..87deeb3
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/EnrollReason.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.biometrics.fingerprint2.ui.enrollment.viewmodel
+
+import android.hardware.fingerprint.FingerprintManager
+
+/**
+ * The reason for enrollment. Represents [FingerprintManager.EnrollReason]
+ */
+enum class EnrollReason {
+  /** The enroll happens on education screen. */
+  FindSensor,
+  /** The enroll happens on enrolling screen. */
+  EnrollEnrolling
+}
+
+/** Convert EnrollReason to original [FingerprintManager.EnrollReason]. */
+fun EnrollReason.toOriginalReason(): Int {
+  return when (this) {
+    EnrollReason.EnrollEnrolling -> FingerprintManager.ENROLL_ENROLL
+    EnrollReason.FindSensor -> FingerprintManager.ENROLL_FIND_SENSOR
+  }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerEnrollStateViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerEnrollStateViewModel.kt
new file mode 100644
index 0000000..73343bd
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerEnrollStateViewModel.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.biometrics.fingerprint2.ui.enrollment.viewmodel
+
+import android.annotation.StringRes
+
+/**
+ * Represents a fingerprint enrollment state. See [FingerprintManager.EnrollmentCallback] for more
+ * information
+ */
+sealed class FingerEnrollStateViewModel {
+  /** Represents enrollment step progress. */
+  data class EnrollProgress(
+    val remainingSteps: Int,
+  ) : FingerEnrollStateViewModel()
+  /** Represents that recoverable error has been encountered during enrollment. */
+  data class EnrollHelp(
+    @StringRes val helpMsgId: Int,
+    val helpString: String,
+  ) : FingerEnrollStateViewModel()
+  /** Represents that an unrecoverable error has been encountered and the operation is complete. */
+  data class EnrollError(
+    @StringRes val errMsgId: Int,
+    val errString: String,
+  ) : FingerEnrollStateViewModel()
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollViewModel.kt
index 31fa03d..cb1beb9 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollViewModel.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollViewModel.kt
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel
 
 import androidx.lifecycle.ViewModel
@@ -21,13 +20,24 @@
 import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractor
 import com.android.systemui.biometrics.shared.model.FingerprintSensorType
 import com.android.systemui.biometrics.shared.model.toSensorType
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.transformLatest
+import kotlinx.coroutines.flow.update
 
-/** Represents all of the fingerprint information needed for fingerprint enrollment. */
-class FingerprintEnrollViewModel(fingerprintManagerInteractor: FingerprintManagerInteractor) :
-  ViewModel() {
+private const val TAG = "FingerprintEnrollViewModel"
+
+/** Represents all of the fingerprint information needed for a fingerprint enrollment process. */
+class FingerprintEnrollViewModel(
+  private val fingerprintManagerInteractor: FingerprintManagerInteractor,
+  backgroundDispatcher: CoroutineDispatcher,
+) : ViewModel() {
 
   /** Represents the stream of [FingerprintSensorType] */
   val sensorType: Flow<FingerprintSensorType> =
@@ -35,14 +45,55 @@
       it.sensorType.toSensorType()
     }
 
-  class FingerprintEnrollViewModelFactory(val interactor: FingerprintManagerInteractor) :
-    ViewModelProvider.Factory {
+  private var _enrollReason: MutableStateFlow<EnrollReason> =
+    MutableStateFlow(EnrollReason.FindSensor)
+  private var _hardwareAuthToken: MutableStateFlow<ByteArray?> = MutableStateFlow(null)
+  private var _consumerShouldEnroll: MutableStateFlow<Boolean> = MutableStateFlow(false)
 
+  /**
+   * A flow that contains a [FingerprintEnrollViewModel] which contains the relevant information for
+   * an enrollment process
+   */
+  val enrollFlow: Flow<FingerEnrollStateViewModel> =
+    combine(_consumerShouldEnroll, _hardwareAuthToken, _enrollReason) {
+        consumerShouldEnroll,
+        hardwareAuthToken,
+        enrollReason ->
+        Triple(consumerShouldEnroll, hardwareAuthToken, enrollReason)
+      }
+      .transformLatest {
+        // transformLatest() instead of transform() is used here for cancelling previous enroll()
+        // whenever |consumerShouldEnroll| is changed. Otherwise the latest value will be suspended
+        // since enroll() is an infinite callback flow.
+        (consumerShouldEnroll, hardwareAuthToken, enrollReason) ->
+        if (consumerShouldEnroll && hardwareAuthToken != null) {
+          fingerprintManagerInteractor.enroll(hardwareAuthToken, enrollReason).collect { emit(it) }
+        }
+      }
+      .flowOn(backgroundDispatcher)
+
+  /** Used to indicate the consumer of the view model is ready for an enrollment. */
+  fun startEnroll(hardwareAuthToken: ByteArray?, enrollReason: EnrollReason) {
+    _enrollReason.update { enrollReason }
+    _hardwareAuthToken.update { hardwareAuthToken }
+    // Update _consumerShouldEnroll after updating the other values.
+    _consumerShouldEnroll.update { true }
+  }
+
+  /** Used to indicate to stop the enrollment. */
+  fun stopEnroll() {
+    _consumerShouldEnroll.update { false }
+  }
+
+  class FingerprintEnrollViewModelFactory(
+    val interactor: FingerprintManagerInteractor,
+    val backgroundDispatcher: CoroutineDispatcher
+  ) : ViewModelProvider.Factory {
     @Suppress("UNCHECKED_CAST")
     override fun <T : ViewModel> create(
       modelClass: Class<T>,
     ): T {
-      return FingerprintEnrollViewModel(interactor) as T
+      return FingerprintEnrollViewModel(interactor, backgroundDispatcher) as T
     }
   }
 }
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrolllmentNavigationViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrolllNavigationViewModel.kt
similarity index 95%
rename from src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrolllmentNavigationViewModel.kt
rename to src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrolllNavigationViewModel.kt
index 6a8a8c4..dafe545 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrolllmentNavigationViewModel.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrolllNavigationViewModel.kt
@@ -29,7 +29,7 @@
 import kotlinx.coroutines.flow.update
 import kotlinx.coroutines.launch
 
-const val TAG = "FingerprintEnrollmentNavigationViewModel"
+private const val TAG = "FingerprintEnrollNavigationViewModel"
 
 /** Interface to validate a gatekeeper hat */
 interface Validator {
@@ -54,7 +54,7 @@
  * This class is responsible for sending a [NavigationStep] which indicates where the user is in the
  * Fingerprint Enrollment flow
  */
-class FingerprintEnrollmentNavigationViewModel(
+class FingerprintEnrollNavigationViewModel(
   private val dispatcher: CoroutineDispatcher,
   private val validator: Validator,
   private val fingerprintManagerInteractor: FingerprintManagerInteractor,
@@ -131,7 +131,7 @@
     }
   }
 
-  class FingerprintEnrollmentNavigationViewModelFactory(
+  class FingerprintEnrollNavigationViewModelFactory(
     private val backgroundDispatcher: CoroutineDispatcher,
     private val fingerprintManagerInteractor: FingerprintManagerInteractor,
     private val fingerprintGatekeeperViewModel: FingerprintGatekeeperViewModel,
@@ -143,7 +143,7 @@
       modelClass: Class<T>,
     ): T {
 
-      return FingerprintEnrollmentNavigationViewModel(
+      return FingerprintEnrollNavigationViewModel(
         backgroundDispatcher,
         object : Validator {
           override fun validateGateKeeper(challenge: Long?): Boolean {
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintGatekeeperViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintGatekeeperViewModel.kt
index 5486e7a..fa4463a 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintGatekeeperViewModel.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintGatekeeperViewModel.kt
@@ -29,6 +29,8 @@
 import kotlinx.coroutines.flow.update
 import kotlinx.coroutines.launch
 
+private const val TAG = "FingerprintGatekeeperViewModel"
+
 sealed interface GatekeeperInfo {
   object Invalid : GatekeeperInfo
   object Timeout : GatekeeperInfo
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsDataSyncController.java b/src/com/android/settings/bluetooth/BluetoothDetailsDataSyncController.java
index 5969ada..a8cd85f 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsDataSyncController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsDataSyncController.java
@@ -64,6 +64,7 @@
         mCompanionDeviceManager = context.getSystemService(CompanionDeviceManager.class);
 
         mCompanionDeviceManager.getAllAssociations().stream().filter(
+                a -> a.getDeviceMacAddress() != null).filter(
                 a -> Objects.equal(mCachedDevice.getAddress(),
                         a.getDeviceMacAddress().toString().toUpperCase())).max(
                 Comparator.comparingLong(AssociationInfo::getTimeApprovedMs)).ifPresent(
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherController.java b/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherController.java
index 188b4ad..562a469 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherController.java
@@ -31,6 +31,8 @@
 
 import com.google.common.annotations.VisibleForTesting;
 
+import java.util.Set;
+
 /**
  * This class handles button preference logic to display for hearing aid device.
  */
@@ -91,7 +93,11 @@
     }
 
     private boolean getButtonPreferenceVisibility(CachedBluetoothDevice cachedDevice) {
-        return isBinauralMode(cachedDevice) && isOnlyOneSideConnected(cachedDevice);
+        // The device is not connected yet. Don't show the button.
+        if (!cachedDevice.isConnectedHearingAidDevice()) {
+            return false;
+        }
+        return isBinauralMode(cachedDevice) && !isOtherSideConnected(cachedDevice);
     }
 
     private void launchPairingDetail() {
@@ -106,16 +112,25 @@
         return cachedDevice.getDeviceMode() == HearingAidInfo.DeviceMode.MODE_BINAURAL;
     }
 
-    private boolean isOnlyOneSideConnected(CachedBluetoothDevice cachedDevice) {
-        if (!cachedDevice.isConnectedAshaHearingAidDevice()) {
-            return false;
+    private boolean isOtherSideConnected(CachedBluetoothDevice cachedDevice) {
+        // Check sub device for ASHA hearing aid
+        if (cachedDevice.isConnectedAshaHearingAidDevice()) {
+            final CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
+            if (subDevice != null && subDevice.isConnectedAshaHearingAidDevice()) {
+                return true;
+            }
         }
 
-        final CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
-        if (subDevice != null && subDevice.isConnectedAshaHearingAidDevice()) {
-            return false;
+        // Check member device for LE audio hearing aid
+        if (cachedDevice.isConnectedLeAudioHearingAidDevice()) {
+            final Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice();
+            for (CachedBluetoothDevice memberDevice : memberDevices) {
+                if (memberDevice.isConnectedLeAudioHearingAidDevice()) {
+                    return true;
+                }
+            }
         }
 
-        return true;
+        return false;
     }
 }
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
index 555868b..f473f2c 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
@@ -69,7 +69,9 @@
     private static final String ENABLE_DUAL_MODE_AUDIO =
             "persist.bluetooth.enable_dual_mode_audio";
     private static final String CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT = "le_audio_enabled_by_default";
-    private static final boolean LE_AUDIO_DEVICE_DETAIL_DEFAULT_VALUE = true;
+    private static final boolean LE_AUDIO_TOGGLE_VISIBLE_DEFAULT_VALUE = true;
+    private static final String LE_AUDIO_TOGGLE_VISIBLE_PROPERTY =
+            "persist.bluetooth.leaudio.toggle_visible";
 
     private LocalBluetoothManager mManager;
     private LocalBluetoothProfileManager mProfileManager;
@@ -465,15 +467,13 @@
     private void updateLeAudioConfig() {
         mIsLeContactSharingEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI,
                 SettingsUIDeviceConfig.BT_LE_AUDIO_CONTACT_SHARING_ENABLED, true);
-        boolean isLeDeviceDetailEnabled = DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_SETTINGS_UI,
-                SettingsUIDeviceConfig.BT_LE_AUDIO_DEVICE_DETAIL_ENABLED,
-                LE_AUDIO_DEVICE_DETAIL_DEFAULT_VALUE);
+        boolean isLeAudioToggleVisible = SystemProperties.getBoolean(
+                LE_AUDIO_TOGGLE_VISIBLE_PROPERTY, LE_AUDIO_TOGGLE_VISIBLE_DEFAULT_VALUE);
         boolean isLeEnabledByDefault = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH,
                 CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT, false);
-        mIsLeAudioToggleEnabled = isLeDeviceDetailEnabled || isLeEnabledByDefault;
+        mIsLeAudioToggleEnabled = isLeAudioToggleVisible || isLeEnabledByDefault;
         Log.d(TAG, "BT_LE_AUDIO_CONTACT_SHARING_ENABLED:" + mIsLeContactSharingEnabled
-                + ", BT_LE_AUDIO_DEVICE_DETAIL_ENABLED:" + isLeDeviceDetailEnabled
+                + ", LE_AUDIO_TOGGLE_VISIBLE_PROPERTY:" + isLeAudioToggleVisible
                 + ", CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT:" + isLeEnabledByDefault);
     }
 
diff --git a/src/com/android/settings/core/SettingsUIDeviceConfig.java b/src/com/android/settings/core/SettingsUIDeviceConfig.java
index 2401ff5..94074df 100644
--- a/src/com/android/settings/core/SettingsUIDeviceConfig.java
+++ b/src/com/android/settings/core/SettingsUIDeviceConfig.java
@@ -42,19 +42,4 @@
      * {@code true} whether or not event_log for generic actions is enabled. Default is true.
      */
     public static final String GENERIC_EVENT_LOGGING_ENABLED = "event_logging_enabled";
-    /**
-     * {@code true} whether to show LE Audio toggle in device detail page. Default is false.
-     */
-    public static final String BT_LE_AUDIO_DEVICE_DETAIL_ENABLED =
-            "bt_le_audio_device_detail_enabled";
-    /**
-     * {@code true} if press and hold nav handle to search is enabled.
-     */
-    public static final String PRESS_HOLD_NAV_HANDLE_TO_SEARCH =
-            "press_hold_nav_handle_to_search";
-    /**
-     * {@code true} if long press home button to search is enabled.
-     */
-    public static final String LONG_PRESS_HOME_BUTTON_TO_SEARCH =
-            "long_press_home_button_to_search";
 }
diff --git a/src/com/android/settings/datausage/AppDataUsagePreference.java b/src/com/android/settings/datausage/AppDataUsagePreference.java
index 2805819..d8c7392 100644
--- a/src/com/android/settings/datausage/AppDataUsagePreference.java
+++ b/src/com/android/settings/datausage/AppDataUsagePreference.java
@@ -38,6 +38,7 @@
     public AppDataUsagePreference(Context context, AppItem item, int percent,
             UidDetailProvider provider) {
         super(context);
+        setKey("app_data_usage_" + item.key);
         mItem = item;
         mPercent = percent;
 
diff --git a/src/com/android/settings/datausage/ChartDataUsagePreference.java b/src/com/android/settings/datausage/ChartDataUsagePreference.java
index 12fb03b..e2a103e 100644
--- a/src/com/android/settings/datausage/ChartDataUsagePreference.java
+++ b/src/com/android/settings/datausage/ChartDataUsagePreference.java
@@ -56,8 +56,6 @@
     private long mStart;
     private long mEnd;
     private NetworkCycleChartData mNetworkCycleChartData;
-    private int mSecondaryColor;
-    private int mSeriesColor;
 
     public ChartDataUsagePreference(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -310,10 +308,4 @@
         mEnd = data.getEndTime();
         notifyChanged();
     }
-
-    public void setColors(int seriesColor, int secondaryColor) {
-        mSeriesColor = seriesColor;
-        mSecondaryColor = secondaryColor;
-        notifyChanged();
-    }
 }
diff --git a/src/com/android/settings/datausage/DataUsageList.java b/src/com/android/settings/datausage/DataUsageList.java
index b030219..39287c19 100644
--- a/src/com/android/settings/datausage/DataUsageList.java
+++ b/src/com/android/settings/datausage/DataUsageList.java
@@ -15,19 +15,15 @@
 package com.android.settings.datausage;
 
 import android.app.Activity;
-import android.app.ActivityManager;
 import android.app.settings.SettingsEnums;
-import android.app.usage.NetworkStats;
 import android.content.Context;
 import android.content.Intent;
-import android.graphics.Color;
 import android.net.ConnectivityManager;
 import android.net.NetworkPolicy;
 import android.net.NetworkTemplate;
 import android.os.Bundle;
 import android.os.UserManager;
 import android.provider.Settings;
-import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.util.EventLog;
 import android.util.Log;
@@ -46,25 +42,18 @@
 import androidx.loader.app.LoaderManager.LoaderCallbacks;
 import androidx.loader.content.Loader;
 import androidx.preference.Preference;
-import androidx.preference.PreferenceGroup;
 
 import com.android.settings.R;
 import com.android.settings.core.SubSettingLauncher;
 import com.android.settings.datausage.CycleAdapter.SpinnerInterface;
-import com.android.settings.datausage.lib.AppDataUsageRepository;
 import com.android.settings.network.MobileDataEnabledListener;
 import com.android.settings.network.MobileNetworkRepository;
-import com.android.settings.network.ProxySubscriptionManager;
 import com.android.settings.widget.LoadingViewController;
-import com.android.settingslib.AppItem;
 import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
 import com.android.settingslib.net.NetworkCycleChartData;
 import com.android.settingslib.net.NetworkCycleChartDataLoader;
-import com.android.settingslib.net.NetworkStatsSummaryLoader;
-import com.android.settingslib.net.UidDetailProvider;
 import com.android.settingslib.utils.ThreadUtils;
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
@@ -85,14 +74,11 @@
 
     private static final String KEY_USAGE_AMOUNT = "usage_amount";
     private static final String KEY_CHART_DATA = "chart_data";
-    private static final String KEY_APPS_GROUP = "apps_group";
     private static final String KEY_TEMPLATE = "template";
     private static final String KEY_APP = "app";
 
     @VisibleForTesting
     static final int LOADER_CHART_DATA = 2;
-    @VisibleForTesting
-    static final int LOADER_SUMMARY = 3;
 
     @VisibleForTesting
     MobileDataEnabledListener mDataStateListener;
@@ -113,18 +99,15 @@
     @Nullable
     private List<NetworkCycleChartData> mCycleData;
 
-    // Caches the cycles for startAppDataUsage usage, which need be cleared when resumed.
-    private ArrayList<Long> mCycles;
     // Spinner will keep the selected cycle even after paused, this only keeps the displayed cycle,
     // which need be cleared when resumed.
     private CycleAdapter.CycleItem mLastDisplayedCycle;
-    private UidDetailProvider mUidDetailProvider;
     private CycleAdapter mCycleAdapter;
     private Preference mUsageAmount;
-    private PreferenceGroup mApps;
     private View mHeader;
     private MobileNetworkRepository mMobileNetworkRepository;
     private SubscriptionInfoEntity mSubscriptionInfoEntity;
+    private DataUsageListAppsController mDataUsageListAppsController;
 
     @Override
     public int getMetricsCategory() {
@@ -148,14 +131,19 @@
             return;
         }
 
-        mUidDetailProvider = new UidDetailProvider(activity);
         mUsageAmount = findPreference(KEY_USAGE_AMOUNT);
         mChart = findPreference(KEY_CHART_DATA);
-        mApps = findPreference(KEY_APPS_GROUP);
 
         processArgument();
+        if (mTemplate == null) {
+            Log.e(TAG, "No template; leaving");
+            finish();
+            return;
+        }
         updateSubscriptionInfoEntity();
         mDataStateListener = new MobileDataEnabledListener(activity, this);
+        mDataUsageListAppsController = use(DataUsageListAppsController.class);
+        mDataUsageListAppsController.init(mTemplate);
     }
 
     @Override
@@ -216,7 +204,6 @@
         super.onResume();
         mLoadingViewController.showLoadingViewDelayed();
         mDataStateListener.start(mSubId);
-        mCycles = null;
         mLastDisplayedCycle = null;
 
         // kick off loader for network history
@@ -224,8 +211,6 @@
         // network history when showing app detail.
         getLoaderManager().restartLoader(LOADER_CHART_DATA,
                 buildArgs(mTemplate), mNetworkCycleDataCallbacks);
-
-        updateBody();
     }
 
     @Override
@@ -234,16 +219,6 @@
         mDataStateListener.stop();
 
         getLoaderManager().destroyLoader(LOADER_CHART_DATA);
-        getLoaderManager().destroyLoader(LOADER_SUMMARY);
-    }
-
-    @Override
-    public void onDestroy() {
-        if (mUidDetailProvider != null) {
-            mUidDetailProvider.clearCache();
-            mUidDetailProvider = null;
-        }
-        super.onDestroy();
     }
 
     @Override
@@ -295,33 +270,6 @@
         updatePolicy();
     }
 
-    /**
-     * Update body content based on current tab. Loads network cycle data from system, and
-     * binds them to visible controls.
-     */
-    private void updateBody() {
-        if (!isAdded()) return;
-
-        final Context context = getActivity();
-
-        // detail mode can change visible menus, invalidate
-        getActivity().invalidateOptionsMenu();
-
-        int seriesColor = context.getColor(R.color.sim_noitification);
-        if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-            final SubscriptionInfo sir = ProxySubscriptionManager.getInstance(context)
-                    .getActiveSubscriptionInfo(mSubId);
-
-            if (sir != null) {
-                seriesColor = sir.getIconTint();
-            }
-        }
-
-        final int secondaryColor = Color.argb(127, Color.red(seriesColor), Color.green(seriesColor),
-                Color.blue(seriesColor));
-        mChart.setColors(seriesColor, secondaryColor);
-    }
-
     private Bundle buildArgs(NetworkTemplate template) {
         final Bundle args = new Bundle();
         args.putParcelable(KEY_TEMPLATE, template);
@@ -352,6 +300,7 @@
         if (mCycleData != null) {
             mCycleAdapter.updateCycleList(mCycleData);
         }
+        mDataUsageListAppsController.setCycleData(mCycleData);
         updateSelectedCycle();
     }
 
@@ -402,67 +351,18 @@
         if (LOGD) Log.d(TAG, "updateDetailData()");
 
         // kick off loader for detailed stats
-        getLoaderManager().restartLoader(LOADER_SUMMARY, null /* args */,
-                mNetworkStatsDetailCallbacks);
+        mDataUsageListAppsController.update(
+                mSubscriptionInfoEntity == null ? null : mSubscriptionInfoEntity.carrierId,
+                mChart.getInspectStart(),
+                mChart.getInspectEnd()
+        );
 
         final long totalBytes = mCycleData != null && !mCycleData.isEmpty()
-            ? mCycleData.get(mCycleSpinner.getSelectedItemPosition()).getTotalUsage() : 0;
+                ? mCycleData.get(mCycleSpinner.getSelectedItemPosition()).getTotalUsage() : 0;
         final CharSequence totalPhrase = DataUsageUtils.formatDataUsage(getActivity(), totalBytes);
         mUsageAmount.setTitle(getString(R.string.data_used_template, totalPhrase));
     }
 
-    /**
-     * Bind the given buckets.
-     */
-    private void bindStats(List<AppDataUsageRepository.Bucket> buckets) {
-        mApps.removeAll();
-        AppDataUsageRepository repository = new AppDataUsageRepository(
-                requireContext(),
-                ActivityManager.getCurrentUser(),
-                mSubscriptionInfoEntity == null ? null : mSubscriptionInfoEntity.carrierId,
-                appItem -> mUidDetailProvider.getUidDetail(appItem.key, true).packageName
-        );
-        for (var itemPercentPair : repository.getAppPercent(buckets)) {
-            final AppDataUsagePreference preference = new AppDataUsagePreference(getContext(),
-                    itemPercentPair.getFirst(), itemPercentPair.getSecond(), mUidDetailProvider);
-            preference.setOnPreferenceClickListener(p -> {
-                AppDataUsagePreference pref = (AppDataUsagePreference) p;
-                startAppDataUsage(pref.getItem());
-                return true;
-            });
-            mApps.addPreference(preference);
-        }
-    }
-
-    @VisibleForTesting
-    void startAppDataUsage(AppItem item) {
-        if (mCycleData == null) {
-            return;
-        }
-        final Bundle args = new Bundle();
-        args.putParcelable(AppDataUsage.ARG_APP_ITEM, item);
-        args.putParcelable(AppDataUsage.ARG_NETWORK_TEMPLATE, mTemplate);
-        if (mCycles == null) {
-            mCycles = new ArrayList<>();
-            for (NetworkCycleChartData data : mCycleData) {
-                if (mCycles.isEmpty()) {
-                    mCycles.add(data.getEndTime());
-                }
-                mCycles.add(data.getStartTime());
-            }
-        }
-        args.putSerializable(AppDataUsage.ARG_NETWORK_CYCLES, mCycles);
-        args.putLong(AppDataUsage.ARG_SELECTED_CYCLE,
-            mCycleData.get(mCycleSpinner.getSelectedItemPosition()).getEndTime());
-
-        new SubSettingLauncher(getContext())
-                .setDestination(AppDataUsage.class.getName())
-                .setTitleRes(R.string.data_usage_app_summary_title)
-                .setArguments(args)
-                .setSourceMetricsCategory(getMetricsCategory())
-                .launch();
-    }
-
     private final OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
         @Override
         public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
@@ -502,44 +402,6 @@
                 }
             };
 
-    private final LoaderCallbacks<NetworkStats> mNetworkStatsDetailCallbacks =
-            new LoaderCallbacks<>() {
-                @Override
-                @NonNull
-                public Loader<NetworkStats> onCreateLoader(int id, Bundle args) {
-                    return new NetworkStatsSummaryLoader.Builder(getContext())
-                            .setStartTime(mChart.getInspectStart())
-                            .setEndTime(mChart.getInspectEnd())
-                            .setNetworkTemplate(mTemplate)
-                            .build();
-                }
-
-                @Override
-                public void onLoadFinished(
-                        @NonNull Loader<NetworkStats> loader, NetworkStats data) {
-                    bindStats(AppDataUsageRepository.Companion.convertToBuckets(data));
-                    updateEmptyVisible();
-                }
-
-                @Override
-                public void onLoaderReset(@NonNull Loader<NetworkStats> loader) {
-                    mApps.removeAll();
-                    updateEmptyVisible();
-                }
-
-                private void updateEmptyVisible() {
-                    if ((mApps.getPreferenceCount() != 0)
-                            != (getPreferenceScreen().getPreferenceCount() != 0)) {
-                        if (mApps.getPreferenceCount() != 0) {
-                            getPreferenceScreen().addPreference(mUsageAmount);
-                            getPreferenceScreen().addPreference(mApps);
-                        } else {
-                            getPreferenceScreen().removeAll();
-                        }
-                    }
-                }
-            };
-
     private static boolean isGuestUser(Context context) {
         if (context == null) return false;
         final UserManager userManager = context.getSystemService(UserManager.class);
diff --git a/src/com/android/settings/datausage/DataUsageListAppsController.kt b/src/com/android/settings/datausage/DataUsageListAppsController.kt
new file mode 100644
index 0000000..cc55e1a
--- /dev/null
+++ b/src/com/android/settings/datausage/DataUsageListAppsController.kt
@@ -0,0 +1,114 @@
+/*
+ * 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.datausage
+
+import android.app.ActivityManager
+import android.content.Context
+import android.net.NetworkTemplate
+import android.os.Bundle
+import androidx.annotation.VisibleForTesting
+import androidx.lifecycle.LifecycleCoroutineScope
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.preference.PreferenceGroup
+import androidx.preference.PreferenceScreen
+import com.android.settings.R
+import com.android.settings.core.BasePreferenceController
+import com.android.settings.core.SubSettingLauncher
+import com.android.settings.datausage.lib.AppDataUsageRepository
+import com.android.settingslib.AppItem
+import com.android.settingslib.net.NetworkCycleChartData
+import com.android.settingslib.net.UidDetailProvider
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+class DataUsageListAppsController(context: Context, preferenceKey: String) :
+    BasePreferenceController(context, preferenceKey) {
+
+    private val uidDetailProvider = UidDetailProvider(context)
+    private lateinit var template: NetworkTemplate
+    private lateinit var repository: AppDataUsageRepository
+    private lateinit var preference: PreferenceGroup
+    private lateinit var lifecycleScope: LifecycleCoroutineScope
+
+    private var cycleData: List<NetworkCycleChartData>? = null
+
+    fun init(template: NetworkTemplate) {
+        this.template = template
+        repository = AppDataUsageRepository(
+            context = mContext,
+            currentUserId = ActivityManager.getCurrentUser(),
+            template = template,
+        ) { appItem: AppItem -> uidDetailProvider.getUidDetail(appItem.key, true).packageName }
+    }
+
+    override fun getAvailabilityStatus() = AVAILABLE
+
+    override fun displayPreference(screen: PreferenceScreen) {
+        super.displayPreference(screen)
+        preference = screen.findPreference(preferenceKey)!!
+    }
+
+    override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
+        lifecycleScope = viewLifecycleOwner.lifecycleScope
+    }
+
+    fun setCycleData(cycleData: List<NetworkCycleChartData>?) {
+        this.cycleData = cycleData
+    }
+
+    fun update(carrierId: Int?, startTime: Long, endTime: Long) = lifecycleScope.launch {
+        val apps = withContext(Dispatchers.Default) {
+            repository.getAppPercent(carrierId, startTime, endTime).map { (appItem, percent) ->
+                AppDataUsagePreference(mContext, appItem, percent, uidDetailProvider).apply {
+                    setOnPreferenceClickListener {
+                        startAppDataUsage(appItem, endTime)
+                        true
+                    }
+                }
+            }
+        }
+        preference.removeAll()
+        for (app in apps) {
+            preference.addPreference(app)
+        }
+    }
+
+    @VisibleForTesting
+    fun startAppDataUsage(item: AppItem, endTime: Long) {
+        val cycleData = cycleData ?: return
+        val args = Bundle().apply {
+            putParcelable(AppDataUsage.ARG_APP_ITEM, item)
+            putParcelable(AppDataUsage.ARG_NETWORK_TEMPLATE, template)
+            val cycles = ArrayList<Long>().apply {
+                for (data in cycleData) {
+                    if (isEmpty()) add(data.endTime)
+                    add(data.startTime)
+                }
+            }
+            putSerializable(AppDataUsage.ARG_NETWORK_CYCLES, cycles)
+            putLong(AppDataUsage.ARG_SELECTED_CYCLE, endTime)
+        }
+        SubSettingLauncher(mContext).apply {
+            setDestination(AppDataUsage::class.java.name)
+            setTitleRes(R.string.data_usage_app_summary_title)
+            setArguments(args)
+            setSourceMetricsCategory(metricsCategory)
+        }.launch()
+    }
+}
diff --git a/src/com/android/settings/datausage/lib/AppDataUsageRepository.kt b/src/com/android/settings/datausage/lib/AppDataUsageRepository.kt
index 3813af5..074a555 100644
--- a/src/com/android/settings/datausage/lib/AppDataUsageRepository.kt
+++ b/src/com/android/settings/datausage/lib/AppDataUsageRepository.kt
@@ -17,11 +17,15 @@
 package com.android.settings.datausage.lib
 
 import android.app.usage.NetworkStats
+import android.app.usage.NetworkStatsManager
 import android.content.Context
 import android.net.NetworkPolicyManager
+import android.net.NetworkTemplate
 import android.os.Process
 import android.os.UserHandle
+import android.util.Log
 import android.util.SparseArray
+import androidx.annotation.VisibleForTesting
 import com.android.settings.R
 import com.android.settingslib.AppItem
 import com.android.settingslib.net.UidDetailProvider
@@ -30,15 +34,18 @@
 class AppDataUsageRepository(
     private val context: Context,
     private val currentUserId: Int,
-    private val carrierId: Int?,
-    private val getPackageName: (AppItem) -> String,
+    private val template: NetworkTemplate,
+    private val getPackageName: (AppItem) -> String?,
 ) {
-    data class Bucket(
-        val uid: Int,
-        val bytes: Long,
-    )
+    private val networkStatsManager = context.getSystemService(NetworkStatsManager::class.java)!!
 
-    fun getAppPercent(buckets: List<Bucket>): List<Pair<AppItem, Int>> {
+    fun getAppPercent(carrierId: Int?, startTime: Long, endTime: Long): List<Pair<AppItem, Int>> {
+        val networkStats = querySummary(startTime, endTime) ?: return emptyList()
+        return getAppPercent(carrierId, convertToBuckets(networkStats))
+    }
+
+    @VisibleForTesting
+    fun getAppPercent(carrierId: Int?, buckets: List<Bucket>): List<Pair<AppItem, Int>> {
         val items = ArrayList<AppItem>()
         val knownItems = SparseArray<AppItem>()
         val profiles = context.userManager.userProfiles
@@ -61,7 +68,7 @@
             item.restricted = true
         }
 
-        val filteredItems = filterItems(items).sorted()
+        val filteredItems = filterItems(carrierId, items).sorted()
         val largest: Long = filteredItems.maxOfOrNull { it.total } ?: 0
         return filteredItems.map { item ->
             val percentTotal = if (largest > 0) (item.total * 100 / largest).toInt() else 0
@@ -69,7 +76,14 @@
         }
     }
 
-    private fun filterItems(items: List<AppItem>): List<AppItem> {
+    private fun querySummary(startTime: Long, endTime: Long): NetworkStats? = try {
+        networkStatsManager.querySummary(template, startTime, endTime)
+    } catch (e: RuntimeException) {
+        Log.e(TAG, "Exception querying network detail.", e)
+        null
+    }
+
+    private fun filterItems(carrierId: Int?, items: List<AppItem>): List<AppItem> {
         // When there is no specified SubscriptionInfo, Wi-Fi data usage will be displayed.
         // In this case, the carrier service package also needs to be hidden.
         if (carrierId != null && carrierId !in context.resources.getIntArray(
@@ -178,7 +192,15 @@
     }
 
     companion object {
-        fun convertToBuckets(stats: NetworkStats): List<Bucket> {
+        private const val TAG = "AppDataUsageRepository"
+
+        @VisibleForTesting
+        data class Bucket(
+            val uid: Int,
+            val bytes: Long,
+        )
+
+        private fun convertToBuckets(stats: NetworkStats): List<Bucket> {
             val buckets = mutableListOf<Bucket>()
             stats.use {
                 val bucket = NetworkStats.Bucket()
diff --git a/src/com/android/settings/development/BluetoothLeAudioDeviceDetailsPreferenceController.java b/src/com/android/settings/development/BluetoothLeAudioDeviceDetailsPreferenceController.java
index 298ced0..980bdaa 100644
--- a/src/com/android/settings/development/BluetoothLeAudioDeviceDetailsPreferenceController.java
+++ b/src/com/android/settings/development/BluetoothLeAudioDeviceDetailsPreferenceController.java
@@ -20,6 +20,7 @@
 import android.bluetooth.BluetoothManager;
 import android.bluetooth.BluetoothStatusCodes;
 import android.content.Context;
+import android.os.SystemProperties;
 import android.provider.DeviceConfig;
 
 import androidx.annotation.VisibleForTesting;
@@ -27,7 +28,6 @@
 import androidx.preference.SwitchPreference;
 
 import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settings.core.SettingsUIDeviceConfig;
 import com.android.settingslib.development.DeveloperOptionsPreferenceController;
 
 /**
@@ -40,9 +40,12 @@
 
     private static final String PREFERENCE_KEY = "bluetooth_show_leaudio_device_details";
     private static final String CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT = "le_audio_enabled_by_default";
-    private static final boolean LE_AUDIO_DEVICE_DETAIL_DEFAULT_VALUE = true;
+    private static final boolean LE_AUDIO_TOGGLE_VISIBLE_DEFAULT_VALUE = true;
     static int sLeAudioSupportedStateCache = BluetoothStatusCodes.ERROR_UNKNOWN;
 
+    static final String LE_AUDIO_TOGGLE_VISIBLE_PROPERTY =
+            "persist.bluetooth.leaudio.toggle_visible";
+
     @VisibleForTesting
     BluetoothAdapter mBluetoothAdapter;
 
@@ -73,10 +76,7 @@
     @Override
     public boolean onPreferenceChange(Preference preference, Object newValue) {
         final boolean isEnabled = (Boolean) newValue;
-        DeviceConfig.setProperty(
-                DeviceConfig.NAMESPACE_SETTINGS_UI,
-                SettingsUIDeviceConfig.BT_LE_AUDIO_DEVICE_DETAIL_ENABLED,
-                isEnabled ? "true" : "false", LE_AUDIO_DEVICE_DETAIL_DEFAULT_VALUE);
+        SystemProperties.set(LE_AUDIO_TOGGLE_VISIBLE_PROPERTY, Boolean.toString(isEnabled));
         return true;
     }
 
@@ -86,25 +86,13 @@
             return;
         }
 
-        final boolean leAudioDeviceDetailEnabled = DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_SETTINGS_UI,
-                SettingsUIDeviceConfig.BT_LE_AUDIO_DEVICE_DETAIL_ENABLED,
-                LE_AUDIO_DEVICE_DETAIL_DEFAULT_VALUE);
+        final boolean isLeAudioToggleVisible = SystemProperties.getBoolean(
+                LE_AUDIO_TOGGLE_VISIBLE_PROPERTY, LE_AUDIO_TOGGLE_VISIBLE_DEFAULT_VALUE);
         final boolean leAudioEnabledByDefault = DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_BLUETOOTH, CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT, false);
 
         mPreference.setEnabled(!leAudioEnabledByDefault);
-        ((SwitchPreference) mPreference).setChecked(leAudioDeviceDetailEnabled
+        ((SwitchPreference) mPreference).setChecked(isLeAudioToggleVisible
                 || leAudioEnabledByDefault);
     }
-
-    @Override
-    protected void onDeveloperOptionsSwitchDisabled() {
-        super.onDeveloperOptionsSwitchDisabled();
-        // Reset the toggle to null when the developer option is disabled
-        DeviceConfig.setProperty(
-                DeviceConfig.NAMESPACE_SETTINGS_UI,
-                SettingsUIDeviceConfig.BT_LE_AUDIO_DEVICE_DETAIL_ENABLED, "null",
-                LE_AUDIO_DEVICE_DETAIL_DEFAULT_VALUE);
-    }
 }
diff --git a/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetector.java b/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetector.java
index ed8cc62..9e970d2 100644
--- a/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetector.java
+++ b/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetector.java
@@ -23,8 +23,6 @@
 import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
 import com.android.settings.fuelgauge.batterytip.tips.LowBatteryTip;
 
-import java.util.concurrent.TimeUnit;
-
 /**
  * Detect whether the battery is too low
  */
@@ -46,9 +44,7 @@
 
     @Override
     public BatteryTip detect() {
-        final boolean lowBattery = mBatteryInfo.batteryLevel <= mWarningLevel
-                || (mBatteryInfo.discharging && mBatteryInfo.remainingTimeUs != 0
-                && mBatteryInfo.remainingTimeUs < TimeUnit.HOURS.toMicros(mPolicy.lowBatteryHour));
+        final boolean lowBattery = mBatteryInfo.batteryLevel <= mWarningLevel;
         final boolean lowBatteryEnabled = mPolicy.lowBatteryEnabled && !mIsPowerSaveMode;
         final boolean dischargingLowBatteryState =
                 mPolicy.testLowBatteryTip || (mBatteryInfo.discharging && lowBattery);
diff --git a/src/com/android/settings/fuelgauge/batteryusage/AnomalyAppItemPreference.java b/src/com/android/settings/fuelgauge/batteryusage/AnomalyAppItemPreference.java
new file mode 100644
index 0000000..2f139ec
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/AnomalyAppItemPreference.java
@@ -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.fuelgauge.batteryusage;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.settings.R;
+
+class AnomalyAppItemPreference extends PowerGaugePreference {
+
+    private static final String TAG = "AnomalyAppItemPreference";
+
+    private CharSequence mAnomalyHintText;
+
+    AnomalyAppItemPreference(Context context) {
+        super(context, /* attrs */ null);
+        setLayoutResource(R.layout.anomaly_app_item_preference);
+    }
+
+    void setAnomalyHint(CharSequence anomalyHintText) {
+        if (!TextUtils.equals(mAnomalyHintText, anomalyHintText)) {
+            mAnomalyHintText = anomalyHintText;
+            notifyChanged();
+        }
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder viewHolder) {
+        super.onBindViewHolder(viewHolder);
+        final LinearLayout warningChipView =
+                (LinearLayout) viewHolder.findViewById(R.id.warning_chip);
+
+        if (!TextUtils.isEmpty(mAnomalyHintText)) {
+            ((TextView) warningChipView.findViewById(R.id.warning_info)).setText(mAnomalyHintText);
+            warningChipView.setVisibility(View.VISIBLE);
+        } else {
+            warningChipView.setVisibility(View.GONE);
+        }
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/AnomalyEventWrapper.java b/src/com/android/settings/fuelgauge/batteryusage/AnomalyEventWrapper.java
new file mode 100644
index 0000000..d535490
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/AnomalyEventWrapper.java
@@ -0,0 +1,231 @@
+/*
+ * 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.batteryusage;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Pair;
+
+import com.android.settings.R;
+import com.android.settings.SettingsActivity;
+import com.android.settings.core.SubSettingLauncher;
+
+import java.util.function.Function;
+
+final class AnomalyEventWrapper {
+    private static final String TAG = "AnomalyEventWrapper";
+
+    private final Context mContext;
+    private final PowerAnomalyEvent mPowerAnomalyEvent;
+
+    private final int mCardStyleId;
+    private final int mResourceIndex;
+
+    private SubSettingLauncher mSubSettingLauncher = null;
+    private Pair<Integer, Integer> mHighlightSlotPair = null;
+    private BatteryDiffEntry mRelatedBatteryDiffEntry = null;
+
+    AnomalyEventWrapper(Context context, PowerAnomalyEvent powerAnomalyEvent) {
+        mContext = context;
+        mPowerAnomalyEvent = powerAnomalyEvent;
+        // Set basic battery tips card info
+        mCardStyleId = mPowerAnomalyEvent.getType().getNumber();
+        mResourceIndex = mPowerAnomalyEvent.getKey().getNumber();
+    }
+
+    private <T> T getInfo(Function<WarningBannerInfo, T> warningBannerInfoSupplier,
+            Function<WarningItemInfo, T> warningItemInfoSupplier) {
+        if (warningBannerInfoSupplier != null && mPowerAnomalyEvent.hasWarningBannerInfo()) {
+            return warningBannerInfoSupplier.apply(mPowerAnomalyEvent.getWarningBannerInfo());
+        } else if (warningItemInfoSupplier != null && mPowerAnomalyEvent.hasWarningItemInfo()) {
+            return warningItemInfoSupplier.apply(mPowerAnomalyEvent.getWarningItemInfo());
+        }
+        return null;
+    }
+
+    private int getResourceId(int resourceId, int resourceIndex, String defType) {
+        final String key = getStringFromArrayResource(resourceId, resourceIndex);
+        return TextUtils.isEmpty(key) ? 0
+                : mContext.getResources().getIdentifier(key, defType, mContext.getPackageName());
+    }
+
+    private String getString(Function<WarningBannerInfo, String> warningBannerInfoSupplier,
+            Function<WarningItemInfo, String> warningItemInfoSupplier,
+            int resourceId, int resourceIndex) {
+        final String string = getInfo(warningBannerInfoSupplier, warningItemInfoSupplier);
+        return (!TextUtils.isEmpty(string) || resourceId <= 0) ? string
+                : getStringFromArrayResource(resourceId, resourceIndex);
+    }
+
+    private String getStringFromArrayResource(int resourceId, int resourceIndex) {
+        if (resourceId <= 0 || resourceIndex < 0) {
+            return null;
+        }
+        final String[] stringArray = mContext.getResources().getStringArray(resourceId);
+        return (resourceIndex >= 0 && resourceIndex < stringArray.length)
+                ? stringArray[resourceIndex] : null;
+    }
+
+    void setRelatedBatteryDiffEntry(BatteryDiffEntry batteryDiffEntry) {
+        mRelatedBatteryDiffEntry = batteryDiffEntry;
+    }
+
+    String getEventId() {
+        return mPowerAnomalyEvent.hasEventId() ? mPowerAnomalyEvent.getEventId() : null;
+    }
+
+    int getIconResId() {
+        return getResourceId(R.array.battery_tips_card_icons, mCardStyleId, "drawable");
+    }
+
+    int getColorResId() {
+        return getResourceId(R.array.battery_tips_card_colors, mCardStyleId, "color");
+    }
+
+    String getTitleString() {
+        final String protoTitleString = getInfo(WarningBannerInfo::getTitleString,
+                WarningItemInfo::getTitleString);
+        if (!TextUtils.isEmpty(protoTitleString)) {
+            return protoTitleString;
+        }
+        final int titleFormatResId = getResourceId(R.array.power_anomaly_title_ids,
+                mResourceIndex, "string");
+        if (mPowerAnomalyEvent.hasWarningBannerInfo()) {
+            return mContext.getString(titleFormatResId);
+        } else if (mPowerAnomalyEvent.hasWarningItemInfo() && mRelatedBatteryDiffEntry != null) {
+            final String appLabel = mRelatedBatteryDiffEntry.getAppLabel();
+            return mContext.getString(titleFormatResId, appLabel);
+        }
+        return null;
+    }
+
+    String getMainBtnString() {
+        return getString(WarningBannerInfo::getMainButtonString,
+                WarningItemInfo::getMainButtonString,
+                R.array.power_anomaly_main_btn_strings, mResourceIndex);
+    }
+
+    String getDismissBtnString() {
+        return getString(WarningBannerInfo::getCancelButtonString,
+                WarningItemInfo::getCancelButtonString,
+                R.array.power_anomaly_dismiss_btn_strings, mResourceIndex);
+    }
+
+    String getAnomalyHintString() {
+        return getStringFromArrayResource(R.array.power_anomaly_hint_messages, mResourceIndex);
+    }
+
+    String getDismissRecordKey() {
+        return mPowerAnomalyEvent.getDismissRecordKey();
+    }
+
+    boolean hasAnomalyEntryKey() {
+        return getAnomalyEntryKey() != null;
+    }
+
+    String getAnomalyEntryKey() {
+        return mPowerAnomalyEvent.hasWarningItemInfo()
+                && mPowerAnomalyEvent.getWarningItemInfo().hasItemKey()
+                ? mPowerAnomalyEvent.getWarningItemInfo().getItemKey() : null;
+    }
+
+    boolean hasSubSettingLauncher() {
+        if (mSubSettingLauncher == null) {
+            mSubSettingLauncher = getSubSettingLauncher();
+        }
+        return mSubSettingLauncher != null;
+    }
+
+    SubSettingLauncher getSubSettingLauncher() {
+        if (mSubSettingLauncher != null) {
+            return mSubSettingLauncher;
+        }
+        final String destinationClassName = getInfo(
+                WarningBannerInfo::getMainButtonDestination, null);
+        if (!TextUtils.isEmpty(destinationClassName)) {
+            final Integer sourceMetricsCategory = getInfo(
+                    WarningBannerInfo::getMainButtonSourceMetricsCategory, null);
+            final String preferenceHighlightKey = getInfo(
+                    WarningBannerInfo::getMainButtonSourceHighlightKey, null);
+            Bundle arguments = Bundle.EMPTY;
+            if (!TextUtils.isEmpty(preferenceHighlightKey)) {
+                arguments = new Bundle(1);
+                arguments.putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY,
+                        preferenceHighlightKey);
+            }
+            mSubSettingLauncher = new SubSettingLauncher(mContext)
+                    .setDestination(destinationClassName)
+                    .setSourceMetricsCategory(sourceMetricsCategory)
+                    .setArguments(arguments);
+        }
+        return mSubSettingLauncher;
+    }
+
+    boolean hasHighlightSlotPair(BatteryLevelData batteryLevelData) {
+        if (mHighlightSlotPair == null) {
+            mHighlightSlotPair = getHighlightSlotPair(batteryLevelData);
+        }
+        return mHighlightSlotPair != null;
+    }
+
+    Pair<Integer, Integer> getHighlightSlotPair(BatteryLevelData batteryLevelData) {
+        if (mHighlightSlotPair != null) {
+            return mHighlightSlotPair;
+        }
+        if (!mPowerAnomalyEvent.hasWarningItemInfo()) {
+            return null;
+        }
+        final WarningItemInfo warningItemInfo = mPowerAnomalyEvent.getWarningItemInfo();
+        final Long startTimestamp = warningItemInfo.hasStartTimestamp()
+                ? warningItemInfo.getStartTimestamp() : null;
+        final Long endTimestamp = warningItemInfo.hasEndTimestamp()
+                ? warningItemInfo.getEndTimestamp() : null;
+        if (startTimestamp != null && endTimestamp != null) {
+            mHighlightSlotPair = batteryLevelData
+                    .getIndexByTimestamps(startTimestamp, endTimestamp);
+            if (mHighlightSlotPair.first == BatteryChartViewModel.SELECTED_INDEX_INVALID
+                    || mHighlightSlotPair.second == BatteryChartViewModel.SELECTED_INDEX_INVALID) {
+                // Drop invalid mHighlightSlotPair index
+                mHighlightSlotPair = null;
+            }
+        }
+        return mHighlightSlotPair;
+    }
+
+    boolean updateTipsCardPreference(BatteryTipsCardPreference preference) {
+        final String titleString = getTitleString();
+        if (TextUtils.isEmpty(titleString)) {
+            return false;
+        }
+        preference.setTitle(titleString);
+        preference.setIconResourceId(getIconResId());
+        preference.setMainButtonStrokeColorResourceId(getColorResId());
+        preference.setMainButtonLabel(getMainBtnString());
+        preference.setDismissButtonLabel(getDismissBtnString());
+        return true;
+    }
+
+    boolean launchSubSetting() {
+        if (!hasSubSettingLauncher()) {
+            return false;
+        }
+        // Navigate to sub setting page
+        mSubSettingLauncher.launch();
+        return true;
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
index 75b7c08..1ae3bef 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
@@ -221,14 +221,20 @@
         refreshUi();
     }
 
+    boolean isHighlightSlotFocused() {
+        return (mDailyHighlightSlotIndex != BatteryChartViewModel.SELECTED_INDEX_INVALID
+                && mDailyHighlightSlotIndex == mDailyChartIndex
+                && mHourlyHighlightSlotIndex != BatteryChartViewModel.SELECTED_INDEX_INVALID
+                && mHourlyHighlightSlotIndex == mHourlyChartIndex);
+    }
+
     void onHighlightSlotIndexUpdate(int dailyHighlightSlotIndex, int hourlyHighlightSlotIndex) {
-        if (mDailyHighlightSlotIndex == dailyHighlightSlotIndex
-                && mHourlyHighlightSlotIndex == hourlyHighlightSlotIndex) {
-            return;
-        }
         mDailyHighlightSlotIndex = dailyHighlightSlotIndex;
         mHourlyHighlightSlotIndex = hourlyHighlightSlotIndex;
         refreshUi();
+        if (mOnSelectedIndexUpdatedListener != null) {
+            mOnSelectedIndexUpdatedListener.onSelectedIndexUpdated();
+        }
     }
 
     void selectHighlightSlotIndex() {
@@ -405,7 +411,7 @@
         final String slotInformation = getSlotInformation();
         return slotInformation == null
                 ? mPrefContext.getString(
-                       R.string.battery_usage_breakdown_title_since_last_full_charge)
+                        R.string.battery_usage_breakdown_title_since_last_full_charge)
                 : mPrefContext.getString(
                         R.string.battery_usage_breakdown_title_for_slot, slotInformation);
     }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java
index e5cff20..2c799fa 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java
@@ -120,12 +120,10 @@
     public void onClick(View view) {
         final int viewId = view.getId();
         if (viewId == R.id.main_button || viewId == R.id.tips_card) {
-            setVisible(false);
             if (mOnConfirmListener != null) {
                 mOnConfirmListener.onConfirm();
             }
         } else if (viewId == R.id.dismiss_button) {
-            setVisible(false);
             if (mOnRejectListener != null) {
                 mOnRejectListener.onReject();
             }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsController.java
index b3a3508..44b2421 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsController.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsController.java
@@ -18,21 +18,15 @@
 
 import android.app.settings.SettingsEnums;
 import android.content.Context;
-import android.os.Bundle;
 import android.text.TextUtils;
 
 import androidx.preference.PreferenceScreen;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.settings.R;
-import com.android.settings.SettingsActivity;
 import com.android.settings.core.BasePreferenceController;
-import com.android.settings.core.SubSettingLauncher;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
-import java.util.function.Function;
-
 /** Controls the update for battery tips card */
 public class BatteryTipsController extends BasePreferenceController {
 
@@ -59,6 +53,10 @@
 
     @VisibleForTesting
     BatteryTipsCardPreference mCardPreference;
+    @VisibleForTesting
+    AnomalyEventWrapper mAnomalyEventWrapper = null;
+    @VisibleForTesting
+    Boolean mIsAcceptable = false;
 
     public BatteryTipsController(Context context) {
         super(context, ROOT_PREFERENCE_KEY);
@@ -85,132 +83,56 @@
         mOnAnomalyRejectListener = listener;
     }
 
-    private <T> T getInfo(PowerAnomalyEvent powerAnomalyEvent,
-                          Function<WarningBannerInfo, T> warningBannerInfoSupplier,
-                          Function<WarningItemInfo, T> warningItemInfoSupplier) {
-        if (warningBannerInfoSupplier != null && powerAnomalyEvent.hasWarningBannerInfo()) {
-            return warningBannerInfoSupplier.apply(powerAnomalyEvent.getWarningBannerInfo());
-        } else if (warningItemInfoSupplier != null && powerAnomalyEvent.hasWarningItemInfo()) {
-            return warningItemInfoSupplier.apply(powerAnomalyEvent.getWarningItemInfo());
+    void acceptTipsCard() {
+        if (mAnomalyEventWrapper == null || !mIsAcceptable) {
+            return;
         }
-        return null;
-    }
-
-    private String getStringFromResource(int resourceId, int resourceIndex) {
-        if (resourceId < 0) {
-            return null;
+        // For anomaly events with same record key, dismissed until next time full charged.
+        final String dismissRecordKey = mAnomalyEventWrapper.getDismissRecordKey();
+        if (!TextUtils.isEmpty(dismissRecordKey)) {
+            DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, dismissRecordKey);
         }
-        final String[] stringArray = mContext.getResources().getStringArray(resourceId);
-        return (resourceIndex >= 0 && resourceIndex < stringArray.length)
-                ? stringArray[resourceIndex] : null;
+        mCardPreference.setVisible(false);
+        mMetricsFeatureProvider.action(
+                mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT,
+                mAnomalyEventWrapper.getEventId());
     }
 
-    private int getResourceId(int resourceId, int resourceIndex, String defType) {
-        final String key = getStringFromResource(resourceId, resourceIndex);
-        return TextUtils.isEmpty(key) ? 0
-                : mContext.getResources().getIdentifier(key, defType, mContext.getPackageName());
-    }
-
-    private String getString(PowerAnomalyEvent powerAnomalyEvent,
-                             Function<WarningBannerInfo, String> warningBannerInfoSupplier,
-                             Function<WarningItemInfo, String> warningItemInfoSupplier,
-                             int resourceId, int resourceIndex) {
-        String string =
-                getInfo(powerAnomalyEvent, warningBannerInfoSupplier, warningItemInfoSupplier);
-        return (!TextUtils.isEmpty(string) || resourceId < 0) ? string
-                : getStringFromResource(resourceId, resourceIndex);
-    }
-
-    /** Generate a key string of current anomaly to record as dismissed in sharedPreferences. */
-    public static String getDismissRecordKey(PowerAnomalyEvent event) {
-        if (!event.hasKey()) {
-            return null;
-        }
-        switch (event.getKey()){
-            case KEY_APP:
-                return event.hasWarningItemInfo()
-                        && event.getWarningItemInfo().hasDismissRecordKey()
-                        ? event.getWarningItemInfo().getDismissRecordKey() : null;
-            default:
-                return event.getKey().name();
-        }
-    }
-
-    void handleBatteryTipsCardUpdated(PowerAnomalyEvent powerAnomalyEvent) {
-        if (powerAnomalyEvent == null) {
+    void handleBatteryTipsCardUpdated(
+            AnomalyEventWrapper anomalyEventWrapper, boolean isAcceptable) {
+        mAnomalyEventWrapper = anomalyEventWrapper;
+        mIsAcceptable = isAcceptable;
+        if (mAnomalyEventWrapper == null) {
             mCardPreference.setVisible(false);
             return;
         }
 
-        // Get card icon and color styles
-        final int cardStyleId = powerAnomalyEvent.getType().getNumber();
-        final int iconResId = getResourceId(
-                R.array.battery_tips_card_icons, cardStyleId, "drawable");
-        final int colorResId = getResourceId(
-                R.array.battery_tips_card_colors, cardStyleId, "color");
-
         // Get card preference strings and navigate fragment info
-        final String eventId = powerAnomalyEvent.hasEventId()
-                ? powerAnomalyEvent.getEventId() : null;
-        final PowerAnomalyKey powerAnomalyKey = powerAnomalyEvent.hasKey()
-                ? powerAnomalyEvent.getKey() : null;
-        final int resourceIndex = powerAnomalyKey != null ? powerAnomalyKey.getNumber() : -1;
+        final String eventId = mAnomalyEventWrapper.getEventId();
 
-        final String titleString = getString(powerAnomalyEvent, WarningBannerInfo::getTitleString,
-                WarningItemInfo::getTitleString, R.array.power_anomaly_titles, resourceIndex);
-        if (titleString.isEmpty()) {
+        // Update card & buttons preference
+        if (!mAnomalyEventWrapper.updateTipsCardPreference(mCardPreference)) {
             mCardPreference.setVisible(false);
             return;
         }
 
-        final String mainBtnString = getString(powerAnomalyEvent,
-                WarningBannerInfo::getMainButtonString, WarningItemInfo::getMainButtonString,
-                R.array.power_anomaly_main_btn_strings, resourceIndex);
-        final String dismissBtnString = getString(powerAnomalyEvent,
-                WarningBannerInfo::getCancelButtonString, WarningItemInfo::getCancelButtonString,
-                R.array.power_anomaly_dismiss_btn_strings, resourceIndex);
-
-        final String destinationClassName = getInfo(powerAnomalyEvent,
-                WarningBannerInfo::getMainButtonDestination, null);
-        final Integer sourceMetricsCategory = getInfo(powerAnomalyEvent,
-                WarningBannerInfo::getMainButtonSourceMetricsCategory, null);
-        final String preferenceHighlightKey = getInfo(powerAnomalyEvent,
-                WarningBannerInfo::getMainButtonSourceHighlightKey, null);
-
-        // Update card preference and main button fragment launcher
-        mCardPreference.setTitle(titleString);
-        mCardPreference.setIconResourceId(iconResId);
-        mCardPreference.setMainButtonStrokeColorResourceId(colorResId);
-        mCardPreference.setMainButtonLabel(mainBtnString);
-        mCardPreference.setDismissButtonLabel(dismissBtnString);
-
         // Set battery tips card listener
         mCardPreference.setOnConfirmListener(() -> {
+            mCardPreference.setVisible(false);
             if (mOnAnomalyConfirmListener != null) {
                 mOnAnomalyConfirmListener.onAnomalyConfirm();
-            } else if (!TextUtils.isEmpty(destinationClassName)) {
-                // Navigate to sub setting page
-                Bundle arguments = Bundle.EMPTY;
-                if (!TextUtils.isEmpty(preferenceHighlightKey)) {
-                    arguments = new Bundle(1);
-                    arguments.putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY,
-                            preferenceHighlightKey);
-                }
-                new SubSettingLauncher(mContext)
-                        .setDestination(destinationClassName)
-                        .setSourceMetricsCategory(sourceMetricsCategory)
-                        .setArguments(arguments)
-                        .launch();
+            } else if (mAnomalyEventWrapper.launchSubSetting()) {
+                mMetricsFeatureProvider.action(
+                        mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, eventId);
             }
-            mMetricsFeatureProvider.action(
-                    mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, eventId);
         });
         mCardPreference.setOnRejectListener(() -> {
+            mCardPreference.setVisible(false);
             if (mOnAnomalyRejectListener != null) {
                 mOnAnomalyRejectListener.onAnomalyReject();
             }
             // For anomaly events with same record key, dismissed until next time full charged.
-            final String dismissRecordKey = getDismissRecordKey(powerAnomalyEvent);
+            final String dismissRecordKey = mAnomalyEventWrapper.getDismissRecordKey();
             if (!TextUtils.isEmpty(dismissRecordKey)) {
                 DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, dismissRecordKey);
             }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java
index d51485a..4db4d3b 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java
@@ -53,6 +53,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 
 /** Controller for battery usage breakdown preference group. */
@@ -93,6 +94,14 @@
     BatteryDiffData mBatteryDiffData;
     @VisibleForTesting
     String mPercentLessThanThresholdText;
+    @VisibleForTesting
+    boolean mIsHighlightSlot;
+    @VisibleForTesting
+    String mAnomalyEventId;
+    @VisibleForTesting
+    String mAnomalyEntryKey;
+    @VisibleForTesting
+    String mAnomalyHintString;
 
     public BatteryUsageBreakdownController(
             Context context, Lifecycle lifecycle, SettingsActivity activity,
@@ -137,6 +146,12 @@
         return false;
     }
 
+    private String getActionKey(String packageName) {
+        final String actionKey = TextUtils.isEmpty(packageName)
+                ? PACKAGE_NAME_NONE : packageName;
+        return mAnomalyEventId == null ? actionKey : actionKey + "|"  + mAnomalyEventId;
+    }
+
     @Override
     public boolean handlePreferenceTreeClick(Preference preference) {
         if (!(preference instanceof PowerGaugePreference)) {
@@ -151,7 +166,7 @@
                         ? SettingsEnums.ACTION_BATTERY_USAGE_SYSTEM_ITEM
                         : SettingsEnums.ACTION_BATTERY_USAGE_APP_ITEM,
                 /* pageId */ SettingsEnums.OPEN_BATTERY_USAGE,
-                TextUtils.isEmpty(packageName) ? PACKAGE_NAME_NONE : packageName,
+                getActionKey(packageName),
                 (int) Math.round(diffEntry.getPercentage()));
         Log.d(TAG, String.format("handleClick() label=%s key=%s package=%s",
                 diffEntry.getAppLabel(), diffEntry.getKey(), packageName));
@@ -211,9 +226,23 @@
      *                            used when showing the footer.
      */
     void handleBatteryUsageUpdated(
-            BatteryDiffData slotUsageData, String slotTimestamp, boolean isAllUsageDataEmpty) {
+            BatteryDiffData slotUsageData, String slotTimestamp,
+            boolean isAllUsageDataEmpty, boolean isHighlightSlot,
+            Optional<AnomalyEventWrapper> optionalAnomalyEventWrapper) {
         mBatteryDiffData = slotUsageData;
         mSlotTimestamp = slotTimestamp;
+        mIsHighlightSlot = isHighlightSlot;
+
+        if (optionalAnomalyEventWrapper != null) {
+            final AnomalyEventWrapper anomalyEventWrapper =
+                    optionalAnomalyEventWrapper.orElse(null);
+            mAnomalyEventId = anomalyEventWrapper != null
+                    ? anomalyEventWrapper.getEventId() : null;
+            mAnomalyEntryKey = anomalyEventWrapper != null
+                    ? anomalyEventWrapper.getAnomalyEntryKey() : null;
+            mAnomalyHintString = anomalyEventWrapper != null
+                    ? anomalyEventWrapper.getAnomalyHintString() : null;
+        }
 
         showCategoryTitle(slotTimestamp);
         showSpinnerAndAppList();
@@ -278,15 +307,15 @@
                 continue;
             }
             final String prefKey = entry.getKey();
-            PowerGaugePreference pref = mAppListPreferenceGroup.findPreference(prefKey);
+            AnomalyAppItemPreference pref = mAppListPreferenceGroup.findPreference(prefKey);
             if (pref != null) {
                 isAdded = true;
             } else {
-                pref = (PowerGaugePreference) mPreferenceCache.get(prefKey);
+                pref = (AnomalyAppItemPreference) mPreferenceCache.get(prefKey);
             }
-            // Creates new innstance if cached preference is not found.
+            // Creates new instance if cached preference is not found.
             if (pref == null) {
-                pref = new PowerGaugePreference(mPrefContext);
+                pref = new AnomalyAppItemPreference(mPrefContext);
                 pref.setKey(prefKey);
                 mPreferenceCache.put(prefKey, pref);
             }
@@ -294,6 +323,10 @@
             pref.setTitle(appLabel);
             pref.setOrder(prefIndex);
             pref.setSingleLineTitle(true);
+            // Updates App item preference style
+            pref.setAnomalyHint(mIsHighlightSlot && mAnomalyEntryKey != null
+                    && mAnomalyEntryKey.equals(entry.getKey())
+                    ? mAnomalyHintString : null);
             // Sets the BatteryDiffEntry to preference for launching detailed page.
             pref.setBatteryDiffEntry(entry);
             pref.setSelectable(entry.validForRestriction());
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java
index 1cbf2a3..fd0f866 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java
@@ -116,8 +116,16 @@
         final Handler handler = new Handler(Looper.getMainLooper());
         final BatteryLevelData batteryLevelData = DataProcessManager.getBatteryLevelData(
                 context, handler, /*isFromPeriodJob=*/ true,
-                batteryDiffDataMap -> DatabaseUtils.sendBatteryUsageSlotData(context,
-                        ConvertUtils.convertToBatteryUsageSlotList(batteryDiffDataMap)));
+                batteryDiffDataMap -> {
+                    DatabaseUtils.sendBatteryUsageSlotData(context,
+                            ConvertUtils.convertToBatteryUsageSlotList(batteryDiffDataMap));
+                    if (batteryDiffDataMap.values().stream().anyMatch(data ->
+                            (!data.getAppDiffEntryList().isEmpty()
+                                    || !data.getSystemDiffEntryList().isEmpty()))) {
+                        FeatureFactory.getFeatureFactory().getPowerUsageFeatureProvider()
+                                .detectSettingsAnomaly(context, /* displayDrain= */ 0);
+                    }
+                });
         if (batteryLevelData == null) {
             Log.d(TAG, "preprocessBatteryUsageSlots() no new battery usage data.");
             return;
@@ -139,8 +147,6 @@
                 // No app usage data or battery diff data at this time.
                 loadAppUsageData(context);
                 preprocessBatteryUsageSlots(context);
-                FeatureFactory.getFeatureFactory().getPowerUsageFeatureProvider()
-                        .detectSettingsAnomaly(context, /* displayDrain= */ 0);
             }
             Log.d(TAG, String.format(
                     "loadUsageDataSafely() in %d/ms", System.currentTimeMillis() - start));
diff --git a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java
index 283b742..fb83302 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java
@@ -52,6 +52,7 @@
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.function.Predicate;
 
 /** Advanced power usage. */
 @SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
@@ -92,9 +93,9 @@
     @VisibleForTesting
     BatteryUsageBreakdownController mBatteryUsageBreakdownController;
     @VisibleForTesting
-    PowerAnomalyEvent mPowerAnomalyEvent;
-    @VisibleForTesting
     Optional<BatteryLevelData> mBatteryLevelData;
+    @VisibleForTesting
+    Optional<AnomalyEventWrapper> mHighlightEventWrapper;
 
     @Override
     public void onCreate(Bundle icicle) {
@@ -188,7 +189,7 @@
             mIsChartDataLoaded = true;
             mBatteryLevelData = null;
             mBatteryUsageMap = null;
-            mPowerAnomalyEvent = null;
+            mHighlightEventWrapper = null;
             restartLoader(LoaderIndex.BATTERY_LEVEL_DATA_LOADER, bundle,
                     mBatteryLevelDataLoaderCallbacks);
         }
@@ -239,8 +240,13 @@
             mScreenOnTimeController.handleSceenOnTimeUpdated(
                     slotUsageData.getScreenOnTime(), slotInformation);
         }
+        // Hide card tips if the related highlight slot was clicked.
+        if (isAppsAnomalyEventFocused()) {
+            mBatteryTipsController.acceptTipsCard();
+        }
         mBatteryUsageBreakdownController.handleBatteryUsageUpdated(
-                slotUsageData, slotInformation, isBatteryUsageMapNullOrEmpty());
+                slotUsageData, slotInformation, isBatteryUsageMapNullOrEmpty(),
+                isAppsAnomalyEventFocused(), mHighlightEventWrapper);
         Log.d(TAG, String.format("Battery usage list shows in %d millis",
                 System.currentTimeMillis() - mResumeTimestamp));
     }
@@ -261,49 +267,95 @@
             return;
         }
         Log.d(TAG, "anomalyEventList = " + anomalyEventList);
-        final PowerAnomalyEvent displayEvent =
-                getHighestScoreAnomalyEvent(getContext(), anomalyEventList);
-        onDisplayAnomalyEventUpdated(displayEvent);
+
+        final Set<String> dismissedPowerAnomalyKeys =
+                DatabaseUtils.getDismissedPowerAnomalyKeys(getContext());
+        Log.d(TAG, "dismissedPowerAnomalyKeys = " + dismissedPowerAnomalyKeys);
+
+        // Choose an app anomaly event with highest score to show highlight slot
+        final PowerAnomalyEvent highlightEvent =
+                getAnomalyEvent(anomalyEventList, PowerAnomalyEvent::hasWarningItemInfo);
+        // Choose an event never dismissed to show as card.
+        // If the slot is already highlighted, the tips card should be the corresponding app
+        // or settings anomaly event.
+        final PowerAnomalyEvent tipsCardEvent =
+                getAnomalyEvent(anomalyEventList,
+                        event -> !dismissedPowerAnomalyKeys.contains(event.getDismissRecordKey())
+                                && (event.equals(highlightEvent) || !event.hasWarningItemInfo()));
+        onDisplayAnomalyEventUpdated(tipsCardEvent, highlightEvent);
     }
 
     @VisibleForTesting
-    void onDisplayAnomalyEventUpdated(PowerAnomalyEvent event) {
-        mPowerAnomalyEvent = event;
+    void onDisplayAnomalyEventUpdated(
+            PowerAnomalyEvent tipsCardEvent, PowerAnomalyEvent highlightEvent) {
         if (mBatteryTipsController == null
                 || mBatteryChartPreferenceController == null
                 || mBatteryUsageBreakdownController == null) {
             return;
         }
 
+        final boolean isSameAnomalyEvent = (tipsCardEvent == highlightEvent);
         // Update battery tips card preference & behaviour
         mBatteryTipsController.setOnAnomalyConfirmListener(null);
         mBatteryTipsController.setOnAnomalyRejectListener(null);
-        mBatteryTipsController.handleBatteryTipsCardUpdated(mPowerAnomalyEvent);
+        final AnomalyEventWrapper tipsCardEventWrapper = (tipsCardEvent == null) ? null :
+                new AnomalyEventWrapper(getContext(), tipsCardEvent);
+        if (tipsCardEventWrapper != null) {
+            tipsCardEventWrapper.setRelatedBatteryDiffEntry(
+                    findRelatedBatteryDiffEntry(tipsCardEventWrapper));
+        }
+        mBatteryTipsController.handleBatteryTipsCardUpdated(
+                tipsCardEventWrapper, isSameAnomalyEvent);
 
         // Update highlight slot effect in battery chart view
         Pair<Integer, Integer> highlightSlotIndexPair = Pair.create(
                 BatteryChartViewModel.SELECTED_INDEX_INVALID,
                 BatteryChartViewModel.SELECTED_INDEX_INVALID);
-        if (mPowerAnomalyEvent != null && mPowerAnomalyEvent.hasWarningItemInfo()) {
-            final WarningItemInfo warningItemInfo = mPowerAnomalyEvent.getWarningItemInfo();
-            final Long startTimestamp = warningItemInfo.hasStartTimestamp()
-                    ? warningItemInfo.getStartTimestamp() : null;
-            final Long endTimestamp = warningItemInfo.hasEndTimestamp()
-                    ? warningItemInfo.getEndTimestamp() : null;
-            if (startTimestamp != null && endTimestamp != null) {
-                highlightSlotIndexPair = mBatteryLevelData.map(levelData ->
-                                levelData.getIndexByTimestamps(startTimestamp, endTimestamp))
-                        .orElse(highlightSlotIndexPair);
-                mBatteryTipsController.setOnAnomalyConfirmListener(
-                        mBatteryChartPreferenceController::selectHighlightSlotIndex);
-                mBatteryTipsController.setOnAnomalyRejectListener(
-                        () -> onDisplayAnomalyEventUpdated(null));
+        mHighlightEventWrapper = Optional.ofNullable(isSameAnomalyEvent ? tipsCardEventWrapper :
+                ((highlightEvent != null)
+                        ? new AnomalyEventWrapper(getContext(), highlightEvent) : null));
+        if (mBatteryLevelData != null && mBatteryLevelData.isPresent()
+                && mHighlightEventWrapper.isPresent()
+                && mHighlightEventWrapper.get().hasHighlightSlotPair(mBatteryLevelData.get())) {
+            highlightSlotIndexPair = mHighlightEventWrapper.get()
+                    .getHighlightSlotPair(mBatteryLevelData.get());
+            if (isSameAnomalyEvent) {
+                // For main button, focus on highlight slot when clicked
+                mBatteryTipsController.setOnAnomalyConfirmListener(() -> {
+                    mBatteryChartPreferenceController.selectHighlightSlotIndex();
+                    mBatteryTipsController.acceptTipsCard();
+                });
             }
         }
         mBatteryChartPreferenceController.onHighlightSlotIndexUpdate(
                 highlightSlotIndexPair.first, highlightSlotIndexPair.second);
     }
 
+    @VisibleForTesting
+    BatteryDiffEntry findRelatedBatteryDiffEntry(AnomalyEventWrapper eventWrapper) {
+        if (eventWrapper == null
+                || mBatteryLevelData == null || mBatteryLevelData.isEmpty()
+                || !eventWrapper.hasHighlightSlotPair(mBatteryLevelData.get())
+                || !eventWrapper.hasAnomalyEntryKey()
+                || mBatteryUsageMap == null) {
+            return null;
+        }
+        final Pair<Integer, Integer> highlightSlotIndexPair =
+                eventWrapper.getHighlightSlotPair(mBatteryLevelData.get());
+        final BatteryDiffData relatedDiffData = mBatteryUsageMap
+                .get(highlightSlotIndexPair.first).get(highlightSlotIndexPair.second);
+        final String anomalyEntryKey = eventWrapper.getAnomalyEntryKey();
+        if (relatedDiffData == null || anomalyEntryKey == null) {
+            return null;
+        }
+        for (BatteryDiffEntry entry : relatedDiffData.getAppDiffEntryList()) {
+            if (anomalyEntryKey.equals(entry.getKey())) {
+                return entry;
+            }
+        }
+        return null;
+    }
+
     private void setBatteryChartPreferenceController() {
         if (mHistPref != null && mBatteryChartPreferenceController != null) {
             mHistPref.setChartPreferenceController(mBatteryChartPreferenceController);
@@ -318,6 +370,11 @@
                 && allBatteryDiffData.getSystemDiffEntryList().isEmpty());
     }
 
+    private boolean isAppsAnomalyEventFocused() {
+        return mBatteryChartPreferenceController != null
+                && mBatteryChartPreferenceController.isHighlightSlotFocused();
+    }
+
     private void logScreenUsageTime() {
         final BatteryDiffData allBatteryDiffData = getAllBatteryDiffData(mBatteryUsageMap);
         if (allBatteryDiffData == null) {
@@ -338,25 +395,22 @@
     }
 
     @VisibleForTesting
-    static PowerAnomalyEvent getHighestScoreAnomalyEvent(
-            Context context, PowerAnomalyEventList anomalyEventList) {
+    static PowerAnomalyEvent getAnomalyEvent(
+            PowerAnomalyEventList anomalyEventList, Predicate<PowerAnomalyEvent> predicate) {
         if (anomalyEventList == null || anomalyEventList.getPowerAnomalyEventsCount() == 0) {
             return null;
         }
-        final Set<String> dismissedPowerAnomalyKeys =
-                DatabaseUtils.getDismissedPowerAnomalyKeys(context);
-        Log.d(TAG, "dismissedPowerAnomalyKeys = " + dismissedPowerAnomalyKeys);
 
-        final PowerAnomalyEvent highestScoreEvent = anomalyEventList.getPowerAnomalyEventsList()
+        final PowerAnomalyEvent filterAnomalyEvent = anomalyEventList.getPowerAnomalyEventsList()
                 .stream()
-                .filter(event -> !dismissedPowerAnomalyKeys.contains(
-                        BatteryTipsController.getDismissRecordKey(event)))
+                .filter(predicate)
                 .max(Comparator.comparing(PowerAnomalyEvent::getScore))
                 .orElse(null);
-        Log.d(TAG, "highestScoreAnomalyEvent = " + highestScoreEvent);
-        return highestScoreEvent;
+        Log.d(TAG, "filterAnomalyEvent = " + filterAnomalyEvent);
+        return filterAnomalyEvent;
     }
 
+
     private static BatteryDiffData getAllBatteryDiffData(
             Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap) {
         return batteryUsageMap == null ? null : batteryUsageMap
diff --git a/src/com/android/settings/fuelgauge/protos/power_anomaly_event.proto b/src/com/android/settings/fuelgauge/protos/power_anomaly_event.proto
index 99df215..caa9c35 100644
--- a/src/com/android/settings/fuelgauge/protos/power_anomaly_event.proto
+++ b/src/com/android/settings/fuelgauge/protos/power_anomaly_event.proto
@@ -18,6 +18,7 @@
     WarningBannerInfo warning_banner_info = 6;
     WarningItemInfo warning_item_info = 7;
   }
+  optional string dismiss_record_key = 8;
 }
 
 // NOTE: Please DO NOT delete enum items or change enum values. Use [deprecated = true] instead.
@@ -32,11 +33,16 @@
 // NOTE: Please DO NOT delete enum items or change enum values. Use [deprecated = true] instead.
 // The enum value will be used to decide pre-defined title and button labels.
 //
-// Next id: 3
+// Next id: 8
 enum PowerAnomalyKey{
   KEY_BRIGHTNESS = 0;
   KEY_SCREEN_TIMEOUT = 1;
-  KEY_APP = 2;
+  KEY_APP_TOTAL_ALWAYS_HIGH = 2;
+  KEY_APP_TOTAL_HIGHER_THAN_USUAL = 3;
+  KEY_APP_BACKGROUND_ALWAYS_HIGH = 4;
+  KEY_APP_BACKGROUND_HIGHER_THAN_USUAL = 5;
+  KEY_APP_FOREGROUND_ALWAYS_HIGH = 6;
+  KEY_APP_FOREGROUND_HIGHER_THAN_USUAL = 7;
 }
 
 message WarningBannerInfo {
@@ -60,6 +66,5 @@
   optional string description_string = 5;
   optional string main_button_string = 6;
   optional string cancel_button_string = 7;
-  optional string dismiss_record_key = 8;
-  optional string item_key = 9;
+  optional string item_key = 8;
 }
diff --git a/src/com/android/settings/network/SubscriptionUtil.java b/src/com/android/settings/network/SubscriptionUtil.java
index 0cd12fe..9974ba2 100644
--- a/src/com/android/settings/network/SubscriptionUtil.java
+++ b/src/com/android/settings/network/SubscriptionUtil.java
@@ -18,7 +18,6 @@
 
 import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
 import static android.telephony.UiccSlotInfo.CARD_STATE_INFO_PRESENT;
-
 import static com.android.internal.util.CollectionUtils.emptyIfNull;
 
 import android.annotation.Nullable;
@@ -56,6 +55,8 @@
 import java.util.Set;
 import java.util.function.Function;
 import java.util.function.Supplier;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -66,6 +67,9 @@
     static final String SUB_ID = "sub_id";
     @VisibleForTesting
     static final String KEY_UNIQUE_SUBSCRIPTION_DISPLAYNAME = "unique_subscription_displayName";
+    private static final String REGEX_DISPLAY_NAME_PREFIXES = "^";
+    private static final String REGEX_DISPLAY_NAME_SUFFIXES = "\\s[0-9]+";
+
     private static List<SubscriptionInfo> sAvailableResultsForTesting;
     private static List<SubscriptionInfo> sActiveResultsForTesting;
 
@@ -281,8 +285,8 @@
                             String displayName = i.getDisplayName().toString();
                             info.originalName =
                                     TextUtils.equals(displayName, PROFILE_GENERIC_DISPLAY_NAME)
-                                    ? context.getResources().getString(R.string.sim_card)
-                                    : displayName.trim();
+                                            ? context.getResources().getString(R.string.sim_card)
+                                            : displayName.trim();
                             return info;
                         });
 
@@ -298,12 +302,17 @@
         // If a display name is duplicate, append the final 4 digits of the phone number.
         // Creates a mapping of Subscription id to original display name + phone number display name
         final Supplier<Stream<DisplayInfo>> uniqueInfos = () -> originalInfos.get().map(info -> {
+            int infoSubId = info.subscriptionInfo.getSubscriptionId();
             String cachedDisplayName = getDisplayNameFromSharedPreference(
-                    context, info.subscriptionInfo.getSubscriptionId());
-            if (!TextUtils.isEmpty(cachedDisplayName)) {
-                Log.d(TAG, "use cached display name : " + cachedDisplayName);
+                    context, infoSubId);
+            if (isValidCachedDisplayName(cachedDisplayName, info.originalName.toString())) {
+                Log.d(TAG, "use cached display name : for subId : " + infoSubId
+                        + "cached display name : " + cachedDisplayName);
                 info.uniqueName = cachedDisplayName;
                 return info;
+            } else {
+                Log.d(TAG, "remove cached display name : " + infoSubId);
+                removeItemFromDisplayNameSharedPreference(context, infoSubId);
             }
 
             if (duplicateOriginalNames.contains(info.originalName)) {
@@ -320,9 +329,8 @@
                 } else {
                     info.uniqueName = info.originalName + " " + lastFourDigits;
                     Log.d(TAG, "Cache display name [" + info.uniqueName + "] for sub id "
-                            + info.subscriptionInfo.getSubscriptionId());
-                    saveDisplayNameToSharedPreference(
-                            context, info.subscriptionInfo.getSubscriptionId(), info.uniqueName);
+                            + infoSubId);
+                    saveDisplayNameToSharedPreference(context, infoSubId, info.uniqueName);
                 }
             } else {
                 info.uniqueName = info.originalName;
@@ -404,10 +412,27 @@
                 .apply();
     }
 
+    private static void removeItemFromDisplayNameSharedPreference(Context context, int subId) {
+        getDisplayNameSharedPreferenceEditor(context)
+                .remove(SUB_ID + subId)
+                .commit();
+    }
+
     private static String getDisplayNameFromSharedPreference(Context context, int subid) {
         return getDisplayNameSharedPreferences(context).getString(SUB_ID + subid, "");
     }
 
+    @VisibleForTesting
+    static boolean isValidCachedDisplayName(String cachedDisplayName, String originalName) {
+        if (TextUtils.isEmpty(cachedDisplayName) || TextUtils.isEmpty(originalName)) {
+            return false;
+        }
+        String regex = REGEX_DISPLAY_NAME_PREFIXES + originalName + REGEX_DISPLAY_NAME_SUFFIXES;
+        Pattern pattern = Pattern.compile(regex);
+        Matcher matcher = pattern.matcher(cachedDisplayName);
+        return matcher.matches();
+    }
+
     public static String getDisplayName(SubscriptionInfo info) {
         final CharSequence name = info.getDisplayName();
         if (name != null) {
diff --git a/src/com/android/settings/network/apn/ApnEditPageProvider.kt b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
new file mode 100644
index 0000000..756d90f
--- /dev/null
+++ b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
@@ -0,0 +1,202 @@
+/*
+ * 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.apn
+
+import android.net.Uri
+import android.os.Bundle
+import androidx.compose.foundation.layout.Column
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringArrayResource
+import androidx.compose.ui.res.stringResource
+import androidx.navigation.NavType
+import androidx.navigation.navArgument
+import com.android.settings.R
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.widget.editor.SettingsExposedDropdownMenuBox
+import com.android.settingslib.spa.widget.editor.SettingsOutlinedTextField
+import com.android.settingslib.spa.widget.preference.SwitchPreference
+import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+import java.util.Base64
+
+const val URI_TYPE = "uriType"
+const val URI = "uri"
+const val SUB_ID = "subId"
+const val MVNO_TYPE = "mvnoType"
+const val MVNO_MATCH_DATA = "mvnoMatchData"
+const val EDIT_URL = "editUrl"
+
+object ApnEditPageProvider : SettingsPageProvider {
+
+    override val name = "Apn"
+    const val TAG = "ApnPageProvider"
+
+    override val parameter = listOf(
+        navArgument(URI_TYPE) { type = NavType.StringType },
+        navArgument(URI) { type = NavType.StringType },
+        navArgument(SUB_ID) { type = NavType.IntType },
+        navArgument(MVNO_TYPE) { type = NavType.StringType },
+        navArgument(MVNO_MATCH_DATA) { type = NavType.StringType },
+    )
+
+    @Composable
+    override fun Page(arguments: Bundle?) {
+        val apnDataInit = ApnData()
+        val apnDataCur = remember {
+            mutableStateOf(apnDataInit)
+        }
+        ApnPage(apnDataCur)
+    }
+
+    fun getRoute(
+        uriType: String,
+        uri: Uri,
+        subId: Int,
+        mMvnoType: String,
+        mMvnoMatchData: String
+    ): String = "${name}/$uriType/${
+        Base64.getUrlEncoder().encodeToString(uri.toString().toByteArray())
+    }/$subId/$mMvnoType/$mMvnoMatchData"
+}
+
+@Composable
+fun ApnPage(apnDataCur: MutableState<ApnData>) {
+    var apnData by apnDataCur
+    val context = LocalContext.current
+    val authTypeOptions = stringArrayResource(R.array.apn_auth_entries).toList()
+    val apnProtocolOptions = stringArrayResource(R.array.apn_protocol_entries).toList()
+    val mvnoTypeOptions = stringArrayResource(R.array.mvno_type_entries).toList()
+
+    RegularScaffold(
+        title = stringResource(id = R.string.apn_edit),
+    ) {
+        Column() {
+            SettingsOutlinedTextField(
+                apnData.name,
+                stringResource(R.string.apn_name),
+                enabled = apnData.nameEnabled
+            ) { apnData = apnData.copy(name = it) }
+            SettingsOutlinedTextField(
+                apnData.apn,
+                stringResource(R.string.apn_apn),
+                enabled = apnData.apnEnabled
+            ) { apnData = apnData.copy(apn = it) }
+            SettingsOutlinedTextField(
+                apnData.proxy,
+                stringResource(R.string.apn_http_proxy),
+                enabled = apnData.proxyEnabled
+            ) { apnData = apnData.copy(proxy = it) }
+            SettingsOutlinedTextField(
+                apnData.port,
+                stringResource(R.string.apn_http_port),
+                enabled = apnData.portEnabled
+            ) { apnData = apnData.copy(port = it) }
+            SettingsOutlinedTextField(
+                apnData.userName,
+                stringResource(R.string.apn_user),
+                enabled = apnData.userNameEnabled
+            ) { apnData = apnData.copy(userName = it) }
+            // TODO: password
+            SettingsOutlinedTextField(
+                apnData.server,
+                stringResource(R.string.apn_server),
+                enabled = apnData.serverEnabled
+            ) { apnData = apnData.copy(server = it) }
+            SettingsOutlinedTextField(
+                apnData.mmsc,
+                stringResource(R.string.apn_mmsc),
+                enabled = apnData.mmscEnabled
+            ) { apnData = apnData.copy(mmsc = it) }
+            SettingsOutlinedTextField(
+                apnData.mmsProxy,
+                stringResource(R.string.apn_mms_proxy),
+                enabled = apnData.mmsProxyEnabled
+            ) { apnData = apnData.copy(mmsProxy = it) }
+            SettingsOutlinedTextField(
+                apnData.mmsPort,
+                stringResource(R.string.apn_mms_port),
+                enabled = apnData.mmsPortEnabled
+            ) { apnData = apnData.copy(mmsPort = it) }
+            SettingsOutlinedTextField(
+                apnData.mcc,
+                stringResource(R.string.apn_mcc),
+                enabled = apnData.mccEnabled
+            ) { apnData = apnData.copy(mcc = it) }
+            SettingsOutlinedTextField(
+                apnData.mnc,
+                stringResource(R.string.apn_mnc),
+                enabled = apnData.mncEnabled
+            ) { apnData = apnData.copy(mnc = it) }
+            SettingsExposedDropdownMenuBox(
+                label = stringResource(R.string.apn_auth_type),
+                options = authTypeOptions,
+                selectedOptionText =
+                authTypeOptions.getOrElse(apnData.authType) { "" },
+                enabled = apnData.authTypeEnabled,
+            ) { apnData = apnData.copy(authType = authTypeOptions.indexOf(it)) }
+            SettingsOutlinedTextField(
+                apnData.apnType,
+                stringResource(R.string.apn_type),
+                enabled = apnData.apnTypeEnabled
+            ) { apnData = apnData.copy(apn = it) } // TODO: updateApnType
+            SettingsExposedDropdownMenuBox(
+                stringResource(R.string.apn_protocol),
+                apnProtocolOptions,
+                apnData.apnProtocol,
+                apnData.apnProtocolEnabled
+            ) { apnData = apnData.copy(apnProtocol = it) }
+            SettingsExposedDropdownMenuBox(
+                stringResource(R.string.apn_roaming_protocol),
+                apnProtocolOptions,
+                apnData.apnRoaming,
+                apnData.apnRoamingEnabled
+            ) { apnData = apnData.copy(apnRoaming = it) }
+            SwitchPreference(
+                object : SwitchPreferenceModel {
+                    override val title = context.resources.getString(R.string.carrier_enabled)
+                    override val changeable =
+                        stateOf(apnData.apnEnableEnabled)
+                    override val checked =
+                        stateOf(apnData.apnEnable)
+                    override val onCheckedChange = { newChecked: Boolean ->
+                        apnData = apnData.copy(apnEnable = newChecked)
+                    }
+                }
+            )
+            SettingsExposedDropdownMenuBox(
+                stringResource(R.string.mvno_type),
+                mvnoTypeOptions,
+                apnData.mvnoType,
+                apnData.mvnoTypeEnabled
+            ) {
+                apnData = apnData.copy(mvnoType = it)
+            } // TODO: mvnoDescription
+            SettingsOutlinedTextField(
+                apnData.mvnoValue,
+                stringResource(R.string.mvno_match_data),
+                enabled = apnData.mvnoValueEnabled
+            ) { apnData = apnData.copy(mvnoValue = it) }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/network/apn/ApnPreference.java b/src/com/android/settings/network/apn/ApnPreference.java
index f277db0..07d371a 100755
--- a/src/com/android/settings/network/apn/ApnPreference.java
+++ b/src/com/android/settings/network/apn/ApnPreference.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.network.apn;
 
+import static com.android.settings.network.apn.ApnEditPageProviderKt.EDIT_URL;
+
 import android.content.ContentUris;
 import android.content.Context;
 import android.content.Intent;
@@ -34,15 +36,21 @@
 import androidx.preference.PreferenceViewHolder;
 
 import com.android.settings.R;
+import com.android.settings.flags.Flags;
+import com.android.settings.spa.SpaActivity;
 
 /**
  * Preference of APN UI entry
  */
-public class ApnPreference extends Preference implements CompoundButton.OnCheckedChangeListener,
-        View.OnClickListener {
-    private static final  String TAG = "ApnPreference";
-
+public class ApnPreference extends Preference
+        implements CompoundButton.OnCheckedChangeListener, View.OnClickListener {
+    private static final String TAG = "ApnPreference";
+    private static String sSelectedKey = null;
+    private static CompoundButton sCurrentChecked = null;
     private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    private boolean mProtectFromCheckedChange = false;
+    private boolean mSelectable = true;
+    private boolean mHideDetails = false;
 
     /**
      * Constructor of Preference
@@ -65,12 +73,6 @@
         this(context, null);
     }
 
-    private static String sSelectedKey = null;
-    private static CompoundButton sCurrentChecked = null;
-    private boolean mProtectFromCheckedChange = false;
-    private boolean mSelectable = true;
-    private boolean mHideDetails = false;
-
     @Override
     public void onBindViewHolder(PreferenceViewHolder view) {
         super.onBindViewHolder(view);
@@ -147,25 +149,32 @@
         }
 
         if (mHideDetails) {
-            Toast.makeText(context, context.getString(
-                    R.string.cannot_change_apn_toast), Toast.LENGTH_LONG).show();
+            Toast.makeText(context, context.getString(R.string.cannot_change_apn_toast),
+                    Toast.LENGTH_LONG).show();
             return;
         }
-        final Uri url = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI, pos);
-        final Intent editIntent = new Intent(Intent.ACTION_EDIT, url);
-        editIntent.putExtra(ApnSettings.SUB_ID, mSubId);
-        editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        context.startActivity(editIntent);
-    }
 
-    public void setSelectable(boolean selectable) {
-        mSelectable = selectable;
+        final Uri url = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI, pos);
+
+        if (Flags.newApnPageEnabled()) {
+            String route = ApnEditPageProvider.INSTANCE.getRoute(EDIT_URL, url, mSubId, "_", "_");
+            SpaActivity.startSpaActivity(context, route);
+        } else {
+            final Intent editIntent = new Intent(Intent.ACTION_EDIT, url);
+            editIntent.putExtra(ApnSettings.SUB_ID, mSubId);
+            editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            context.startActivity(editIntent);
+        }
     }
 
     public boolean getSelectable() {
         return mSelectable;
     }
 
+    public void setSelectable(boolean selectable) {
+        mSelectable = selectable;
+    }
+
     public void setSubId(int subId) {
         mSubId = subId;
     }
diff --git a/src/com/android/settings/network/apn/ApnStatus.kt b/src/com/android/settings/network/apn/ApnStatus.kt
new file mode 100644
index 0000000..8a2d613
--- /dev/null
+++ b/src/com/android/settings/network/apn/ApnStatus.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.network.apn
+
+import android.provider.Telephony
+import android.telephony.TelephonyManager
+
+data class ApnData(
+    val name: String = "",
+    val apn: String = "",
+    val proxy: String = "",
+    val port: String = "",
+    val userName: String = "",
+    val passWord: String = "",
+    val server: String = "",
+    val mmsc: String = "",
+    val mmsProxy: String = "",
+    val mmsPort: String = "",
+    val mcc: String = "",
+    val mnc: String = "",
+    val authType: Int = -1,
+    val apnType: String = "",
+    val apnProtocol: String = "",
+    val apnRoaming: String = "",
+    val apnEnable: Boolean = true,
+    val bearer: Int = 0,
+    val mvnoType: String = "",
+    var mvnoValue: String = "",
+    val bearerBitmask: Int = 0,
+    val edited: Int = Telephony.Carriers.USER_EDITED,
+    val userEditable: Int = 1,
+    val carrierId: Int = TelephonyManager.UNKNOWN_CARRIER_ID
+) {
+    var nameEnabled = true
+    var apnEnabled = true
+    var proxyEnabled = true
+    var portEnabled = true
+    var userNameEnabled = true
+    var passWordEnabled = true
+    var serverEnabled = true
+    var mmscEnabled = true
+    var mmsProxyEnabled = true
+    var mmsPortEnabled = true
+    var mccEnabled = true
+    var mncEnabled = true
+    var authTypeEnabled = true
+    var apnTypeEnabled = true
+    var apnProtocolEnabled = true
+    var apnRoamingEnabled = true
+    var apnEnableEnabled = true
+    var bearerEnabled = true
+    var mvnoTypeEnabled = true
+    var mvnoValueEnabled = false
+}
\ No newline at end of file
diff --git a/src/com/android/settings/panel/PanelSlicesAdapter.java b/src/com/android/settings/panel/PanelSlicesAdapter.java
index 57d8aa4..a2360d8 100644
--- a/src/com/android/settings/panel/PanelSlicesAdapter.java
+++ b/src/com/android/settings/panel/PanelSlicesAdapter.java
@@ -61,15 +61,12 @@
     private final List<LiveData<Slice>> mSliceLiveData;
     private final int mMetricsCategory;
     private final PanelFragment mPanelFragment;
-    private final String mSliceClickActionLabel;
 
     public PanelSlicesAdapter(
             PanelFragment fragment, Map<Uri, LiveData<Slice>> sliceLiveData, int metricsCategory) {
         mPanelFragment = fragment;
         mSliceLiveData = new ArrayList<>(sliceLiveData.values());
         mMetricsCategory = metricsCategory;
-        mSliceClickActionLabel = mPanelFragment.getContext().getString(
-                R.string.accessibility_action_label_panel_slice);
     }
 
     @NonNull
@@ -77,7 +74,7 @@
     public SliceRowViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
         final Context context = viewGroup.getContext();
         final LayoutInflater inflater = LayoutInflater.from(context);
-        View view;
+        final View view;
         if (viewType == PanelContent.VIEW_TYPE_SLIDER) {
             view = inflater.inflate(R.layout.panel_slice_slider_row, viewGroup, false);
         } else {
@@ -189,7 +186,6 @@
                     return;
                 }
                 sliceView.setTag(ROW_VIEW_TAG, new Object());
-
                 sliceView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
                     @Override
                     public void onLayoutChange(View v, int left, int top, int right, int bottom,
@@ -208,15 +204,17 @@
          * Update the action label for TalkBack to be more specific
          * @param view the RowView within the Slice
          */
-        private void setActionLabel(View view) {
+        @VisibleForTesting void setActionLabel(View view) {
             view.setAccessibilityDelegate(new View.AccessibilityDelegate() {
                 @Override
                 public void onInitializeAccessibilityNodeInfo(View host,
                         AccessibilityNodeInfo info) {
                     super.onInitializeAccessibilityNodeInfo(host, info);
+
                     AccessibilityNodeInfo.AccessibilityAction customClick =
-                            new AccessibilityNodeInfo.AccessibilityAction(
-                                    ACTION_CLICK, mSliceClickActionLabel);
+                            new AccessibilityNodeInfo.AccessibilityAction(ACTION_CLICK, host
+                                    .getResources()
+                                    .getString(R.string.accessibility_action_label_panel_slice));
                     info.addAction(customClick);
                 }
             });
diff --git a/src/com/android/settings/privatespace/PrivateSpaceDashboardFragment.java b/src/com/android/settings/privatespace/PrivateSpaceDashboardFragment.java
index 9e1d0d5..f72bcd9 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceDashboardFragment.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceDashboardFragment.java
@@ -18,7 +18,7 @@
 
 import android.app.settings.SettingsEnums;
 import android.content.Context;
-import android.util.FeatureFlagUtils;
+import android.os.Flags;
 
 import com.android.settings.R;
 import com.android.settings.dashboard.DashboardFragment;
@@ -56,13 +56,8 @@
             new BaseSearchIndexProvider(R.xml.private_space_settings) {
                 @Override
                 protected boolean isPageSearchEnabled(Context context) {
-                    // Temporary workaround for hiding PS Settings until the trunk stable feature
-                    // flag is available.
-                    // TODO(b/295516544): Remove this workaround when trunk stable feature flag is
-                    // available.
                     return SafetyCenterManagerWrapper.get().isEnabled(context)
-                            && FeatureFlagUtils.isEnabled(context,
-                            FeatureFlagUtils.SETTINGS_PRIVATE_SPACE_SETTINGS);
+                            && Flags.allowPrivateProfile();
                 }
 
                 @Override
diff --git a/src/com/android/settings/privatespace/PrivateSpaceSafetySource.java b/src/com/android/settings/privatespace/PrivateSpaceSafetySource.java
index b07c623..4910a7b 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceSafetySource.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceSafetySource.java
@@ -20,11 +20,11 @@
 import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.content.Intent;
+import android.os.Flags;
 import android.os.UserManager;
 import android.safetycenter.SafetyEvent;
 import android.safetycenter.SafetySourceData;
 import android.safetycenter.SafetySourceStatus;
-import android.util.FeatureFlagUtils;
 import android.util.Log;
 
 import com.android.settings.R;
@@ -35,7 +35,7 @@
 /** Private Space safety source for the Safety Center */
 public final class PrivateSpaceSafetySource {
     public static final String SAFETY_SOURCE_ID = "AndroidPrivateSpace";
-    private static final String TAG = "PrivateSpaceSafetySource";
+    private static final String TAG = "PrivateSpaceSafetySrc";
 
     private PrivateSpaceSafetySource() {}
 
@@ -54,10 +54,7 @@
             return;
         }
 
-        // Temporary workaround to help prevent the PS Settings showing up in droidfood builds.
-        // TODO(b/295516544): remove this when the trunk stable feature flag for PS is available.
-        if (!FeatureFlagUtils.isEnabled(context,
-                FeatureFlagUtils.SETTINGS_PRIVATE_SPACE_SETTINGS)) {
+        if (!Flags.allowPrivateProfile()) {
             // Setting null safetySourceData so that an old entry gets cleared out and this way
             // provide a response since SC always expects one on rescan.
             SafetyCenterManagerWrapper.get().setSafetySourceData(
diff --git a/src/com/android/settings/safetycenter/LockScreenSafetySource.java b/src/com/android/settings/safetycenter/LockScreenSafetySource.java
index 4e085c1..6960fc6 100644
--- a/src/com/android/settings/safetycenter/LockScreenSafetySource.java
+++ b/src/com/android/settings/safetycenter/LockScreenSafetySource.java
@@ -46,11 +46,11 @@
     private static final int REQUEST_CODE_SCREEN_LOCK = 1;
     private static final int REQUEST_CODE_SCREEN_LOCK_SETTINGS = 2;
 
-    private LockScreenSafetySource() {
-    }
+    private LockScreenSafetySource() {}
 
     /** Sets lock screen safety data for Safety Center. */
-    public static void setSafetySourceData(Context context,
+    public static void setSafetySourceData(
+            Context context,
             ScreenLockPreferenceDetailsUtils screenLockPreferenceDetailsUtils,
             SafetyEvent safetyEvent) {
         if (!SafetyCenterManagerWrapper.get().isEnabled(context)) {
@@ -63,59 +63,61 @@
         }
 
         if (!screenLockPreferenceDetailsUtils.isAvailable()) {
-            SafetyCenterManagerWrapper.get().setSafetySourceData(
-                    context,
-                    SAFETY_SOURCE_ID,
-                    /* safetySourceData= */ null,
-                    safetyEvent
-            );
+            SafetyCenterManagerWrapper.get()
+                    .setSafetySourceData(
+                            context, SAFETY_SOURCE_ID, /* safetySourceData= */ null, safetyEvent);
             return;
         }
 
         final int userId = UserHandle.myUserId();
-        final RestrictedLockUtils.EnforcedAdmin admin = RestrictedLockUtilsInternal
-                .checkIfPasswordQualityIsSet(context, userId);
-        final PendingIntent pendingIntent = createPendingIntent(context,
-                screenLockPreferenceDetailsUtils.getLaunchChooseLockGenericFragmentIntent(
-                        SettingsEnums.SAFETY_CENTER), REQUEST_CODE_SCREEN_LOCK);
-        final IconAction gearMenuIconAction = createGearMenuIconAction(context,
-                screenLockPreferenceDetailsUtils);
-        final boolean enabled =
+        final RestrictedLockUtils.EnforcedAdmin admin =
+                RestrictedLockUtilsInternal.checkIfPasswordQualityIsSet(context, userId);
+        final PendingIntent pendingIntent =
+                createPendingIntent(
+                        context,
+                        screenLockPreferenceDetailsUtils.getLaunchChooseLockGenericFragmentIntent(
+                                SettingsEnums.SAFETY_CENTER),
+                        REQUEST_CODE_SCREEN_LOCK);
+        final IconAction gearMenuIconAction =
+                createGearMenuIconAction(context, screenLockPreferenceDetailsUtils);
+        final boolean lockScreenAllowedByAdmin =
                 !screenLockPreferenceDetailsUtils.isPasswordQualityManaged(userId, admin);
         final boolean isLockPatternSecure = screenLockPreferenceDetailsUtils.isLockPatternSecure();
-        final int severityLevel = enabled
-                ? isLockPatternSecure
-                        ? SafetySourceData.SEVERITY_LEVEL_INFORMATION
-                        : SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION
-                : SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED;
+        final int severityLevel =
+                lockScreenAllowedByAdmin
+                        ? isLockPatternSecure
+                                ? SafetySourceData.SEVERITY_LEVEL_INFORMATION
+                                : SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION
+                        : SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED;
 
-
-        final SafetySourceStatus status = new SafetySourceStatus.Builder(
-                context.getString(R.string.unlock_set_unlock_launch_picker_title),
-                screenLockPreferenceDetailsUtils.getSummary(UserHandle.myUserId()),
-                severityLevel)
-                .setPendingIntent(pendingIntent)
-                .setEnabled(enabled)
-                .setIconAction(gearMenuIconAction).build();
+        final SafetySourceStatus status =
+                new SafetySourceStatus.Builder(
+                                context.getString(R.string.unlock_set_unlock_launch_picker_title),
+                                lockScreenAllowedByAdmin
+                                        ? screenLockPreferenceDetailsUtils.getSummary(
+                                                UserHandle.myUserId())
+                                        : context.getString(R.string.disabled_by_policy_title),
+                                severityLevel)
+                        .setPendingIntent(lockScreenAllowedByAdmin ? pendingIntent : null)
+                        .setEnabled(lockScreenAllowedByAdmin)
+                        .setIconAction(lockScreenAllowedByAdmin ? gearMenuIconAction : null)
+                        .build();
         final SafetySourceData.Builder safetySourceDataBuilder =
                 new SafetySourceData.Builder().setStatus(status);
-        if (enabled && !isLockPatternSecure) {
+        if (lockScreenAllowedByAdmin && !isLockPatternSecure) {
             safetySourceDataBuilder.addIssue(createNoScreenLockIssue(context, pendingIntent));
         }
         final SafetySourceData safetySourceData = safetySourceDataBuilder.build();
 
-        SafetyCenterManagerWrapper.get().setSafetySourceData(
-                context,
-                SAFETY_SOURCE_ID,
-                safetySourceData,
-                safetyEvent
-        );
+        SafetyCenterManagerWrapper.get()
+                .setSafetySourceData(context, SAFETY_SOURCE_ID, safetySourceData, safetyEvent);
     }
 
     /** Notifies Safety Center of a change in lock screen settings. */
     public static void onLockScreenChange(Context context) {
         setSafetySourceData(
-                context, new ScreenLockPreferenceDetailsUtils(context),
+                context,
+                new ScreenLockPreferenceDetailsUtils(context),
                 new SafetyEvent.Builder(SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build());
 
         // Also send refreshed safety center data for biometrics, since changing lockscreen settings
@@ -123,45 +125,45 @@
         BiometricsSafetySource.onBiometricsChanged(context);
     }
 
-    private static IconAction createGearMenuIconAction(Context context,
-            ScreenLockPreferenceDetailsUtils screenLockPreferenceDetailsUtils) {
-        return screenLockPreferenceDetailsUtils.shouldShowGearMenu() ? new IconAction(
-                IconAction.ICON_TYPE_GEAR,
-                createPendingIntent(context,
-                        screenLockPreferenceDetailsUtils.getLaunchScreenLockSettingsIntent(
-                                SettingsEnums.SAFETY_CENTER),
-                        REQUEST_CODE_SCREEN_LOCK_SETTINGS))
+    private static IconAction createGearMenuIconAction(
+            Context context, ScreenLockPreferenceDetailsUtils screenLockPreferenceDetailsUtils) {
+        return screenLockPreferenceDetailsUtils.shouldShowGearMenu()
+                ? new IconAction(
+                        IconAction.ICON_TYPE_GEAR,
+                        createPendingIntent(
+                                context,
+                                screenLockPreferenceDetailsUtils.getLaunchScreenLockSettingsIntent(
+                                        SettingsEnums.SAFETY_CENTER),
+                                REQUEST_CODE_SCREEN_LOCK_SETTINGS))
                 : null;
     }
 
-    private static PendingIntent createPendingIntent(Context context, Intent intent,
-            int requestCode) {
-        return PendingIntent
-                .getActivity(
-                        context,
-                        requestCode,
-                        intent,
-                        PendingIntent.FLAG_IMMUTABLE);
+    private static PendingIntent createPendingIntent(
+            Context context, Intent intent, int requestCode) {
+        return PendingIntent.getActivity(
+                context, requestCode, intent, PendingIntent.FLAG_IMMUTABLE);
     }
 
-    private static SafetySourceIssue createNoScreenLockIssue(Context context,
-            PendingIntent pendingIntent) {
-        final SafetySourceIssue.Action action = new SafetySourceIssue.Action.Builder(
-                SET_SCREEN_LOCK_ACTION_ID,
-                context.getString(R.string.no_screen_lock_issue_action_label),
-                pendingIntent).build();
+    private static SafetySourceIssue createNoScreenLockIssue(
+            Context context, PendingIntent pendingIntent) {
+        final SafetySourceIssue.Action action =
+                new SafetySourceIssue.Action.Builder(
+                                SET_SCREEN_LOCK_ACTION_ID,
+                                context.getString(R.string.no_screen_lock_issue_action_label),
+                                pendingIntent)
+                        .build();
         // Custom notification deliberately has zero actions
         final SafetySourceIssue.Notification customNotification =
-            new SafetySourceIssue.Notification.Builder(
-                context.getString(R.string.no_screen_lock_issue_notification_title),
-                context.getString(R.string.no_screen_lock_issue_notification_text))
-                .build();
+                new SafetySourceIssue.Notification.Builder(
+                                context.getString(R.string.no_screen_lock_issue_notification_title),
+                                context.getString(R.string.no_screen_lock_issue_notification_text))
+                        .build();
         return new SafetySourceIssue.Builder(
-                NO_SCREEN_LOCK_ISSUE_ID,
-                context.getString(R.string.no_screen_lock_issue_title),
-                context.getString(R.string.no_screen_lock_issue_summary),
-                SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION,
-                NO_SCREEN_LOCK_ISSUE_TYPE_ID)
+                        NO_SCREEN_LOCK_ISSUE_ID,
+                        context.getString(R.string.no_screen_lock_issue_title),
+                        context.getString(R.string.no_screen_lock_issue_summary),
+                        SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION,
+                        NO_SCREEN_LOCK_ISSUE_TYPE_ID)
                 .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DEVICE)
                 .addAction(action)
                 .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_MANUAL)
diff --git a/src/com/android/settings/spa/SettingsSpaEnvironment.kt b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
index f08a2de..40cc9a2 100644
--- a/src/com/android/settings/spa/SettingsSpaEnvironment.kt
+++ b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.util.FeatureFlagUtils
+import com.android.settings.network.apn.ApnEditPageProvider
 import com.android.settings.spa.about.AboutPhonePageProvider
 import com.android.settings.spa.app.AllAppListPageProvider
 import com.android.settings.spa.app.AppsMainPageProvider
@@ -34,8 +35,8 @@
 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.WifiControlAppListProvider
 import com.android.settings.spa.app.specialaccess.UseFullScreenIntentAppListProvider
+import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider
 import com.android.settings.spa.app.storage.StorageAppListPageProvider
 import com.android.settings.spa.core.instrumentation.SpaLogProvider
 import com.android.settings.spa.development.UsageStatsPageProvider
@@ -95,6 +96,7 @@
                 AboutPhonePageProvider,
                 StorageAppListPageProvider.Apps,
                 StorageAppListPageProvider.Games,
+                ApnEditPageProvider,
                 ) + togglePermissionAppListTemplate.createPageProviders(),
             rootPages = listOf(
                 HomePageProvider.createSettingsPage()
diff --git a/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt b/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt
index f403743..a15401c 100644
--- a/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt
+++ b/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt
@@ -115,7 +115,8 @@
                 override val resId = R.raw.user_aspect_ratio_education
                 override val resourceType = ResourceType.LOTTIE
             })
-        }
+        },
+        noMoreOptions = true,
     )
 }
 
diff --git a/src/com/android/settings/system/SystemDashboardFragment.java b/src/com/android/settings/system/SystemDashboardFragment.java
index 79c5b9f..678b675 100644
--- a/src/com/android/settings/system/SystemDashboardFragment.java
+++ b/src/com/android/settings/system/SystemDashboardFragment.java
@@ -16,9 +16,7 @@
 package com.android.settings.system;
 
 import android.app.settings.SettingsEnums;
-import android.content.Context;
 import android.os.Bundle;
-import android.provider.SearchIndexableResource;
 
 import androidx.preference.Preference;
 import androidx.preference.PreferenceGroup;
@@ -29,9 +27,6 @@
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settingslib.search.SearchIndexable;
 
-import java.util.Arrays;
-import java.util.List;
-
 @SearchIndexable
 public class SystemDashboardFragment extends DashboardFragment {
 
@@ -85,13 +80,5 @@
      * For Search.
      */
     public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
-            new BaseSearchIndexProvider() {
-                @Override
-                public List<SearchIndexableResource> getXmlResourcesToIndex(
-                        Context context, boolean enabled) {
-                    final SearchIndexableResource sir = new SearchIndexableResource(context);
-                    sir.xmlResId = R.xml.system_dashboard_fragment;
-                    return Arrays.asList(sir);
-                }
-            };
-}
\ No newline at end of file
+            new BaseSearchIndexProvider(R.xml.system_dashboard_fragment);
+}
diff --git a/src/com/android/settings/system/SystemUpdateManagerExt.kt b/src/com/android/settings/system/SystemUpdateManagerExt.kt
new file mode 100644
index 0000000..8ddf174
--- /dev/null
+++ b/src/com/android/settings/system/SystemUpdateManagerExt.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.system
+
+import android.content.Context
+import android.os.Bundle
+import android.os.SystemUpdateManager
+import android.util.Log
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+private const val TAG = "SystemUpdateManagerExt"
+
+/**
+ * Gets the system update status.
+ *
+ * Note: [SystemUpdateManager.retrieveSystemUpdateInfo] must be called on worker thread to avoid
+ * StrictMode violation.
+ */
+suspend fun Context.getSystemUpdateInfo(): Bundle? = withContext(Dispatchers.Default) {
+    val updateManager = getSystemService(SystemUpdateManager::class.java)!!
+    try {
+        updateManager.retrieveSystemUpdateInfo()
+    } catch (e: Exception) {
+        Log.w(TAG, "Error getting system update info.")
+        null
+    }
+}
diff --git a/src/com/android/settings/system/SystemUpdatePreferenceController.java b/src/com/android/settings/system/SystemUpdatePreferenceController.java
deleted file mode 100644
index b2a22ff..0000000
--- a/src/com/android/settings/system/SystemUpdatePreferenceController.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2016 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.system;
-
-import static android.content.Context.CARRIER_CONFIG_SERVICE;
-import static android.content.Context.SYSTEM_UPDATE_SERVICE;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.PersistableBundle;
-import android.os.SystemUpdateManager;
-import android.os.UserManager;
-import android.telephony.CarrierConfigManager;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
-
-import com.android.settings.R;
-import com.android.settings.Utils;
-import com.android.settings.core.BasePreferenceController;
-
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.FutureTask;
-
-public class SystemUpdatePreferenceController extends BasePreferenceController {
-
-    private static final String TAG = "SysUpdatePrefContr";
-
-    private static final String KEY_SYSTEM_UPDATE_SETTINGS = "system_update_settings";
-
-    private final UserManager mUm;
-    private final SystemUpdateManager mUpdateManager;
-
-    public SystemUpdatePreferenceController(Context context) {
-        super(context, KEY_SYSTEM_UPDATE_SETTINGS);
-        mUm = UserManager.get(context);
-        mUpdateManager = (SystemUpdateManager) context.getSystemService(SYSTEM_UPDATE_SERVICE);
-    }
-
-    @Override
-    public int getAvailabilityStatus() {
-        return mContext.getResources().getBoolean(R.bool.config_show_system_update_settings)
-                && mUm.isAdminUser()
-                ? AVAILABLE
-                : UNSUPPORTED_ON_DEVICE;
-    }
-
-    @Override
-    public void displayPreference(PreferenceScreen screen) {
-        super.displayPreference(screen);
-        if (isAvailable()) {
-            Utils.updatePreferenceToSpecificActivityOrRemove(mContext, screen,
-                    getPreferenceKey(),
-                    Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY);
-        }
-    }
-
-    @Override
-    public boolean handlePreferenceTreeClick(Preference preference) {
-        if (TextUtils.equals(getPreferenceKey(), preference.getKey())) {
-            CarrierConfigManager configManager =
-                    (CarrierConfigManager) mContext.getSystemService(CARRIER_CONFIG_SERVICE);
-            PersistableBundle b = configManager.getConfig();
-            if (b != null && b.getBoolean(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_BOOL)) {
-                ciActionOnSysUpdate(b);
-            }
-        }
-        // always return false here because this handler does not want to block other handlers.
-        return false;
-    }
-
-    @Override
-    public CharSequence getSummary() {
-        CharSequence summary = mContext.getString(R.string.android_version_summary,
-                Build.VERSION.RELEASE_OR_PREVIEW_DISPLAY);
-        final FutureTask<Bundle> bundleFutureTask = new FutureTask<>(
-                // Put the API call in a future to avoid StrictMode violation.
-                () -> mUpdateManager.retrieveSystemUpdateInfo());
-        final Bundle updateInfo;
-        try {
-            bundleFutureTask.run();
-            updateInfo = bundleFutureTask.get();
-        } catch (InterruptedException | ExecutionException e) {
-            Log.w(TAG, "Error getting system update info.");
-            return summary;
-        }
-        switch (updateInfo.getInt(SystemUpdateManager.KEY_STATUS)) {
-            case SystemUpdateManager.STATUS_WAITING_DOWNLOAD:
-            case SystemUpdateManager.STATUS_IN_PROGRESS:
-            case SystemUpdateManager.STATUS_WAITING_INSTALL:
-            case SystemUpdateManager.STATUS_WAITING_REBOOT:
-                summary = mContext.getText(R.string.android_version_pending_update_summary);
-                break;
-            case SystemUpdateManager.STATUS_UNKNOWN:
-                Log.d(TAG, "Update statue unknown");
-                // fall through to next branch
-            case SystemUpdateManager.STATUS_IDLE:
-                final String version = updateInfo.getString(SystemUpdateManager.KEY_TITLE);
-                if (!TextUtils.isEmpty(version)) {
-                    summary = mContext.getString(R.string.android_version_summary, version);
-                }
-                break;
-        }
-        return summary;
-    }
-
-    /**
-     * Trigger client initiated action (send intent) on system update
-     */
-    private void ciActionOnSysUpdate(PersistableBundle b) {
-        String intentStr = b.getString(CarrierConfigManager.
-                KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING);
-        if (!TextUtils.isEmpty(intentStr)) {
-            String extra = b.getString(CarrierConfigManager.
-                    KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING);
-            String extraVal = b.getString(CarrierConfigManager.
-                    KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING);
-
-            Intent intent = new Intent(intentStr);
-            if (!TextUtils.isEmpty(extra)) {
-                intent.putExtra(extra, extraVal);
-            }
-            Log.d(TAG, "ciActionOnSysUpdate: broadcasting intent " + intentStr +
-                    " with extra " + extra + ", " + extraVal);
-            intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
-            mContext.getApplicationContext().sendBroadcast(intent);
-        }
-    }
-}
diff --git a/src/com/android/settings/system/SystemUpdatePreferenceController.kt b/src/com/android/settings/system/SystemUpdatePreferenceController.kt
new file mode 100644
index 0000000..01df065
--- /dev/null
+++ b/src/com/android/settings/system/SystemUpdatePreferenceController.kt
@@ -0,0 +1,137 @@
+/*
+ * 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.system
+
+import android.content.Context
+import android.content.Intent
+import android.os.Build
+import android.os.PersistableBundle
+import android.os.SystemUpdateManager
+import android.os.UserManager
+import android.telephony.CarrierConfigManager
+import android.util.Log
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import androidx.preference.Preference
+import androidx.preference.PreferenceScreen
+import com.android.settings.R
+import com.android.settings.Utils
+import com.android.settings.core.BasePreferenceController
+import com.android.settingslib.spaprivileged.framework.common.userManager
+import kotlinx.coroutines.launch
+
+open class SystemUpdatePreferenceController(context: Context, preferenceKey: String) :
+    BasePreferenceController(context, preferenceKey) {
+    private val userManager: UserManager = context.userManager
+    private lateinit var preference: Preference
+
+    override fun getAvailabilityStatus() =
+        if (mContext.resources.getBoolean(R.bool.config_show_system_update_settings) &&
+            userManager.isAdminUser
+        ) AVAILABLE else UNSUPPORTED_ON_DEVICE
+
+    override fun displayPreference(screen: PreferenceScreen) {
+        super.displayPreference(screen)
+        preference = screen.findPreference(preferenceKey)!!
+        if (isAvailable) {
+            Utils.updatePreferenceToSpecificActivityOrRemove(
+                mContext,
+                screen,
+                preferenceKey,
+                Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY,
+            )
+        }
+    }
+
+    override fun handlePreferenceTreeClick(preference: Preference): Boolean {
+        if (preferenceKey == preference.key) {
+            val configManager = mContext.getSystemService(CarrierConfigManager::class.java)!!
+            configManager.getConfig(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_BOOL)?.let {
+                if (it.getBoolean(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_BOOL)) {
+                    ciActionOnSysUpdate(it)
+                }
+            }
+        }
+        // always return false here because this handler does not want to block other handlers.
+        return false
+    }
+
+    override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
+        viewLifecycleOwner.lifecycleScope.launch {
+            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
+                preference.summary = calculateSummary()
+            }
+        }
+    }
+
+    private suspend fun calculateSummary(): String {
+        val updateInfo = mContext.getSystemUpdateInfo() ?: return getReleaseVersionSummary()
+
+        val status = updateInfo.getInt(SystemUpdateManager.KEY_STATUS)
+        if (status == SystemUpdateManager.STATUS_UNKNOWN) {
+            Log.d(TAG, "Update statue unknown")
+        }
+        when (status) {
+            SystemUpdateManager.STATUS_WAITING_DOWNLOAD,
+            SystemUpdateManager.STATUS_IN_PROGRESS,
+            SystemUpdateManager.STATUS_WAITING_INSTALL,
+            SystemUpdateManager.STATUS_WAITING_REBOOT -> {
+                return mContext.getString(R.string.android_version_pending_update_summary)
+            }
+
+            SystemUpdateManager.STATUS_IDLE,
+            SystemUpdateManager.STATUS_UNKNOWN -> {
+                val version = updateInfo.getString(SystemUpdateManager.KEY_TITLE)
+                if (!version.isNullOrEmpty()) {
+                    return mContext.getString(R.string.android_version_summary, version)
+                }
+            }
+        }
+        return getReleaseVersionSummary()
+    }
+
+    private fun getReleaseVersionSummary(): String = mContext.getString(
+        R.string.android_version_summary,
+        Build.VERSION.RELEASE_OR_PREVIEW_DISPLAY,
+    )
+
+    /**
+     * Trigger client initiated action (send intent) on system update
+     */
+    private fun ciActionOnSysUpdate(b: PersistableBundle) {
+        val intentStr = b.getString(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING)
+        if (intentStr.isNullOrEmpty()) return
+        val extra = b.getString(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING)
+        val extraVal =
+            b.getString(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING)
+        Log.d(
+            TAG,
+            "ciActionOnSysUpdate: broadcasting intent $intentStr with extra $extra, $extraVal"
+        )
+        val intent = Intent(intentStr).apply {
+            if (!extra.isNullOrEmpty()) putExtra(extra, extraVal)
+            addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND)
+        }
+        mContext.applicationContext.sendBroadcast(intent)
+    }
+
+    companion object {
+        private const val TAG = "SysUpdatePrefContr"
+    }
+}
diff --git a/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java b/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java
index a64ec89..a93a986 100644
--- a/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java
+++ b/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java
@@ -122,6 +122,9 @@
                 && TextUtils.equals(mHighlightKey, getItem(position).getKey()))) {
             // This position should be highlighted. If it's highlighted before - skip animation.
             addHighlightBackground(holder, !mFadeInAnimated);
+            if (v != null) {
+                v.requestAccessibilityFocus();
+            }
         } else if (Boolean.TRUE.equals(v.getTag(R.id.preference_highlighted))) {
             // View with highlight is reused for a view that should not have highlight
             removeHighlightBackground(holder, false /* animate */);
diff --git a/tests/robotests/res/values-mcc999/config.xml b/tests/robotests/res/values-mcc999/config.xml
index 82edea5..a5767e4 100644
--- a/tests/robotests/res/values-mcc999/config.xml
+++ b/tests/robotests/res/values-mcc999/config.xml
@@ -53,7 +53,6 @@
     <bool name="config_show_pointer_speed">false</bool>
     <bool name="config_show_vibrate_input_devices">false</bool>
     <bool name="config_show_reset_dashboard">false</bool>
-    <bool name="config_show_system_update_settings">false</bool>
     <bool name="config_show_device_model">false</bool>
     <bool name="config_show_top_level_accessibility">false</bool>
     <bool name="config_show_top_level_battery">false</bool>
diff --git a/tests/robotests/src/com/android/settings/accessibility/HearingAidUtilsTest.java b/tests/robotests/src/com/android/settings/accessibility/HearingAidUtilsTest.java
index 6d45af2..9368ec8 100644
--- a/tests/robotests/src/com/android/settings/accessibility/HearingAidUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/HearingAidUtilsTest.java
@@ -156,14 +156,13 @@
     }
 
     @Test
-    public void launchHearingAidPairingDialog_deviceSupportsCsip_csipEnabled_noDialog() {
+    public void launchHearingAidPairingDialog_deviceSupportsCsip_noDialog() {
         when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
         when(mCachedBluetoothDevice.getDeviceMode()).thenReturn(
                 HearingAidInfo.DeviceMode.MODE_BINAURAL);
         when(mCachedBluetoothDevice.getDeviceSide()).thenReturn(
                 HearingAidInfo.DeviceSide.SIDE_LEFT);
         makeDeviceSupportCsip();
-        makeDeviceEnableCsip(true);
 
         HearingAidUtils.launchHearingAidPairingDialog(mFragmentManager, mCachedBluetoothDevice,
                 TEST_LAUNCH_PAGE);
@@ -174,24 +173,6 @@
     }
 
     @Test
-    public void launchHearingAidPairingDialog_deviceSupportsCsip_csipDisabled_dialogShown() {
-        when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
-        when(mCachedBluetoothDevice.getDeviceMode()).thenReturn(
-                HearingAidInfo.DeviceMode.MODE_BINAURAL);
-        when(mCachedBluetoothDevice.getDeviceSide()).thenReturn(
-                HearingAidInfo.DeviceSide.SIDE_LEFT);
-        makeDeviceSupportCsip();
-        makeDeviceEnableCsip(false);
-
-        HearingAidUtils.launchHearingAidPairingDialog(mFragmentManager, mCachedBluetoothDevice,
-                TEST_LAUNCH_PAGE);
-
-        shadowMainLooper().idle();
-        final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
-        assertThat(dialog.isShowing()).isTrue();
-    }
-
-    @Test
     public void launchHearingAidPairingDialog_dialogShown() {
         when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
         when(mCachedBluetoothDevice.getDeviceMode()).thenReturn(
@@ -213,11 +194,6 @@
         when(mCachedBluetoothDevice.getProfiles()).thenReturn(uuids);
     }
 
-    private void makeDeviceEnableCsip(boolean enabled) {
-        when(mCsipSetCoordinatorProfile.isEnabled(mCachedBluetoothDevice.getDevice()))
-                .thenReturn(enabled);
-    }
-
     private void setupEnvironment() {
         ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
diff --git a/tests/robotests/src/com/android/settings/accessibility/ScreenFlashNotificationColorDialogFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ScreenFlashNotificationColorDialogFragmentTest.java
index 04b48c0..f3fa69d 100644
--- a/tests/robotests/src/com/android/settings/accessibility/ScreenFlashNotificationColorDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ScreenFlashNotificationColorDialogFragmentTest.java
@@ -39,21 +39,21 @@
 import android.graphics.Color;
 
 import androidx.appcompat.app.AlertDialog;
-import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.testing.FragmentScenario;
+import androidx.lifecycle.Lifecycle;
 
 import com.android.settings.R;
 import com.android.settings.testutils.FakeTimer;
 
+import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.Robolectric;
 import org.robolectric.RobolectricTestRunner;
-import org.robolectric.shadows.ShadowContextWrapper;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowLooper;
 import org.robolectric.util.ReflectionHelpers;
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Timer;
 import java.util.function.Consumer;
@@ -61,7 +61,7 @@
 @RunWith(RobolectricTestRunner.class)
 public class ScreenFlashNotificationColorDialogFragmentTest {
 
-    private ShadowContextWrapper mShadowContextWrapper;
+    private FragmentScenario<TestScreenFlashNotificationColorDialogFragment> mFragmentScenario;
     private ScreenFlashNotificationColorDialogFragment mDialogFragment;
     private AlertDialog mAlertDialog;
     private ColorSelectorLayout mColorSelectorLayout;
@@ -69,35 +69,32 @@
 
     @Before
     public void setUp() {
-        FragmentActivity fragmentActivity = Robolectric.setupActivity(FragmentActivity.class);
-        mShadowContextWrapper = shadowOf(fragmentActivity);
-
         mCurrentColor = ROSE.mColorInt;
-        mDialogFragment = createFragment();
+        mFragmentScenario = FragmentScenario.launch(
+                TestScreenFlashNotificationColorDialogFragment.class,
+                /* fragmentArgs= */ null,
+                R.style.Theme_AlertDialog_SettingsLib,
+                Lifecycle.State.INITIALIZED);
+        setupFragment();
+    }
 
-        mDialogFragment.show(fragmentActivity.getSupportFragmentManager(), "test");
-
-        mAlertDialog = (AlertDialog) mDialogFragment.getDialog();
-        if (mAlertDialog != null) {
-            mColorSelectorLayout = mAlertDialog.findViewById(R.id.color_selector_preference);
-        }
+    @After
+    public void cleanUp() {
+        mFragmentScenario.close();
     }
 
     @Test
-    @Ignore
     public void test_assertShow() {
         assertThat(mAlertDialog.isShowing()).isTrue();
     }
 
     @Test
-    @Ignore
     public void clickNeutral_assertShow() {
         performClickOnDialog(BUTTON_NEUTRAL);
         assertThat(mAlertDialog.isShowing()).isTrue();
     }
 
     @Test
-    @Ignore
     public void clickNeutral_assertStartPreview() {
         performClickOnDialog(BUTTON_NEUTRAL);
         getTimerFromFragment().runOneTask();
@@ -106,7 +103,6 @@
     }
 
     @Test
-    @Ignore
     public void clickNeutral_flushAllScheduledTasks_assertStopPreview() {
         performClickOnDialog(BUTTON_NEUTRAL);
         getTimerFromFragment().runAllTasks();
@@ -115,31 +111,28 @@
     }
 
     @Test
-    @Ignore
     public void clickNegative_assertNotShow() {
         performClickOnDialog(BUTTON_NEGATIVE);
         assertThat(mAlertDialog.isShowing()).isFalse();
     }
 
     @Test
-    @Ignore
     public void clickPositive_assertNotShow() {
         performClickOnDialog(BUTTON_POSITIVE);
         assertThat(mAlertDialog.isShowing()).isFalse();
     }
 
     @Test
-    @Ignore
     public void clickNeutralAndPause_assertStopPreview() {
         performClickOnDialog(BUTTON_NEUTRAL);
         getTimerFromFragment().runOneTask();
-        mDialogFragment.onPause();
+        // move the state from RESUMED to CREATED to make fragment's onPause() to be called
+        mFragmentScenario.moveToState(Lifecycle.State.CREATED);
 
         assertStopPreview();
     }
 
     @Test
-    @Ignore
     public void clickNeutralAndClickNegative_assertStopPreview() {
         performClickOnDialog(BUTTON_NEUTRAL);
         getTimerFromFragment().runOneTask();
@@ -149,7 +142,6 @@
     }
 
     @Test
-    @Ignore
     public void clickNeutralAndClickPositive_assertStopPreview() {
         performClickOnDialog(BUTTON_NEUTRAL);
         getTimerFromFragment().runOneTask();
@@ -159,7 +151,6 @@
     }
 
     @Test
-    @Ignore
     public void clickNeutralAndClickColor_assertStartPreview() {
         performClickOnDialog(BUTTON_NEUTRAL);
         getTimerFromFragment().runOneTask();
@@ -177,7 +168,6 @@
     }
 
     @Test
-    @Ignore
     public void clickColorAndClickNegative_assertColor() {
         checkColorButton(AZURE);
         performClickOnDialog(BUTTON_NEGATIVE);
@@ -187,7 +177,6 @@
     }
 
     @Test
-    @Ignore
     public void clickColorAndClickPositive_assertColor() {
         checkColorButton(BLUE);
         performClickOnDialog(BUTTON_POSITIVE);
@@ -201,23 +190,32 @@
 
     private void performClickOnDialog(int whichButton) {
         mAlertDialog.getButton(whichButton).performClick();
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
     }
 
     private Intent getLastCapturedIntent() {
-        final List<Intent> capturedIntents = new ArrayList<>(
-                mShadowContextWrapper.getBroadcastIntents());
+        final List<Intent> capturedIntents =
+                shadowOf(RuntimeEnvironment.getApplication()).getBroadcastIntents();
         final int size = capturedIntents.size();
         return capturedIntents.get(size - 1);
     }
 
-    private ScreenFlashNotificationColorDialogFragment createFragment() {
-        ScreenFlashNotificationColorDialogFragmentWithFakeTimer fragment =
-                new ScreenFlashNotificationColorDialogFragmentWithFakeTimer();
-        ReflectionHelpers.setField(fragment, "mCurrentColor", mCurrentColor);
-        ReflectionHelpers.setField(fragment, "mConsumer",
-                (Consumer<Integer>) selectedColor -> mCurrentColor = selectedColor);
+    private void setupFragment() {
+        mFragmentScenario.onFragment(fragment -> {
+            ReflectionHelpers.setField(fragment, "mCurrentColor", mCurrentColor);
+            ReflectionHelpers.setField(fragment, "mConsumer",
+                    (Consumer<Integer>) selectedColor -> mCurrentColor = selectedColor);
+        });
+        mFragmentScenario.moveToState(Lifecycle.State.RESUMED);
 
-        return fragment;
+        mFragmentScenario.onFragment(fragment -> {
+            assertThat(fragment.getDialog()).isNotNull();
+            assertThat(fragment.requireDialog().isShowing()).isTrue();
+            assertThat(fragment.requireDialog()).isInstanceOf(AlertDialog.class);
+            mAlertDialog = (AlertDialog) fragment.requireDialog();
+            mDialogFragment = fragment;
+            mColorSelectorLayout = mAlertDialog.findViewById(R.id.color_selector_preference);
+        });
     }
 
     private FakeTimer getTimerFromFragment() {
@@ -243,7 +241,7 @@
      * A {@link ScreenFlashNotificationColorDialogFragment} that uses a fake timer so that it won't
      * create unmanageable timer threads during test.
      */
-    public static class ScreenFlashNotificationColorDialogFragmentWithFakeTimer extends
+    public static class TestScreenFlashNotificationColorDialogFragment extends
             ScreenFlashNotificationColorDialogFragment {
 
         @Override
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherControllerTest.java
index 090fb0c..2bd2d74 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherControllerTest.java
@@ -34,6 +34,9 @@
 import org.mockito.junit.MockitoRule;
 import org.robolectric.RobolectricTestRunner;
 
+import java.util.HashSet;
+import java.util.Set;
+
 /** Tests for {@link BluetoothDetailsPairOtherController}. */
 @RunWith(RobolectricTestRunner.class)
 public class BluetoothDetailsPairOtherControllerTest extends BluetoothDetailsControllerTestBase  {
@@ -60,8 +63,9 @@
         mScreen.addPreference(mSpacePreference);
     }
 
+    /** Test the pair other side button title during initialization. */
     @Test
-    public void init_leftSideDevice_rightSideButtonTitle() {
+    public void init_leftSideDevice_pairRightSideButtonTitle() {
         when(mCachedDevice.getDeviceSide()).thenReturn(HearingAidInfo.DeviceSide.SIDE_LEFT);
 
         mController.init(mScreen);
@@ -70,8 +74,9 @@
                 mContext.getString(R.string.bluetooth_pair_right_ear_button));
     }
 
+    /** Test the pair other side button title during initialization. */
     @Test
-    public void init_rightSideDevice_leftSideButtonTitle() {
+    public void init_rightSideDevice_pairLeftSideButtonTitle() {
         when(mCachedDevice.getDeviceSide()).thenReturn(HearingAidInfo.DeviceSide.SIDE_RIGHT);
 
         mController.init(mScreen);
@@ -80,9 +85,10 @@
                 mContext.getString(R.string.bluetooth_pair_left_ear_button));
     }
 
+    /** Test the pair other side button visibility during initialization. */
     @Test
-    public void init_isNotConnectedAshaHearingAidDevice_notVisiblePreference() {
-        when(mCachedDevice.isConnectedAshaHearingAidDevice()).thenReturn(false);
+    public void init_isNotConnectedHearingAidDevice_preferenceIsNotVisible() {
+        when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(false);
 
         mController.init(mScreen);
 
@@ -90,23 +96,49 @@
         assertThat(mSpacePreference.isVisible()).isFalse();
     }
 
+    /**
+     * Test if the controller is available.
+     * Conditions:
+     *      1. Hearing aids is not connected
+     * Expected result:
+     *      The controller is not available. No need to show pair other side hint for
+     *      not connected device.
+     */
     @Test
-    public void isAvailable_isNotConnectedAshaHearingAidDevice_notAvailable() {
-        when(mCachedDevice.isConnectedAshaHearingAidDevice()).thenReturn(false);
+    public void isAvailable_isNotConnectedHearingAidDevice_notAvailable() {
+        when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(false);
 
         assertThat(mController.isAvailable()).isFalse();
     }
 
+    /**
+     * Test if the controller is available.
+     * Conditions:
+     *      1. Monaural hearing aids
+     * Expected result:
+     *      The controller is not available. No need to show pair other side hint for
+     *      monaural device.
+     */
     @Test
-    public void isAvailable_isConnectedAshaHearingAidDevice_isMonaural_notAvailable() {
-        when(mCachedDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
+    public void isAvailable_isConnectedHearingAidDevice_isMonaural_notAvailable() {
+        when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
         when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_MONAURAL);
 
         assertThat(mController.isAvailable()).isFalse();
     }
 
+    /**
+     * Test if the controller is available.
+     * Conditions:
+     *      1. Binaural ASHA hearing aids
+     *      2. Sub device is added
+     *      3. Sub device is connected
+     * Expected result:
+     *      The controller is not available. Both sides are already paired and connected.
+     */
     @Test
-    public void isAvailable_subDeviceIsConnectedAshaHearingAidDevice_notAvailable() {
+    public void isAvailable_ashaDevice_otherDeviceIsConnected_notAvailable() {
+        when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
         when(mCachedDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
         when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_BINAURAL);
         when(mSubCachedDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
@@ -115,8 +147,18 @@
         assertThat(mController.isAvailable()).isFalse();
     }
 
+    /**
+     * Test if the controller is available.
+     * Conditions:
+     *      1. Binaural ASHA hearing aids
+     *      2. Sub device is added
+     *      3. Sub device is not connected
+     * Expected result:
+     *      The controller is available. Need to show the hint to pair the other side.
+     */
     @Test
-    public void isAvailable_subDeviceIsNotConnectedAshaHearingAidDevice_available() {
+    public void isAvailable_ashaDevice_otherDeviceIsNotConnected_available() {
+        when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
         when(mCachedDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
         when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_BINAURAL);
         when(mSubCachedDevice.isConnectedAshaHearingAidDevice()).thenReturn(false);
@@ -125,8 +167,17 @@
         assertThat(mController.isAvailable()).isTrue();
     }
 
+    /**
+     * Test if the controller is available.
+     * Conditions:
+     *      1. Binaural ASHA hearing aids
+     *      2. No sub device added
+     * Expected result:
+     *      The controller is available. Need to show the hint to pair the other side.
+     */
     @Test
-    public void isAvailable_subDeviceNotExist_available() {
+    public void isAvailable_ashaDevice_otherDeviceIsNotExist_available() {
+        when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
         when(mCachedDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
         when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_BINAURAL);
         when(mCachedDevice.getSubDevice()).thenReturn(null);
@@ -134,8 +185,67 @@
         assertThat(mController.isAvailable()).isTrue();
     }
 
+    /**
+     * Test if the controller is available.
+     * Conditions:
+     *      1. Binaural LE Audio hearing aids
+     *      2. Member device is added
+     *      3. Member device is connected
+     * Expected result:
+     *      The controller is not available. Both sides are already paired and connected.
+     */
     @Test
-    public void refresh_leftSideDevice_leftSideButtonTitle() {
+    public void isAvailable_leAudioDevice_otherDeviceIsConnected_notAvailable() {
+        when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
+        when(mCachedDevice.isConnectedLeAudioHearingAidDevice()).thenReturn(true);
+        when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_BINAURAL);
+        when(mSubCachedDevice.isConnectedLeAudioHearingAidDevice()).thenReturn(true);
+        when(mCachedDevice.getMemberDevice()).thenReturn(Set.of(mSubCachedDevice));
+
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    /**
+     * Test if the controller is available.
+     * Conditions:
+     *      1. Binaural LE Audio hearing aids
+     *      2. Member device is added
+     *      3. Member device is not connected
+     * Expected result:
+     *      The controller is available. Need to show the hint to pair the other side.
+     */
+    @Test
+    public void isAvailable_leAudioDevice_otherDeviceIsNotConnected_available() {
+        when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
+        when(mCachedDevice.isConnectedLeAudioHearingAidDevice()).thenReturn(true);
+        when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_BINAURAL);
+        when(mSubCachedDevice.isConnectedLeAudioHearingAidDevice()).thenReturn(false);
+        when(mCachedDevice.getMemberDevice()).thenReturn(Set.of(mSubCachedDevice));
+
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    /**
+     * Test if the controller is available.
+     * Conditions:
+     *      1. Binaural LE Audio hearing aids
+     *      2. No member device added
+     * Expected result:
+     *      The controller is available. Need to show the hint to pair the other side.
+     */
+    @Test
+    public void isAvailable_leAudioDevice_otherDeviceIsNotExist_available() {
+        when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
+        when(mCachedDevice.isConnectedLeAudioHearingAidDevice()).thenReturn(true);
+        when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_BINAURAL);
+        when(mCachedDevice.getMemberDevice()).thenReturn(new HashSet<>());
+
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    /** Test the pair other side button title after refreshing. */
+    @Test
+    public void refresh_rightSideDevice_pairLeftSideButtonTitle() {
         when(mCachedDevice.getDeviceSide()).thenReturn(HearingAidInfo.DeviceSide.SIDE_RIGHT);
         mController.init(mScreen);
 
@@ -145,9 +255,10 @@
                 mContext.getString(R.string.bluetooth_pair_left_ear_button));
     }
 
+    /** Test the pair other side button visibility after refreshing. */
     @Test
-    public void refresh_isNotConnectedAshaHearingAidDevice_notVisiblePreference() {
-        when(mCachedDevice.isConnectedAshaHearingAidDevice()).thenReturn(false);
+    public void refresh_isNotConnectedHearingAidDevice_preferenceIsNotVisible() {
+        when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(false);
         mController.init(mScreen);
 
         mController.refresh();
diff --git a/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java b/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java
index 4640efe..b16d336 100644
--- a/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java
+++ b/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java
@@ -17,7 +17,6 @@
 package com.android.settings.datausage;
 
 import static com.google.common.truth.Truth.assertThat;
-
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
@@ -44,19 +43,15 @@
 import androidx.preference.PreferenceManager;
 
 import com.android.settings.R;
-import com.android.settings.SettingsActivity;
 import com.android.settings.network.MobileDataEnabledListener;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.widget.LoadingViewController;
-import com.android.settingslib.AppItem;
 import com.android.settingslib.NetworkPolicyEditor;
 import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
-import com.android.settingslib.net.NetworkCycleChartData;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.Robolectric;
@@ -67,9 +62,6 @@
 import org.robolectric.annotation.Implements;
 import org.robolectric.util.ReflectionHelpers;
 
-import java.util.ArrayList;
-import java.util.List;
-
 @RunWith(RobolectricTestRunner.class)
 public class DataUsageListTest {
 
@@ -196,34 +188,6 @@
     }
 
     @Test
-    public void startAppDataUsage_shouldAddCyclesInfoToLaunchArguments() {
-        final long startTime = 1521583200000L;
-        final long endTime = 1521676800000L;
-        final List<NetworkCycleChartData> data = new ArrayList<>();
-        final NetworkCycleChartData.Builder builder = new NetworkCycleChartData.Builder();
-        builder.setStartTime(startTime)
-                .setEndTime(endTime);
-        data.add(builder.build());
-        ReflectionHelpers.setField(mDataUsageList, "mCycleData", data);
-        final Spinner spinner = mock(Spinner.class);
-        when(spinner.getSelectedItemPosition()).thenReturn(0);
-        ReflectionHelpers.setField(mDataUsageList, "mCycleSpinner", spinner);
-        final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
-
-        mDataUsageList.startAppDataUsage(new AppItem());
-
-        verify(mActivity).startActivity(intent.capture());
-        final Bundle arguments =
-                intent.getValue().getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS);
-        assertThat(arguments.getLong(AppDataUsage.ARG_SELECTED_CYCLE)).isEqualTo(endTime);
-        final ArrayList<Long> cycles =
-                (ArrayList) arguments.getSerializable(AppDataUsage.ARG_NETWORK_CYCLES);
-        assertThat(cycles).hasSize(2);
-        assertThat(cycles.get(0)).isEqualTo(endTime);
-        assertThat(cycles.get(1)).isEqualTo(startTime);
-    }
-
-    @Test
     public void onViewCreated_shouldHideCycleSpinner() {
         final View view = new View(mActivity);
         final View header = getHeader();
@@ -255,7 +219,6 @@
         mDataUsageList.onPause();
 
         verify(mLoaderManager).destroyLoader(DataUsageList.LOADER_CHART_DATA);
-        verify(mLoaderManager).destroyLoader(DataUsageList.LOADER_SUMMARY);
     }
 
     private View getHeader() {
diff --git a/tests/robotests/src/com/android/settings/development/BluetoothLeAudioDeviceDetailsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BluetoothLeAudioDeviceDetailsPreferenceControllerTest.java
index b405f9e..13bc6a4 100644
--- a/tests/robotests/src/com/android/settings/development/BluetoothLeAudioDeviceDetailsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/BluetoothLeAudioDeviceDetailsPreferenceControllerTest.java
@@ -16,6 +16,9 @@
 
 package com.android.settings.development;
 
+import static com.android.settings.development.BluetoothLeAudioDeviceDetailsPreferenceController
+        .LE_AUDIO_TOGGLE_VISIBLE_PROPERTY;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.spy;
@@ -25,12 +28,11 @@
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothStatusCodes;
 import android.content.Context;
-import android.provider.DeviceConfig;
+import android.os.SystemProperties;
 
 import androidx.preference.PreferenceScreen;
 import androidx.preference.SwitchPreference;
 
-import com.android.settings.core.SettingsUIDeviceConfig;
 import com.android.settings.testutils.shadow.ShadowDeviceConfig;
 
 import org.junit.After;
@@ -77,9 +79,8 @@
     public void onPreferenceChanged_settingEnabled_shouldTurnOnLeAudioDeviceDetailSetting() {
         mController.sLeAudioSupportedStateCache = BluetoothStatusCodes.FEATURE_SUPPORTED;
         mController.onPreferenceChange(mPreference, true /* new value */);
-        final boolean isEnabled = DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_SETTINGS_UI,
-                SettingsUIDeviceConfig.BT_LE_AUDIO_DEVICE_DETAIL_ENABLED, false);
+        final boolean isEnabled = SystemProperties.getBoolean(
+                LE_AUDIO_TOGGLE_VISIBLE_PROPERTY, false);
 
         assertThat(isEnabled).isTrue();
     }
@@ -88,9 +89,8 @@
     public void onPreferenceChanged_settingDisabled_shouldTurnOffLeAudioDeviceDetailSetting() {
         mController.sLeAudioSupportedStateCache = BluetoothStatusCodes.FEATURE_SUPPORTED;
         mController.onPreferenceChange(mPreference, false /* new value */);
-        final boolean isEnabled = DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_SETTINGS_UI,
-                SettingsUIDeviceConfig.BT_LE_AUDIO_DEVICE_DETAIL_ENABLED, false);
+        final boolean isEnabled = SystemProperties.getBoolean(
+                LE_AUDIO_TOGGLE_VISIBLE_PROPERTY, true);
 
         assertThat(isEnabled).isFalse();
     }
@@ -98,18 +98,15 @@
     @Test
     public void updateState_settingEnabled_preferenceShouldBeChecked() {
         mController.sLeAudioSupportedStateCache = BluetoothStatusCodes.FEATURE_SUPPORTED;
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
-                SettingsUIDeviceConfig.BT_LE_AUDIO_DEVICE_DETAIL_ENABLED, "true", false);
+        SystemProperties.set(LE_AUDIO_TOGGLE_VISIBLE_PROPERTY, "true");
         mController.updateState(mPreference);
-
         verify(mPreference).setChecked(true);
     }
 
     @Test
     public void updateState_settingDisabled_preferenceShouldNotBeChecked() {
         mController.sLeAudioSupportedStateCache = BluetoothStatusCodes.FEATURE_SUPPORTED;
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
-                SettingsUIDeviceConfig.BT_LE_AUDIO_DEVICE_DETAIL_ENABLED, "false", false);
+        SystemProperties.set(LE_AUDIO_TOGGLE_VISIBLE_PROPERTY, "false");
         mController.updateState(mPreference);
 
         verify(mPreference).setChecked(false);
diff --git a/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java
index 3ad14e5..19eac82 100644
--- a/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java
@@ -132,23 +132,23 @@
     }
 
     @Test
-    public void isSliceableCorrectKey_returnsTrue() {
+    public void isPublicSliceCorrectKey_returnsTrue() {
         final AmbientDisplayAlwaysOnPreferenceController controller =
                 new AmbientDisplayAlwaysOnPreferenceController(mContext,
                         "ambient_display_always_on");
-        assertThat(controller.isSliceable()).isTrue();
+        assertThat(controller.isPublicSlice()).isTrue();
     }
 
     @Test
-    public void isSliceableIncorrectKey_returnsFalse() {
+    public void isPublicSliceIncorrectKey_returnsFalse() {
         final AmbientDisplayAlwaysOnPreferenceController controller =
                 new AmbientDisplayAlwaysOnPreferenceController(mContext, "bad_key");
-        assertThat(controller.isSliceable()).isFalse();
+        assertThat(controller.isPublicSlice()).isFalse();
     }
 
     @Test
-    public void isPublicSlice_returnTrue() {
-        assertThat(mController.isPublicSlice()).isTrue();
+    public void isSliceable_returnTrue() {
+        assertThat(mController.isSliceable()).isTrue();
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/display/FoldLockBehaviorSettingsTest.java b/tests/robotests/src/com/android/settings/display/FoldLockBehaviorSettingsTest.java
index 37b9391..991f529 100644
--- a/tests/robotests/src/com/android/settings/display/FoldLockBehaviorSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/display/FoldLockBehaviorSettingsTest.java
@@ -24,13 +24,19 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 
+import com.android.settings.testutils.shadow.ShadowSystemSettings;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
 
 @RunWith(RobolectricTestRunner.class)
+@Config(shadows = {
+    ShadowSystemSettings.class,
+})
 public class FoldLockBehaviorSettingsTest {
 
     private Context mContext;
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetectorTest.java
index 7104206..c9e201b 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetectorTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetectorTest.java
@@ -53,7 +53,6 @@
         mPolicy = spy(new BatteryTipPolicy(RuntimeEnvironment.application));
         mContext = RuntimeEnvironment.application;
         ReflectionHelpers.setField(mPolicy, "lowBatteryEnabled", true);
-        ReflectionHelpers.setField(mPolicy, "lowBatteryHour", 3);
         mBatteryInfo.discharging = true;
 
         mLowBatteryDetector = new LowBatteryDetector(mContext, mPolicy, mBatteryInfo,
@@ -78,13 +77,9 @@
 
     @Test
     public void testDetect_lowBattery_tipNew() {
-        mBatteryInfo.batteryLevel = 3;
+        mBatteryInfo.batteryLevel = 20;
         mBatteryInfo.remainingTimeUs = TimeUnit.DAYS.toMillis(1);
         assertThat(mLowBatteryDetector.detect().getState()).isEqualTo(BatteryTip.StateType.NEW);
-
-        mBatteryInfo.batteryLevel = 50;
-        mBatteryInfo.remainingTimeUs = TimeUnit.MINUTES.toMillis(1);
-        assertThat(mLowBatteryDetector.detect().getState()).isEqualTo(BatteryTip.StateType.NEW);
     }
 
     @Test
@@ -104,9 +99,9 @@
     }
 
     @Test
-    public void testDetect_timeEstimationZero_tipInvisible() {
+    public void testDetect_lowTimeEstimation_tipInvisible() {
         mBatteryInfo.batteryLevel = 50;
-        mBatteryInfo.remainingTimeUs = 0;
+        mBatteryInfo.remainingTimeUs = TimeUnit.MINUTES.toMillis(1);
         assertThat(mLowBatteryDetector.detect().isVisible()).isFalse();
     }
 
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/AnomalyEventWrapperTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/AnomalyEventWrapperTest.java
new file mode 100644
index 0000000..60e0af0
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/AnomalyEventWrapperTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.batteryusage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+
+import com.android.settings.testutils.BatteryTestUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.TimeZone;
+
+@RunWith(RobolectricTestRunner.class)
+public class AnomalyEventWrapperTest {
+    private AnomalyEventWrapper mAnomalyEventWrapper;
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"));
+        mContext = spy(RuntimeEnvironment.application);
+    }
+
+    @Test
+    public void getDismissRecordKey_returnExpectedResult() {
+        mAnomalyEventWrapper = new AnomalyEventWrapper(mContext,
+                BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent());
+        assertThat(mAnomalyEventWrapper.getDismissRecordKey())
+                .isEqualTo("KEY_BRIGHTNESS");
+
+        mAnomalyEventWrapper = new AnomalyEventWrapper(mContext,
+                BatteryTestUtils.createScreenTimeoutAnomalyEvent());
+        assertThat(mAnomalyEventWrapper.getDismissRecordKey())
+                .isEqualTo("KEY_SCREEN_TIMEOUT");
+
+        mAnomalyEventWrapper = new AnomalyEventWrapper(mContext,
+                BatteryTestUtils.createAppAnomalyEvent());
+        assertThat(mAnomalyEventWrapper.getDismissRecordKey())
+                .isEqualTo("KEY_APP_1");
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreferenceTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreferenceTest.java
index 630ff45..63cb1b3 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreferenceTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -58,13 +59,14 @@
     private BatteryTipsCardPreference mBatteryTipsCardPreference;
     private PowerUsageAdvanced mPowerUsageAdvanced;
     private BatteryTipsController mBatteryTipsController;
+    private BatteryChartPreferenceController mBatteryChartPreferenceController;
 
     @Mock
     private View mFakeView;
     @Mock
-    private BatteryChartPreferenceController mBatteryChartPreferenceController;
-    @Mock
     private BatteryUsageBreakdownController mBatteryUsageBreakdownController;
+    @Mock
+    private BatteryDiffEntry mFakeEntry;
 
     @Before
     public void setUp() {
@@ -73,8 +75,13 @@
         mFeatureFactory = FakeFeatureFactory.setupForTest();
         mBatteryTipsCardPreference = new BatteryTipsCardPreference(mContext, /*attrs=*/ null);
         mBatteryTipsController = new BatteryTipsController(mContext);
+        mBatteryChartPreferenceController =
+                spy(new BatteryChartPreferenceController(mContext, null, null));
+        mBatteryChartPreferenceController.mPrefContext = mContext;
         mBatteryTipsController.mCardPreference = mBatteryTipsCardPreference;
-        mPowerUsageAdvanced = new PowerUsageAdvanced();
+
+        mPowerUsageAdvanced = spy(new PowerUsageAdvanced());
+        doReturn(mContext).when(mPowerUsageAdvanced).getContext();
         mPowerUsageAdvanced.mBatteryTipsController = mBatteryTipsController;
         mPowerUsageAdvanced.mBatteryChartPreferenceController = mBatteryChartPreferenceController;
         mPowerUsageAdvanced.mBatteryUsageBreakdownController = mBatteryUsageBreakdownController;
@@ -82,6 +89,7 @@
                 1694354400000L, 1,      // 2023-09-10 22:00:00
                 1694361600000L, 2,      // 2023-09-11 00:00:00
                 1694368800000L, 3)));    // 2023-09-11 02:00:00
+        doReturn("TestEntriesKey").when(mFakeEntry).getKey();
     }
 
     @Test
@@ -99,7 +107,8 @@
         when(mFakeView.getId()).thenReturn(R.id.main_button);
         doNothing().when(mContext).startActivity(captor.capture());
 
-        mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(adaptiveBrightnessAnomaly);
+        mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(
+                adaptiveBrightnessAnomaly, adaptiveBrightnessAnomaly);
         mBatteryTipsCardPreference.onClick(mFakeView);
 
         assertThat(mBatteryTipsCardPreference.isVisible()).isFalse();
@@ -110,18 +119,21 @@
         assertThat(intent.getIntExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, -1))
                 .isEqualTo(SettingsEnums.DISPLAY);
         verify(mFeatureFactory.metricsFeatureProvider).action(
+                mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, "BrightnessAnomaly");
+        verify(mFeatureFactory.metricsFeatureProvider).action(
                 mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, "BrightnessAnomaly");
     }
 
     @Test
-    public void onClick_dismissBtn_cardDismissAndLogged() {
+    public void onClick_dismissBtnOfSettingsAnomaly_cardDismissAndLogged() {
         final PowerAnomalyEvent screenTimeoutAnomaly =
                 BatteryTestUtils.createScreenTimeoutAnomalyEvent();
         DatabaseUtils.removeDismissedPowerAnomalyKeys(mContext);
         when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
         when(mFakeView.getId()).thenReturn(R.id.dismiss_button);
 
-        mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(screenTimeoutAnomaly);
+        mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(
+                screenTimeoutAnomaly, screenTimeoutAnomaly);
         mBatteryTipsCardPreference.onClick(mFakeView);
 
         assertThat(mBatteryTipsCardPreference.isVisible()).isFalse();
@@ -129,6 +141,8 @@
         assertThat(DatabaseUtils.getDismissedPowerAnomalyKeys(mContext))
                 .contains(PowerAnomalyKey.KEY_SCREEN_TIMEOUT.name());
         verify(mFeatureFactory.metricsFeatureProvider).action(
+                mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, "ScreenTimeoutAnomaly");
+        verify(mFeatureFactory.metricsFeatureProvider).action(
                 mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_DISMISS, "ScreenTimeoutAnomaly");
     }
 
@@ -137,30 +151,40 @@
         final PowerAnomalyEvent appsAnomaly = BatteryTestUtils.createAppAnomalyEvent();
         when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
         when(mFakeView.getId()).thenReturn(R.id.main_button);
+        doNothing().when(mBatteryChartPreferenceController).selectHighlightSlotIndex();
+        when(mPowerUsageAdvanced.findRelatedBatteryDiffEntry(any())).thenReturn(mFakeEntry);
 
-        mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(appsAnomaly);
+        mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(appsAnomaly, appsAnomaly);
         mBatteryTipsCardPreference.onClick(mFakeView);
 
         assertThat(mBatteryTipsCardPreference.isVisible()).isFalse();
         verify(mContext, never()).startActivity(any(Intent.class));
+        verify(mBatteryChartPreferenceController).onHighlightSlotIndexUpdate(
+                eq(1), eq(0));
         verify(mBatteryChartPreferenceController).selectHighlightSlotIndex();
         verify(mFeatureFactory.metricsFeatureProvider).action(
+                mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, "AppAnomaly");
+        verify(mFeatureFactory.metricsFeatureProvider).action(
                 mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, "AppAnomaly");
     }
 
     @Test
-    public void onClick_dismissBtnOfAppsAnomaly_removeHighlightSlotIndex() {
+    public void onClick_dismissBtnOfAppsAnomaly_keepHighlightSlotIndex() {
         final PowerAnomalyEvent appsAnomaly = BatteryTestUtils.createAppAnomalyEvent();
         when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
         when(mFakeView.getId()).thenReturn(R.id.dismiss_button);
+        when(mPowerUsageAdvanced.findRelatedBatteryDiffEntry(any())).thenReturn(mFakeEntry);
 
-        mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(appsAnomaly);
+        mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(appsAnomaly, appsAnomaly);
         mBatteryTipsCardPreference.onClick(mFakeView);
 
         assertThat(mBatteryTipsCardPreference.isVisible()).isFalse();
+        verify(mContext, never()).startActivity(any(Intent.class));
         verify(mBatteryChartPreferenceController).onHighlightSlotIndexUpdate(
-                eq(BatteryChartViewModel.SELECTED_INDEX_INVALID),
-                eq(BatteryChartViewModel.SELECTED_INDEX_INVALID));
+                eq(1), eq(0));
+        verify(mBatteryChartPreferenceController, never()).selectHighlightSlotIndex();
+        verify(mFeatureFactory.metricsFeatureProvider).action(
+                mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, "AppAnomaly");
         verify(mFeatureFactory.metricsFeatureProvider).action(
                 mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_DISMISS, "AppAnomaly");
     }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsControllerTest.java
index 913c00a..b8afe98 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsControllerTest.java
@@ -16,8 +16,6 @@
 
 package com.android.settings.fuelgauge.batteryusage;
 
-import static com.google.common.truth.Truth.assertThat;
-
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -70,30 +68,18 @@
 
     @Test
     public void handleBatteryTipsCardUpdated_null_hidePreference() {
-        mBatteryTipsController.handleBatteryTipsCardUpdated(/* powerAnomalyEvents= */ null);
+        mBatteryTipsController.handleBatteryTipsCardUpdated(/* powerAnomalyEvents= */ null, false);
 
         verify(mBatteryTipsCardPreference).setVisible(false);
     }
 
     @Test
-    public void getDismissRecordKey_returnExpectedResult() {
-        assertThat(BatteryTipsController.getDismissRecordKey(
-                BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent()))
-                .isEqualTo("KEY_BRIGHTNESS");
-        assertThat(BatteryTipsController.getDismissRecordKey(
-                BatteryTestUtils.createScreenTimeoutAnomalyEvent()))
-                .isEqualTo("KEY_SCREEN_TIMEOUT");
-        assertThat(BatteryTipsController.getDismissRecordKey(
-                BatteryTestUtils.createAppAnomalyEvent()))
-                .isEqualTo("KEY_APP_1");
-    }
-
-    @Test
     public void handleBatteryTipsCardUpdated_adaptiveBrightnessAnomaly_showAnomaly() {
         PowerAnomalyEvent event = BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent();
         when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
 
-        mBatteryTipsController.handleBatteryTipsCardUpdated(event);
+        mBatteryTipsController.handleBatteryTipsCardUpdated(
+                new AnomalyEventWrapper(mContext, event), false);
 
         // Check pre-defined string
         verify(mBatteryTipsCardPreference).setTitle(
@@ -114,7 +100,8 @@
         PowerAnomalyEvent event = BatteryTestUtils.createScreenTimeoutAnomalyEvent();
         when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
 
-        mBatteryTipsController.handleBatteryTipsCardUpdated(event);
+        mBatteryTipsController.handleBatteryTipsCardUpdated(
+                new AnomalyEventWrapper(mContext, event), false);
 
         verify(mBatteryTipsCardPreference).setTitle("Reduce screen timeout to extend battery life");
         verify(mBatteryTipsCardPreference).setIconResourceId(R.drawable.ic_battery_tips_lightbulb);
@@ -139,7 +126,8 @@
                 .build();
         when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
 
-        mBatteryTipsController.handleBatteryTipsCardUpdated(event);
+        mBatteryTipsController.handleBatteryTipsCardUpdated(
+                new AnomalyEventWrapper(mContext, event), false);
 
         verify(mBatteryTipsCardPreference).setTitle(testTitle);
         verify(mBatteryTipsCardPreference).setIconResourceId(R.drawable.ic_battery_tips_lightbulb);
@@ -157,10 +145,13 @@
         PowerAnomalyEvent event = BatteryTestUtils.createAppAnomalyEvent();
         when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
 
-        mBatteryTipsController.handleBatteryTipsCardUpdated(event);
+        AnomalyEventWrapper eventWrapper = new AnomalyEventWrapper(mContext, event);
+        eventWrapper.setRelatedBatteryDiffEntry(
+                new BatteryDiffEntry(mContext, "", "Chrome", 0));
+        mBatteryTipsController.handleBatteryTipsCardUpdated(eventWrapper, false);
 
         verify(mBatteryTipsCardPreference).setTitle(
-                "Chrome used more battery than usual in foreground");
+                "Chrome used more battery than usual");
         verify(mBatteryTipsCardPreference).setIconResourceId(
                 R.drawable.ic_battery_tips_warning_icon);
         verify(mBatteryTipsCardPreference).setMainButtonStrokeColorResourceId(
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java
index d89c06b..a721ad4 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java
@@ -69,7 +69,7 @@
     @Mock
     private BatteryHistEntry mBatteryHistEntry;
     @Mock
-    private PowerGaugePreference mPowerGaugePreference;
+    private AnomalyAppItemPreference mAnomalyAppItemPreference;
 
     private Context mContext;
     private FakeFeatureFactory mFeatureFactory;
@@ -123,13 +123,14 @@
         BatteryDiffEntry.sResourceCache.put(
                 "fakeBatteryDiffEntryKey",
                 new BatteryEntry.NameAndIcon("fakeName", /*icon=*/ null, /*iconId=*/ 1));
+        doReturn(mAnomalyAppItemPreference).when(mAppListPreferenceGroup).findPreference(PREF_KEY);
     }
 
     @Test
     public void onDestroy_clearPreferenceCacheAndPreferenceGroupRemoveAll() {
         // Ensures the testing environment is correct.
         mBatteryUsageBreakdownController.mPreferenceCache.put(
-                PREF_KEY, mPowerGaugePreference);
+                PREF_KEY, mAnomalyAppItemPreference);
         assertThat(mBatteryUsageBreakdownController.mPreferenceCache).hasSize(1);
 
         mBatteryUsageBreakdownController.onDestroy();
@@ -178,7 +179,6 @@
         doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
         doReturn(appLabel).when(mBatteryDiffEntry).getAppLabel();
         doReturn(PREF_KEY).when(mBatteryDiffEntry).getKey();
-        doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).findPreference(PREF_KEY);
 
         mBatteryUsageBreakdownController.addAllPreferences();
 
@@ -188,27 +188,25 @@
     @Test
     public void removeAndCacheAllUnusedPreferences_removePref_buildCacheAndRemoveAllPreference() {
         doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount();
-        doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).getPreference(0);
+        doReturn(mAnomalyAppItemPreference).when(mAppListPreferenceGroup).getPreference(0);
         doReturn(PREF_KEY2).when(mBatteryHistEntry).getKey();
-        doReturn(PREF_KEY).when(mPowerGaugePreference).getKey();
-        doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).findPreference(PREF_KEY);
+        doReturn(PREF_KEY).when(mAnomalyAppItemPreference).getKey();
         // Ensures the testing data is correct.
         assertThat(mBatteryUsageBreakdownController.mPreferenceCache).isEmpty();
 
         mBatteryUsageBreakdownController.removeAndCacheAllUnusedPreferences();
 
         assertThat(mBatteryUsageBreakdownController.mPreferenceCache.get(PREF_KEY))
-                .isEqualTo(mPowerGaugePreference);
-        verify(mAppListPreferenceGroup).removePreference(mPowerGaugePreference);
+                .isEqualTo(mAnomalyAppItemPreference);
+        verify(mAppListPreferenceGroup).removePreference(mAnomalyAppItemPreference);
     }
 
     @Test
     public void removeAndCacheAllUnusedPreferences_keepPref_KeepAllPreference() {
         doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount();
-        doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).getPreference(0);
+        doReturn(mAnomalyAppItemPreference).when(mAppListPreferenceGroup).getPreference(0);
         doReturn(PREF_KEY).when(mBatteryDiffEntry).getKey();
-        doReturn(PREF_KEY).when(mPowerGaugePreference).getKey();
-        doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).findPreference(PREF_KEY);
+        doReturn(PREF_KEY).when(mAnomalyAppItemPreference).getKey();
         // Ensures the testing data is correct.
         assertThat(mBatteryUsageBreakdownController.mPreferenceCache).isEmpty();
 
@@ -232,10 +230,10 @@
     @Test
     public void handlePreferenceTreeClick_forAppEntry_returnTrue() {
         mBatteryDiffEntry.mConsumerType = ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY;
-        doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry();
+        doReturn(mBatteryDiffEntry).when(mAnomalyAppItemPreference).getBatteryDiffEntry();
 
         assertThat(mBatteryUsageBreakdownController.handlePreferenceTreeClick(
-                mPowerGaugePreference)).isTrue();
+                mAnomalyAppItemPreference)).isTrue();
         verify(mMetricsFeatureProvider)
                 .action(
                         SettingsEnums.OPEN_BATTERY_USAGE,
@@ -248,10 +246,10 @@
     @Test
     public void handlePreferenceTreeClick_forSystemEntry_returnTrue() {
         mBatteryDiffEntry.mConsumerType = ConvertUtils.CONSUMER_TYPE_UID_BATTERY;
-        doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry();
+        doReturn(mBatteryDiffEntry).when(mAnomalyAppItemPreference).getBatteryDiffEntry();
 
         assertThat(mBatteryUsageBreakdownController.handlePreferenceTreeClick(
-                mPowerGaugePreference)).isTrue();
+                mAnomalyAppItemPreference)).isTrue();
         verify(mMetricsFeatureProvider)
                 .action(
                         SettingsEnums.OPEN_BATTERY_USAGE,
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
index f06dc63..cd594d3 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
@@ -72,6 +72,7 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
         mContext = spy(RuntimeEnvironment.application);
         ConvertUtils.sUsageSource = ConvertUtils.EMPTY_USAGE_SOURCE;
         when(mContext.getPackageManager()).thenReturn(mMockPackageManager);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvancedTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvancedTest.java
index 953c2d4..9753bd2 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvancedTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvancedTest.java
@@ -20,8 +20,8 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.ArgumentMatchers.notNull;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.content.Context;
@@ -41,7 +41,9 @@
 
 import java.util.Map;
 import java.util.Optional;
+import java.util.Set;
 import java.util.TimeZone;
+import java.util.function.Predicate;
 
 @RunWith(RobolectricTestRunner.class)
 @Config(shadows = ShadowDashboardFragment.class)
@@ -50,6 +52,9 @@
     private Context mContext;
     private PowerUsageAdvanced mPowerUsageAdvanced;
 
+    private Predicate<PowerAnomalyEvent> mCardFilterPredicate;
+    private Predicate<PowerAnomalyEvent> mSlotFilterPredicate;
+
     @Mock
     private BatteryTipsController mBatteryTipsController;
     @Mock
@@ -65,7 +70,7 @@
         TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"));
         mContext = spy(RuntimeEnvironment.application);
 
-        mPowerUsageAdvanced = new PowerUsageAdvanced();
+        mPowerUsageAdvanced = spy(new PowerUsageAdvanced());
         mPowerUsageAdvanced.mBatteryTipsController = mBatteryTipsController;
         mPowerUsageAdvanced.mBatteryChartPreferenceController = mBatteryChartPreferenceController;
         mPowerUsageAdvanced.mScreenOnTimeController = mScreenOnTimeController;
@@ -74,43 +79,63 @@
                 1694354400000L, 1,      // 2023-09-10 22:00:00
                 1694361600000L, 2,      // 2023-09-11 00:00:00
                 1694368800000L, 3)));    // 2023-09-11 02:00:00
+        doReturn(mContext).when(mPowerUsageAdvanced).getContext();
+        mSlotFilterPredicate = PowerAnomalyEvent::hasWarningItemInfo;
     }
 
     @Test
-    public void getHighestScoreAnomalyEvent_withEmptyOrNullList_getNull() {
-        assertThat(PowerUsageAdvanced.getHighestScoreAnomalyEvent(mContext, null)).isNull();
-        assertThat(PowerUsageAdvanced.getHighestScoreAnomalyEvent(
-                mContext, BatteryTestUtils.createEmptyPowerAnomalyEventList())).isNull();
+    public void getFilterAnomalyEvent_withEmptyOrNullList_getNull() {
+        prepareCardFilterPredicate(null);
+        assertThat(PowerUsageAdvanced
+                .getAnomalyEvent(null, mCardFilterPredicate)).isNull();
+        assertThat(PowerUsageAdvanced
+                .getAnomalyEvent(null, mSlotFilterPredicate)).isNull();
+        assertThat(PowerUsageAdvanced.getAnomalyEvent(
+                BatteryTestUtils.createEmptyPowerAnomalyEventList(), mCardFilterPredicate))
+                .isNull();
+        assertThat(PowerUsageAdvanced.getAnomalyEvent(
+                BatteryTestUtils.createEmptyPowerAnomalyEventList(), mSlotFilterPredicate))
+                .isNull();
     }
 
     @Test
-    public void getHighestScoreAnomalyEvent_withoutDismissed_getHighestScoreEvent() {
+    public void getFilterAnomalyEvent_withoutDismissed_getHighestScoreEvent() {
         final PowerAnomalyEventList powerAnomalyEventList =
                 BatteryTestUtils.createNonEmptyPowerAnomalyEventList();
 
-        final PowerAnomalyEvent highestScoreEvent =
-                PowerUsageAdvanced.getHighestScoreAnomalyEvent(mContext, powerAnomalyEventList);
+        final PowerAnomalyEvent slotEvent =
+                PowerUsageAdvanced.getAnomalyEvent(powerAnomalyEventList,
+                        mSlotFilterPredicate);
+        prepareCardFilterPredicate(slotEvent);
+        final PowerAnomalyEvent cardEvent =
+                PowerUsageAdvanced.getAnomalyEvent(powerAnomalyEventList,
+                        mCardFilterPredicate);
 
-        assertThat(highestScoreEvent)
-                .isEqualTo(BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent());
+        assertThat(cardEvent).isEqualTo(BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent());
+        assertThat(slotEvent).isNull();
     }
 
     @Test
-    public void getHighestScoreAnomalyEvent_withBrightnessDismissed_getScreenTimeout() {
+    public void getFilterAnomalyEvent_withBrightnessDismissed_getScreenTimeout() {
         final PowerAnomalyEventList powerAnomalyEventList =
                 BatteryTestUtils.createNonEmptyPowerAnomalyEventList();
         DatabaseUtils.removeDismissedPowerAnomalyKeys(mContext);
         DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, PowerAnomalyKey.KEY_BRIGHTNESS.name());
 
-        final PowerAnomalyEvent highestScoreEvent =
-                PowerUsageAdvanced.getHighestScoreAnomalyEvent(mContext, powerAnomalyEventList);
+        final PowerAnomalyEvent slotEvent =
+                PowerUsageAdvanced.getAnomalyEvent(powerAnomalyEventList,
+                        mSlotFilterPredicate);
+        prepareCardFilterPredicate(slotEvent);
+        final PowerAnomalyEvent cardEvent =
+                PowerUsageAdvanced.getAnomalyEvent(powerAnomalyEventList,
+                        mCardFilterPredicate);
 
-        assertThat(highestScoreEvent)
-                .isEqualTo(BatteryTestUtils.createScreenTimeoutAnomalyEvent());
+        assertThat(cardEvent).isEqualTo(BatteryTestUtils.createScreenTimeoutAnomalyEvent());
+        assertThat(slotEvent).isNull();
     }
 
     @Test
-    public void getHighestScoreAnomalyEvent_withAllDismissed_getNull() {
+    public void getFilterAnomalyEvent_withAllDismissed_getNull() {
         final PowerAnomalyEventList powerAnomalyEventList =
                 BatteryTestUtils.createNonEmptyPowerAnomalyEventList();
         DatabaseUtils.removeDismissedPowerAnomalyKeys(mContext);
@@ -118,20 +143,26 @@
             DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, key.name());
         }
 
-        final PowerAnomalyEvent highestScoreEvent =
-                PowerUsageAdvanced.getHighestScoreAnomalyEvent(mContext, powerAnomalyEventList);
+        final PowerAnomalyEvent slotEvent =
+                PowerUsageAdvanced.getAnomalyEvent(powerAnomalyEventList,
+                        mSlotFilterPredicate);
+        prepareCardFilterPredicate(slotEvent);
+        final PowerAnomalyEvent cardEvent =
+                PowerUsageAdvanced.getAnomalyEvent(powerAnomalyEventList,
+                        mCardFilterPredicate);
 
-        assertThat(highestScoreEvent).isNull();
+        assertThat(cardEvent).isNull();
+        assertThat(slotEvent).isNull();
     }
 
     @Test
     public void onDisplayAnomalyEventUpdated_withSettingsAnomalyEvent_skipHighlightSlotEffect() {
         final PowerAnomalyEvent event = BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent();
 
-        mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(event);
+        mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(event, event);
 
-        assertThat(mPowerUsageAdvanced.mPowerAnomalyEvent).isEqualTo(event);
-        verify(mBatteryTipsController).handleBatteryTipsCardUpdated(eq(event));
+        assertThat(mPowerUsageAdvanced.mHighlightEventWrapper.get().getEventId())
+                .isEqualTo(event.getEventId());
         verify(mPowerUsageAdvanced.mBatteryTipsController).setOnAnomalyConfirmListener(isNull());
         verify(mPowerUsageAdvanced.mBatteryTipsController).setOnAnomalyRejectListener(isNull());
         verify(mPowerUsageAdvanced.mBatteryChartPreferenceController).onHighlightSlotIndexUpdate(
@@ -140,46 +171,44 @@
     }
 
     @Test
-    public void onDisplayAnomalyEventUpdated_withAppAnomalyEvent_setHighlightSlotEffect() {
+    public void onDisplayAnomalyEventUpdated_onlyAppAnomalyEvent_setHighlightSlotEffect() {
         final PowerAnomalyEvent event = BatteryTestUtils.createAppAnomalyEvent();
 
-        mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(event);
+        mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(event, event);
 
-        assertThat(mPowerUsageAdvanced.mPowerAnomalyEvent).isEqualTo(event);
-        verify(mBatteryTipsController).handleBatteryTipsCardUpdated(eq(event));
+        assertThat(mPowerUsageAdvanced.mHighlightEventWrapper.get().getEventId())
+                .isEqualTo(event.getEventId());
         verify(mBatteryTipsController).setOnAnomalyConfirmListener(isNull());
         verify(mBatteryTipsController).setOnAnomalyRejectListener(isNull());
-
-        assertThat(event.getWarningItemInfo().hasStartTimestamp()).isTrue();
-        assertThat(event.getWarningItemInfo().hasEndTimestamp()).isTrue();
         assertThat(mPowerUsageAdvanced.mBatteryLevelData.get().getIndexByTimestamps(
                 event.getWarningItemInfo().getStartTimestamp(),
                 event.getWarningItemInfo().getEndTimestamp()
         )).isEqualTo(Pair.create(1, 0));
         verify(mBatteryChartPreferenceController).onHighlightSlotIndexUpdate(eq(1), eq(0));
         verify(mBatteryTipsController).setOnAnomalyConfirmListener(notNull());
-        verify(mBatteryTipsController).setOnAnomalyRejectListener(notNull());
     }
 
     @Test
-    public void onDisplayAnomalyEventUpdated_withNull_removeHighlightSlotEffect() {
-        final PowerAnomalyEvent event = BatteryTestUtils.createAppAnomalyEvent();
+    public void onDisplayAnomalyEventUpdated_withSettingsCardAndAppsSlotEvent_showExpected() {
+        final PowerAnomalyEvent settingsEvent =
+                BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent();
+        final PowerAnomalyEvent appsEvent =
+                BatteryTestUtils.createAppAnomalyEvent();
 
-        mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(event);
-        mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(null);
+        mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(settingsEvent, appsEvent);
 
-        assertThat(mPowerUsageAdvanced.mPowerAnomalyEvent).isNull();
-        verify(mBatteryTipsController, times(2))
-                .setOnAnomalyConfirmListener(isNull());
-        verify(mBatteryTipsController, times(2))
-                .setOnAnomalyRejectListener(isNull());
-        verify(mBatteryTipsController).setOnAnomalyConfirmListener(notNull());
-        verify(mBatteryTipsController).setOnAnomalyRejectListener(notNull());
-
-        verify(mBatteryChartPreferenceController)
-                .onHighlightSlotIndexUpdate(eq(1), eq(0));
-        verify(mBatteryChartPreferenceController).onHighlightSlotIndexUpdate(
-                eq(BatteryChartViewModel.SELECTED_INDEX_INVALID),
-                eq(BatteryChartViewModel.SELECTED_INDEX_INVALID));
+        assertThat(mPowerUsageAdvanced.mHighlightEventWrapper.get().getEventId())
+                .isEqualTo(appsEvent.getEventId());
+        verify(mBatteryChartPreferenceController).onHighlightSlotIndexUpdate(eq(1), eq(0));
+        verify(mBatteryTipsController).setOnAnomalyConfirmListener(isNull());
+        verify(mBatteryTipsController).setOnAnomalyRejectListener(isNull());
     }
-}
+
+    private void prepareCardFilterPredicate(PowerAnomalyEvent slotEvent) {
+        final Set<String> dismissedPowerAnomalyKeys =
+                DatabaseUtils.getDismissedPowerAnomalyKeys(mContext);
+        mCardFilterPredicate = event -> !dismissedPowerAnomalyKeys.contains(
+                event.getDismissRecordKey())
+                && (event.equals(slotEvent) || !event.hasWarningItemInfo());
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/NotificationAssistantPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/NotificationAssistantPreferenceControllerTest.java
index c8cf290..54a6bd4 100644
--- a/tests/robotests/src/com/android/settings/notification/NotificationAssistantPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/NotificationAssistantPreferenceControllerTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
@@ -235,7 +236,7 @@
         mPreferenceController.getDefaultNASIntent();
         mPreferenceController.updateState(mPreference);
 
-        verify(mPreference).setSwitchEnabled(eq(false));
+        verify(mPreference, atLeastOnce()).setSwitchEnabled(eq(false));
         assertFalse(mPreference.isEnabled());
     }
 }
diff --git a/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceTest.java b/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceTest.java
index 6abfe69..942db3f 100644
--- a/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceTest.java
@@ -116,6 +116,7 @@
         when(mPreference.isEnabled()).thenReturn(true);
         doCallRealMethod().when(mPreference).init();
 
+        mPreference.setStream(STREAM);
         mPreference.init();
 
         verify(mPreference, never()).updateContentDescription(CONTENT_DESCRIPTION);
@@ -136,6 +137,7 @@
     @Test
     public void init_changeProgress_overrideStateDescriptionCalled() {
         final int progress = 4;
+        when(mPreference.isEnabled()).thenReturn(true);
         when(mPreference.formatStateDescription(progress)).thenReturn(CONTENT_DESCRIPTION);
         doCallRealMethod().when(mPreference).init();
 
@@ -157,6 +159,7 @@
         when(mAudioManager.getStreamMaxVolume(STREAM)).thenReturn(max);
         when(mAudioManager.getStreamMinVolumeInt(STREAM)).thenReturn(min);
         when(mAudioManager.getStreamVolume(STREAM)).thenReturn(progress);
+        when(mPreference.isEnabled()).thenReturn(true);
         when(mPreference.getMin()).thenReturn(min);
         when(mPreference.getMax()).thenReturn(max);
         when(mPreference.getContext()).thenReturn(mContext);
@@ -168,6 +171,8 @@
         mPreference.setStream(STREAM);
         mPreference.init();
 
+        verify(mSeekBarVolumizerFactory).create(eq(STREAM), eq(null), mSbvc.capture());
+
         // On progress change, Round down the percent to match it with what the talkback says.
         // (b/285458191)
         // when progress is 4, the percent is 0.187. The state description should be set to 18%.
diff --git a/tests/robotests/src/com/android/settings/notification/app/ConversationPriorityPreferenceTest.java b/tests/robotests/src/com/android/settings/notification/app/ConversationPriorityPreferenceTest.java
index 2d2fcc8..178aee5 100644
--- a/tests/robotests/src/com/android/settings/notification/app/ConversationPriorityPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/notification/app/ConversationPriorityPreferenceTest.java
@@ -21,17 +21,15 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.content.Context;
-import android.graphics.drawable.Drawable;
 import android.util.Pair;
+import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.widget.TextView;
 
 import androidx.preference.PreferenceViewHolder;
 
@@ -50,7 +48,8 @@
 
     @Before
     public void setUp() {
-        mContext = RuntimeEnvironment.application;
+        Context context = spy(RuntimeEnvironment.application.getApplicationContext());
+        mContext = new ContextThemeWrapper(context, R.style.Theme_Settings);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/notification/app/ImportancePreferenceTest.java b/tests/robotests/src/com/android/settings/notification/app/ImportancePreferenceTest.java
index 39a5714..c5733bf 100644
--- a/tests/robotests/src/com/android/settings/notification/app/ImportancePreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/notification/app/ImportancePreferenceTest.java
@@ -28,13 +28,15 @@
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
+import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
 
+import androidx.preference.PreferenceViewHolder;
+
 import com.android.settings.R;
-import com.android.settings.notification.app.ImportancePreference;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -42,8 +44,6 @@
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-import androidx.preference.PreferenceViewHolder;
-
 @RunWith(RobolectricTestRunner.class)
 public class ImportancePreferenceTest {
 
@@ -51,7 +51,8 @@
 
     @Before
     public void setUp() {
-        mContext = RuntimeEnvironment.application;
+        Context context = spy(RuntimeEnvironment.application.getApplicationContext());
+        mContext = new ContextThemeWrapper(context, R.style.Theme_Settings);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/panel/PanelSlicesAdapterTest.java b/tests/robotests/src/com/android/settings/panel/PanelSlicesAdapterTest.java
index 516d088..9322317 100644
--- a/tests/robotests/src/com/android/settings/panel/PanelSlicesAdapterTest.java
+++ b/tests/robotests/src/com/android/settings/panel/PanelSlicesAdapterTest.java
@@ -33,9 +33,13 @@
 
 import android.content.Context;
 import android.net.Uri;
+import android.text.TextUtils;
 import android.view.LayoutInflater;
+import android.view.View;
 import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.FrameLayout;
+import android.widget.LinearLayout;
 
 import androidx.lifecycle.LiveData;
 import androidx.slice.Slice;
@@ -44,6 +48,7 @@
 import com.android.settings.panel.PanelSlicesAdapter.SliceRowViewHolder;
 import com.android.settings.testutils.FakeFeatureFactory;
 
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -94,7 +99,6 @@
                                 .get()
                                 .getSupportFragmentManager()
                                 .findFragmentById(R.id.main_content));
-
     }
 
     private void addTestLiveData(Uri uri) {
@@ -106,6 +110,61 @@
         mData.put(uri, liveData);
     }
 
+    /**
+     * Edge case where fragment context is not available.
+     */
+    @Test
+    public void withPanelFragmentContextNull_createAdapter_noExceptionThrown() {
+        when(mPanelFragment.getContext()).thenReturn(null);
+
+        final PanelSlicesAdapter adapter = spy(new PanelSlicesAdapter(mPanelFragment, mData, 0));
+
+        Assert.assertNotNull(adapter);
+    }
+
+    /**
+     * ViewHolder should load and set the action label correctly.
+     */
+    @Test
+    public void setActionLabel_loadsActionLabel() {
+        addTestLiveData(VOLUME_NOTIFICATION_URI);
+        final PanelSlicesAdapter adapter = new PanelSlicesAdapter(mPanelFragment, mData, 0);
+        final ViewGroup view = new FrameLayout(mContext);
+        final SliceRowViewHolder viewHolder = adapter.onCreateViewHolder(view, VIEW_TYPE_SLIDER);
+
+        // now let's see if setActionLabel can load and set the label correctly.
+        LinearLayout llRow = new LinearLayout(mContext);
+        viewHolder.setActionLabel(llRow);
+
+        boolean isLabelSet = isActionLabelSet(llRow);
+        Assert.assertTrue("Action label was not set correctly.", isLabelSet);
+    }
+
+    /**
+     * @param rowView the view with id row_view
+     * @return whether the accessibility action label is set
+     */
+    private boolean isActionLabelSet(View rowView) {
+        View.AccessibilityDelegate delegate = rowView.getAccessibilityDelegate();
+        if (delegate == null) {
+            return false;
+        }
+        AccessibilityNodeInfo node = new AccessibilityNodeInfo(rowView);
+        delegate.onInitializeAccessibilityNodeInfo(rowView, node);
+
+        boolean foundLabel = false;
+        final String expectedLabel =
+                mContext.getString(R.string.accessibility_action_label_panel_slice);
+        for (AccessibilityNodeInfo.AccessibilityAction action : node.getActionList()) {
+            if (action.equals(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK)
+                    && TextUtils.equals(action.getLabel(), expectedLabel)) {
+                foundLabel = true;
+                break;
+            }
+        }
+        return foundLabel;
+    }
+
     @Test
     public void sizeOfAdapter_shouldNotExceedMaxNum() {
         for (int i = 0; i < MAX_NUM_OF_SLICES + 2; i++) {
@@ -141,7 +200,7 @@
     }
 
     @Test
-    public void onCreateViewHolder_viewTypeSlider_verifyActionLabelSet() {
+    public void onBindViewHolder_viewTypeSlider_verifyActionLabelSet() {
         addTestLiveData(VOLUME_NOTIFICATION_URI);
 
         final PanelSlicesAdapter adapter =
diff --git a/tests/robotests/src/com/android/settings/system/SystemUpdatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/system/SystemUpdatePreferenceControllerTest.java
deleted file mode 100644
index 61aa294..0000000
--- a/tests/robotests/src/com/android/settings/system/SystemUpdatePreferenceControllerTest.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2016 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.system;
-
-import static android.os.SystemUpdateManager.KEY_STATUS;
-import static android.os.SystemUpdateManager.KEY_TITLE;
-import static android.os.SystemUpdateManager.STATUS_IDLE;
-import static android.os.SystemUpdateManager.STATUS_UNKNOWN;
-import static android.os.SystemUpdateManager.STATUS_WAITING_DOWNLOAD;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.SystemUpdateManager;
-
-import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
-
-import com.android.settings.R;
-import com.android.settings.testutils.shadow.ShadowUserManager;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowApplication;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(shadows = ShadowUserManager.class)
-public class SystemUpdatePreferenceControllerTest {
-
-    @Mock
-    private PreferenceScreen mScreen;
-    @Mock
-    private SystemUpdateManager mSystemUpdateManager;
-
-    private Context mContext;
-    private ShadowUserManager mShadowUserManager;
-    private SystemUpdatePreferenceController mController;
-    private Preference mPreference;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mContext = RuntimeEnvironment.application;
-        mShadowUserManager = ShadowUserManager.getShadow();
-
-        ShadowApplication.getInstance().setSystemService(Context.SYSTEM_UPDATE_SERVICE,
-                mSystemUpdateManager);
-        mController = new SystemUpdatePreferenceController(mContext);
-        mPreference = new Preference(RuntimeEnvironment.application);
-        mPreference.setKey(mController.getPreferenceKey());
-        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
-    }
-
-    @After
-    public void cleanUp() {
-        mShadowUserManager.setIsAdminUser(false);
-    }
-
-    @Test
-    public void updateNonIndexable_ifAvailable_shouldNotUpdate() {
-        final List<String> keys = new ArrayList<>();
-        mShadowUserManager.setIsAdminUser(true);
-
-        mController.updateNonIndexableKeys(keys);
-
-        assertThat(keys).isEmpty();
-    }
-
-    @Test
-    public void updateNonIndexable_ifNotAvailable_shouldUpdate() {
-        mShadowUserManager.setIsAdminUser(false);
-        final List<String> keys = new ArrayList<>();
-
-        mController.updateNonIndexableKeys(keys);
-
-        assertThat(keys).hasSize(1);
-    }
-
-    @Test
-    public void displayPrefs_ifVisible_butNotAdminUser_shouldNotDisplay() {
-        mShadowUserManager.setIsAdminUser(false);
-        mController.displayPreference(mScreen);
-
-        assertThat(mPreference.isVisible()).isFalse();
-    }
-
-    @Test
-    @Config(qualifiers = "mcc999")
-    public void displayPrefs_ifAdminUser_butNotVisible_shouldNotDisplay() {
-        mShadowUserManager.setIsAdminUser(true);
-        mController.displayPreference(mScreen);
-
-        assertThat(mPreference.isVisible()).isFalse();
-    }
-
-    @Test
-    public void displayPrefs_ifAvailable_shouldDisplay() {
-        mShadowUserManager.setIsAdminUser(true);
-
-        mController.displayPreference(mScreen);
-
-        assertThat(mPreference.isVisible()).isTrue();
-    }
-
-    @Test
-    public void updateState_systemUpdateStatusUnknown_shouldSetToAndroidVersion() {
-        final Bundle bundle = new Bundle();
-        bundle.putInt(KEY_STATUS, STATUS_UNKNOWN);
-        when(mSystemUpdateManager.retrieveSystemUpdateInfo()).thenReturn(bundle);
-
-        mController.updateState(mPreference);
-
-        assertThat(mPreference.getSummary())
-                .isEqualTo(mContext.getString(R.string.android_version_summary,
-                        Build.VERSION.RELEASE_OR_PREVIEW_DISPLAY));
-    }
-
-    @Test
-    public void updateState_systemUpdateStatusIdle_shouldSetToAndroidVersion() {
-        final String testReleaseName = "ANDROID TEST VERSION";
-
-        final Bundle bundle = new Bundle();
-        bundle.putInt(KEY_STATUS, STATUS_IDLE);
-        bundle.putString(KEY_TITLE, testReleaseName);
-        when(mSystemUpdateManager.retrieveSystemUpdateInfo()).thenReturn(bundle);
-
-        mController.updateState(mPreference);
-
-        assertThat(mPreference.getSummary())
-                .isEqualTo(mContext.getString(R.string.android_version_summary, testReleaseName));
-    }
-
-    @Test
-    public void updateState_systemUpdateInProgress_shouldSetToUpdatePending() {
-        final Bundle bundle = new Bundle();
-        bundle.putInt(KEY_STATUS, STATUS_WAITING_DOWNLOAD);
-        when(mSystemUpdateManager.retrieveSystemUpdateInfo()).thenReturn(bundle);
-
-        mController.updateState(mPreference);
-
-        assertThat(mPreference.getSummary())
-                .isEqualTo(mContext.getString(R.string.android_version_pending_update_summary));
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java b/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java
index 1035560..e98ea1b 100644
--- a/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java
+++ b/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java
@@ -247,6 +247,7 @@
                 .setEventId("BrightnessAnomaly")
                 .setType(PowerAnomalyType.TYPE_SETTINGS_BANNER)
                 .setKey(PowerAnomalyKey.KEY_BRIGHTNESS)
+                .setDismissRecordKey(PowerAnomalyKey.KEY_BRIGHTNESS.name())
                 .setScore(1.2f)
                 .setWarningBannerInfo(WarningBannerInfo.newBuilder()
                         .setMainButtonDestination(DisplaySettings.class.getName())
@@ -264,6 +265,7 @@
                 .setEventId("ScreenTimeoutAnomaly")
                 .setType(PowerAnomalyType.TYPE_SETTINGS_BANNER)
                 .setKey(PowerAnomalyKey.KEY_SCREEN_TIMEOUT)
+                .setDismissRecordKey(PowerAnomalyKey.KEY_SCREEN_TIMEOUT.name())
                 .setScore(1.1f)
                 .setWarningBannerInfo(WarningBannerInfo.newBuilder()
                         .setMainButtonDestination(ScreenTimeoutSettings.class.getName())
@@ -280,15 +282,12 @@
         return PowerAnomalyEvent.newBuilder()
                 .setEventId("AppAnomaly")
                 .setType(PowerAnomalyType.TYPE_APPS_ITEM)
-                .setKey(PowerAnomalyKey.KEY_APP)
+                .setKey(PowerAnomalyKey.KEY_APP_TOTAL_HIGHER_THAN_USUAL)
+                .setDismissRecordKey("KEY_APP_1")
                 .setScore(2.0f)
                 .setWarningItemInfo(WarningItemInfo.newBuilder()
-                        .setDismissRecordKey("KEY_APP_1")
                         .setStartTimestamp(1694361600000L)  // 2023-09-11 00:00:00
                         .setEndTimestamp(1694368800000L)    // 2023-09-11 02:00:00
-                        .setTitleString("Chrome used more battery than usual in foreground")
-                        .setMainButtonString("Check")
-                        .setCancelButtonString("Got it")
                         .build())
                 .build();
     }
diff --git a/tests/robotests/src/com/android/settings/widget/HighlightablePreferenceGroupAdapterTest.java b/tests/robotests/src/com/android/settings/widget/HighlightablePreferenceGroupAdapterTest.java
index 00f2e19..d6d5abf 100644
--- a/tests/robotests/src/com/android/settings/widget/HighlightablePreferenceGroupAdapterTest.java
+++ b/tests/robotests/src/com/android/settings/widget/HighlightablePreferenceGroupAdapterTest.java
@@ -174,6 +174,20 @@
         assertThat(mViewHolder.itemView.getTag(R.id.preference_highlighted)).isNull();
     }
 
+    /**
+     * When background is being updated, we also request the a11y focus on the preference
+     */
+    @Test
+    public void updateBackground_shouldRequestAccessibilityFocus() {
+        View viewItem = mock(View.class);
+        mViewHolder = PreferenceViewHolder.createInstanceForTests(viewItem);
+        ReflectionHelpers.setField(mAdapter, "mHighlightPosition", 10);
+
+        mAdapter.updateBackground(mViewHolder, 10);
+
+        verify(viewItem).requestAccessibilityFocus();
+    }
+
     @Test
     public void updateBackground_highlight_shouldAnimateBackgroundAndSetHighlightedTag() {
         ReflectionHelpers.setField(mAdapter, "mHighlightPosition", 10);
diff --git a/tests/spa_unit/src/com/android/settings/datausage/DataUsageListAppsControllerTest.kt b/tests/spa_unit/src/com/android/settings/datausage/DataUsageListAppsControllerTest.kt
new file mode 100644
index 0000000..af5dc89
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/datausage/DataUsageListAppsControllerTest.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.datausage
+
+import android.content.Context
+import android.content.Intent
+import android.net.NetworkTemplate
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.SettingsActivity
+import com.android.settingslib.AppItem
+import com.android.settingslib.net.NetworkCycleChartData
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doNothing
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@RunWith(AndroidJUnit4::class)
+class DataUsageListAppsControllerTest {
+
+    private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+        doNothing().whenever(mock).startActivity(any())
+    }
+
+    private val controller = DataUsageListAppsController(context, "test_key")
+
+    @Before
+    fun setUp() {
+        controller.init(mock<NetworkTemplate>())
+        val data = NetworkCycleChartData.Builder().apply {
+            setStartTime(START_TIME)
+            setEndTime(END_TIME)
+        }.build()
+        controller.setCycleData(listOf(data))
+    }
+
+    @Test
+    fun startAppDataUsage_shouldAddCyclesInfoToLaunchArguments() {
+        controller.startAppDataUsage(AppItem(), END_TIME)
+
+        val intent = argumentCaptor<Intent> {
+            verify(context).startActivity(capture())
+        }.firstValue
+        val arguments = intent.getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS)!!
+        assertThat(arguments.getLong(AppDataUsage.ARG_SELECTED_CYCLE)).isEqualTo(END_TIME)
+        assertThat(
+            arguments.getSerializable(AppDataUsage.ARG_NETWORK_CYCLES, ArrayList::class.java)
+        ).containsExactly(END_TIME, START_TIME).inOrder()
+    }
+
+    private companion object {
+        const val START_TIME = 1521583200000L
+        const val END_TIME = 1521676800000L
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/datausage/lib/AppDataUsageRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/datausage/lib/AppDataUsageRepositoryTest.kt
index 016d6d2..531e6e7 100644
--- a/tests/spa_unit/src/com/android/settings/datausage/lib/AppDataUsageRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/datausage/lib/AppDataUsageRepositoryTest.kt
@@ -20,12 +20,13 @@
 import android.content.pm.UserInfo
 import android.content.res.Resources
 import android.net.NetworkPolicyManager
+import android.net.NetworkTemplate
 import android.os.UserHandle
 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.settings.datausage.lib.AppDataUsageRepository.Bucket
+import com.android.settings.datausage.lib.AppDataUsageRepository.Companion.Bucket
 import com.android.settingslib.AppItem
 import com.android.settingslib.spaprivileged.framework.common.userManager
 import com.google.common.truth.Truth.assertThat
@@ -72,15 +73,15 @@
         val repository = AppDataUsageRepository(
             context = context,
             currentUserId = USER_ID,
-            carrierId = null,
-            getPackageName = { "" },
+            template = Template,
+            getPackageName = { null },
         )
         val buckets = listOf(
             Bucket(uid = APP_ID_1, bytes = 1),
             Bucket(uid = APP_ID_2, bytes = 2),
         )
 
-        val appPercentList = repository.getAppPercent(buckets)
+        val appPercentList = repository.getAppPercent(null, buckets)
 
         assertThat(appPercentList).hasSize(2)
         appPercentList[0].first.apply {
@@ -102,15 +103,15 @@
         val repository = AppDataUsageRepository(
             context = context,
             currentUserId = USER_ID,
-            carrierId = HIDING_CARRIER_ID,
-            getPackageName = { if (it.key == APP_ID_1) HIDING_PACKAGE_NAME else "" },
+            template = Template,
+            getPackageName = { if (it.key == APP_ID_1) HIDING_PACKAGE_NAME else null },
         )
         val buckets = listOf(
             Bucket(uid = APP_ID_1, bytes = 1),
             Bucket(uid = APP_ID_2, bytes = 2),
         )
 
-        val appPercentList = repository.getAppPercent(buckets)
+        val appPercentList = repository.getAppPercent(HIDING_CARRIER_ID, buckets)
 
         assertThat(appPercentList).hasSize(1)
         appPercentList[0].first.apply {
@@ -127,5 +128,7 @@
         const val APP_ID_2 = 110002
         const val HIDING_CARRIER_ID = 4
         const val HIDING_PACKAGE_NAME = "hiding.package.name"
+
+        val Template: NetworkTemplate = mock<NetworkTemplate>()
     }
 }
diff --git a/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt
new file mode 100644
index 0000000..c6c37d5
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt
@@ -0,0 +1,170 @@
+/*
+ * 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.apn
+
+import android.content.Context
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsOn
+import androidx.compose.ui.test.hasText
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onChild
+import androidx.compose.ui.test.onChildAt
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performScrollToNode
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.google.common.truth.Truth
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class ApnEditPageProviderTest {
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    private val context: Context = ApplicationProvider.getApplicationContext()
+    private val apnName = "apn_name"
+    private val mmsc = "mmsc"
+    private val mmsProxy = "mms_proxy"
+    private val mnc = "mnc"
+    private val apnType = "apn_type"
+    private val apnRoaming = "IPv4"
+    private val apnEnable = context.resources.getString(R.string.carrier_enabled)
+    private val apnData = ApnData(
+        name = apnName,
+        mmsc = mmsc,
+        mmsProxy = mmsProxy,
+        mnc = mnc,
+        apnType = apnType,
+        apnRoaming = apnRoaming,
+        apnEnable = true
+    )
+
+    @Test
+    fun apnEditPageProvider_name() {
+        Truth.assertThat(ApnEditPageProvider.name).isEqualTo("Apn")
+    }
+
+    @Test
+    fun title_displayed() {
+        composeTestRule.setContent {
+            ApnPage(remember {
+                mutableStateOf(apnData)
+            })
+        }
+        composeTestRule.onNodeWithText(context.getString(R.string.apn_edit)).assertIsDisplayed()
+    }
+
+    @Test
+    fun name_displayed() {
+        composeTestRule.setContent {
+            ApnPage(remember {
+                mutableStateOf(apnData)
+            })
+        }
+        composeTestRule.onNodeWithText(apnName, true).assertIsDisplayed()
+    }
+
+    @Test
+    fun mmsc_displayed() {
+        composeTestRule.setContent {
+            ApnPage(remember {
+                mutableStateOf(apnData)
+            })
+        }
+        composeTestRule.onRoot().onChild().onChildAt(0)
+            .performScrollToNode(hasText(mmsc, true))
+        composeTestRule.onNodeWithText(mmsc, true).assertIsDisplayed()
+    }
+
+    @Test
+    fun mms_proxy_displayed() {
+        composeTestRule.setContent {
+            ApnPage(remember {
+                mutableStateOf(apnData)
+            })
+        }
+        composeTestRule.onRoot().onChild().onChildAt(0)
+            .performScrollToNode(hasText(mmsProxy, true))
+        composeTestRule.onNodeWithText(mmsProxy, true).assertIsDisplayed()
+    }
+
+    @Test
+    fun mnc_displayed() {
+        composeTestRule.setContent {
+            ApnPage(remember {
+                mutableStateOf(apnData)
+            })
+        }
+        composeTestRule.onRoot().onChild().onChildAt(0)
+            .performScrollToNode(hasText(mnc, true))
+        composeTestRule.onNodeWithText(mnc, true).assertIsDisplayed()
+    }
+
+    @Test
+    fun apn_type_displayed() {
+        composeTestRule.setContent {
+            ApnPage(remember {
+                mutableStateOf(apnData)
+            })
+        }
+        composeTestRule.onRoot().onChild().onChildAt(0)
+            .performScrollToNode(hasText(apnType, true))
+        composeTestRule.onNodeWithText(apnType, true).assertIsDisplayed()
+    }
+
+    @Test
+    fun apn_roaming_displayed() {
+        composeTestRule.setContent {
+            ApnPage(remember {
+                mutableStateOf(apnData)
+            })
+        }
+        composeTestRule.onRoot().onChild().onChildAt(0)
+            .performScrollToNode(hasText(apnRoaming, true))
+        composeTestRule.onNodeWithText(apnRoaming, true).assertIsDisplayed()
+    }
+
+    @Test
+    fun carrier_enabled_displayed() {
+        composeTestRule.setContent {
+            ApnPage(remember {
+                mutableStateOf(apnData)
+            })
+        }
+        composeTestRule.onRoot().onChild().onChildAt(0)
+            .performScrollToNode(hasText(apnEnable, true))
+        composeTestRule.onNodeWithText(apnEnable, true).assertIsDisplayed()
+    }
+
+    @Test
+    fun carrier_enabled_isChecked() {
+        composeTestRule.setContent {
+            ApnPage(remember {
+                mutableStateOf(apnData)
+            })
+        }
+        composeTestRule.onRoot().onChild().onChildAt(0)
+            .performScrollToNode(hasText(apnEnable, true))
+        composeTestRule.onNodeWithText(apnEnable, true).assertIsOn()
+    }
+}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreferenceTest.kt
index 0deeaf7..2524308 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreferenceTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreferenceTest.kt
@@ -30,7 +30,6 @@
 import androidx.compose.ui.test.onNodeWithText
 import androidx.compose.ui.test.onRoot
 import androidx.compose.ui.test.performClick
-import androidx.compose.ui.test.printToLog
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
@@ -38,6 +37,7 @@
 import com.android.settings.Utils
 import com.android.settings.applications.AppStoreUtil
 import com.android.settingslib.applications.AppUtils
+import com.android.settingslib.spa.testutils.delay
 import com.android.settingslib.spa.testutils.waitUntilExists
 import com.android.settingslib.spaprivileged.model.app.userHandle
 import org.junit.After
@@ -45,13 +45,13 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mockito.any
-import org.mockito.Mockito.eq
-import org.mockito.Mockito.verify
 import org.mockito.MockitoSession
 import org.mockito.Spy
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
 import org.mockito.quality.Strictness
-import org.mockito.Mockito.`when` as whenever
 
 @RunWith(AndroidJUnit4::class)
 class AppInstallerInfoPreferenceTest {
@@ -136,7 +136,6 @@
         setContent(instantApp)
         waitUntilDisplayed()
 
-        composeTestRule.onRoot().printToLog("AAA")
         composeTestRule.onNodeWithText("More info on installer label")
             .assertIsDisplayed()
             .assertIsEnabled()
@@ -147,7 +146,6 @@
         setContent()
         waitUntilDisplayed()
 
-        composeTestRule.onRoot().printToLog("AAA")
         composeTestRule.onNodeWithText("App installed from installer label")
             .assertIsDisplayed()
             .assertIsEnabled()
@@ -158,6 +156,7 @@
         setContent()
         waitUntilDisplayed()
         composeTestRule.onRoot().performClick()
+        composeTestRule.delay()
 
         verify(context).startActivityAsUser(STORE_LINK, APP.userHandle)
     }
diff --git a/tests/spa_unit/src/com/android/settings/system/SystemUpdateManagerExtKtTest.kt b/tests/spa_unit/src/com/android/settings/system/SystemUpdateManagerExtKtTest.kt
new file mode 100644
index 0000000..0ba91df
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/system/SystemUpdateManagerExtKtTest.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.system
+
+import android.content.Context
+import android.os.Bundle
+import android.os.SystemUpdateManager
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+
+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.whenever
+
+@RunWith(AndroidJUnit4::class)
+class SystemUpdateManagerExtKtTest {
+
+    private val mockSystemUpdateManager = mock<SystemUpdateManager>()
+
+    private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+        on { getSystemService(SystemUpdateManager::class.java) } doReturn mockSystemUpdateManager
+    }
+
+    @Test
+    fun getSystemUpdateInfo() = runTest {
+        val bundle = Bundle()
+        whenever(mockSystemUpdateManager.retrieveSystemUpdateInfo()).thenReturn(bundle)
+
+        val info = context.getSystemUpdateInfo()
+
+        assertThat(info).isSameInstanceAs(bundle)
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/system/SystemUpdatePreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/system/SystemUpdatePreferenceControllerTest.kt
new file mode 100644
index 0000000..17cdf04
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/system/SystemUpdatePreferenceControllerTest.kt
@@ -0,0 +1,168 @@
+/*
+ * 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.system
+
+import android.content.Context
+import android.os.Build
+import android.os.Bundle
+import android.os.SystemClock
+import android.os.SystemUpdateManager
+import android.os.UserManager
+import androidx.lifecycle.testing.TestLifecycleOwner
+import androidx.preference.Preference
+import androidx.preference.PreferenceScreen
+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.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.whenever
+
+@RunWith(AndroidJUnit4::class)
+class SystemUpdatePreferenceControllerTest {
+    private val mockUserManager = mock<UserManager>()
+    private val mockSystemUpdateManager = mock<SystemUpdateManager>()
+
+    private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+        on { userManager } doReturn mockUserManager
+        on { getSystemService(SystemUpdateManager::class.java) } doReturn mockSystemUpdateManager
+    }
+
+    private val resources = spy(context.resources) {
+        on { getBoolean(R.bool.config_show_system_update_settings) } doReturn true
+    }
+
+    private val preference = Preference(context).apply { key = KEY }
+    private val preferenceScreen = mock<PreferenceScreen> {
+        onGeneric { findPreference(KEY) } doReturn preference
+    }
+    private val controller = SystemUpdatePreferenceController(context, KEY)
+
+    @Before
+    fun setUp() {
+        whenever(context.resources).thenReturn(resources)
+    }
+
+    @Test
+    fun updateNonIndexable_ifAvailable_shouldNotUpdate() {
+        whenever(mockUserManager.isAdminUser).thenReturn(true)
+        val keys = mutableListOf<String>()
+
+        controller.updateNonIndexableKeys(keys)
+
+        assertThat(keys).isEmpty()
+    }
+
+    @Test
+    fun updateNonIndexable_ifNotAvailable_shouldUpdate() {
+        whenever(mockUserManager.isAdminUser).thenReturn(false)
+        val keys = mutableListOf<String>()
+
+        controller.updateNonIndexableKeys(keys)
+
+        assertThat(keys).containsExactly(KEY)
+    }
+
+    @Test
+    fun displayPrefs_ifVisible_butNotAdminUser_shouldNotDisplay() {
+        whenever(mockUserManager.isAdminUser).thenReturn(false)
+
+        controller.displayPreference(preferenceScreen)
+
+        assertThat(preference.isVisible).isFalse()
+    }
+
+    @Test
+    fun displayPrefs_ifAdminUser_butNotVisible_shouldNotDisplay() {
+        whenever(mockUserManager.isAdminUser).thenReturn(true)
+        whenever(resources.getBoolean(R.bool.config_show_system_update_settings)).thenReturn(false)
+
+        controller.displayPreference(preferenceScreen)
+
+        assertThat(preference.isVisible).isFalse()
+    }
+
+    @Test
+    fun displayPrefs_ifAvailable_shouldDisplay() {
+        whenever(mockUserManager.isAdminUser).thenReturn(true)
+
+        controller.displayPreference(preferenceScreen)
+
+        assertThat(preference.isVisible).isTrue()
+    }
+
+    @Test
+    fun updateState_systemUpdateStatusUnknown_shouldSetToAndroidVersion() {
+        val bundle = Bundle().apply {
+            putInt(SystemUpdateManager.KEY_STATUS, SystemUpdateManager.STATUS_UNKNOWN)
+        }
+        whenever(mockSystemUpdateManager.retrieveSystemUpdateInfo()).thenReturn(bundle)
+        controller.displayPreference(preferenceScreen)
+
+        controller.onViewCreated(TestLifecycleOwner())
+        SystemClock.sleep(100)
+
+        assertThat(preference.summary).isEqualTo(
+            context.getString(
+                R.string.android_version_summary,
+                Build.VERSION.RELEASE_OR_PREVIEW_DISPLAY,
+            )
+        )
+    }
+
+    @Test
+    fun updateState_systemUpdateStatusIdle_shouldSetToAndroidVersion() {
+        val testReleaseName = "ANDROID TEST VERSION"
+        val bundle = Bundle().apply {
+            putInt(SystemUpdateManager.KEY_STATUS, SystemUpdateManager.STATUS_IDLE)
+            putString(SystemUpdateManager.KEY_TITLE, testReleaseName)
+        }
+        whenever(mockSystemUpdateManager.retrieveSystemUpdateInfo()).thenReturn(bundle)
+        controller.displayPreference(preferenceScreen)
+
+        controller.onViewCreated(TestLifecycleOwner())
+        SystemClock.sleep(100)
+
+        assertThat(preference.summary)
+            .isEqualTo(context.getString(R.string.android_version_summary, testReleaseName))
+    }
+
+    @Test
+    fun updateState_systemUpdateInProgress_shouldSetToUpdatePending() {
+        val bundle = Bundle().apply {
+            putInt(SystemUpdateManager.KEY_STATUS, SystemUpdateManager.STATUS_WAITING_DOWNLOAD)
+        }
+        whenever(mockSystemUpdateManager.retrieveSystemUpdateInfo()).thenReturn(bundle)
+        controller.displayPreference(preferenceScreen)
+
+        controller.onViewCreated(TestLifecycleOwner())
+        SystemClock.sleep(100)
+
+        assertThat(preference.summary)
+            .isEqualTo(context.getString(R.string.android_version_pending_update_summary))
+    }
+
+    private companion object {
+        const val KEY = "test_key"
+    }
+}
diff --git a/tests/uitests/src/com/android/settings/ui/ConnectedDeviceTests.java b/tests/uitests/src/com/android/settings/ui/ConnectedDeviceTests.java
deleted file mode 100644
index 36beb90..0000000
--- a/tests/uitests/src/com/android/settings/ui/ConnectedDeviceTests.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.ui;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.content.Context;
-import android.content.Intent;
-import android.nfc.NfcAdapter;
-import android.nfc.NfcManager;
-import android.os.RemoteException;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
-import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.UiDevice;
-import androidx.test.uiautomator.UiObject2;
-import androidx.test.uiautomator.Until;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@Ignore
-@MediumTest
-@RunWith(AndroidJUnit4.class)
-public class ConnectedDeviceTests {
-
-    private static final String SETTINGS_PACKAGE = "com.android.settings";
-    private static final int TIMEOUT = 2000;
-    private UiDevice mDevice;
-
-    @Before
-    public void setUp() throws Exception {
-        mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
-        try {
-            mDevice.setOrientationNatural();
-        } catch (RemoteException e) {
-            throw new RuntimeException("failed to freeze device orientation", e);
-        }
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        mDevice.pressBack();
-        mDevice.pressHome();
-    }
-
-    // This NFC toggle test is set up this way since there's no way to set
-    // the NFC flag to enabled or disabled without touching UI.
-    // This way, we get coverage for whether or not the toggle button works.
-    @Test
-    public void testNFCToggle() throws Exception {
-        NfcManager manager = (NfcManager) InstrumentationRegistry.getTargetContext()
-                .getSystemService(Context.NFC_SERVICE);
-        NfcAdapter nfcAdapter = manager.getDefaultAdapter();
-        boolean nfcInitiallyEnabled = nfcAdapter.isEnabled();
-        InstrumentationRegistry.getContext().startActivity(new Intent()
-                .setClassName(
-                        SETTINGS_PACKAGE,
-                        "com.android.settings.Settings$ConnectedDeviceDashboardActivity"));
-        UiObject2 nfcSetting = mDevice.wait(Until.findObject(By.text("NFC")), TIMEOUT);
-        nfcSetting.click();
-        Thread.sleep(TIMEOUT * 2);
-        if (nfcInitiallyEnabled) {
-            assertFalse("NFC wasn't disabled on toggle", nfcAdapter.isEnabled());
-            nfcSetting.click();
-            Thread.sleep(TIMEOUT * 2);
-            assertTrue("NFC wasn't enabled on toggle", nfcAdapter.isEnabled());
-        } else {
-            assertTrue("NFC wasn't enabled on toggle", nfcAdapter.isEnabled());
-            nfcSetting.click();
-            Thread.sleep(TIMEOUT * 2);
-            assertFalse("NFC wasn't disabled on toggle", nfcAdapter.isEnabled());
-        }
-    }
-}
diff --git a/tests/uitests/src/com/android/settings/ui/DataUsageSettingsTest.kt b/tests/uitests/src/com/android/settings/ui/DataUsageSettingsTest.kt
new file mode 100644
index 0000000..413fae7
--- /dev/null
+++ b/tests/uitests/src/com/android/settings/ui/DataUsageSettingsTest.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.ui
+
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.settings.ui.testutils.SettingsTestUtils.assertHasTexts
+import com.android.settings.ui.testutils.SettingsTestUtils.startMainActivityFromHomeScreen
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class DataUsageSettingsTest {
+    private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+
+    @Before
+    fun setUp() {
+        device.startMainActivityFromHomeScreen(Settings.ACTION_DATA_USAGE_SETTINGS)
+    }
+
+    @Test
+    fun hasTexts() {
+        device.assertHasTexts(ON_SCREEN_TEXTS)
+    }
+
+    private companion object {
+        val ON_SCREEN_TEXTS = listOf(
+            "0 B",
+            "Data Saver",
+            "Wi‑Fi data usage",
+        )
+    }
+}
diff --git a/tests/uitests/src/com/android/settings/ui/DataUsageSettingsTests.java b/tests/uitests/src/com/android/settings/ui/DataUsageSettingsTests.java
deleted file mode 100644
index 3befca3..0000000
--- a/tests/uitests/src/com/android/settings/ui/DataUsageSettingsTests.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.ui;
-
-import android.os.RemoteException;
-import android.provider.Settings;
-import android.system.helpers.SettingsHelper;
-import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-
-import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.UiDevice;
-import androidx.test.uiautomator.Until;
-
-import org.junit.Ignore;
-
-@Ignore
-public class DataUsageSettingsTests extends InstrumentationTestCase {
-
-    private static final String SETTINGS_PACKAGE = "com.android.settings";
-    private static final int TIMEOUT = 2000;
-    private UiDevice mDevice;
-
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        mDevice = UiDevice.getInstance(getInstrumentation());
-        try {
-            mDevice.setOrientationNatural();
-        } catch (RemoteException e) {
-            throw new RuntimeException("failed to freeze device orientaion", e);
-        }
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        // Need to finish settings activity
-        mDevice.pressBack();
-        mDevice.pressHome();
-        super.tearDown();
-    }
-
-    @MediumTest
-    public void testElementsOnDataUsageScreen() throws Exception {
-        launchDataUsageSettings();
-        assertNotNull("Data usage element not found",
-                mDevice.wait(Until.findObject(By.text("Usage")),
-                TIMEOUT));
-        assertNotNull("Data usage bar not found",
-                mDevice.wait(Until.findObject(By.res(SETTINGS_PACKAGE,
-                "color_bar")), TIMEOUT));
-        assertNotNull("WiFi Data usage element not found",
-                mDevice.wait(Until.findObject(By.text("Wi-Fi data usage")),
-                TIMEOUT));
-        assertNotNull("Network restrictions element not found",
-                mDevice.wait(Until.findObject(By.text("Network restrictions")),
-                TIMEOUT));
-    }
-
-    public void launchDataUsageSettings() throws Exception {
-        SettingsHelper.launchSettingsPage(getInstrumentation().getContext(),
-                Settings.ACTION_SETTINGS);
-        mDevice.wait(Until
-                .findObject(By.text("Network & Internet")), TIMEOUT)
-                .click();
-        Thread.sleep(TIMEOUT * 2);
-        assertNotNull("Network & internet screen not loaded", mDevice.wait(
-                Until.findObject(By.text("Data usage")), TIMEOUT));
-        mDevice.wait(Until
-                .findObject(By.text("Data usage")), TIMEOUT)
-                .click();
-    }
-}
diff --git a/tests/uitests/src/com/android/settings/ui/DevelopmentSettingsTest.kt b/tests/uitests/src/com/android/settings/ui/DevelopmentSettingsTest.kt
index 1afa3ab..1215383 100644
--- a/tests/uitests/src/com/android/settings/ui/DevelopmentSettingsTest.kt
+++ b/tests/uitests/src/com/android/settings/ui/DevelopmentSettingsTest.kt
@@ -16,13 +16,14 @@
 
 package com.android.settings.ui
 
-import android.os.SystemClock
 import android.provider.Settings
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.By
 import androidx.test.uiautomator.UiDevice
 import com.android.settings.ui.testutils.SettingsTestUtils.assertHasTexts
+import com.android.settings.ui.testutils.SettingsTestUtils.clickObject
 import com.android.settings.ui.testutils.SettingsTestUtils.startMainActivityFromHomeScreen
 import org.junit.Before
 import org.junit.Test
@@ -35,8 +36,11 @@
 
     @Before
     fun setUp() {
-        device.executeShellCommand("settings put global development_settings_enabled 1")
-        SystemClock.sleep(1000)
+        device.startMainActivityFromHomeScreen(Settings.ACTION_DEVICE_INFO_SETTINGS)
+        device.assertHasTexts(listOf(BUILD_NUMBER))
+        repeat(7) {  // Enable development mode
+            device.clickObject(By.text(BUILD_NUMBER))
+        }
         device.startMainActivityFromHomeScreen(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS)
     }
 
@@ -46,6 +50,7 @@
     }
 
     private companion object {
+        private const val BUILD_NUMBER = "Build number"
         val ON_SCREEN_TEXTS = listOf(
             "Use developer options",
             "Memory",
diff --git a/tests/uitests/src/com/android/settings/ui/MoreWirelessSettingsTests.java b/tests/uitests/src/com/android/settings/ui/MoreWirelessSettingsTests.java
index 5bfc59d..25b4767 100644
--- a/tests/uitests/src/com/android/settings/ui/MoreWirelessSettingsTests.java
+++ b/tests/uitests/src/com/android/settings/ui/MoreWirelessSettingsTests.java
@@ -24,6 +24,7 @@
 import android.test.suitebuilder.annotation.MediumTest;
 
 import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.Direction;
 import androidx.test.uiautomator.UiDevice;
 import androidx.test.uiautomator.UiObject2;
 import androidx.test.uiautomator.Until;
@@ -86,8 +87,8 @@
     public void testVPNMenuLoad() throws Exception {
         SettingsHelper.launchSettingsPage(getInstrumentation().getContext(),
                 Settings.ACTION_WIRELESS_SETTINGS);
-        mDevice.wait(Until
-                 .findObject(By.text("VPN")), TIMEOUT)
+        mDevice.findObject(By.res(SETTINGS_PACKAGE, "main_content"))
+                .scrollUntil(Direction.DOWN, Until.findObject(By.text("VPN")))
                  .click();
         Thread.sleep(TIMEOUT);
         UiObject2 usbTethering = mDevice.wait(Until
diff --git a/tests/uitests/src/com/android/settings/ui/NetworkOperatorSettingsTest.kt b/tests/uitests/src/com/android/settings/ui/NetworkOperatorSettingsTest.kt
new file mode 100644
index 0000000..701ba2f
--- /dev/null
+++ b/tests/uitests/src/com/android/settings/ui/NetworkOperatorSettingsTest.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.ui
+
+import android.provider.Settings
+import android.telephony.SubscriptionManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.settings.ui.testutils.SettingsTestUtils.assertHasTexts
+import com.android.settings.ui.testutils.SettingsTestUtils.startMainActivityFromHomeScreen
+import com.google.common.truth.TruthJUnit.assume
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class NetworkOperatorSettingsTest {
+    private val instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val device = UiDevice.getInstance(instrumentation)
+
+    @Before
+    fun setUp() {
+        assume().that(
+            instrumentation.context.getSystemService(SubscriptionManager::class.java)!!
+                .availableSubscriptionInfoList
+        ).isNotEmpty()
+        device.startMainActivityFromHomeScreen(Settings.ACTION_NETWORK_OPERATOR_SETTINGS)
+    }
+
+    @Test
+    fun hasTexts() {
+        device.assertHasTexts(ON_SCREEN_TEXTS)
+    }
+
+    private companion object {
+        val ON_SCREEN_TEXTS = listOf(
+            "Use SIM",
+            "0 B",
+            "Calls preference",
+            "SMS preference",
+            "Mobile data",
+            "Roaming",
+            "App data usage",
+            "Data warning & limit",
+            "Preferred network type",
+            "Carrier settings version",
+            "Automatically select network",
+            "Choose network",
+            "Access Point Names",
+        )
+    }
+}
diff --git a/tests/uitests/src/com/android/settings/ui/NfcSettingsTest.kt b/tests/uitests/src/com/android/settings/ui/NfcSettingsTest.kt
new file mode 100644
index 0000000..2fbbfe5
--- /dev/null
+++ b/tests/uitests/src/com/android/settings/ui/NfcSettingsTest.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.ui
+
+import android.nfc.NfcAdapter
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.settings.ui.testutils.SettingsTestUtils.assertHasTexts
+import com.android.settings.ui.testutils.SettingsTestUtils.startMainActivityFromHomeScreen
+import com.google.common.truth.TruthJUnit.assume
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class NfcSettingsTest {
+    private val instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val device = UiDevice.getInstance(instrumentation)
+
+    @Before
+    fun setUp() {
+        assume().that(NfcAdapter.getDefaultAdapter(instrumentation.context)).isNotNull()
+        device.startMainActivityFromHomeScreen(Settings.ACTION_NFC_SETTINGS)
+    }
+
+    @Test
+    fun hasTexts() {
+        device.assertHasTexts(ON_SCREEN_TEXTS)
+    }
+
+    private companion object {
+        val ON_SCREEN_TEXTS = listOf(
+            "Use NFC",
+            "Require device unlock for NFC",
+            "Contactless payments",
+        )
+    }
+}
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index 0ded397..c0d63e1 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -27,6 +27,7 @@
         "platform-test-annotations",
         "truth-prebuilt",
         "kotlinx_coroutines_test",
+        "flag-junit-base",
         // Don't add SettingsLib libraries here - you can use them directly as they are in the
         // instrumented Settings app.
     ],
diff --git a/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FakeFingerprintManagerInteractor.kt b/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FakeFingerprintManagerInteractor.kt
index e2bdd17..f807f70 100644
--- a/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FakeFingerprintManagerInteractor.kt
+++ b/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FakeFingerprintManagerInteractor.kt
@@ -22,8 +22,10 @@
 import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractor
 import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintAuthAttemptViewModel
 import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.EnrollReason
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerEnrollStateViewModel
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flow
 
 /** Fake to be used by other classes to easily fake the FingerprintManager implementation. */
 class FakeFingerprintManagerInteractor : FingerprintManagerInteractor {
@@ -32,6 +34,7 @@
   var enrolledFingerprintsInternal: MutableList<FingerprintViewModel> = mutableListOf()
   var challengeToGenerate: Pair<Long, ByteArray> = Pair(-1L, byteArrayOf())
   var authenticateAttempt = FingerprintAuthAttemptViewModel.Success(1)
+  val enrollStateViewModel = FingerEnrollStateViewModel.EnrollProgress(1)
   var pressToAuthEnabled = true
 
   var sensorProps =
@@ -53,16 +56,25 @@
   override suspend fun generateChallenge(gateKeeperPasswordHandle: Long): Pair<Long, ByteArray> {
     return challengeToGenerate
   }
-  override val enrolledFingerprints: Flow<List<FingerprintViewModel>> =
-    flowOf(enrolledFingerprintsInternal)
 
-  override val canEnrollFingerprints: Flow<Boolean> =
-    flowOf(enrolledFingerprintsInternal.size < enrollableFingerprints)
+  override val enrolledFingerprints: Flow<List<FingerprintViewModel>> = flow {
+    emit(enrolledFingerprintsInternal)
+  }
 
-  override val sensorPropertiesInternal: Flow<FingerprintSensorPropertiesInternal?> =
-    flowOf(sensorProps.first())
+  override val canEnrollFingerprints: Flow<Boolean> = flow {
+    emit(enrolledFingerprintsInternal.size < enrollableFingerprints)
+  }
 
-  override val maxEnrollableFingerprints: Flow<Int> = flowOf(enrollableFingerprints)
+  override val sensorPropertiesInternal: Flow<FingerprintSensorPropertiesInternal?> = flow {
+    emit(sensorProps.first())
+  }
+
+  override val maxEnrollableFingerprints: Flow<Int> = flow { emit(enrollableFingerprints) }
+
+  override suspend fun enroll(
+    hardwareAuthToken: ByteArray?,
+    enrollReason: EnrollReason
+  ): Flow<FingerEnrollStateViewModel> = flow { emit(enrollStateViewModel) }
 
   override suspend fun removeFingerprint(fp: FingerprintViewModel): Boolean {
     return enrolledFingerprintsInternal.remove(fp)
diff --git a/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FingerprintManagerInteractorTest.kt b/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FingerprintManagerInteractorTest.kt
index 70943f0..de2c494 100644
--- a/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FingerprintManagerInteractorTest.kt
+++ b/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FingerprintManagerInteractorTest.kt
@@ -30,6 +30,8 @@
 import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
 import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintAuthAttemptViewModel
 import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.EnrollReason.FindSensor
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerEnrollStateViewModel
 import com.android.settings.password.ChooseLockSettingsHelper
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.cancelAndJoin
@@ -143,7 +145,7 @@
         .thenReturn(byteArray)
 
       val generateChallengeCallback: ArgumentCaptor<FingerprintManager.GenerateChallengeCallback> =
-        ArgumentCaptor.forClass(FingerprintManager.GenerateChallengeCallback::class.java)
+        argumentCaptor()
 
       var result: Pair<Long, ByteArray?>? = null
       val job = testScope.launch { result = underTest.generateChallenge(1L) }
@@ -165,8 +167,7 @@
       val fingerprintViewModelToRemove = FingerprintViewModel("Finger 2", 1, 2L)
       val fingerprintToRemove = Fingerprint("Finger 2", 1, 2L)
 
-      val removalCallback: ArgumentCaptor<FingerprintManager.RemovalCallback> =
-        ArgumentCaptor.forClass(FingerprintManager.RemovalCallback::class.java)
+      val removalCallback: ArgumentCaptor<FingerprintManager.RemovalCallback> = argumentCaptor()
 
       var result: Boolean? = null
       val job =
@@ -189,8 +190,7 @@
       val fingerprintViewModelToRemove = FingerprintViewModel("Finger 2", 1, 2L)
       val fingerprintToRemove = Fingerprint("Finger 2", 1, 2L)
 
-      val removalCallback: ArgumentCaptor<FingerprintManager.RemovalCallback> =
-        ArgumentCaptor.forClass(FingerprintManager.RemovalCallback::class.java)
+      val removalCallback: ArgumentCaptor<FingerprintManager.RemovalCallback> = argumentCaptor()
 
       var result: Boolean? = null
       val job =
@@ -229,8 +229,7 @@
       var result: FingerprintAuthAttemptViewModel? = null
       val job = launch { result = underTest.authenticate() }
 
-      val authCallback: ArgumentCaptor<FingerprintManager.AuthenticationCallback> =
-        ArgumentCaptor.forClass(FingerprintManager.AuthenticationCallback::class.java)
+      val authCallback: ArgumentCaptor<FingerprintManager.AuthenticationCallback> = argumentCaptor()
 
       runCurrent()
 
@@ -257,8 +256,7 @@
       var result: FingerprintAuthAttemptViewModel? = null
       val job = launch { result = underTest.authenticate() }
 
-      val authCallback: ArgumentCaptor<FingerprintManager.AuthenticationCallback> =
-        ArgumentCaptor.forClass(FingerprintManager.AuthenticationCallback::class.java)
+      val authCallback: ArgumentCaptor<FingerprintManager.AuthenticationCallback> = argumentCaptor()
 
       runCurrent()
 
@@ -280,8 +278,82 @@
         )
     }
 
+  @Test
+  fun testEnroll_progress() =
+    testScope.runTest {
+      val token = byteArrayOf(5, 3, 2)
+      var result: FingerEnrollStateViewModel? = null
+      val job = launch { underTest.enroll(token, FindSensor).collect { result = it } }
+      val enrollCallback: ArgumentCaptor<FingerprintManager.EnrollmentCallback> = argumentCaptor()
+      runCurrent()
+
+      verify(fingerprintManager)
+        .enroll(
+          eq(token),
+          any(CancellationSignal::class.java),
+          anyInt(),
+          capture(enrollCallback),
+          eq(FingerprintManager.ENROLL_FIND_SENSOR)
+        )
+      enrollCallback.value.onEnrollmentProgress(1)
+      runCurrent()
+      job.cancelAndJoin()
+
+      assertThat(result).isEqualTo(FingerEnrollStateViewModel.EnrollProgress(1))
+    }
+
+  @Test
+  fun testEnroll_help() =
+    testScope.runTest {
+      val token = byteArrayOf(5, 3, 2)
+      var result: FingerEnrollStateViewModel? = null
+      val job = launch { underTest.enroll(token, FindSensor).collect { result = it } }
+      val enrollCallback: ArgumentCaptor<FingerprintManager.EnrollmentCallback> = argumentCaptor()
+      runCurrent()
+
+      verify(fingerprintManager)
+        .enroll(
+          eq(token),
+          any(CancellationSignal::class.java),
+          anyInt(),
+          capture(enrollCallback),
+          eq(FingerprintManager.ENROLL_FIND_SENSOR)
+        )
+      enrollCallback.value.onEnrollmentHelp(-1, "help")
+      runCurrent()
+      job.cancelAndJoin()
+
+      assertThat(result).isEqualTo(FingerEnrollStateViewModel.EnrollHelp(-1, "help"))
+    }
+
+  @Test
+  fun testEnroll_error() =
+    testScope.runTest {
+      val token = byteArrayOf(5, 3, 2)
+      var result: FingerEnrollStateViewModel? = null
+      val job = launch { underTest.enroll(token, FindSensor).collect { result = it } }
+      val enrollCallback: ArgumentCaptor<FingerprintManager.EnrollmentCallback> = argumentCaptor()
+      runCurrent()
+
+      verify(fingerprintManager)
+        .enroll(
+          eq(token),
+          any(CancellationSignal::class.java),
+          anyInt(),
+          capture(enrollCallback),
+          eq(FingerprintManager.ENROLL_FIND_SENSOR)
+        )
+      enrollCallback.value.onEnrollmentError(-2, "error")
+      runCurrent()
+      job.cancelAndJoin()
+
+      assertThat(result).isEqualTo(FingerEnrollStateViewModel.EnrollError(-2, "error"))
+    }
+
   private fun <T : Any> safeEq(value: T): T = eq(value) ?: value
   private fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
   private fun <T> any(type: Class<T>): T = Mockito.any<T>(type)
   private fun <T> whenever(methodCall: T): OngoingStubbing<T> = `when`(methodCall)
+  inline fun <reified T : Any> argumentCaptor(): ArgumentCaptor<T> =
+    ArgumentCaptor.forClass(T::class.java)
 }
diff --git a/tests/unit/src/com/android/settings/network/SubscriptionUtilTest.java b/tests/unit/src/com/android/settings/network/SubscriptionUtilTest.java
index f063042..587e734 100644
--- a/tests/unit/src/com/android/settings/network/SubscriptionUtilTest.java
+++ b/tests/unit/src/com/android/settings/network/SubscriptionUtilTest.java
@@ -18,9 +18,7 @@
 
 import static com.android.settings.network.SubscriptionUtil.KEY_UNIQUE_SUBSCRIPTION_DISPLAYNAME;
 import static com.android.settings.network.SubscriptionUtil.SUB_ID;
-
 import static com.google.common.truth.Truth.assertThat;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -185,7 +183,7 @@
     @Ignore
     @Test
     public void getUniqueDisplayNames_identicalCarriers_fourDigitsUsed() {
-        // Both subscriptoins have the same display name.
+        // Both subscriptions have the same display name.
         final SubscriptionInfo info1 = mock(SubscriptionInfo.class);
         final SubscriptionInfo info2 = mock(SubscriptionInfo.class);
         when(info1.getSubscriptionId()).thenReturn(SUBID_1);
@@ -215,7 +213,7 @@
     @Ignore
     @Test
     public void getUniqueDisplayNames_identicalCarriersAfterTrim_fourDigitsUsed() {
-        // Both subscriptoins have the same display name.
+        // Both subscriptions have the same display name.
         final SubscriptionInfo info1 = mock(SubscriptionInfo.class);
         final SubscriptionInfo info2 = mock(SubscriptionInfo.class);
         when(info1.getSubscriptionId()).thenReturn(SUBID_1);
@@ -244,8 +242,8 @@
 
     @Ignore
     @Test
-    public void getUniqueDisplayNames_phoneNumberBlocked_subscriptoinIdFallback() {
-        // Both subscriptoins have the same display name.
+    public void getUniqueDisplayNames_phoneNumberBlocked_subscriptionIdFallback() {
+        // Both subscriptions have the same display name.
         final SubscriptionInfo info1 = mock(SubscriptionInfo.class);
         final SubscriptionInfo info2 = mock(SubscriptionInfo.class);
         when(info1.getSubscriptionId()).thenReturn(SUBID_1);
@@ -273,9 +271,9 @@
 
     @Ignore
     @Test
-    public void getUniqueDisplayNames_phoneNumberIdentical_subscriptoinIdFallback() {
+    public void getUniqueDisplayNames_phoneNumberIdentical_subscriptionIdFallback() {
         // TODO have three here from the same carrier
-        // Both subscriptoins have the same display name.
+        // Both subscriptions have the same display name.
         final SubscriptionInfo info1 = mock(SubscriptionInfo.class);
         final SubscriptionInfo info2 = mock(SubscriptionInfo.class);
         final SubscriptionInfo info3 = mock(SubscriptionInfo.class);
@@ -464,8 +462,8 @@
         SharedPreferences sp = mock(SharedPreferences.class);
         when(mContext.getSharedPreferences(
                 KEY_UNIQUE_SUBSCRIPTION_DISPLAYNAME, Context.MODE_PRIVATE)).thenReturn(sp);
-        when(sp.getString(eq(SUB_ID + SUBID_1), anyString())).thenReturn(CARRIER_1 + "6789");
-        when(sp.getString(eq(SUB_ID + SUBID_2), anyString())).thenReturn(CARRIER_1 + "4321");
+        when(sp.getString(eq(SUB_ID + SUBID_1), anyString())).thenReturn(CARRIER_1 + " 6789");
+        when(sp.getString(eq(SUB_ID + SUBID_2), anyString())).thenReturn(CARRIER_1 + " 4321");
 
 
         final CharSequence nameOfSub1 =
@@ -475,8 +473,41 @@
 
         assertThat(nameOfSub1).isNotNull();
         assertThat(nameOfSub2).isNotNull();
-        assertEquals(CARRIER_1 + "6789", nameOfSub1.toString());
-        assertEquals(CARRIER_1 + "4321", nameOfSub2.toString());
+        assertEquals(CARRIER_1 + " 6789", nameOfSub1.toString());
+        assertEquals(CARRIER_1 + " 4321", nameOfSub2.toString());
+    }
+
+    @Test
+    public void getUniqueDisplayName_hasRecordAndNameIsChanged_doesNotUseRecordBeTheResult() {
+        final SubscriptionInfo info1 = mock(SubscriptionInfo.class);
+        final SubscriptionInfo info2 = mock(SubscriptionInfo.class);
+        when(info1.getSubscriptionId()).thenReturn(SUBID_1);
+        when(info2.getSubscriptionId()).thenReturn(SUBID_2);
+        when(info1.getDisplayName()).thenReturn(CARRIER_1);
+        when(info2.getDisplayName()).thenReturn(CARRIER_2);
+        when(mSubMgr.getAvailableSubscriptionInfoList()).thenReturn(
+                Arrays.asList(info1, info2));
+
+        SharedPreferences sp = mock(SharedPreferences.class);
+        SharedPreferences.Editor editor = mock(SharedPreferences.Editor.class);
+        when(mContext.getSharedPreferences(
+                KEY_UNIQUE_SUBSCRIPTION_DISPLAYNAME, Context.MODE_PRIVATE)).thenReturn(sp);
+        when(sp.edit()).thenReturn(editor);
+        when(editor.remove(anyString())).thenReturn(editor);
+
+        when(sp.getString(eq(SUB_ID + SUBID_1), anyString())).thenReturn(CARRIER_1 + " 6789");
+        when(sp.getString(eq(SUB_ID + SUBID_2), anyString())).thenReturn(CARRIER_1 + " 4321");
+
+
+        final CharSequence nameOfSub1 =
+                SubscriptionUtil.getUniqueSubscriptionDisplayName(info1, mContext);
+        final CharSequence nameOfSub2 =
+                SubscriptionUtil.getUniqueSubscriptionDisplayName(info2, mContext);
+
+        assertThat(nameOfSub1).isNotNull();
+        assertThat(nameOfSub2).isNotNull();
+        assertEquals(CARRIER_1 + " 6789", nameOfSub1.toString());
+        assertEquals(CARRIER_2.toString(), nameOfSub2.toString());
     }
 
     @Test
@@ -501,4 +532,60 @@
 
         assertTrue(SubscriptionUtil.isSimHardwareVisible(mContext));
     }
+
+    @Test
+    public void isValidCachedDisplayName_matchesRule1_returnTrue() {
+        String originalName = "originalName";
+        String cacheString = "originalName 1234";
+
+        assertThat(SubscriptionUtil.isValidCachedDisplayName(cacheString, originalName)).isTrue();
+    }
+
+    @Test
+    public void isValidCachedDisplayName_matchesRule2_returnTrue() {
+        String originalName = "original Name";
+        String cacheString = originalName + " " + 1234;
+
+        assertThat(SubscriptionUtil.isValidCachedDisplayName(cacheString, originalName)).isTrue();
+    }
+
+    @Test
+    public void isValidCachedDisplayName_nameIsEmpty1_returnFalse() {
+        String originalName = "original Name";
+        String cacheString = "";
+
+        assertThat(SubscriptionUtil.isValidCachedDisplayName(cacheString, originalName)).isFalse();
+    }
+
+    @Test
+    public void isValidCachedDisplayName_nameIsEmpty2_returnFalse() {
+        String originalName = "";
+        String cacheString = "originalName 1234";
+
+        assertThat(SubscriptionUtil.isValidCachedDisplayName(cacheString, originalName)).isFalse();
+    }
+
+    @Test
+    public void isValidCachedDisplayName_nameIsDifferent_returnFalse() {
+        String originalName = "original Name";
+        String cacheString = "originalName 1234";
+
+        assertThat(SubscriptionUtil.isValidCachedDisplayName(cacheString, originalName)).isFalse();
+    }
+
+    @Test
+    public void isValidCachedDisplayName_noNumber_returnFalse() {
+        String originalName = "original Name";
+        String cacheString = originalName;
+
+        assertThat(SubscriptionUtil.isValidCachedDisplayName(cacheString, originalName)).isFalse();
+    }
+
+    @Test
+    public void isValidCachedDisplayName_noSpace_returnFalse() {
+        String originalName = "original Name";
+        String cacheString = originalName;
+
+        assertThat(SubscriptionUtil.isValidCachedDisplayName(cacheString, originalName)).isFalse();
+    }
 }
diff --git a/tests/unit/src/com/android/settings/privatespace/PrivateSpaceSafetySourceTest.java b/tests/unit/src/com/android/settings/privatespace/PrivateSpaceSafetySourceTest.java
index 2dc00e1..ddf5287 100644
--- a/tests/unit/src/com/android/settings/privatespace/PrivateSpaceSafetySourceTest.java
+++ b/tests/unit/src/com/android/settings/privatespace/PrivateSpaceSafetySourceTest.java
@@ -27,10 +27,11 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.os.Flags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.safetycenter.SafetyEvent;
 import android.safetycenter.SafetySourceData;
 import android.safetycenter.SafetySourceStatus;
-import android.util.FeatureFlagUtils;
 
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -39,6 +40,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -51,15 +53,13 @@
             new SafetyEvent.Builder(SAFETY_EVENT_TYPE_DEVICE_REBOOTED).build();
     private Context mContext = ApplicationProvider.getApplicationContext();
     @Mock private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper;
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     /** Required setup before a test. */
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         SafetyCenterManagerWrapper.sInstance = mSafetyCenterManagerWrapper;
-
-        FeatureFlagUtils
-                .setEnabled(mContext, FeatureFlagUtils.SETTINGS_PRIVATE_SPACE_SETTINGS, true);
     }
 
     /** Required setup after a test. */
@@ -83,6 +83,7 @@
     @Test
     public void onDeviceRebootedEvent_whenSafetyCenterEnabled_setsData() {
         when(mSafetyCenterManagerWrapper.isEnabled(mContext)).thenReturn(true);
+        mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
 
         PrivateSpaceSafetySource.setSafetySourceData(mContext, EVENT_TYPE_DEVICE_REBOOTED);
 
@@ -90,13 +91,11 @@
                 any(), eq(SAFETY_SOURCE_ID), any(), eq(EVENT_TYPE_DEVICE_REBOOTED));
     }
 
-    // TODO(b/295516544): Modify this test for the new trunk stable flag instead when available.
     /** Tests that when the feature is disabled null data is set. */
     @Test
     public void setSafetySourceData_whenFeatureDisabled_setsNullData() {
         when(mSafetyCenterManagerWrapper.isEnabled(mContext)).thenReturn(true);
-        FeatureFlagUtils
-                .setEnabled(mContext, FeatureFlagUtils.SETTINGS_PRIVATE_SPACE_SETTINGS, false);
+        mSetFlagsRule.disableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
 
         PrivateSpaceSafetySource.setSafetySourceData(mContext, EVENT_TYPE_DEVICE_REBOOTED);
 
@@ -105,15 +104,13 @@
                 any(), eq(SAFETY_SOURCE_ID), captor.capture(), eq(EVENT_TYPE_DEVICE_REBOOTED));
         SafetySourceData safetySourceData = captor.getValue();
         assertThat(safetySourceData).isNull();
-
-        FeatureFlagUtils
-                .setEnabled(mContext, FeatureFlagUtils.SETTINGS_PRIVATE_SPACE_SETTINGS, true);
     }
 
     /** Tests that setSafetySourceData sets the source status enabled. */
     @Test
     public void setSafetySourceData_setsEnabled() {
         when(mSafetyCenterManagerWrapper.isEnabled(mContext)).thenReturn(true);
+        mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
 
         PrivateSpaceSafetySource.setSafetySourceData(mContext, EVENT_TYPE_DEVICE_REBOOTED);
 
@@ -129,6 +126,7 @@
     @Test
     public void setSafetySourceData_setsPsIntent() {
         when(mSafetyCenterManagerWrapper.isEnabled(mContext)).thenReturn(true);
+        mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
 
         PrivateSpaceSafetySource.setSafetySourceData(mContext, EVENT_TYPE_DEVICE_REBOOTED);
 
diff --git a/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java b/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java
index 4a6afb1..3538727 100644
--- a/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java
+++ b/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java
@@ -64,14 +64,11 @@
 
     private Context mApplicationContext;
 
-    @Mock
-    private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper;
+    @Mock private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper;
 
-    @Mock
-    private ScreenLockPreferenceDetailsUtils mScreenLockPreferenceDetailsUtils;
+    @Mock private ScreenLockPreferenceDetailsUtils mScreenLockPreferenceDetailsUtils;
 
-    @Mock
-    private LockPatternUtils mLockPatternUtils;
+    @Mock private LockPatternUtils mLockPatternUtils;
 
     @Before
     public void setUp() {
@@ -94,11 +91,11 @@
         when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(false);
         when(mScreenLockPreferenceDetailsUtils.isAvailable()).thenReturn(true);
 
-        LockScreenSafetySource.setSafetySourceData(mApplicationContext,
-                mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
+        LockScreenSafetySource.setSafetySourceData(
+                mApplicationContext, mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
 
-        verify(mSafetyCenterManagerWrapper, never()).setSafetySourceData(
-                any(), any(), any(), any());
+        verify(mSafetyCenterManagerWrapper, never())
+                .setSafetySourceData(any(), any(), any(), any());
     }
 
     @Test
@@ -106,11 +103,12 @@
         when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
         when(mScreenLockPreferenceDetailsUtils.isAvailable()).thenReturn(false);
 
-        LockScreenSafetySource.setSafetySourceData(mApplicationContext,
-                mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
+        LockScreenSafetySource.setSafetySourceData(
+                mApplicationContext, mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
 
-        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
-                any(), eq(LockScreenSafetySource.SAFETY_SOURCE_ID), eq(null), any());
+        verify(mSafetyCenterManagerWrapper)
+                .setSafetySourceData(
+                        any(), eq(LockScreenSafetySource.SAFETY_SOURCE_ID), eq(null), any());
     }
 
     @Test
@@ -118,11 +116,12 @@
         whenScreenLockIsEnabled();
         when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
 
-        LockScreenSafetySource.setSafetySourceData(mApplicationContext,
-                mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
+        LockScreenSafetySource.setSafetySourceData(
+                mApplicationContext, mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
 
-        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
-                any(), eq(LockScreenSafetySource.SAFETY_SOURCE_ID), any(), any());
+        verify(mSafetyCenterManagerWrapper)
+                .setSafetySourceData(
+                        any(), eq(LockScreenSafetySource.SAFETY_SOURCE_ID), any(), any());
     }
 
     @Test
@@ -130,11 +129,11 @@
         whenScreenLockIsEnabled();
         when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
 
-        LockScreenSafetySource.setSafetySourceData(mApplicationContext,
-                mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
+        LockScreenSafetySource.setSafetySourceData(
+                mApplicationContext, mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
 
-        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
-                any(), any(), any(), eq(EVENT_SOURCE_STATE_CHANGED));
+        verify(mSafetyCenterManagerWrapper)
+                .setSafetySourceData(any(), any(), any(), eq(EVENT_SOURCE_STATE_CHANGED));
     }
 
     @Test
@@ -142,26 +141,28 @@
         whenScreenLockIsEnabled();
         when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
 
-        LockScreenSafetySource.setSafetySourceData(mApplicationContext,
-                mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
+        LockScreenSafetySource.setSafetySourceData(
+                mApplicationContext, mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
 
         ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
-        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
-                any(), any(), captor.capture(), any());
+        verify(mSafetyCenterManagerWrapper)
+                .setSafetySourceData(any(), any(), captor.capture(), any());
         SafetySourceData safetySourceData = captor.getValue();
         SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
 
         assertThat(safetySourceStatus.getTitle().toString())
-                .isEqualTo(ResourcesUtils.getResourcesString(
-                        mApplicationContext,
-                        "unlock_set_unlock_launch_picker_title"));
-        assertThat(safetySourceStatus.getSummary().toString())
-                .isEqualTo(SUMMARY);
+                .isEqualTo(
+                        ResourcesUtils.getResourcesString(
+                                mApplicationContext, "unlock_set_unlock_launch_picker_title"));
+        assertThat(safetySourceStatus.getSummary().toString()).isEqualTo(SUMMARY);
         assertThat(safetySourceStatus.getPendingIntent().getIntent()).isNotNull();
         assertThat(safetySourceStatus.getPendingIntent().getIntent().getAction())
                 .isEqualTo(FAKE_ACTION_OPEN_SUB_SETTING);
         assertThat(
-                safetySourceStatus.getPendingIntent().getIntent().getStringExtra(EXTRA_DESTINATION))
+                        safetySourceStatus
+                                .getPendingIntent()
+                                .getIntent()
+                                .getStringExtra(EXTRA_DESTINATION))
                 .isEqualTo(FAKE_CHOOSE_LOCK_GENERIC_FRAGMENT);
     }
 
@@ -173,12 +174,16 @@
         when(mScreenLockPreferenceDetailsUtils.isPasswordQualityManaged(anyInt(), any()))
                 .thenReturn(false);
 
-        LockScreenSafetySource.setSafetySourceData(mApplicationContext,
-                mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
+        LockScreenSafetySource.setSafetySourceData(
+                mApplicationContext, mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
 
         ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
-        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
-                any(), eq(LockScreenSafetySource.SAFETY_SOURCE_ID), captor.capture(), any());
+        verify(mSafetyCenterManagerWrapper)
+                .setSafetySourceData(
+                        any(),
+                        eq(LockScreenSafetySource.SAFETY_SOURCE_ID),
+                        captor.capture(),
+                        any());
         SafetySourceData safetySourceData = captor.getValue();
         SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
 
@@ -194,12 +199,16 @@
         when(mScreenLockPreferenceDetailsUtils.isPasswordQualityManaged(anyInt(), any()))
                 .thenReturn(false);
 
-        LockScreenSafetySource.setSafetySourceData(mApplicationContext,
-                mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
+        LockScreenSafetySource.setSafetySourceData(
+                mApplicationContext, mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
 
         ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
-        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
-                any(), eq(LockScreenSafetySource.SAFETY_SOURCE_ID), captor.capture(), any());
+        verify(mSafetyCenterManagerWrapper)
+                .setSafetySourceData(
+                        any(),
+                        eq(LockScreenSafetySource.SAFETY_SOURCE_ID),
+                        captor.capture(),
+                        any());
         SafetySourceData safetySourceData = captor.getValue();
         SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
 
@@ -215,12 +224,16 @@
         when(mScreenLockPreferenceDetailsUtils.isPasswordQualityManaged(anyInt(), any()))
                 .thenReturn(true);
 
-        LockScreenSafetySource.setSafetySourceData(mApplicationContext,
-                mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
+        LockScreenSafetySource.setSafetySourceData(
+                mApplicationContext, mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
 
         ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
-        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
-                any(), eq(LockScreenSafetySource.SAFETY_SOURCE_ID), captor.capture(), any());
+        verify(mSafetyCenterManagerWrapper)
+                .setSafetySourceData(
+                        any(),
+                        eq(LockScreenSafetySource.SAFETY_SOURCE_ID),
+                        captor.capture(),
+                        any());
         SafetySourceData safetySourceData = captor.getValue();
         SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
 
@@ -236,12 +249,16 @@
         when(mScreenLockPreferenceDetailsUtils.isPasswordQualityManaged(anyInt(), any()))
                 .thenReturn(true);
 
-        LockScreenSafetySource.setSafetySourceData(mApplicationContext,
-                mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
+        LockScreenSafetySource.setSafetySourceData(
+                mApplicationContext, mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
 
         ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
-        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
-                any(), eq(LockScreenSafetySource.SAFETY_SOURCE_ID), captor.capture(), any());
+        verify(mSafetyCenterManagerWrapper)
+                .setSafetySourceData(
+                        any(),
+                        eq(LockScreenSafetySource.SAFETY_SOURCE_ID),
+                        captor.capture(),
+                        any());
         SafetySourceData safetySourceData = captor.getValue();
         SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
 
@@ -257,12 +274,12 @@
         when(mScreenLockPreferenceDetailsUtils.isPasswordQualityManaged(anyInt(), any()))
                 .thenReturn(false);
 
-        LockScreenSafetySource.setSafetySourceData(mApplicationContext,
-                mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
+        LockScreenSafetySource.setSafetySourceData(
+                mApplicationContext, mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
 
         ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
-        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
-                any(), any(), captor.capture(), any());
+        verify(mSafetyCenterManagerWrapper)
+                .setSafetySourceData(any(), any(), captor.capture(), any());
         SafetySourceData safetySourceData = captor.getValue();
 
         assertThat(safetySourceData.getIssues()).isEmpty();
@@ -276,34 +293,41 @@
         when(mScreenLockPreferenceDetailsUtils.isPasswordQualityManaged(anyInt(), any()))
                 .thenReturn(false);
 
-        LockScreenSafetySource.setSafetySourceData(mApplicationContext,
-                mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
+        LockScreenSafetySource.setSafetySourceData(
+                mApplicationContext, mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
 
         ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
-        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
-                any(), eq(LockScreenSafetySource.SAFETY_SOURCE_ID), captor.capture(), any());
+        verify(mSafetyCenterManagerWrapper)
+                .setSafetySourceData(
+                        any(),
+                        eq(LockScreenSafetySource.SAFETY_SOURCE_ID),
+                        captor.capture(),
+                        any());
         SafetySourceData safetySourceData = captor.getValue();
 
         assertThat(safetySourceData.getIssues()).hasSize(1);
         SafetySourceIssue issue = safetySourceData.getIssues().get(0);
         assertThat(issue.getId()).isEqualTo(LockScreenSafetySource.NO_SCREEN_LOCK_ISSUE_ID);
-        assertThat(issue.getTitle().toString()).isEqualTo(
-                ResourcesUtils.getResourcesString(mApplicationContext,
-                        "no_screen_lock_issue_title"));
-        assertThat(issue.getSummary().toString()).isEqualTo(
-                ResourcesUtils.getResourcesString(mApplicationContext,
-                        "no_screen_lock_issue_summary"));
-        assertThat(issue.getSeverityLevel()).isEqualTo(
-                SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION);
-        assertThat(issue.getIssueTypeId()).isEqualTo(
-                LockScreenSafetySource.NO_SCREEN_LOCK_ISSUE_TYPE_ID);
+        assertThat(issue.getTitle().toString())
+                .isEqualTo(
+                        ResourcesUtils.getResourcesString(
+                                mApplicationContext, "no_screen_lock_issue_title"));
+        assertThat(issue.getSummary().toString())
+                .isEqualTo(
+                        ResourcesUtils.getResourcesString(
+                                mApplicationContext, "no_screen_lock_issue_summary"));
+        assertThat(issue.getSeverityLevel())
+                .isEqualTo(SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION);
+        assertThat(issue.getIssueTypeId())
+                .isEqualTo(LockScreenSafetySource.NO_SCREEN_LOCK_ISSUE_TYPE_ID);
         assertThat(issue.getIssueCategory()).isEqualTo(SafetySourceIssue.ISSUE_CATEGORY_DEVICE);
         assertThat(issue.getActions()).hasSize(1);
         SafetySourceIssue.Action action = issue.getActions().get(0);
         assertThat(action.getId()).isEqualTo(LockScreenSafetySource.SET_SCREEN_LOCK_ACTION_ID);
-        assertThat(action.getLabel().toString()).isEqualTo(
-                ResourcesUtils.getResourcesString(mApplicationContext,
-                        "no_screen_lock_issue_action_label"));
+        assertThat(action.getLabel().toString())
+                .isEqualTo(
+                        ResourcesUtils.getResourcesString(
+                                mApplicationContext, "no_screen_lock_issue_action_label"));
         assertThat(action.getPendingIntent().getIntent().getAction())
                 .isEqualTo(FAKE_ACTION_OPEN_SUB_SETTING);
         assertThat(action.getPendingIntent().getIntent().getStringExtra(EXTRA_DESTINATION))
@@ -318,12 +342,12 @@
         when(mScreenLockPreferenceDetailsUtils.isPasswordQualityManaged(anyInt(), any()))
                 .thenReturn(true);
 
-        LockScreenSafetySource.setSafetySourceData(mApplicationContext,
-                mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
+        LockScreenSafetySource.setSafetySourceData(
+                mApplicationContext, mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
 
         ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
-        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
-                any(), any(), captor.capture(), any());
+        verify(mSafetyCenterManagerWrapper)
+                .setSafetySourceData(any(), any(), captor.capture(), any());
         SafetySourceData safetySourceData = captor.getValue();
 
         assertThat(safetySourceData.getIssues()).isEmpty();
@@ -337,12 +361,16 @@
         when(mScreenLockPreferenceDetailsUtils.isPasswordQualityManaged(anyInt(), any()))
                 .thenReturn(true);
 
-        LockScreenSafetySource.setSafetySourceData(mApplicationContext,
-                mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
+        LockScreenSafetySource.setSafetySourceData(
+                mApplicationContext, mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
 
         ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
-        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
-                any(), eq(LockScreenSafetySource.SAFETY_SOURCE_ID), captor.capture(), any());
+        verify(mSafetyCenterManagerWrapper)
+                .setSafetySourceData(
+                        any(),
+                        eq(LockScreenSafetySource.SAFETY_SOURCE_ID),
+                        captor.capture(),
+                        any());
         SafetySourceData safetySourceData = captor.getValue();
 
         assertThat(safetySourceData.getIssues()).isEmpty();
@@ -355,16 +383,28 @@
         when(mScreenLockPreferenceDetailsUtils.isPasswordQualityManaged(anyInt(), any()))
                 .thenReturn(true);
 
-        LockScreenSafetySource.setSafetySourceData(mApplicationContext,
-                mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
+        LockScreenSafetySource.setSafetySourceData(
+                mApplicationContext, mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
 
         ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
-        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
-                any(), eq(LockScreenSafetySource.SAFETY_SOURCE_ID), captor.capture(), any());
+        verify(mSafetyCenterManagerWrapper)
+                .setSafetySourceData(
+                        any(),
+                        eq(LockScreenSafetySource.SAFETY_SOURCE_ID),
+                        captor.capture(),
+                        any());
         SafetySourceData safetySourceData = captor.getValue();
         SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
 
         assertThat(safetySourceStatus.isEnabled()).isFalse();
+        assertThat(safetySourceStatus.getPendingIntent()).isNull();
+        assertThat(safetySourceStatus.getIconAction()).isNull();
+        assertThat(safetySourceStatus.getSeverityLevel())
+                .isEqualTo(SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED);
+        assertThat(safetySourceStatus.getSummary().toString())
+                .isEqualTo(
+                        ResourcesUtils.getResourcesString(
+                                mApplicationContext, "disabled_by_policy_title"));
     }
 
     @Test
@@ -374,16 +414,25 @@
         when(mScreenLockPreferenceDetailsUtils.isPasswordQualityManaged(anyInt(), any()))
                 .thenReturn(false);
 
-        LockScreenSafetySource.setSafetySourceData(mApplicationContext,
-                mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
+        LockScreenSafetySource.setSafetySourceData(
+                mApplicationContext, mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
 
         ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
-        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
-                any(), eq(LockScreenSafetySource.SAFETY_SOURCE_ID), captor.capture(), any());
+        verify(mSafetyCenterManagerWrapper)
+                .setSafetySourceData(
+                        any(),
+                        eq(LockScreenSafetySource.SAFETY_SOURCE_ID),
+                        captor.capture(),
+                        any());
         SafetySourceData safetySourceData = captor.getValue();
         SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
 
         assertThat(safetySourceStatus.isEnabled()).isTrue();
+        assertThat(safetySourceStatus.getPendingIntent()).isNotNull();
+        assertThat(safetySourceStatus.getIconAction()).isNotNull();
+        assertThat(safetySourceStatus.getSeverityLevel())
+                .isEqualTo(SafetySourceData.SEVERITY_LEVEL_INFORMATION);
+        assertThat(safetySourceStatus.getSummary().toString()).isEqualTo(SUMMARY);
     }
 
     @Test
@@ -392,20 +441,23 @@
         when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
         when(mScreenLockPreferenceDetailsUtils.shouldShowGearMenu()).thenReturn(true);
 
-        LockScreenSafetySource.setSafetySourceData(mApplicationContext,
-                mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
+        LockScreenSafetySource.setSafetySourceData(
+                mApplicationContext, mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
 
-        final ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(
-                SafetySourceData.class);
-        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
-                any(), eq(LockScreenSafetySource.SAFETY_SOURCE_ID), captor.capture(), any());
+        final ArgumentCaptor<SafetySourceData> captor =
+                ArgumentCaptor.forClass(SafetySourceData.class);
+        verify(mSafetyCenterManagerWrapper)
+                .setSafetySourceData(
+                        any(),
+                        eq(LockScreenSafetySource.SAFETY_SOURCE_ID),
+                        captor.capture(),
+                        any());
         final IconAction iconAction = captor.getValue().getStatus().getIconAction();
 
         assertThat(iconAction.getIconType()).isEqualTo(IconAction.ICON_TYPE_GEAR);
         assertThat(iconAction.getPendingIntent().getIntent().getAction())
                 .isEqualTo(FAKE_ACTION_OPEN_SUB_SETTING);
-        assertThat(
-                iconAction.getPendingIntent().getIntent().getStringExtra(EXTRA_DESTINATION))
+        assertThat(iconAction.getPendingIntent().getIntent().getStringExtra(EXTRA_DESTINATION))
                 .isEqualTo(FAKE_SCREEN_LOCK_SETTINGS);
     }
 
@@ -415,12 +467,16 @@
         when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
         when(mScreenLockPreferenceDetailsUtils.shouldShowGearMenu()).thenReturn(false);
 
-        LockScreenSafetySource.setSafetySourceData(mApplicationContext,
-                mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
+        LockScreenSafetySource.setSafetySourceData(
+                mApplicationContext, mScreenLockPreferenceDetailsUtils, EVENT_SOURCE_STATE_CHANGED);
 
         ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
-        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
-                any(), eq(LockScreenSafetySource.SAFETY_SOURCE_ID), captor.capture(), any());
+        verify(mSafetyCenterManagerWrapper)
+                .setSafetySourceData(
+                        any(),
+                        eq(LockScreenSafetySource.SAFETY_SOURCE_ID),
+                        captor.capture(),
+                        any());
         SafetySourceData safetySourceData = captor.getValue();
         SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
 
@@ -434,10 +490,12 @@
 
         LockScreenSafetySource.onLockScreenChange(mApplicationContext);
 
-        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
-                any(), eq(LockScreenSafetySource.SAFETY_SOURCE_ID), any(), any());
-        verify(mSafetyCenterManagerWrapper).setSafetySourceData(
-                any(), eq(BiometricsSafetySource.SAFETY_SOURCE_ID), any(), any());
+        verify(mSafetyCenterManagerWrapper)
+                .setSafetySourceData(
+                        any(), eq(LockScreenSafetySource.SAFETY_SOURCE_ID), any(), any());
+        verify(mSafetyCenterManagerWrapper)
+                .setSafetySourceData(
+                        any(), eq(BiometricsSafetySource.SAFETY_SOURCE_ID), any(), any());
     }
 
     @Test
@@ -447,8 +505,8 @@
 
         LockScreenSafetySource.onLockScreenChange(mApplicationContext);
 
-        verify(mSafetyCenterManagerWrapper, never()).setSafetySourceData(
-                any(), any(), any(), any());
+        verify(mSafetyCenterManagerWrapper, never())
+                .setSafetySourceData(any(), any(), any(), any());
     }
 
     private void whenScreenLockIsEnabled() {
@@ -456,8 +514,8 @@
         when(mScreenLockPreferenceDetailsUtils.getSummary(anyInt())).thenReturn(SUMMARY);
 
         Intent launchChooseLockGenericFragment = new Intent(FAKE_ACTION_OPEN_SUB_SETTING);
-        launchChooseLockGenericFragment.putExtra(EXTRA_DESTINATION,
-                FAKE_CHOOSE_LOCK_GENERIC_FRAGMENT);
+        launchChooseLockGenericFragment.putExtra(
+                EXTRA_DESTINATION, FAKE_CHOOSE_LOCK_GENERIC_FRAGMENT);
         when(mScreenLockPreferenceDetailsUtils.getLaunchChooseLockGenericFragmentIntent(anyInt()))
                 .thenReturn(launchChooseLockGenericFragment);
 
diff --git a/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java b/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java
index caae44a..85bd0e2 100644
--- a/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java
+++ b/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java
@@ -30,9 +30,10 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.os.Flags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.safetycenter.SafetyEvent;
 import android.safetycenter.SafetySourceData;
-import android.util.FeatureFlagUtils;
 
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -43,6 +44,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -62,6 +64,7 @@
     @Mock private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper;
 
     @Mock private LockPatternUtils mLockPatternUtils;
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     @Before
     public void setUp() {
@@ -241,15 +244,10 @@
     }
 
     /** Tests that the PS source sets null data when it's disabled. */
-    // TODO(b/295516544): Modify this test for the new trunk stable flag instead when available.
     @Test
     public void onReceive_onRefresh_withPrivateSpaceFeatureDisabled_setsNullData() {
         when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
-        FeatureFlagUtils
-                .setEnabled(
-                        mApplicationContext,
-                        FeatureFlagUtils.SETTINGS_PRIVATE_SPACE_SETTINGS,
-                        false);
+        mSetFlagsRule.disableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
 
         Intent intent =
                 new Intent()
@@ -265,12 +263,6 @@
                 .setSafetySourceData(any(), any(), captor.capture(), any());
 
         assertThat(captor.getValue()).isEqualTo(null);
-
-        FeatureFlagUtils
-                .setEnabled(
-                        mApplicationContext,
-                        FeatureFlagUtils.SETTINGS_PRIVATE_SPACE_SETTINGS,
-                        true);
     }
 
     @Test