Merge "Call detect anomaly in period job only when there is new battery usage data." into main
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index c64187f..998ecc5 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -645,6 +645,7 @@
         <activity android:name="Settings$FaceSettingsActivity"
             android:label="@string/security_settings_face_preference_title"
             android:exported="true"
+            android:theme="@style/Theme.Settings.NoActionBar"
             android:icon="@drawable/ic_face_header">
             <intent-filter>
                 <action android:name="android.settings.FACE_SETTINGS" />
@@ -659,6 +660,7 @@
         <activity android:name="Settings$FaceSettingsInternalActivity"
                   android:label="@string/security_settings_face_preference_title"
                   android:exported="false"
+                  android:theme="@style/Theme.Settings.NoActionBar"
                   android:icon="@drawable/ic_face_header"
                   android:taskAffinity="com.android.settings.root">
             <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
diff --git a/aconfig/Android.bp b/aconfig/Android.bp
index d4960c2..d0a8afe 100644
--- a/aconfig/Android.bp
+++ b/aconfig/Android.bp
@@ -9,6 +9,7 @@
         "settings_connecteddevice_flag_declarations.aconfig",
         "settings_globalintl_flag_declarations.aconfig",
         "settings_apn_flag_declarations.aconfig",
+        "settings_onboarding_experience_flag_declarations.aconfig"
     ],
 }
 
diff --git a/aconfig/settings_onboarding_experience_flag_declarations.aconfig b/aconfig/settings_onboarding_experience_flag_declarations.aconfig
new file mode 100644
index 0000000..6fb5377
--- /dev/null
+++ b/aconfig/settings_onboarding_experience_flag_declarations.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.settings.flags"
+
+flag {
+  name: "enable_sound_backup"
+  namespace: "onboarding_experience"
+  description: "Feature flag for B&R sound settings."
+  bug: "278975761"
+}
diff --git a/res-product/values/strings.xml b/res-product/values/strings.xml
index 63ab6ac..0e1713c 100644
--- a/res-product/values/strings.xml
+++ b/res-product/values/strings.xml
@@ -738,4 +738,11 @@
     <string name="daltonizer_feature_summary" product="default">Adjust how colors display on your phone</string>
     <!-- The daltonizer feature summary display as a subtext as an item in a list. -->
     <string name="daltonizer_feature_summary" product="tablet">Adjust how colors display on your tablet</string>
+
+    <!-- Output device type for the phone speaker that is available for spatializer effect. [CHAR LIMIT=NONE]-->
+    <string name="spatial_audio_speaker" product="default">Phone speakers</string>
+    <!-- Output device type for the phone speaker that is available for spatializer effect. [CHAR LIMIT=NONE]-->
+    <string name="spatial_audio_speaker" product="tablet">Tablet speakers</string>
+    <!-- Output device type for the phone speaker that is available for spatializer effect. [CHAR LIMIT=NONE]-->
+    <string name="spatial_audio_speaker" product="device">Device speakers</string>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index eb8cfe7..89f3bd4 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -7387,7 +7387,7 @@
     <string name="vibrate_when_ringing_option_ramping_ringer">Vibrate first then ring gradually</string>
 
     <!-- Sound: Title for the option enabling spatializer effect. [CHAR LIMIT=30] -->
-    <string name="spatial_audio_title">Spatial audio</string>
+    <string name="spatial_audio_title">Spatial Audio</string>
 
     <!-- Sound: Other sounds: Title for the option enabling touch sounds for dial pad tones. [CHAR LIMIT=30] -->
     <string name="dial_pad_tones_title">Dial pad tones</string>
@@ -7434,9 +7434,6 @@
     <!-- Setting summary for controlling how caption text display in real time [CHAR LIMIT=NONE]-->
     <string name="live_caption_summary">Automatically caption media</string>
 
