Merge "Import translations. DO NOT MERGE ANYWHERE" into main
diff --git a/aconfig/settings_flag_declarations.aconfig b/aconfig/settings_flag_declarations.aconfig
new file mode 100644
index 0000000..c4c33b0
--- /dev/null
+++ b/aconfig/settings_flag_declarations.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.settings.flags"
+
+flag {
+    name: "show_factory_reset_cancel_button"
+    namespace: "android_settings"
+    description: "This flag controls whether to show a Cancel button when factory reset"
+    bug: "300634367"
+}
diff --git a/res/layout/content_protection_preference_fragment.xml b/res/layout/content_protection_preference_fragment.xml
index a412d86..4c7352e 100644
--- a/res/layout/content_protection_preference_fragment.xml
+++ b/res/layout/content_protection_preference_fragment.xml
@@ -36,10 +36,11 @@
         settings:lottie_rawRes="@drawable/content_protection_preference_illustration"
         settings:searchable="false" />
 
-    <SwitchPreference
+    <com.android.settingslib.RestrictedSwitchPreference
         android:key="content_protection_preference_user_consent_work_profile_switch"
         android:title="@string/content_protection_preference_user_consent_work_profile_switch_title"
-        settings:isPreferenceVisible="false" />
+        settings:restrictedSwitchSummary="@string/summary_placeholder"
+        settings:controller="com.android.settings.security.ContentProtectionWorkSwitchController"/>
 
     <com.android.settingslib.widget.FooterPreference
         android:key="content_protection_preference_subpage_footer"
diff --git a/res/layout/fingerprint_v2_enroll_introduction.xml b/res/layout/fingerprint_v2_enroll_introduction.xml
index 2fd1f9c..cf39206 100644
--- a/res/layout/fingerprint_v2_enroll_introduction.xml
+++ b/res/layout/fingerprint_v2_enroll_introduction.xml
@@ -24,6 +24,7 @@
 
     <LinearLayout
         style="@style/SudContentFrame"
+        android:id="@+id/enroll_intro_content_view"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:clipChildren="false"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6524c20..55269e5 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1301,7 +1301,20 @@
     <!-- Title for Private Space account login error screen. [CHAR LIMIT=60] -->
     <string name="privatespace_retry_signin_title">Sign in to set up Private Space</string>
     <!-- Summary for the Private Space account login error screen. [CHAR LIMIT=NONE] -->
-    <string name="privatespace_retry_summary">You need to sign in to a Account to set up Private Space</string>
+    <string name="privatespace_retry_summary">You need to sign in to an account to set up Private Space</string>
+
+    <!-- TODO(b/309950257): Remove below strings once QSTIle fulfillment is complete. -->
+    <!-- Header in hide Private Space settings page to unhide Private Space. [CHAR LIMIT=60] -->
+    <string name="privatespace_unhide_header">To show Private Space (Not final UX)</string>
+    <!-- Text in hide Private Space settings page on how to open Private Space setting. [CHAR LIMIT=NONE] -->
+    <string name="privatespace_open_settings">Open the Settings App</string>
+    <!-- Text in hide Private Space settings page on how to open Private Space setting. [CHAR LIMIT=NONE] -->
+    <string name="privatespace_tap_settings">Tap on Security &amp; privacy > Private Space > Hide Private Space when locked</string>
+    <!-- Text in hide Private Space settings page to off hide toggle. [CHAR LIMIT=60] -->
+    <string name="privatespace_turnoff_hide">Turn off \‘Hide Private Space when locked\’ toggle</string>
+    <!-- Note in hide Private Space settings page to inform that this is a development feature. [CHAR LIMIT=NONE] -->
+    <string name="privatespace_development_note">Note to Googlers: The development of this feature is still in progress</string>
+
 
     <!-- Text shown when "Add fingerprint" button is disabled -->
     <string name="fingerprint_add_max">You can add up to <xliff:g id="count" example="5">%d</xliff:g> fingerprints</string>
diff --git a/res/xml/privatespace_hide_locked.xml b/res/xml/privatespace_hide_locked.xml
index 60a814b..f26d207 100644
--- a/res/xml/privatespace_hide_locked.xml
+++ b/res/xml/privatespace_hide_locked.xml
@@ -21,7 +21,7 @@
     <com.android.settingslib.widget.IllustrationPreference
         android:key="privatespace_hide_video"
         settings:searchable="false"
-        settings:lottie_rawRes="@raw/lottie_privatespace_hide_placeholder"/>
+        settings:lottie_rawRes="@drawable/privatespace_placeholder_image"/>
 
     <com.android.settingslib.widget.MainSwitchPreference
         android:key="hide_when_locked"
@@ -34,27 +34,33 @@
         android:selectable="false"
         settings:searchable="false" />
 
+        <Preference
+        android:key="private_space_note"
+        android:summary="@string/privatespace_development_note"
+        android:selectable="false"
+        settings:searchable="false" />
+
     <PreferenceCategory
-        android:title="@string/privatespace_access_header">
+        android:title="@string/privatespace_unhide_header">
 
         <Preference
         android:key="search_when_locked_footer"
         android:icon="@drawable/counter_1_24dp"
-        android:title="@string/privatespace_search_description"
+        android:title="@string/privatespace_open_settings"
         android:selectable="false"
         settings:searchable="false" />
 
         <Preference
             android:key="tap_tile_footer"
             android:icon="@drawable/counter_2_24dp"
-            android:title="@string/privatespace_tap_tile_description"
+            android:title="@string/privatespace_tap_settings"
             android:selectable="false"
             settings:searchable="false" />
 
         <Preference
-            android:key="unlock_profile_footer"
+            android:key="turn_off_footer"
             android:icon="@drawable/counter_3_24dp"
-            android:title="@string/privatespace_unlock_description"
+            android:title="@string/privatespace_turnoff_hide"
             android:selectable="false"
             settings:searchable="false" />
 
diff --git a/src/com/android/settings/MainClear.java b/src/com/android/settings/MainClear.java
index 58fc0d5..0aba5ca 100644
--- a/src/com/android/settings/MainClear.java
+++ b/src/com/android/settings/MainClear.java
@@ -63,6 +63,7 @@
 
 import com.android.settings.core.InstrumentedFragment;
 import com.android.settings.enterprise.ActionDisabledByAdminDialogHelper;
+import com.android.settings.flags.Flags;
 import com.android.settings.network.SubscriptionUtil;
 import com.android.settings.password.ChooseLockSettingsHelper;
 import com.android.settings.password.ConfirmLockPattern;
@@ -431,14 +432,24 @@
 
         final GlifLayout layout = mContentView.findViewById(R.id.setup_wizard_layout);
         final FooterBarMixin mixin = layout.getMixin(FooterBarMixin.class);
+        final Activity activity = getActivity();
         mixin.setPrimaryButton(
-                new FooterButton.Builder(getActivity())
+                new FooterButton.Builder(activity)
                         .setText(R.string.main_clear_button_text)
                         .setListener(mInitiateListener)
                         .setButtonType(ButtonType.OTHER)
                         .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
-                        .build()
-        );
+                        .build());
+        if (Flags.showFactoryResetCancelButton()) {
+            mixin.setSecondaryButton(
+                    new FooterButton.Builder(activity)
+                            .setText(android.R.string.cancel)
+                            .setListener(view -> activity.onBackPressed())
+                            .setButtonType(ButtonType.CANCEL)
+                            .setTheme(
+                                    com.google.android.setupdesign.R.style.SudGlifButton_Secondary)
+                            .build());
+        }
         mInitiateButton = mixin.getPrimaryButton();
     }
 
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
index 594cf7a..308b3d5 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
@@ -1399,12 +1399,14 @@
             super.onBindViewHolder(view);
             mView = view.itemView;
             mDeleteView = view.itemView.findViewById(R.id.delete_button);
-            mDeleteView.setOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    if (mOnDeleteClickListener != null) {
-                        mOnDeleteClickListener.onDeleteClick(FingerprintPreference.this);
-                    }
+            if (mFingerprint != null) {
+                mDeleteView.setContentDescription(
+                        mDeleteView.getContentDescription()
+                                + " " + mFingerprint.getName().toString());
+            }
+            mDeleteView.setOnClickListener(v -> {
+                if (mOnDeleteClickListener != null) {
+                    mOnDeleteClickListener.onDeleteClick(FingerprintPreference.this);
                 }
             });
         }
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollIntroV2Fragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollIntroV2Fragment.kt
index b1ab301..32d201d 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollIntroV2Fragment.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollIntroV2Fragment.kt
@@ -186,6 +186,9 @@
       return view
     }
 
