Merge "Fix Notifications SettingsRoboTests" 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/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/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/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/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/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
index 1cca81a..756d90f 100644
--- a/src/com/android/settings/network/apn/ApnEditPageProvider.kt
+++ b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
@@ -20,16 +20,23 @@
 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
 
@@ -56,7 +63,10 @@
     @Composable
     override fun Page(arguments: Bundle?) {
         val apnDataInit = ApnData()
-        ApnPage(apnDataInit)
+        val apnDataCur = remember {
+            mutableStateOf(apnDataInit)
+        }
+        ApnPage(apnDataCur)
     }
 
     fun getRoute(
@@ -71,8 +81,13 @@
 }
 
 @Composable
-fun ApnPage(apnDataInit: ApnData) {
-    var apnData by remember { mutableStateOf(apnDataInit) }
+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),
     ) {
@@ -133,11 +148,50 @@
                 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),
diff --git a/src/com/android/settings/network/apn/ApnStatus.kt b/src/com/android/settings/network/apn/ApnStatus.kt
index 78734d0..8a2d613 100644
--- a/src/com/android/settings/network/apn/ApnStatus.kt
+++ b/src/com/android/settings/network/apn/ApnStatus.kt
@@ -36,7 +36,7 @@
     val apnType: String = "",
     val apnProtocol: String = "",
     val apnRoaming: String = "",
-    val apnEnable: Int = 1,
+    val apnEnable: Boolean = true,
     val bearer: Int = 0,
     val mvnoType: String = "",
     var mvnoValue: String = "",
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/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/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/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/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/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt
index bfdb408..c6c37d5 100644
--- a/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt
@@ -17,9 +17,17 @@
 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
@@ -34,8 +42,22 @@
     val composeTestRule = createComposeRule()
 
     private val context: Context = ApplicationProvider.getApplicationContext()
-
-    val apnData = ApnData(name = "apn_name")
+    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() {
@@ -45,7 +67,9 @@
     @Test
     fun title_displayed() {
         composeTestRule.setContent {
-            ApnPage(apnData)
+            ApnPage(remember {
+                mutableStateOf(apnData)
+            })
         }
         composeTestRule.onNodeWithText(context.getString(R.string.apn_edit)).assertIsDisplayed()
     }
@@ -53,8 +77,94 @@
     @Test
     fun name_displayed() {
         composeTestRule.setContent {
-            ApnPage(apnData)
+            ApnPage(remember {
+                mutableStateOf(apnData)
+            })
         }
-        composeTestRule.onNodeWithText("apn_name", true).assertIsDisplayed()
+        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/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/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/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();
+    }
 }