-    <!-- Output device type for the phone speaker that is available for spatializer effect. [CHAR LIMIT=NONE]-->
-    <string name="spatial_audio_speaker">Phone speaker</string>
-
     <!-- Output device type for the wired headphones that is available for spatializer effect. [CHAR LIMIT=NONE]-->
     <string name="spatial_audio_wired_headphones">Wired headphones</string>
 
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/bluetooth/BluetoothDetailsProfilesController.java b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
index 555868b..f473f2c 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
@@ -69,7 +69,9 @@
     private static final String ENABLE_DUAL_MODE_AUDIO =
             "persist.bluetooth.enable_dual_mode_audio";
     private static final String CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT = "le_audio_enabled_by_default";
-    private static final boolean LE_AUDIO_DEVICE_DETAIL_DEFAULT_VALUE = true;
+    private static final boolean LE_AUDIO_TOGGLE_VISIBLE_DEFAULT_VALUE = true;
+    private static final String LE_AUDIO_TOGGLE_VISIBLE_PROPERTY =
+            "persist.bluetooth.leaudio.toggle_visible";
 
     private LocalBluetoothManager mManager;
     private LocalBluetoothProfileManager mProfileManager;
@@ -465,15 +467,13 @@
     private void updateLeAudioConfig() {
         mIsLeContactSharingEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI,
                 SettingsUIDeviceConfig.BT_LE_AUDIO_CONTACT_SHARING_ENABLED, true);
-        boolean isLeDeviceDetailEnabled = DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_SETTINGS_UI,
-                SettingsUIDeviceConfig.BT_LE_AUDIO_DEVICE_DETAIL_ENABLED,
-                LE_AUDIO_DEVICE_DETAIL_DEFAULT_VALUE);
+        boolean isLeAudioToggleVisible = SystemProperties.getBoolean(
+                LE_AUDIO_TOGGLE_VISIBLE_PROPERTY, LE_AUDIO_TOGGLE_VISIBLE_DEFAULT_VALUE);
         boolean isLeEnabledByDefault = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH,
                 CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT, false);
-        mIsLeAudioToggleEnabled = isLeDeviceDetailEnabled || isLeEnabledByDefault;
+        mIsLeAudioToggleEnabled = isLeAudioToggleVisible || isLeEnabledByDefault;
         Log.d(TAG, "BT_LE_AUDIO_CONTACT_SHARING_ENABLED:" + mIsLeContactSharingEnabled
-                + ", BT_LE_AUDIO_DEVICE_DETAIL_ENABLED:" + isLeDeviceDetailEnabled
+                + ", LE_AUDIO_TOGGLE_VISIBLE_PROPERTY:" + isLeAudioToggleVisible
                 + ", CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT:" + isLeEnabledByDefault);
     }
 
diff --git a/src/com/android/settings/core/SettingsUIDeviceConfig.java b/src/com/android/settings/core/SettingsUIDeviceConfig.java
index 404b0b4..94074df 100644
--- a/src/com/android/settings/core/SettingsUIDeviceConfig.java
+++ b/src/com/android/settings/core/SettingsUIDeviceConfig.java
@@ -42,9 +42,4 @@
      * {@code true} whether or not event_log for generic actions is enabled. Default is true.
      */
     public static final String GENERIC_EVENT_LOGGING_ENABLED = "event_logging_enabled";
-    /**
-     * {@code true} whether to show LE Audio toggle in device detail page. Default is false.
-     */
-    public static final String BT_LE_AUDIO_DEVICE_DETAIL_ENABLED =
-            "bt_le_audio_device_detail_enabled";
 }
diff --git a/src/com/android/settings/datausage/ChartDataUsagePreference.java b/src/com/android/settings/datausage/ChartDataUsagePreference.java
index 12fb03b..e2a103e 100644
--- a/src/com/android/settings/datausage/ChartDataUsagePreference.java
+++ b/src/com/android/settings/datausage/ChartDataUsagePreference.java
@@ -56,8 +56,6 @@
     private long mStart;
     private long mEnd;
     private NetworkCycleChartData mNetworkCycleChartData;
-    private int mSecondaryColor;
-    private int mSeriesColor;
 
     public ChartDataUsagePreference(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -310,10 +308,4 @@
         mEnd = data.getEndTime();
         notifyChanged();
     }
-
-    public void setColors(int seriesColor, int secondaryColor) {
-        mSeriesColor = seriesColor;
-        mSecondaryColor = secondaryColor;
-        notifyChanged();
-    }
 }