+  /**
+   * TODO (b/305269201): This link isn't displaying for screenshot tests.
+   */
   private fun setFooterLink(view: View) {
     val footerLink: TextView = view.requireViewById(R.id.footer_learn_more)
     footerLink.movementMethod = LinkMovementMethod.getInstance()
diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java
index 6b50b70..561a51a 100644
--- a/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java
+++ b/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java
@@ -25,7 +25,9 @@
 import android.content.DialogInterface.OnShowListener;
 import android.content.Intent;
 import android.content.pm.UserInfo;
+import android.content.pm.UserProperties;
 import android.os.Bundle;
+import android.os.Flags;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Log;
@@ -183,7 +185,10 @@
         final UserManager userManager = UserManager.get(context);
         for (int i = userHandles.size() - 1; i >= 0; i--) {
             UserInfo userInfo = userManager.getUserInfo(userHandles.get(i).getIdentifier());
-            if (userInfo == null || userInfo.isCloneProfile()) {
+            if (userInfo == null
+                    || userInfo.isCloneProfile()
+                    || (Flags.allowPrivateProfile()
+                        && shouldHideUserInQuietMode(userHandles.get(i), userManager))) {
                 if (DEBUG) {
                     Log.d(TAG, "Delete the user: " + userHandles.get(i).getIdentifier());
                 }
@@ -214,7 +219,10 @@
         final UserManager userManager = UserManager.get(context);
         for (UserHandle userHandle : List.copyOf(tile.pendingIntentMap.keySet())) {
             UserInfo userInfo = userManager.getUserInfo(userHandle.getIdentifier());
-            if (userInfo == null || userInfo.isCloneProfile()) {
+            if (userInfo == null
+                    || userInfo.isCloneProfile()
+                    || (Flags.allowPrivateProfile()
+                        && shouldHideUserInQuietMode(userHandle, userManager))) {
                 if (DEBUG) {
                     Log.d(TAG, "Delete the user: " + userHandle.getIdentifier());
                 }
@@ -223,4 +231,11 @@
             }
         }
     }
+
+    private static boolean shouldHideUserInQuietMode(
+            UserHandle userHandle, UserManager userManager) {
+        UserProperties userProperties = userManager.getUserProperties(userHandle);
+        return userProperties.getHideInSettingsInQuietMode()
+                && userManager.isQuietModeEnabled(userHandle);
+    }
 }
diff --git a/src/com/android/settings/dashboard/profileselector/UserAdapter.java b/src/com/android/settings/dashboard/profileselector/UserAdapter.java
index 0552a81..66ba815 100644
--- a/src/com/android/settings/dashboard/profileselector/UserAdapter.java
+++ b/src/com/android/settings/dashboard/profileselector/UserAdapter.java
@@ -17,7 +17,6 @@
 package com.android.settings.dashboard.profileselector;
 
 import static android.app.admin.DevicePolicyResources.Strings.Settings.PERSONAL_CATEGORY_HEADER;
-import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_CATEGORY_HEADER;
 
 import android.app.ActivityManager;
 import android.app.admin.DevicePolicyManager;
@@ -51,11 +50,13 @@
     /** Holder for user details */
     public static class UserDetails {
         private final UserHandle mUserHandle;
+        private final UserManager mUserManager;
         private final Drawable mIcon;
         private final String mTitle;
 
         public UserDetails(UserHandle userHandle, UserManager um, Context context) {
             mUserHandle = userHandle;
+            mUserManager = um;
             UserInfo userInfo = um.getUserInfo(mUserHandle.getIdentifier());
             int tintColor = Utils.getColorAttrDefaultColor(context,
                     com.android.internal.R.attr.materialColorPrimaryContainer);
@@ -73,16 +74,13 @@
             DevicePolicyManager devicePolicyManager =
                     Objects.requireNonNull(context.getSystemService(DevicePolicyManager.class));
             DevicePolicyResourcesManager resources = devicePolicyManager.getResources();
-            int userHandle = mUserHandle.getIdentifier();
-            if (userHandle == UserHandle.USER_CURRENT
-                    || userHandle == ActivityManager.getCurrentUser()) {
+            int userId = mUserHandle.getIdentifier();
+            if (userId == UserHandle.USER_CURRENT || userId == ActivityManager.getCurrentUser()) {
                 return resources.getString(PERSONAL_CATEGORY_HEADER,
                         () -> context.getString(
                                 com.android.settingslib.R.string.category_personal));
-            } else {
-                return resources.getString(WORK_CATEGORY_HEADER,
-                        () -> context.getString(com.android.settingslib.R.string.category_work));
             }
+            return (String) mUserManager.getBadgedLabelForUser(/* label= */ "", mUserHandle);
         }
     }
 
diff --git a/src/com/android/settings/datausage/BillingCyclePreference.kt b/src/com/android/settings/datausage/BillingCyclePreference.kt
index 619f7e9..a6904bc 100644
--- a/src/com/android/settings/datausage/BillingCyclePreference.kt
+++ b/src/com/android/settings/datausage/BillingCyclePreference.kt
@@ -16,15 +16,21 @@
 
 import android.app.settings.SettingsEnums
 import android.content.Context
-import android.content.Intent
 import android.net.NetworkTemplate
 import android.os.Bundle
 import android.util.AttributeSet
-import androidx.preference.Preference
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.res.stringResource
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.settings.R
 import com.android.settings.core.SubSettingLauncher
 import com.android.settings.datausage.lib.BillingCycleRepository
-import com.android.settings.network.MobileDataEnabledListener
+import com.android.settings.network.mobileDataEnabledFlow
+import com.android.settings.spa.preference.ComposePreference
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import kotlinx.coroutines.flow.map
 
 /**
  * Preference which displays billing cycle of subscription
@@ -36,45 +42,31 @@
     context: Context,
     attrs: AttributeSet?,
     private val repository: BillingCycleRepository = BillingCycleRepository(context),
-) : Preference(context, attrs), TemplatePreference {
-    private lateinit var template: NetworkTemplate
-    private var subId = 0
-
-    private val listener = MobileDataEnabledListener(context) {
-        updateEnabled()
-    }
+) : ComposePreference(context, attrs), TemplatePreference {
 
     override fun setTemplate(template: NetworkTemplate, subId: Int) {
-        this.template = template
-        this.subId = subId
-        summary = null
-        updateEnabled()
-        intent = intent
+        setContent {
+            val isModifiable by remember {
+                context.mobileDataEnabledFlow(subId).map { repository.isModifiable(subId) }
+            }.collectAsStateWithLifecycle(initialValue = false)
+
+            Preference(object : PreferenceModel {
+                override val title = stringResource(R.string.billing_cycle)
+                override val enabled = { isModifiable }
+                override val onClick = { launchBillingCycleSettings(template) }
+            })
+        }
     }
 
-    override fun onAttached() {
-        super.onAttached()
-        listener.start(subId)
-    }
-
-    override fun onDetached() {
-        listener.stop()
-        super.onDetached()
-    }
-
-    private fun updateEnabled() {
-        isEnabled = repository.isModifiable(subId)
-    }
-
-    override fun getIntent(): Intent {
+    private fun launchBillingCycleSettings(template: NetworkTemplate) {
         val args = Bundle().apply {
             putParcelable(DataUsageList.EXTRA_NETWORK_TEMPLATE, template)
         }
-        return SubSettingLauncher(context).apply {
+        SubSettingLauncher(context).apply {
             setDestination(BillingCycleSettings::class.java.name)
             setArguments(args)
             setTitleRes(R.string.billing_cycle)
             setSourceMetricsCategory(SettingsEnums.PAGE_UNKNOWN)
-        }.toIntent()
+        }.launch()
     }
 }
diff --git a/src/com/android/settings/network/MobileIconGroupExt.kt b/src/com/android/settings/network/MobileIconGroupExt.kt
new file mode 100644
index 0000000..0435ef0
--- /dev/null
+++ b/src/com/android/settings/network/MobileIconGroupExt.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network
+
+import android.content.Context
+import android.telephony.SubscriptionManager
+import android.text.Html
+import com.android.settingslib.SignalIcon
+
+fun SignalIcon.MobileIconGroup.getSummaryForSub(context: Context, subId: Int): String =
+    when (dataContentDescription) {
+        0 -> ""
+        else -> {
+            SubscriptionManager.getResourcesForSubId(context, subId)
+                .getString(dataContentDescription)
+        }
+    }
+
+fun String.maybeToHtml(): CharSequence = when {
+    contains(HTML_TAG) -> Html.fromHtml(this, Html.FROM_HTML_MODE_LEGACY)
+    else -> this
+}
+
+private const val HTML_TAG = "</"
diff --git a/src/com/android/settings/network/ProviderModelSliceHelper.java b/src/com/android/settings/network/ProviderModelSliceHelper.java
index e0d1eb1..686990b 100644
--- a/src/com/android/settings/network/ProviderModelSliceHelper.java
+++ b/src/com/android/settings/network/ProviderModelSliceHelper.java
@@ -15,6 +15,7 @@
  */
 package com.android.settings.network;
 
+import static com.android.settings.network.MobileIconGroupExtKt.maybeToHtml;
 import static com.android.settings.network.telephony.MobileNetworkUtils.NO_CELL_DATA_TYPE_ICON;
 
 import android.app.PendingIntent;
@@ -31,7 +32,6 @@
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
-import android.text.Html;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -176,7 +176,7 @@
 
     protected ListBuilder.RowBuilder createCarrierRow(String networkTypeDescription) {
         final String title = getMobileTitle();
-        final String summary = getMobileSummary(networkTypeDescription);
+        final CharSequence summary = getMobileSummary(networkTypeDescription);
         Drawable drawable = mContext.getDrawable(
                 R.drawable.ic_signal_strength_zero_bar_no_internet);
         try {
@@ -195,7 +195,7 @@
                 .setTitleItem(levelIcon, ListBuilder.ICON_IMAGE)
                 .addEndItem(toggleAction)
                 .setPrimaryAction(primaryAction)
-                .setSubtitle(Html.fromHtml(summary, Html.FROM_HTML_MODE_LEGACY));
+                .setSubtitle(summary);
         return rowBuilder;
     }
 
@@ -255,7 +255,7 @@
         return drawable;
     }
 
-    private String getMobileSummary(String networkTypeDescription) {
+    private CharSequence getMobileSummary(String networkTypeDescription) {
         if (!isMobileDataEnabled()) {
             return mContext.getString(R.string.mobile_data_off_summary);
         }
@@ -268,7 +268,7 @@
                     mContext.getString(R.string.mobile_data_connection_active),
                     networkTypeDescription);
         }
-        return summary;
+        return maybeToHtml(summary);
     }
 
     protected String getMobileTitle() {
diff --git a/src/com/android/settings/network/SubscriptionsPreferenceController.java b/src/com/android/settings/network/SubscriptionsPreferenceController.java
index cc62189..0c3e6bd 100644
--- a/src/com/android/settings/network/SubscriptionsPreferenceController.java
+++ b/src/com/android/settings/network/SubscriptionsPreferenceController.java
@@ -19,6 +19,8 @@
 import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE;
 import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
 
+import static com.android.settings.network.MobileIconGroupExtKt.getSummaryForSub;
+import static com.android.settings.network.MobileIconGroupExtKt.maybeToHtml;
 import static com.android.settings.network.telephony.MobileNetworkUtils.NO_CELL_DATA_TYPE_ICON;
 import static com.android.settingslib.mobile.MobileMappings.getIconKey;
 import static com.android.settingslib.mobile.MobileMappings.mapIconSets;
@@ -39,7 +41,6 @@
 import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyDisplayInfo;
 import android.telephony.TelephonyManager;
-import android.text.Html;
 import android.util.ArraySet;
 import android.util.Log;
 
@@ -289,18 +290,19 @@
         String result = mSubsPrefCtrlInjector.getNetworkType(mContext, mConfig,
                 mTelephonyDisplayInfo, subId, isCarrierNetworkActive, mCarrierNetworkChangeMode);
         if (mSubsPrefCtrlInjector.isActiveCellularNetwork(mContext) || isCarrierNetworkActive) {
+            String connectionState = mContext.getString(isDds
+                    ? R.string.mobile_data_connection_active
+                    : R.string.mobile_data_temp_connection_active);
             if (result.isEmpty()) {
-                result = mContext.getString(isDds ? R.string.mobile_data_connection_active
-                        : R.string.mobile_data_temp_connection_active);
+                return connectionState;
             } else {
-                result = mContext.getString(R.string.preference_summary_default_combination,
-                        mContext.getString(isDds ? R.string.mobile_data_connection_active
-                                : R.string.mobile_data_temp_connection_active), result);
+                result = mContext.getString(
+                        R.string.preference_summary_default_combination, connectionState, result);
             }
         } else if (!isDataInService) {
-            result = mContext.getString(R.string.mobile_data_no_connection);
+            return mContext.getString(R.string.mobile_data_no_connection);
         }
-        return Html.fromHtml(result, Html.FROM_HTML_MODE_LEGACY);
+        return maybeToHtml(result);
     }
 
     @VisibleForTesting
@@ -579,10 +581,7 @@
                 return "";
             }
 
-            int resId = iconGroup.dataContentDescription;
-            return resId != 0
-                    ? SubscriptionManager.getResourcesForSubId(context, subId).getString(resId)
-                    : "";
+            return getSummaryForSub(iconGroup, context, subId);
         }
 
         /**
diff --git a/src/com/android/settings/network/telephony/NetworkProviderWorker.java b/src/com/android/settings/network/telephony/NetworkProviderWorker.java
index c731bfd..ddfc031 100644
--- a/src/com/android/settings/network/telephony/NetworkProviderWorker.java
+++ b/src/com/android/settings/network/telephony/NetworkProviderWorker.java
@@ -17,6 +17,7 @@
 package com.android.settings.network.telephony;
 
 import static com.android.settings.network.InternetUpdater.INTERNET_ETHERNET;
+import static com.android.settings.network.MobileIconGroupExtKt.getSummaryForSub;
 import static com.android.settingslib.mobile.MobileMappings.getIconKey;
 import static com.android.settingslib.mobile.MobileMappings.mapIconSets;
 
@@ -284,19 +285,14 @@
 
     private String updateNetworkTypeName(Context context, Config config,
             TelephonyDisplayInfo telephonyDisplayInfo, int subId) {
-        String iconKey = getIconKey(telephonyDisplayInfo);
-        int resId = mapIconSets(config).get(iconKey).dataContentDescription;
         if (mWifiPickerTrackerHelper != null
                 && mWifiPickerTrackerHelper.isCarrierNetworkActive()) {
             MobileIconGroup carrierMergedWifiIconGroup = TelephonyIcons.CARRIER_MERGED_WIFI;
-            resId = carrierMergedWifiIconGroup.dataContentDescription;
-            return resId != 0
-                    ? SubscriptionManager.getResourcesForSubId(context, subId)
-                    .getString(resId) : "";
+            return getSummaryForSub(carrierMergedWifiIconGroup, context, subId);
         }
 
-        return resId != 0
-                ? SubscriptionManager.getResourcesForSubId(context, subId).getString(resId) : "";
+        String iconKey = getIconKey(telephonyDisplayInfo);
+        return getSummaryForSub(mapIconSets(config).get(iconKey), context, subId);
     }
 
     @VisibleForTesting
diff --git a/src/com/android/settings/security/ContentProtectionPreferenceFragment.java b/src/com/android/settings/security/ContentProtectionPreferenceFragment.java
index 476d93e..5c72b4d 100644
--- a/src/com/android/settings/security/ContentProtectionPreferenceFragment.java
+++ b/src/com/android/settings/security/ContentProtectionPreferenceFragment.java
@@ -34,17 +34,11 @@
 public class ContentProtectionPreferenceFragment extends DashboardFragment {
     private static final String TAG = "ContentProtectionPreferenceFragment";
 
-    @VisibleForTesting
-    static final String KEY_WORK_PROFILE_SWITCH =
-            "content_protection_preference_user_consent_work_profile_switch";
-
     // Required by @SearchIndexable to make the fragment and preferences to be indexed.
     // Do not rename.
     public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
             new BaseSearchIndexProvider(R.layout.content_protection_preference_fragment);
 
-    private SwitchPreference mWorkProfileSwitch;
-
     @Override
     public void onAttach(Context context) {
         super.onAttach(context);
@@ -53,14 +47,6 @@
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
-
-        mWorkProfileSwitch = getPreferenceScreen().findPreference(KEY_WORK_PROFILE_SWITCH);
-        // If any work profile on the device, display the disable toggle unchecked
-        if (Utils.getManagedProfile(getContext().getSystemService(UserManager.class)) != null) {
-            mWorkProfileSwitch.setVisible(true);
-            mWorkProfileSwitch.setEnabled(false);
-            mWorkProfileSwitch.setChecked(false);
-        }
     }
 
     @Override
diff --git a/src/com/android/settings/security/ContentProtectionWorkSwitchController.java b/src/com/android/settings/security/ContentProtectionWorkSwitchController.java
new file mode 100644
index 0000000..0404dcd
--- /dev/null
+++ b/src/com/android/settings/security/ContentProtectionWorkSwitchController.java
@@ -0,0 +1,86 @@
+/*
+ * 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.security;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.core.TogglePreferenceController;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+/** Preference controller for content protection work profile switch bar. */
+public class ContentProtectionWorkSwitchController extends TogglePreferenceController {
+
+    public ContentProtectionWorkSwitchController(
+            @NonNull Context context, @NonNull String preferenceKey) {
+        super(context, preferenceKey);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return getManagedProfile() != null ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+    }
+
+    // The switch is always set to unchecked until Android V by design
+    @Override
+    public boolean isChecked() {
+        return false;
+    }
+
+    // The switch is disabled until Android V by design
+    @Override
+    public boolean setChecked(boolean isChecked) {
+        return false;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+
+        RestrictedSwitchPreference switchPreference = screen.findPreference(getPreferenceKey());
+        UserHandle managedProfile = getManagedProfile();
+        if (managedProfile != null) {
+            switchPreference.setDisabledByAdmin(getEnforcedAdmin(managedProfile));
+        }
+    }
+
+    @Override
+    public int getSliceHighlightMenuRes() {
+        return R.string.menu_key_security;
+    }
+
+    @VisibleForTesting
+    @Nullable
+    protected UserHandle getManagedProfile() {
+        return Utils.getManagedProfile(mContext.getSystemService(UserManager.class));
+    }
+
+    @VisibleForTesting
+    @Nullable
+    protected RestrictedLockUtils.EnforcedAdmin getEnforcedAdmin(
+            @NonNull UserHandle managedProfile) {
+        return RestrictedLockUtils.getProfileOrDeviceOwner(mContext, managedProfile);
+    }
+}
diff --git a/src/com/android/settings/spa/preference/ComposePreference.kt b/src/com/android/settings/spa/preference/ComposePreference.kt
index aec85a9..aa5c32c 100644
--- a/src/com/android/settings/spa/preference/ComposePreference.kt
+++ b/src/com/android/settings/spa/preference/ComposePreference.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.util.AttributeSet
+import androidx.annotation.VisibleForTesting
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.platform.ViewCompositionStrategy
@@ -26,13 +27,23 @@
 import com.android.settings.R
 import com.android.settingslib.spa.framework.theme.SettingsTheme
 
-class ComposePreference @JvmOverloads constructor(
+open class ComposePreference @JvmOverloads constructor(
     context: Context,
     attrs: AttributeSet? = null,
     defStyleAttr: Int = 0,
     defStyleRes: Int = 0,
 ) : Preference(context, attrs, defStyleAttr, defStyleRes) {
-    var content: @Composable () -> Unit = {}
+    private var content: @Composable () -> Unit = {}
+
+    fun setContent(content: @Composable () -> Unit) {
+        this.content = content
+    }
+
+    @VisibleForTesting
+    @Composable
+    fun Content() {
+        content()
+    }
 
     init {
         layoutResource = R.layout.preference_compose
diff --git a/src/com/android/settings/spa/preference/ComposePreferenceController.kt b/src/com/android/settings/spa/preference/ComposePreferenceController.kt
index 3ddb66b..9dd8282 100644
--- a/src/com/android/settings/spa/preference/ComposePreferenceController.kt
+++ b/src/com/android/settings/spa/preference/ComposePreferenceController.kt
@@ -29,7 +29,7 @@
     override fun displayPreference(screen: PreferenceScreen) {
         super.displayPreference(screen)
         preference = screen.findPreference(preferenceKey)!!
-        preference.content = { Content() }
+        preference.setContent { Content() }
     }
 
     @Composable
diff --git a/src/com/android/settings/system/ClientInitiatedActionRepository.kt b/src/com/android/settings/system/ClientInitiatedActionRepository.kt
new file mode 100644
index 0000000..24c04b4
--- /dev/null
+++ b/src/com/android/settings/system/ClientInitiatedActionRepository.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.telephony.CarrierConfigManager
+import android.util.Log
+
+class ClientInitiatedActionRepository(private val context: Context) {
+    private val configManager = context.getSystemService(CarrierConfigManager::class.java)!!
+
+    /**
+     * Trigger client initiated action (send intent) on system update
+     */
+    fun onSystemUpdate() {
+        val bundle =
+            configManager.getConfig(
+                CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_BOOL,
+                CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING,
+                CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING,
+                CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING,
+            )
+
+        if (!bundle.getBoolean(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_BOOL)) return
+
+        val action =
+            bundle.getString(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING)
+        if (action.isNullOrEmpty()) return
+        val extra = bundle.getString(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING)
+        val extraValue =
+            bundle.getString(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING)
+        Log.d(TAG, "onSystemUpdate: broadcasting intent $action with extra $extra, $extraValue")
+        val intent = Intent(action).apply {
+            if (!extra.isNullOrEmpty()) putExtra(extra, extraValue)
+            addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND)
+        }
+        context.applicationContext.sendBroadcast(intent)
+    }
+
+    companion object {
+        private const val TAG = "ClientInitiatedAction"
+    }
+}
diff --git a/src/com/android/settings/system/SystemUpdatePreferenceController.kt b/src/com/android/settings/system/SystemUpdatePreferenceController.kt
index 01df065..fa135aa 100644
--- a/src/com/android/settings/system/SystemUpdatePreferenceController.kt
+++ b/src/com/android/settings/system/SystemUpdatePreferenceController.kt
@@ -17,12 +17,9 @@
 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
@@ -39,6 +36,7 @@
 open class SystemUpdatePreferenceController(context: Context, preferenceKey: String) :
     BasePreferenceController(context, preferenceKey) {
     private val userManager: UserManager = context.userManager
+    private val clientInitiatedActionRepository = ClientInitiatedActionRepository(context)
     private lateinit var preference: Preference
 
     override fun getAvailabilityStatus() =
@@ -61,12 +59,7 @@
 
     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)
-                }
-            }
+            clientInitiatedActionRepository.onSystemUpdate()
         }
         // always return false here because this handler does not want to block other handlers.
         return false
@@ -111,26 +104,6 @@
         Build.VERSION.RELEASE_OR_PREVIEW_DISPLAY,
     )
 
-    /**
-     * Trigger client initiated action (send intent) on system update
-     */
-    private fun ciActionOnSysUpdate(b: PersistableBundle) {
-        val intentStr = b.getString(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING)
-        if (intentStr.isNullOrEmpty()) return
-        val extra = b.getString(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING)
-        val extraVal =
-            b.getString(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING)
-        Log.d(
-            TAG,
-            "ciActionOnSysUpdate: broadcasting intent $intentStr with extra $extra, $extraVal"
-        )
-        val intent = Intent(intentStr).apply {
-            if (!extra.isNullOrEmpty()) putExtra(extra, extraVal)
-            addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND)
-        }
-        mContext.applicationContext.sendBroadcast(intent)
-    }
-
     companion object {
         private const val TAG = "SysUpdatePrefContr"
     }
diff --git a/src/com/android/settings/users/MultiUserSwitchBarController.java b/src/com/android/settings/users/MultiUserSwitchBarController.java
index 33651c3..238e86e 100644
--- a/src/com/android/settings/users/MultiUserSwitchBarController.java
+++ b/src/com/android/settings/users/MultiUserSwitchBarController.java
@@ -57,11 +57,10 @@
             mSwitchBar.setDisabledByAdmin(RestrictedLockUtilsInternal
                     .checkIfRestrictionEnforced(mContext, UserManager.DISALLOW_USER_SWITCH,
                             UserHandle.myUserId()));