diff --git a/src/com/android/settings/datausage/DataUsageList.java b/src/com/android/settings/datausage/DataUsageList.java
index e4bad12..39287c19 100644
--- a/src/com/android/settings/datausage/DataUsageList.java
+++ b/src/com/android/settings/datausage/DataUsageList.java
@@ -18,14 +18,12 @@
 import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.content.Intent;
-import android.graphics.Color;
 import android.net.ConnectivityManager;
 import android.net.NetworkPolicy;
 import android.net.NetworkTemplate;
 import android.os.Bundle;
 import android.os.UserManager;
 import android.provider.Settings;
-import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.util.EventLog;
 import android.util.Log;
@@ -50,7 +48,6 @@
 import com.android.settings.datausage.CycleAdapter.SpinnerInterface;
 import com.android.settings.network.MobileDataEnabledListener;
 import com.android.settings.network.MobileNetworkRepository;
-import com.android.settings.network.ProxySubscriptionManager;
 import com.android.settings.widget.LoadingViewController;
 import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
 import com.android.settingslib.net.NetworkCycleChartData;
@@ -214,8 +211,6 @@
         // network history when showing app detail.
         getLoaderManager().restartLoader(LOADER_CHART_DATA,
                 buildArgs(mTemplate), mNetworkCycleDataCallbacks);
-
-        updateBody();
     }
 
     @Override
@@ -275,33 +270,6 @@
         updatePolicy();
     }
 