-
         } else if (mUserCapabilities.mDisallowAddUser) {
+            onSwitchToggled(false);
             mSwitchBar.setDisabledByAdmin(RestrictedLockUtilsInternal
-                    .checkIfRestrictionEnforced(mContext, UserManager.DISALLOW_ADD_USER,
-                            UserHandle.myUserId()));
+                    .checkIfAddUserDisallowed(mContext, UserHandle.myUserId()));
         } else {
             mSwitchBar.setEnabled(!mUserCapabilities.mDisallowSwitchUser
                     && !mUserCapabilities.mIsGuest && mUserCapabilities.isAdmin());
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintPreferenceTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintPreferenceTest.java
index 116591d..c8c867c 100644
--- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintPreferenceTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.Mockito.verify;
 
 import android.content.Context;
+import android.hardware.fingerprint.Fingerprint;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.FrameLayout;
@@ -60,6 +61,21 @@
     }
 
     @Test
+    public void deleteContentDescription() {
+        final FrameLayout layout = new FrameLayout(mContext);
+        LayoutInflater.from(mContext).inflate(mPreference.getSecondTargetResId(), layout, true);
+        final String fingerprintName = "fingerprint test";
+        mPreference.setFingerprint(new Fingerprint(fingerprintName, 0, 0));
+        final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(layout);
+        mPreference.onBindViewHolder(holder);
+
+        final View view = layout.findViewById(R.id.delete_button);
+        String expectedContentDescription =
+                mContext.getString(R.string.delete) + " " + fingerprintName;
+        assertThat(view.getContentDescription().toString()).isEqualTo(expectedContentDescription);
+    }
+
+    @Test
     public void bindAndClickDeleteButton_shouldInvokeOnDeleteListener() {
         final FrameLayout layout = new FrameLayout(mContext);
         LayoutInflater.from(mContext).inflate(mPreference.getSecondTargetResId(), layout, true);
diff --git a/tests/robotests/src/com/android/settings/security/ContentProtectionPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/security/ContentProtectionPreferenceFragmentTest.java
index c9b1c64..2bd91a6 100644
--- a/tests/robotests/src/com/android/settings/security/ContentProtectionPreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/security/ContentProtectionPreferenceFragmentTest.java
@@ -18,25 +18,18 @@
 
 import static android.app.settings.SettingsEnums.CONTENT_PROTECTION_PREFERENCE;
 
-import static com.android.settings.security.ContentProtectionPreferenceFragment.KEY_WORK_PROFILE_SWITCH;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.pm.UserInfo;
-import android.os.UserHandle;
-import android.os.UserManager;
 import android.provider.SearchIndexableResource;
 
 import androidx.preference.PreferenceScreen;
 import androidx.preference.SwitchPreference;
 
 import com.android.settings.R;
-import com.android.settings.Utils;
 import com.android.settings.testutils.XmlTestUtils;
 import com.android.settings.testutils.shadow.ShadowDashboardFragment;
 import com.android.settings.testutils.shadow.ShadowUtils;
@@ -44,13 +37,11 @@
 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 java.util.Arrays;
 import java.util.List;
 
 @RunWith(RobolectricTestRunner.class)
@@ -60,14 +51,9 @@
             ShadowUtils.class,
         })
 public class ContentProtectionPreferenceFragmentTest {
-    private static final int TEST_PRIMARY_USER_ID = 10;
-    private static final int TEST_MANAGED_PROFILE_ID = 11;
-
     private ContentProtectionPreferenceFragment mFragment;
-    @Mock private UserManager mMockUserManager;
     private Context mContext;
     private PreferenceScreen mScreen;
-    private SwitchPreference mWorkProfileSwitch;
 
     @Before
     public void setUp() throws Exception {
@@ -79,66 +65,6 @@
 
         doReturn(mContext).when(mFragment).getContext();
         doReturn(mScreen).when(mFragment).getPreferenceScreen();
-
-        mWorkProfileSwitch = new SwitchPreference(mContext);
-        mWorkProfileSwitch.setVisible(false);
-        doReturn(mWorkProfileSwitch).when(mScreen).findPreference(KEY_WORK_PROFILE_SWITCH);
-
-        doReturn(mMockUserManager).when(mContext).getSystemService(UserManager.class);
-        doReturn(TEST_PRIMARY_USER_ID).when(mMockUserManager).getUserHandle();
-        UserInfo primaryUser =
-                new UserInfo(
-                        TEST_PRIMARY_USER_ID,
-                        null,
-                        UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_PRIMARY);
-        doReturn(primaryUser).when(mMockUserManager).getUserInfo(TEST_PRIMARY_USER_ID);
-        UserInfo managedProfile =
-                new UserInfo(
-                        TEST_MANAGED_PROFILE_ID,
-                        null,
-                        UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_MANAGED_PROFILE);
-        doReturn(managedProfile).when(mMockUserManager).getUserInfo(TEST_MANAGED_PROFILE_ID);
-    }
-
-    @Test
-    public void onActivityCreated_workProfileDisplayWorkSwitch() {
-        UserHandle[] userHandles =
-                new UserHandle[] {
-                    new UserHandle(TEST_PRIMARY_USER_ID), new UserHandle(TEST_MANAGED_PROFILE_ID)
-                };
-        doReturn(Arrays.asList(userHandles)).when(mMockUserManager).getUserProfiles();
-
-        assertThat(Utils.getManagedProfile(mMockUserManager).getIdentifier())
-                .isEqualTo(TEST_MANAGED_PROFILE_ID);
-
-        mFragment.onActivityCreated(null);
-
-        assertThat(mWorkProfileSwitch.isVisible()).isTrue();
-        assertThat(mWorkProfileSwitch.isChecked()).isFalse();
-        assertThat(mWorkProfileSwitch.isEnabled()).isFalse();
-    }
-
-    @Test
-    public void onActivityCreated_fullyManagedMode_bottomSwitchInvisible() {
-        final ComponentName componentName =
-                ComponentName.unflattenFromString("com.android.test/.DeviceAdminReceiver");
-        ShadowUtils.setDeviceOwnerComponent(componentName);
-
-        mFragment.onActivityCreated(null);
-
-        assertThat(mWorkProfileSwitch.isVisible()).isFalse();
-    }
-
-    @Test
-    public void onActivityCreated_personalProfileHideWorkSwitch() {
-        UserHandle[] userHandles = new UserHandle[] {new UserHandle(TEST_PRIMARY_USER_ID)};
-        doReturn(Arrays.asList(userHandles)).when(mMockUserManager).getUserProfiles();
-
-        assertThat(Utils.getManagedProfile(mMockUserManager)).isNull();
-
-        mFragment.onActivityCreated(null);
-
-        assertThat(mWorkProfileSwitch.isVisible()).isFalse();
     }
 
     @Test
@@ -175,4 +101,3 @@
         assertThat(indexRes.get(0).xmlResId).isEqualTo(mFragment.getPreferenceScreenResId());
     }
 }