-    /**
-     * Update body content based on current tab. Loads network cycle data from system, and
-     * binds them to visible controls.
-     */
-    private void updateBody() {
-        if (!isAdded()) return;
-
-        final Context context = getActivity();
-
-        // detail mode can change visible menus, invalidate
-        getActivity().invalidateOptionsMenu();
-
-        int seriesColor = context.getColor(R.color.sim_noitification);
-        if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-            final SubscriptionInfo sir = ProxySubscriptionManager.getInstance(context)
-                    .getActiveSubscriptionInfo(mSubId);
-
-            if (sir != null) {
-                seriesColor = sir.getIconTint();
-            }
-        }
-
-        final int secondaryColor = Color.argb(127, Color.red(seriesColor), Color.green(seriesColor),
-                Color.blue(seriesColor));
-        mChart.setColors(seriesColor, secondaryColor);
-    }
-
     private Bundle buildArgs(NetworkTemplate template) {
         final Bundle args = new Bundle();
         args.putParcelable(KEY_TEMPLATE, template);
diff --git a/src/com/android/settings/development/BluetoothLeAudioDeviceDetailsPreferenceController.java b/src/com/android/settings/development/BluetoothLeAudioDeviceDetailsPreferenceController.java
index 298ced0..980bdaa 100644
--- a/src/com/android/settings/development/BluetoothLeAudioDeviceDetailsPreferenceController.java
+++ b/src/com/android/settings/development/BluetoothLeAudioDeviceDetailsPreferenceController.java
@@ -20,6 +20,7 @@
 import android.bluetooth.BluetoothManager;
 import android.bluetooth.BluetoothStatusCodes;
 import android.content.Context;
+import android.os.SystemProperties;
 import android.provider.DeviceConfig;
 
 import androidx.annotation.VisibleForTesting;
@@ -27,7 +28,6 @@
 import androidx.preference.SwitchPreference;
 
 import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settings.core.SettingsUIDeviceConfig;
 import com.android.settingslib.development.DeveloperOptionsPreferenceController;
 
 /**
@@ -40,9 +40,12 @@
 
     private static final String PREFERENCE_KEY = "bluetooth_show_leaudio_device_details";
     private static final String CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT = "le_audio_enabled_by_default";
-    private static final boolean LE_AUDIO_DEVICE_DETAIL_DEFAULT_VALUE = true;
+    private static final boolean LE_AUDIO_TOGGLE_VISIBLE_DEFAULT_VALUE = true;
     static int sLeAudioSupportedStateCache = BluetoothStatusCodes.ERROR_UNKNOWN;
 
+    static final String LE_AUDIO_TOGGLE_VISIBLE_PROPERTY =
+            "persist.bluetooth.leaudio.toggle_visible";
+
     @VisibleForTesting
     BluetoothAdapter mBluetoothAdapter;
 
@@ -73,10 +76,7 @@
     @Override
     public boolean onPreferenceChange(Preference preference, Object newValue) {
         final boolean isEnabled = (Boolean) newValue;
-        DeviceConfig.setProperty(
-                DeviceConfig.NAMESPACE_SETTINGS_UI,
-                SettingsUIDeviceConfig.BT_LE_AUDIO_DEVICE_DETAIL_ENABLED,
-                isEnabled ? "true" : "false", LE_AUDIO_DEVICE_DETAIL_DEFAULT_VALUE);
+        SystemProperties.set(LE_AUDIO_TOGGLE_VISIBLE_PROPERTY, Boolean.toString(isEnabled));
         return true;
     }
 
@@ -86,25 +86,13 @@
             return;
         }
 
-        final boolean leAudioDeviceDetailEnabled = DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_SETTINGS_UI,
-                SettingsUIDeviceConfig.BT_LE_AUDIO_DEVICE_DETAIL_ENABLED,
-                LE_AUDIO_DEVICE_DETAIL_DEFAULT_VALUE);
+        final boolean isLeAudioToggleVisible = SystemProperties.getBoolean(
+                LE_AUDIO_TOGGLE_VISIBLE_PROPERTY, LE_AUDIO_TOGGLE_VISIBLE_DEFAULT_VALUE);
         final boolean leAudioEnabledByDefault = DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_BLUETOOTH, CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT, false);
 
         mPreference.setEnabled(!leAudioEnabledByDefault);
-        ((SwitchPreference) mPreference).setChecked(leAudioDeviceDetailEnabled
+        ((SwitchPreference) mPreference).setChecked(isLeAudioToggleVisible
                 || leAudioEnabledByDefault);
     }
-
-    @Override
-    protected void onDeveloperOptionsSwitchDisabled() {
-        super.onDeveloperOptionsSwitchDisabled();
-        // Reset the toggle to null when the developer option is disabled
-        DeviceConfig.setProperty(
-                DeviceConfig.NAMESPACE_SETTINGS_UI,
-                SettingsUIDeviceConfig.BT_LE_AUDIO_DEVICE_DETAIL_ENABLED, "null",
-                LE_AUDIO_DEVICE_DETAIL_DEFAULT_VALUE);
-    }
 }
diff --git a/src/com/android/settings/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 c62cc78..756d90f 100644
--- a/src/com/android/settings/network/apn/ApnEditPageProvider.kt
+++ b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
@@ -16,16 +16,27 @@
 
 package com.android.settings.network.apn
 
-import android.content.Context
 import android.net.Uri
 import android.os.Bundle
+import androidx.compose.foundation.layout.Column
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringArrayResource
 import androidx.compose.ui.res.stringResource
 import androidx.navigation.NavType
 import androidx.navigation.navArgument
 import com.android.settings.R
 import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.widget.editor.SettingsExposedDropdownMenuBox
+import com.android.settingslib.spa.widget.editor.SettingsOutlinedTextField
+import com.android.settingslib.spa.widget.preference.SwitchPreference
+import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
 import com.android.settingslib.spa.widget.scaffold.RegularScaffold
 import java.util.Base64
 
@@ -51,8 +62,11 @@
 
     @Composable
     override fun Page(arguments: Bundle?) {
-        val context = LocalContext.current
-        ApnPage(context)
+        val apnDataInit = ApnData()
+        val apnDataCur = remember {
+            mutableStateOf(apnDataInit)
+        }
+        ApnPage(apnDataCur)
     }
 
     fun getRoute(
@@ -67,9 +81,122 @@
 }
 
 @Composable
-fun ApnPage(context: Context) {
+fun ApnPage(apnDataCur: MutableState<ApnData>) {
+    var apnData by apnDataCur
+    val context = LocalContext.current
+    val authTypeOptions = stringArrayResource(R.array.apn_auth_entries).toList()
+    val apnProtocolOptions = stringArrayResource(R.array.apn_protocol_entries).toList()
+    val mvnoTypeOptions = stringArrayResource(R.array.mvno_type_entries).toList()
+
     RegularScaffold(
         title = stringResource(id = R.string.apn_edit),
     ) {
+        Column() {
+            SettingsOutlinedTextField(
+                apnData.name,
+                stringResource(R.string.apn_name),
+                enabled = apnData.nameEnabled
+            ) { apnData = apnData.copy(name = it) }
+            SettingsOutlinedTextField(
+                apnData.apn,
+                stringResource(R.string.apn_apn),
+                enabled = apnData.apnEnabled
+            ) { apnData = apnData.copy(apn = it) }
+            SettingsOutlinedTextField(
+                apnData.proxy,
+                stringResource(R.string.apn_http_proxy),
+                enabled = apnData.proxyEnabled
+            ) { apnData = apnData.copy(proxy = it) }
+            SettingsOutlinedTextField(
+                apnData.port,
+                stringResource(R.string.apn_http_port),
+                enabled = apnData.portEnabled
+            ) { apnData = apnData.copy(port = it) }
+            SettingsOutlinedTextField(
+                apnData.userName,
+                stringResource(R.string.apn_user),
+                enabled = apnData.userNameEnabled
+            ) { apnData = apnData.copy(userName = it) }
+            // TODO: password
+            SettingsOutlinedTextField(
+                apnData.server,
+                stringResource(R.string.apn_server),
+                enabled = apnData.serverEnabled
+            ) { apnData = apnData.copy(server = it) }
+            SettingsOutlinedTextField(
+                apnData.mmsc,
+                stringResource(R.string.apn_mmsc),
+                enabled = apnData.mmscEnabled
+            ) { apnData = apnData.copy(mmsc = it) }
+            SettingsOutlinedTextField(
+                apnData.mmsProxy,
+                stringResource(R.string.apn_mms_proxy),
+                enabled = apnData.mmsProxyEnabled
+            ) { apnData = apnData.copy(mmsProxy = it) }
+            SettingsOutlinedTextField(
+                apnData.mmsPort,
+                stringResource(R.string.apn_mms_port),
+                enabled = apnData.mmsPortEnabled
+            ) { apnData = apnData.copy(mmsPort = it) }
+            SettingsOutlinedTextField(
+                apnData.mcc,
+                stringResource(R.string.apn_mcc),
+                enabled = apnData.mccEnabled
+            ) { apnData = apnData.copy(mcc = it) }
+            SettingsOutlinedTextField(
+                apnData.mnc,
+                stringResource(R.string.apn_mnc),
+                enabled = apnData.mncEnabled
+            ) { apnData = apnData.copy(mnc = it) }
+            SettingsExposedDropdownMenuBox(
+                label = stringResource(R.string.apn_auth_type),
+                options = authTypeOptions,
+                selectedOptionText =
+                authTypeOptions.getOrElse(apnData.authType) { "" },
+                enabled = apnData.authTypeEnabled,
+            ) { apnData = apnData.copy(authType = authTypeOptions.indexOf(it)) }
+            SettingsOutlinedTextField(
+                apnData.apnType,
+                stringResource(R.string.apn_type),
+                enabled = apnData.apnTypeEnabled
+            ) { apnData = apnData.copy(apn = it) } // TODO: updateApnType
+            SettingsExposedDropdownMenuBox(
+                stringResource(R.string.apn_protocol),
+                apnProtocolOptions,
+                apnData.apnProtocol,
+                apnData.apnProtocolEnabled
+            ) { apnData = apnData.copy(apnProtocol = it) }
+            SettingsExposedDropdownMenuBox(
+                stringResource(R.string.apn_roaming_protocol),
+                apnProtocolOptions,
+                apnData.apnRoaming,
+                apnData.apnRoamingEnabled
+            ) { apnData = apnData.copy(apnRoaming = it) }
+            SwitchPreference(
+                object : SwitchPreferenceModel {
+                    override val title = context.resources.getString(R.string.carrier_enabled)
+                    override val changeable =
+                        stateOf(apnData.apnEnableEnabled)
+                    override val checked =
+                        stateOf(apnData.apnEnable)
+                    override val onCheckedChange = { newChecked: Boolean ->
+                        apnData = apnData.copy(apnEnable = newChecked)
+                    }
+                }
+            )
+            SettingsExposedDropdownMenuBox(
+                stringResource(R.string.mvno_type),
+                mvnoTypeOptions,
+                apnData.mvnoType,
+                apnData.mvnoTypeEnabled
+            ) {
+                apnData = apnData.copy(mvnoType = it)
+            } // TODO: mvnoDescription
+            SettingsOutlinedTextField(
+                apnData.mvnoValue,
+                stringResource(R.string.mvno_match_data),
+                enabled = apnData.mvnoValueEnabled
+            ) { apnData = apnData.copy(mvnoValue = it) }
+        }
     }
 }
\ No newline at end of file
diff --git a/src/com/android/settings/network/apn/ApnStatus.kt b/src/com/android/settings/network/apn/ApnStatus.kt
new file mode 100644
index 0000000..8a2d613
--- /dev/null
+++ b/src/com/android/settings/network/apn/ApnStatus.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.apn
+
+import android.provider.Telephony
+import android.telephony.TelephonyManager
+
+data class ApnData(
+    val name: String = "",
+    val apn: String = "",
+    val proxy: String = "",
+    val port: String = "",
+    val userName: String = "",
+    val passWord: String = "",
+    val server: String = "",
+    val mmsc: String = "",
+    val mmsProxy: String = "",
+    val mmsPort: String = "",
+    val mcc: String = "",
+    val mnc: String = "",
+    val authType: Int = -1,
+    val apnType: String = "",
+    val apnProtocol: String = "",
+    val apnRoaming: String = "",
+    val apnEnable: Boolean = true,
+    val bearer: Int = 0,
+    val mvnoType: String = "",
+    var mvnoValue: String = "",
+    val bearerBitmask: Int = 0,
+    val edited: Int = Telephony.Carriers.USER_EDITED,
+    val userEditable: Int = 1,
+    val carrierId: Int = TelephonyManager.UNKNOWN_CARRIER_ID
+) {
+    var nameEnabled = true
+    var apnEnabled = true
+    var proxyEnabled = true
+    var portEnabled = true
+    var userNameEnabled = true
+    var passWordEnabled = true
+    var serverEnabled = true
+    var mmscEnabled = true
+    var mmsProxyEnabled = true
+    var mmsPortEnabled = true
+    var mccEnabled = true
+    var mncEnabled = true
+    var authTypeEnabled = true
+    var apnTypeEnabled = true
+    var apnProtocolEnabled = true
+    var apnRoamingEnabled = true
+    var apnEnableEnabled = true
+    var bearerEnabled = true
+    var mvnoTypeEnabled = true
+    var mvnoValueEnabled = false
+}
\ No newline at end of file
diff --git a/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/development/BluetoothLeAudioDeviceDetailsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BluetoothLeAudioDeviceDetailsPreferenceControllerTest.java
index b405f9e..13bc6a4 100644
--- a/tests/robotests/src/com/android/settings/development/BluetoothLeAudioDeviceDetailsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/BluetoothLeAudioDeviceDetailsPreferenceControllerTest.java
@@ -16,6 +16,9 @@
 
 package com.android.settings.development;
 
+import static com.android.settings.development.BluetoothLeAudioDeviceDetailsPreferenceController
+        .LE_AUDIO_TOGGLE_VISIBLE_PROPERTY;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.spy;
@@ -25,12 +28,11 @@
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothStatusCodes;
 import android.content.Context;
-import android.provider.DeviceConfig;
+import android.os.SystemProperties;
 
 import androidx.preference.PreferenceScreen;
 import androidx.preference.SwitchPreference;
 
-import com.android.settings.core.SettingsUIDeviceConfig;
 import com.android.settings.testutils.shadow.ShadowDeviceConfig;
 
 import org.junit.After;
@@ -77,9 +79,8 @@
     public void onPreferenceChanged_settingEnabled_shouldTurnOnLeAudioDeviceDetailSetting() {
         mController.sLeAudioSupportedStateCache = BluetoothStatusCodes.FEATURE_SUPPORTED;
         mController.onPreferenceChange(mPreference, true /* new value */);
-        final boolean isEnabled = DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_SETTINGS_UI,
-                SettingsUIDeviceConfig.BT_LE_AUDIO_DEVICE_DETAIL_ENABLED, false);
+        final boolean isEnabled = SystemProperties.getBoolean(
+                LE_AUDIO_TOGGLE_VISIBLE_PROPERTY, false);
 
         assertThat(isEnabled).isTrue();
     }
@@ -88,9 +89,8 @@
     public void onPreferenceChanged_settingDisabled_shouldTurnOffLeAudioDeviceDetailSetting() {
         mController.sLeAudioSupportedStateCache = BluetoothStatusCodes.FEATURE_SUPPORTED;
         mController.onPreferenceChange(mPreference, false /* new value */);
-        final boolean isEnabled = DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_SETTINGS_UI,
-                SettingsUIDeviceConfig.BT_LE_AUDIO_DEVICE_DETAIL_ENABLED, false);
+        final boolean isEnabled = SystemProperties.getBoolean(
+                LE_AUDIO_TOGGLE_VISIBLE_PROPERTY, true);
 
         assertThat(isEnabled).isFalse();
     }
@@ -98,18 +98,15 @@
     @Test
     public void updateState_settingEnabled_preferenceShouldBeChecked() {
         mController.sLeAudioSupportedStateCache = BluetoothStatusCodes.FEATURE_SUPPORTED;
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
-                SettingsUIDeviceConfig.BT_LE_AUDIO_DEVICE_DETAIL_ENABLED, "true", false);
+        SystemProperties.set(LE_AUDIO_TOGGLE_VISIBLE_PROPERTY, "true");
         mController.updateState(mPreference);