-
diff --git a/tests/robotests/src/com/android/settings/security/ContentProtectionWorkSwitchControllerTest.java b/tests/robotests/src/com/android/settings/security/ContentProtectionWorkSwitchControllerTest.java
new file mode 100644
index 0000000..8d35e4d
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/security/ContentProtectionWorkSwitchControllerTest.java
@@ -0,0 +1,163 @@
+/*
+ * 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.security;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.UserHandle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+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;
+
+@RunWith(RobolectricTestRunner.class)
+public class ContentProtectionWorkSwitchControllerTest {
+    private static final UserHandle TEST_USER_HANDLE = UserHandle.of(10);
+
+    private final Context mContext = ApplicationProvider.getApplicationContext();
+
+    @Mock private PreferenceScreen mMockPreferenceScreen;
+    private ContentProtectionWorkSwitchController mController;
+    private UserHandle mManagedProfileUserHandle;
+    private RestrictedLockUtils.EnforcedAdmin mEnforcedAdmin;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mController = new TestContentProtectionWorkSwitchController();
+    }
+
+    @Test
+    public void isAvailable_managedProfile_available() {
+        mManagedProfileUserHandle = TEST_USER_HANDLE;
+
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void isAvailable_noManagedProfile_notAvailable() {
+        mManagedProfileUserHandle = null;
+
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void isChecked_noManagedProfile_alwaysOff() {
+        mManagedProfileUserHandle = null;
+
+        assertThat(mController.isChecked()).isFalse();
+    }
+
+    @Test
+    public void isChecked_managedProfile_alwaysOff() {
+        mManagedProfileUserHandle = TEST_USER_HANDLE;
+
+        assertThat(mController.isChecked()).isFalse();
+    }
+
+    @Test
+    public void setChecked_alwaysFalse() {
+        assertThat(mController.setChecked(true)).isFalse();
+        assertThat(mController.setChecked(false)).isFalse();
+    }
+
+    @Test
+    public void displayPreference_managedProfile_disabled() {
+        mManagedProfileUserHandle = TEST_USER_HANDLE;
+        mEnforcedAdmin = new RestrictedLockUtils.EnforcedAdmin();
+        RestrictedSwitchPreference mockSwitchPreference = mock(RestrictedSwitchPreference.class);
+        when(mMockPreferenceScreen.findPreference(any())).thenReturn(mockSwitchPreference);
+        when(mockSwitchPreference.getKey()).thenReturn(mController.getPreferenceKey());
+
+        mController.displayPreference(mMockPreferenceScreen);
+
+        assertThat(mController.isAvailable()).isTrue();
+        verify(mockSwitchPreference).setDisabledByAdmin(mEnforcedAdmin);
+    }
+
+    @Test
+    public void displayPreference_noManagedProfile_notDisabled() {
+        mManagedProfileUserHandle = null;
+        mEnforcedAdmin = new RestrictedLockUtils.EnforcedAdmin();
+        RestrictedSwitchPreference mockSwitchPreference = mock(RestrictedSwitchPreference.class);
+        when(mMockPreferenceScreen.findPreference(any())).thenReturn(mockSwitchPreference);
+        when(mockSwitchPreference.getKey()).thenReturn(mController.getPreferenceKey());
+
+        mController.displayPreference(mMockPreferenceScreen);
+
+        assertThat(mController.isAvailable()).isFalse();
+        verify(mockSwitchPreference, never()).setDisabledByAdmin(any());
+    }
+
+    @Test
+    public void displayPreference_noEnforcedAdmin_notDisabled() {
+        mManagedProfileUserHandle = null;
+        mEnforcedAdmin = null;
+        RestrictedSwitchPreference mockSwitchPreference = mock(RestrictedSwitchPreference.class);
+        when(mMockPreferenceScreen.findPreference(any())).thenReturn(mockSwitchPreference);
+        when(mockSwitchPreference.getKey()).thenReturn(mController.getPreferenceKey());
+
+        mController.displayPreference(mMockPreferenceScreen);
+
+        assertThat(mController.isAvailable()).isFalse();
+        verify(mockSwitchPreference, never()).setDisabledByAdmin(any());
+    }
+
+    private class TestContentProtectionWorkSwitchController
+            extends ContentProtectionWorkSwitchController {
+
+        TestContentProtectionWorkSwitchController() {
+            super(ContentProtectionWorkSwitchControllerTest.this.mContext, "key");
+        }
+
+        @Override
+        @Nullable
+        protected UserHandle getManagedProfile() {
+            return mManagedProfileUserHandle;
+        }
+
+        @Override
+        @Nullable
+        protected RestrictedLockUtils.EnforcedAdmin getEnforcedAdmin(
+                @NonNull UserHandle managedProfile) {
+            return mEnforcedAdmin;
+        }
+    }
+}
diff --git a/tests/screenshot/Android.bp b/tests/screenshot/Android.bp
new file mode 100644
index 0000000..e20b5d3
--- /dev/null
+++ b/tests/screenshot/Android.bp
@@ -0,0 +1,70 @@
+//
+// 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.
+
+android_app {
+    name: "ScreenshotTestStub",
+    defaults: [
+        "SettingsLibDefaults",
+    ],
+    platform_apis: true,
+    certificate: "platform",
+    privileged: true,
+    use_resource_processor: true,
+
+    static_libs: [
+        "Settings-core",
+        "androidx.fragment_fragment-testing",
+        "androidx.fragment_fragment",
+        "androidx.test.runner",
+        "androidx.test.core",
+    ],
+    uses_libs: ["org.apache.http.legacy"],
+
+    aaptflags: ["--extra-packages com.android.settings"],
+    manifest: "AndroidManifest.xml",
+}
+
+android_test {
+    name: "SettingsScreenshotTests",
+    platform_apis: true,
+    certificate: "platform",
+    test_suites: ["device-tests"],
+    srcs: [
+        "src/**/*.kt",
+    ],
+    static_libs: [
+        "androidx.fragment_fragment-testing",
+        "androidx.fragment_fragment",
+        "androidx.test.rules",
+        "androidx.test.ext.junit",
+        "platform-screenshot-diff-core",
+        "Settings-testutils2",
+        "androidx.test.core",
+        "androidx.test.espresso.core",
+        "kotlinx-coroutines-android",
+        "androidx.lifecycle_lifecycle-runtime-testing",
+        "kotlinx_coroutines_test",
+        "Settings-core",
+        "androidx.test.runner",
+    ],
+    uses_libs: ["org.apache.http.legacy"],
+    compile_multilib: "both",
+    manifest: "AndroidManifest.xml",
+    test_config: "AndroidTest.xml",
+    use_embedded_native_libs: false,
+    asset_dirs: ["assets"],
+    instrumentation_for: "ScreenshotTestStub",
+    data: [":ScreenshotTestStub"],
+}
diff --git a/tests/screenshot/AndroidManifest.xml b/tests/screenshot/AndroidManifest.xml
new file mode 100644
index 0000000..9cbc882
--- /dev/null
+++ b/tests/screenshot/AndroidManifest.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="com.android.settings.tests.screenshot"
+    >
+
+    <application android:debuggable="true">
+        <provider
+            android:name="com.android.settings.slices.SettingsSliceProvider"
+            android:authorities="com.android.settings.tests.screenshot.disabled"
+            android:enabled="false"
+            tools:node="remove"
+            tools:replace="android:authorities" />
+
+    </application>
+
+    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+    <uses-permission android:name="android.permission.READ_LOGS" />
+    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:label="Android Settings Screenshot tests"
+        android:targetPackage="com.android.settings.tests.screenshot" />
+
+</manifest>
diff --git a/tests/screenshot/AndroidTest.xml b/tests/screenshot/AndroidTest.xml
new file mode 100644
index 0000000..7496ffd
--- /dev/null
+++ b/tests/screenshot/AndroidTest.xml
@@ -0,0 +1,36 @@
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<configuration description="Runs settings screendiff tests.">
+    <option name="test-suite-tag" value="apct-instrumentation" />
+    <option name="test-suite-tag" value="apct" />
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <option name="optimized-property-setting" value="true" />
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="SettingsScreenshotTests.apk" />
+    </target_preparer>
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="directory-keys"
+            value="/data/user/0/com.android.settings.tests.screenshot/" />
+        <option name="collect-on-run-ended-only" value="true" />
+    </metrics_collector>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="com.android.settings.tests.screenshot" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+    </test>
+</configuration>
diff --git "a/tests/screenshot/assets/pixel_4a_\0505g\051/fp_enroll_intro.png" "b/tests/screenshot/assets/pixel_4a_\0505g\051/fp_enroll_intro.png"
new file mode 100644
index 0000000..1129250
--- /dev/null
+++ "b/tests/screenshot/assets/pixel_4a_\0505g\051/fp_enroll_intro.png"
Binary files differ
diff --git a/tests/screenshot/src/com/android/settings/tests/screenshot/BasicScreenshotTest.kt b/tests/screenshot/src/com/android/settings/tests/screenshot/BasicScreenshotTest.kt
new file mode 100644
index 0000000..bf28b54
--- /dev/null
+++ b/tests/screenshot/src/com/android/settings/tests/screenshot/BasicScreenshotTest.kt
@@ -0,0 +1,147 @@
+/*
+ * 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.tests.screenshot
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.Color
+import android.os.Bundle
+import android.view.View
+import androidx.fragment.app.testing.FragmentScenario
+import androidx.fragment.app.testing.launchFragmentInContainer
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.settings.R
+import com.android.settings.biometrics.fingerprint2.shared.model.Default
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollIntroV2Fragment
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollNavigationViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintGatekeeperViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintScrollViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.GatekeeperInfo
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.NavState
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Start
+import com.android.settings.testutils2.FakeFingerprintManagerInteractor
+import kotlinx.coroutines.test.StandardTestDispatcher
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import platform.test.screenshot.GoldenImagePathManager
+import platform.test.screenshot.ScreenshotTestRule
+import platform.test.screenshot.matchers.MSSIMMatcher
+
+@RunWith(AndroidJUnit4::class)
+class BasicScreenshotTest {
+  @Rule
+  @JvmField
+  var rule: ScreenshotTestRule =
+    ScreenshotTestRule(
+      GoldenImagePathManager(
+        InstrumentationRegistry.getInstrumentation().getContext(),
+        InstrumentationRegistry.getInstrumentation()
+          .getTargetContext()
+          .getFilesDir()
+          .getAbsolutePath() + "/settings_screenshots"
+      )
+    )
+
+  private var context: Context = ApplicationProvider.getApplicationContext()
+  private var interactor = FakeFingerprintManagerInteractor()
+
+  private val gatekeeperViewModel =
+    FingerprintGatekeeperViewModel(
+      GatekeeperInfo.GatekeeperPasswordInfo(byteArrayOf(1, 2, 3), 100L),
+      interactor
+    )
+
+  private val backgroundDispatcher = StandardTestDispatcher()
+  private lateinit var fragmentScenario: FragmentScenario<FingerprintEnrollIntroV2Fragment>
+  val navState = NavState(true)
+
+  private val navigationViewModel = FingerprintEnrollNavigationViewModel(
+      backgroundDispatcher,
+      interactor,
+      gatekeeperViewModel,
+      Start.next(navState),
+      navState,
+      Default,
+    )
+  private var fingerprintViewModel = FingerprintEnrollViewModel(
+      interactor, gatekeeperViewModel, navigationViewModel,
+    )
+  private var fingerprintScrollViewModel = FingerprintScrollViewModel()
+
+  @Before
+  fun setup() {
+    val factory =
+      object : ViewModelProvider.Factory {
+        @Suppress("UNCHECKED_CAST")
+        override fun <T : ViewModel> create(
+          modelClass: Class<T>,
+        ): T {
+          return when (modelClass) {
+            FingerprintEnrollViewModel::class.java -> fingerprintViewModel
+            FingerprintScrollViewModel::class.java -> fingerprintScrollViewModel
+            FingerprintEnrollNavigationViewModel::class.java -> navigationViewModel
+            FingerprintGatekeeperViewModel::class.java -> gatekeeperViewModel
+            else -> null
+          }
+            as T
+        }
+      }
+
+    fragmentScenario =
+      launchFragmentInContainer(Bundle(), R.style.SudThemeGlif) {
+        FingerprintEnrollIntroV2Fragment(factory)
+      }
+  }
+
+  /** Renders a [view] into a [Bitmap]. */
+  private fun viewToBitmap(view: View): Bitmap {
+    val bitmap =
+      Bitmap.createBitmap(
+        view.measuredWidth,
+        view.measuredHeight,
+        Bitmap.Config.ARGB_8888,
+      )
+    val canvas = Canvas(bitmap)
+    view.draw(canvas)
+    return bitmap
+  }
+
+  @Test
+  fun testEnrollIntro() {
+    fragmentScenario.onFragment { fragment ->
+      val view = fragment.requireView().findViewById<View>(R.id.enroll_intro_content_view)!!
+      view.setBackgroundColor(Color.BLACK)
+    }
+    fragmentScenario.onFragment { fragment ->
+      val view = fragment.requireView().findViewById<View>(R.id.enroll_intro_content_view)!!
+      rule.assertBitmapAgainstGolden(
+        viewToBitmap(view),
+        "fp_enroll_intro",
+        MSSIMMatcher()
+      )
+    }
+
+  }
+}
diff --git a/tests/spa_unit/src/com/android/settings/datausage/BillingCyclePreferenceTest.kt b/tests/spa_unit/src/com/android/settings/datausage/BillingCyclePreferenceTest.kt
index 2e2620b..4bf3851 100644
--- a/tests/spa_unit/src/com/android/settings/datausage/BillingCyclePreferenceTest.kt
+++ b/tests/spa_unit/src/com/android/settings/datausage/BillingCyclePreferenceTest.kt
@@ -18,40 +18,69 @@
 
 import android.content.Context
 import android.net.NetworkTemplate
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
 import com.android.settings.datausage.lib.BillingCycleRepository
-import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.kotlin.doReturn
 import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
 
 @RunWith(AndroidJUnit4::class)
 class BillingCyclePreferenceTest {
+    @get:Rule
+    val composeTestRule = createComposeRule()
 
-    private val mockBillingCycleRepository = mock<BillingCycleRepository> {
-        on { isModifiable(SUB_ID) } doReturn false
-    }
+    private val mockBillingCycleRepository = mock<BillingCycleRepository>()
 
     private val context: Context = ApplicationProvider.getApplicationContext()
 
     private val preference = BillingCyclePreference(context, null, mockBillingCycleRepository)
 
     @Test
-    fun isEnabled_initialState() {
-        val enabled = preference.isEnabled
+    fun setTemplate_titleDisplayed() {
+        setTemplate()
 
-        assertThat(enabled).isTrue()
+        composeTestRule.onNodeWithText(context.getString(R.string.billing_cycle))
+            .assertIsDisplayed()
     }
 
     @Test
-    fun isEnabled_afterSetTemplate_updated() {
+    fun setTemplate_modifiable_enabled() {
+        mockBillingCycleRepository.stub {
+            on { isModifiable(SUB_ID) } doReturn true
+        }
+
+        setTemplate()
+
+        composeTestRule.onNodeWithText(context.getString(R.string.billing_cycle)).assertIsEnabled()
+    }
+
+    @Test
+    fun setTemplate_notModifiable_notEnabled() {
+        mockBillingCycleRepository.stub {
+            on { isModifiable(SUB_ID) } doReturn false
+        }
+
+        setTemplate()
+
+        composeTestRule.onNodeWithText(context.getString(R.string.billing_cycle))
+            .assertIsNotEnabled()
+    }
+
+    private fun setTemplate() {
         preference.setTemplate(mock<NetworkTemplate>(), SUB_ID)
-
-        val enabled = preference.isEnabled
-
-        assertThat(enabled).isFalse()
+        composeTestRule.setContent {
+            preference.Content()
+        }
     }
 
     private companion object {
diff --git a/tests/spa_unit/src/com/android/settings/network/MobileIconGroupExtTest.kt b/tests/spa_unit/src/com/android/settings/network/MobileIconGroupExtTest.kt
new file mode 100644
index 0000000..641051d
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/MobileIconGroupExtTest.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network
+
+import android.text.Spanned
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class MobileIconGroupExtTest {
+    @Test
+    fun maybeToHtml_withoutHtmlTag() {
+        val actual = CONNECTED_5G.maybeToHtml()
+
+        assertThat(actual).isSameInstanceAs(CONNECTED_5G)
+    }
+
+    @Test
+    fun maybeToHtml_withHtmlTag() {
+        val actual = CONNECTED_5GE.maybeToHtml()
+
+        assertThat(actual).isInstanceOf(Spanned::class.java)
+    }
+
+    private companion object {
+        private const val CONNECTED_5G = "Connected / 5G"
+        private const val CONNECTED_5GE = "Connected / <i>5G <small>E</small></i>"
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/spa/preference/ComposePreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/spa/preference/ComposePreferenceControllerTest.kt
index 36817d1..cdfa514 100644
--- a/tests/spa_unit/src/com/android/settings/spa/preference/ComposePreferenceControllerTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/preference/ComposePreferenceControllerTest.kt
@@ -61,7 +61,7 @@
         controller.displayPreference(preferenceScreen)
 
         composeTestRule.setContent {
-            preference.content()
+            preference.Content()
         }
         composeTestRule.onNodeWithText(TEXT).assertIsDisplayed()
     }
diff --git a/tests/spa_unit/src/com/android/settings/spa/preference/ComposePreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/preference/ComposePreferenceTest.kt
index 28bde3a..a2fe752 100644
--- a/tests/spa_unit/src/com/android/settings/spa/preference/ComposePreferenceTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/preference/ComposePreferenceTest.kt
@@ -43,7 +43,7 @@
 
     @Test
     fun onBindViewHolder() {
-        preference.content = {
+        preference.setContent {
             Text(TEXT)
         }
 
diff --git a/tests/spa_unit/src/com/android/settings/system/ClientInitiatedActionRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/system/ClientInitiatedActionRepositoryTest.kt
new file mode 100644
index 0000000..f202668
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/system/ClientInitiatedActionRepositoryTest.kt
@@ -0,0 +1,78 @@
+/*
+ * 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.telephony.CarrierConfigManager
+import androidx.core.os.persistableBundleOf
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.anyVararg
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.stub
+import org.mockito.kotlin.verify
+
+@RunWith(AndroidJUnit4::class)
+class ClientInitiatedActionRepositoryTest {
+    private val mockCarrierConfigManager = mock<CarrierConfigManager>()
+
+    private val context = mock<Context> {
+        on { applicationContext } doReturn mock
+        on { getSystemService(CarrierConfigManager::class.java) } doReturn mockCarrierConfigManager
+    }
+
+    private val repository = ClientInitiatedActionRepository(context)
+
+    @Test
+    fun onSystemUpdate_notEnabled() {
+        mockCarrierConfigManager.stub {
+            on { getConfig(anyVararg()) } doReturn persistableBundleOf()
+        }
+
+        repository.onSystemUpdate()
+
+        verify(context, never()).sendBroadcast(any())
+    }
+
+    @Test
+    fun onSystemUpdate_enabled() {
+        mockCarrierConfigManager.stub {
+            on { getConfig(anyVararg()) } doReturn persistableBundleOf(
+                CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_BOOL to true,
+                CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING to ACTION,
+            )
+        }
+
+        repository.onSystemUpdate()
+
+        val intent = argumentCaptor<Intent> {
+            verify(context).sendBroadcast(capture())
+        }.firstValue
+        assertThat(intent.action).isEqualTo(ACTION)
+    }
+
+    private companion object {
+        const val ACTION = "ACTION"
+    }
+}
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index 0f045a8..327b6aa 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -21,6 +21,7 @@
         "aconfig_settings_flags_lib",
         "androidx.arch.core_core-testing",
         "androidx.test.core",
+        "androidx.test.espresso.core",
         "androidx.test.rules",
         "androidx.test.ext.junit",
         "androidx.preference_preference",
diff --git a/tests/unit/src/com/android/settings/MainClearTest.kt b/tests/unit/src/com/android/settings/MainClearTest.kt
new file mode 100644
index 0000000..05f06df
--- /dev/null
+++ b/tests/unit/src/com/android/settings/MainClearTest.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
+
+import android.platform.test.flag.junit.SetFlagsRule
+import androidx.test.core.app.ActivityScenario
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.action.ViewActions.click
+import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.Settings.FactoryResetActivity
+import com.android.settings.flags.Flags
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Test [MainClear]. */
+@RunWith(AndroidJUnit4::class)
+class MainClearTest {
+    @get:Rule
+    val mSetFlagsRule = SetFlagsRule()
+
+    @Test
+    fun factoryResetCancelButton_flagDisabled_noCancelButton() {
+        mSetFlagsRule.disableFlags(Flags.FLAG_SHOW_FACTORY_RESET_CANCEL_BUTTON)
+        ActivityScenario.launch(FactoryResetActivity::class.java).use {
+            ensurePrimaryButton()
+            onView(withText(android.R.string.cancel)).check(doesNotExist())
+            it.onActivity { activity -> assertThat(activity.isFinishing).isFalse() }
+        }
+    }
+
+    @Test
+    fun factoryResetCancelButton_flagEnabled_showCancelButton() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_SHOW_FACTORY_RESET_CANCEL_BUTTON)
+        ActivityScenario.launch(FactoryResetActivity::class.java).use {
+            ensurePrimaryButton()
+            it.onActivity { activity -> assertThat(activity.isFinishing).isFalse() }
+
+            // Note: onView CANNOT be called within onActivity block, which runs in the main thread
+            onView(withText(android.R.string.cancel)).check(matches(isDisplayed())).perform(click())
+
+            it.onActivity { activity -> assertThat(activity.isFinishing).isTrue() }
+        }
+    }
+
+    private fun ensurePrimaryButton() {
+        onView(withText(R.string.main_clear_button_text)).check(matches(isDisplayed()))
+    }
+}
\ No newline at end of file
diff --git a/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java b/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java
index 436d37f..2d63c71 100644
--- a/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java
+++ b/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java
@@ -42,7 +42,6 @@
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
-import android.text.Html;
 
 import androidx.slice.Slice;
 import androidx.slice.builders.ListBuilder;
@@ -56,7 +55,6 @@
 import com.android.wifitrackerlib.WifiEntry;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -166,11 +164,9 @@
         assertThat(testItem).isNull();
     }
 
-    @Ignore
     @Test
     public void createCarrierRow_hasDdsAndActiveNetworkIsNotCellular_verifyTitleAndSummary() {
         String expectDisplayName = "Name1";
-        CharSequence expectedSubtitle = Html.fromHtml("5G", Html.FROM_HTML_MODE_LEGACY);
         String networkType = "5G";
         mockConnections(true, ServiceState.STATE_IN_SERVICE, expectDisplayName,
                 true, true);
@@ -180,19 +176,17 @@
                 networkType);
 
         assertThat(testRowBuild.getTitle()).isEqualTo(expectDisplayName);
-        assertThat(testRowBuild.getSubtitle()).isEqualTo(expectedSubtitle);
+        assertThat(testRowBuild.getSubtitle()).isEqualTo("5G");
     }
 
-    @Ignore
     @Test
     public void createCarrierRow_wifiOnhasDdsAndActiveNetworkIsCellular_verifyTitleAndSummary() {
         String expectDisplayName = "Name1";
         String networkType = "5G";
         String connectedText = ResourcesUtils.getResourcesString(mContext,
                 "mobile_data_connection_active");
-        CharSequence expectedSubtitle = Html.fromHtml(ResourcesUtils.getResourcesString(mContext,
-                "preference_summary_default_combination", connectedText, networkType),
-                Html.FROM_HTML_MODE_LEGACY);
+        CharSequence expectedSubtitle = ResourcesUtils.getResourcesString(mContext,
+                "preference_summary_default_combination", connectedText, networkType);
         mockConnections(true, ServiceState.STATE_IN_SERVICE, expectDisplayName,
                 true, true);
         addNetworkTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
@@ -204,13 +198,11 @@
         assertThat(testRowBuild.getSubtitle()).isEqualTo(expectedSubtitle);
     }
 
-    @Ignore
     @Test
     public void createCarrierRow_noNetworkAvailable_verifyTitleAndSummary() {
         String expectDisplayName = "Name1";
-        CharSequence expectedSubtitle = Html.fromHtml(
-                ResourcesUtils.getResourcesString(mContext, "mobile_data_no_connection"),
-                Html.FROM_HTML_MODE_LEGACY);
+        CharSequence expectedSubtitle =
+                ResourcesUtils.getResourcesString(mContext, "mobile_data_no_connection");
         String networkType = "";
 
         mockConnections(true, ServiceState.STATE_OUT_OF_SERVICE, expectDisplayName,
diff --git a/tests/unit/src/com/android/settings/network/SubscriptionsPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/SubscriptionsPreferenceControllerTest.java
index c4abdd1..bca12c1 100644
--- a/tests/unit/src/com/android/settings/network/SubscriptionsPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/network/SubscriptionsPreferenceControllerTest.java
@@ -53,7 +53,6 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyDisplayInfo;
 import android.telephony.TelephonyManager;
-import android.text.Html;
 
 import androidx.lifecycle.LifecycleOwner;
 import androidx.lifecycle.LifecycleRegistry;
@@ -266,8 +265,6 @@
     @Test
     @UiThreadTest
     public void displayPreference_providerAndHasMultiSimAndActive_connectedAndRat() {
-        final CharSequence expectedSummary =
-                Html.fromHtml("Connected / 5G", Html.FROM_HTML_MODE_LEGACY);
         final String networkType = "5G";
         final List<SubscriptionInfo> sub = setupMockSubscriptions(2);
         doReturn(sub.get(0)).when(mSubscriptionManager).getDefaultDataSubscriptionInfo();
@@ -281,14 +278,12 @@
         mController.onResume();
         mController.displayPreference(mPreferenceScreen);
 
-        assertThat(mPreferenceCategory.getPreference(0).getSummary()).isEqualTo(expectedSummary);
+        assertThat(mPreferenceCategory.getPreference(0).getSummary()).isEqualTo("Connected / 5G");
     }
 
     @Test
     @UiThreadTest
     public void displayPreference_providerAndHasMultiSimAndActiveCarrierWifi_connectedAndWPlus() {
-        final CharSequence expectedSummary =
-                Html.fromHtml("Connected / W+", Html.FROM_HTML_MODE_LEGACY);
         final String networkType = "W+";
         final List<SubscriptionInfo> sub = setupMockSubscriptions(2);
         doReturn(sub.get(0)).when(mSubscriptionManager).getDefaultDataSubscriptionInfo();
@@ -302,7 +297,7 @@
         mController.onResume();
         mController.displayPreference(mPreferenceScreen);
 
-        assertThat(mPreferenceCategory.getPreference(0).getSummary()).isEqualTo(expectedSummary);
+        assertThat(mPreferenceCategory.getPreference(0).getSummary()).isEqualTo("Connected / W+");
     }
 
     @Test
@@ -310,8 +305,6 @@
     public void displayPreference_providerAndHasMultiSimButMobileDataOff_notAutoConnect() {
         final String dataOffSummary =
                 ResourcesUtils.getResourcesString(mContext, "mobile_data_off_summary");
-        final CharSequence expectedSummary =
-                Html.fromHtml(dataOffSummary, Html.FROM_HTML_MODE_LEGACY);
         final String networkType = "5G";
         final List<SubscriptionInfo> sub = setupMockSubscriptions(2);
         doReturn(sub.get(0)).when(mSubscriptionManager).getDefaultDataSubscriptionInfo();
@@ -324,14 +317,12 @@
         mController.onResume();
         mController.displayPreference(mPreferenceScreen);
 
-        assertThat(mPreferenceCategory.getPreference(0).getSummary())
-            .isEqualTo(expectedSummary.toString());
+        assertThat(mPreferenceCategory.getPreference(0).getSummary()).isEqualTo(dataOffSummary);
     }
 
     @Test
     @UiThreadTest
     public void displayPreference_providerAndHasMultiSimAndNotActive_showRatOnly() {
-        final CharSequence expectedSummary = Html.fromHtml("5G", Html.FROM_HTML_MODE_LEGACY);
         final String networkType = "5G";
         final List<SubscriptionInfo> sub = setupMockSubscriptions(2);
         doReturn(sub.get(0)).when(mSubscriptionManager).getDefaultDataSubscriptionInfo();
@@ -345,7 +336,7 @@
         mController.onResume();
         mController.displayPreference(mPreferenceScreen);
 
-        assertThat(mPreferenceCategory.getPreference(0).getSummary()).isEqualTo(expectedSummary);
+        assertThat(mPreferenceCategory.getPreference(0).getSummary()).isEqualTo(networkType);
     }
 
     @Test
@@ -362,8 +353,6 @@
     @Test
     @UiThreadTest
     public void onTelephonyDisplayInfoChanged_providerAndHasMultiSimAndActive_connectedAndRat() {
-        final CharSequence expectedSummary =
-                Html.fromHtml("Connected / LTE", Html.FROM_HTML_MODE_LEGACY);
         final String networkType = "LTE";
         final List<SubscriptionInfo> sub = setupMockSubscriptions(2);
         final TelephonyDisplayInfo telephonyDisplayInfo =
@@ -383,14 +372,12 @@
         mController.onTelephonyDisplayInfoChanged(sub.get(0).getSubscriptionId(),
                 telephonyDisplayInfo);
 
-        assertThat(mPreferenceCategory.getPreference(0).getSummary()).isEqualTo(expectedSummary);
+        assertThat(mPreferenceCategory.getPreference(0).getSummary()).isEqualTo("Connected / LTE");
     }
 
     @Test
     @UiThreadTest
     public void onTelephonyDisplayInfoChanged_providerAndHasMultiSimAndNotActive_showRat() {
-        final CharSequence expectedSummary =
-                Html.fromHtml("LTE", Html.FROM_HTML_MODE_LEGACY);
         final String networkType = "LTE";
         final List<SubscriptionInfo> sub = setupMockSubscriptions(2);
         final TelephonyDisplayInfo telephonyDisplayInfo =
@@ -409,7 +396,7 @@
         mController.onTelephonyDisplayInfoChanged(sub.get(0).getSubscriptionId(),
                 telephonyDisplayInfo);
 
-        assertThat(mPreferenceCategory.getPreference(0).getSummary()).isEqualTo(expectedSummary);
+        assertThat(mPreferenceCategory.getPreference(0).getSummary()).isEqualTo(networkType);
     }
 
     @Test
@@ -417,8 +404,6 @@
     public void onTelephonyDisplayInfoChanged_providerAndHasMultiSimAndOutOfService_noConnection() {
         final String noConnectionSummary =
                 ResourcesUtils.getResourcesString(mContext, "mobile_data_no_connection");
-        final CharSequence expectedSummary =
-                Html.fromHtml(noConnectionSummary, Html.FROM_HTML_MODE_LEGACY);
         final String networkType = "LTE";
         final List<SubscriptionInfo> sub = setupMockSubscriptions(2);
         final TelephonyDisplayInfo telephonyDisplayInfo =
@@ -437,7 +422,8 @@
         mController.onTelephonyDisplayInfoChanged(sub.get(0).getSubscriptionId(),
                 telephonyDisplayInfo);
 
-        assertThat(mPreferenceCategory.getPreference(0).getSummary()).isEqualTo(expectedSummary);
+        assertThat(mPreferenceCategory.getPreference(0).getSummary())
+                .isEqualTo(noConnectionSummary);
     }
 
     @Test