-
         verify(mPreference).setChecked(true);
     }
 
     @Test
     public void updateState_settingDisabled_preferenceShouldNotBeChecked() {
         mController.sLeAudioSupportedStateCache = BluetoothStatusCodes.FEATURE_SUPPORTED;
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
-                SettingsUIDeviceConfig.BT_LE_AUDIO_DEVICE_DETAIL_ENABLED, "false", false);
+        SystemProperties.set(LE_AUDIO_TOGGLE_VISIBLE_PROPERTY, "false");
         mController.updateState(mPreference);
 
         verify(mPreference).setChecked(false);
diff --git a/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java
index 3ad14e5..19eac82 100644
--- a/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java
@@ -132,23 +132,23 @@
     }
 
     @Test
-    public void isSliceableCorrectKey_returnsTrue() {
+    public void isPublicSliceCorrectKey_returnsTrue() {
         final AmbientDisplayAlwaysOnPreferenceController controller =
                 new AmbientDisplayAlwaysOnPreferenceController(mContext,
                         "ambient_display_always_on");
-        assertThat(controller.isSliceable()).isTrue();
+        assertThat(controller.isPublicSlice()).isTrue();
     }
 
     @Test
-    public void isSliceableIncorrectKey_returnsFalse() {
+    public void isPublicSliceIncorrectKey_returnsFalse() {
         final AmbientDisplayAlwaysOnPreferenceController controller =
                 new AmbientDisplayAlwaysOnPreferenceController(mContext, "bad_key");
-        assertThat(controller.isSliceable()).isFalse();
+        assertThat(controller.isPublicSlice()).isFalse();
     }
 
     @Test
-    public void isPublicSlice_returnTrue() {
-        assertThat(mController.isPublicSlice()).isTrue();
+    public void isSliceable_returnTrue() {
+        assertThat(mController.isSliceable()).isTrue();
     }
 
     @Test
diff --git a/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt
new file mode 100644
index 0000000..c6c37d5
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.apn
+
+import android.content.Context
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsOn
+import androidx.compose.ui.test.hasText
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onChild
+import androidx.compose.ui.test.onChildAt
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performScrollToNode
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.google.common.truth.Truth
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class ApnEditPageProviderTest {
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    private val context: Context = ApplicationProvider.getApplicationContext()
+    private val apnName = "apn_name"
+    private val mmsc = "mmsc"
+    private val mmsProxy = "mms_proxy"
+    private val mnc = "mnc"
+    private val apnType = "apn_type"
+    private val apnRoaming = "IPv4"
+    private val apnEnable = context.resources.getString(R.string.carrier_enabled)
+    private val apnData = ApnData(
+        name = apnName,
+        mmsc = mmsc,
+        mmsProxy = mmsProxy,
+        mnc = mnc,
+        apnType = apnType,
+        apnRoaming = apnRoaming,
+        apnEnable = true
+    )
+
+    @Test
+    fun apnEditPageProvider_name() {
+        Truth.assertThat(ApnEditPageProvider.name).isEqualTo("Apn")
+    }
+
+    @Test
+    fun title_displayed() {
+        composeTestRule.setContent {
+            ApnPage(remember {
+                mutableStateOf(apnData)
+            })
+        }
+        composeTestRule.onNodeWithText(context.getString(R.string.apn_edit)).assertIsDisplayed()
+    }
+
+    @Test
+    fun name_displayed() {
+        composeTestRule.setContent {
+            ApnPage(remember {
+                mutableStateOf(apnData)
+            })
+        }
+        composeTestRule.onNodeWithText(apnName, true).assertIsDisplayed()
+    }
+
+    @Test
+    fun mmsc_displayed() {
+        composeTestRule.setContent {
+            ApnPage(remember {
+                mutableStateOf(apnData)
+            })
+        }
+        composeTestRule.onRoot().onChild().onChildAt(0)
+            .performScrollToNode(hasText(mmsc, true))
+        composeTestRule.onNodeWithText(mmsc, true).assertIsDisplayed()
+    }
+
+    @Test
+    fun mms_proxy_displayed() {
+        composeTestRule.setContent {
+            ApnPage(remember {
+                mutableStateOf(apnData)
+            })
+        }
+        composeTestRule.onRoot().onChild().onChildAt(0)
+            .performScrollToNode(hasText(mmsProxy, true))
+        composeTestRule.onNodeWithText(mmsProxy, true).assertIsDisplayed()
+    }
+
+    @Test
+    fun mnc_displayed() {
+        composeTestRule.setContent {
+            ApnPage(remember {
+                mutableStateOf(apnData)
+            })
+        }
+        composeTestRule.onRoot().onChild().onChildAt(0)
+            .performScrollToNode(hasText(mnc, true))
+        composeTestRule.onNodeWithText(mnc, true).assertIsDisplayed()
+    }
+
+    @Test
+    fun apn_type_displayed() {
+        composeTestRule.setContent {
+            ApnPage(remember {
+                mutableStateOf(apnData)
+            })
+        }
+        composeTestRule.onRoot().onChild().onChildAt(0)
+            .performScrollToNode(hasText(apnType, true))
+        composeTestRule.onNodeWithText(apnType, true).assertIsDisplayed()
+    }
+
+    @Test
+    fun apn_roaming_displayed() {
+        composeTestRule.setContent {
+            ApnPage(remember {
+                mutableStateOf(apnData)
+            })
+        }
+        composeTestRule.onRoot().onChild().onChildAt(0)
+            .performScrollToNode(hasText(apnRoaming, true))
+        composeTestRule.onNodeWithText(apnRoaming, true).assertIsDisplayed()
+    }
+
+    @Test
+    fun carrier_enabled_displayed() {
+        composeTestRule.setContent {
+            ApnPage(remember {
+                mutableStateOf(apnData)
+            })
+        }
+        composeTestRule.onRoot().onChild().onChildAt(0)
+            .performScrollToNode(hasText(apnEnable, true))
+        composeTestRule.onNodeWithText(apnEnable, true).assertIsDisplayed()
+    }
+
+    @Test
+    fun carrier_enabled_isChecked() {
+        composeTestRule.setContent {
+            ApnPage(remember {
+                mutableStateOf(apnData)
+            })
+        }
+        composeTestRule.onRoot().onChild().onChildAt(0)
+            .performScrollToNode(hasText(apnEnable, true))
+        composeTestRule.onNodeWithText(apnEnable, true).assertIsOn()
+    }
+}
\ No newline at end of file
diff --git a/tests/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();
+    }
 }