Merge "Re-enable settings password tests." into main
diff --git a/aconfig/settings_experience_flag_declarations.aconfig b/aconfig/settings_experience_flag_declarations.aconfig
index 9fe3f32..d5caccf 100644
--- a/aconfig/settings_experience_flag_declarations.aconfig
+++ b/aconfig/settings_experience_flag_declarations.aconfig
@@ -7,3 +7,13 @@
     description: "Change to the new APN page."
     bug: "298906796"
 }
+
+flag {
+    name: "refactor_print_settings"
+    namespace: "settings_experience"
+    description: "Refactor the PrintSettings page."
+    bug: "320076351"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/res/layout/private_space_gaia_education_screen.xml b/res/layout/private_space_gaia_education_screen.xml
deleted file mode 100644
index e5c0f85..0000000
--- a/res/layout/private_space_gaia_education_screen.xml
+++ /dev/null
@@ -1,111 +0,0 @@
-<!--
-  ~ Copyright (C) 2024 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.
-  -->
-
-<com.google.android.setupdesign.GlifLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/ps_account_intro_screen"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:filterTouchesWhenObscured="true">
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical">
-        <ImageView
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="24dp"
-            android:layout_marginRight="24dp"
-            android:contentDescription="@null"
-            android:src="@drawable/ic_security_privacy_alert_primary"/>
-        <TextView
-            style="@style/PrivateSpaceSetupTextFontStyle"
-            android:fontFamily="google-sans"
-            android:text="@string/private_space_gaia_education_title"
-            android:layout_margin="24dp"
-            android:layout_marginTop="40dp"
-            android:gravity="center"
-            android:textSize="28sp"/>
-        <TextView
-            style="@style/PrivateSpaceSetupTextFontStyle"
-            android:layout_marginLeft="24dp"
-            android:layout_marginRight="24dp"
-            android:layout_marginBottom="40dp"
-            android:textSize="14sp"
-            android:text="@string/private_space_gaia_education_description"/>
-        <TextView
-            style="@style/PrivateSpaceSetupTextFontStyle"
-            android:layout_marginLeft="24dp"
-            android:layout_marginRight="24dp"
-            android:layout_marginBottom="16dp"
-            android:textSize="14sp"
-            android:text="@string/private_space_gaia_education_header"/>
-        <RelativeLayout
-            style="@style/PrivateSpaceSetupBulletPointLayoutStyle"
-            android:paddingTop="8dp"
-            android:paddingBottom="8dp">
-            <ImageView
-                android:id="@+id/point1"
-                style="@style/PrivateSpaceBulletPointIconStyle"
-                android:contentDescription="@null"
-                android:src="@drawable/ic_text_dot" />
-            <TextView
-                style="@style/PrivateSpaceBulletPointTextFontStyle"
-                android:layout_toRightOf="@+id/point1"
-                android:textSize="14sp"
-                android:text="@string/private_space_gaia_education_bullet1"/>
-        </RelativeLayout>
-        <RelativeLayout
-            style="@style/PrivateSpaceSetupBulletPointLayoutStyle">
-            <ImageView
-                android:id="@+id/point2"
-                style="@style/PrivateSpaceBulletPointIconStyle"
-                android:contentDescription="@null"
-                android:src="@drawable/ic_text_dot" />
-            <TextView
-                style="@style/PrivateSpaceBulletPointTextFontStyle"
-                android:layout_toRightOf="@+id/point2"
-                android:textSize="14sp"
-                android:text="@string/private_space_gaia_education_bullet2"/>
-        </RelativeLayout>
-        <RelativeLayout
-            style="@style/PrivateSpaceSetupBulletPointLayoutStyle">
-            <ImageView
-                android:id="@+id/point3"
-                style="@style/PrivateSpaceBulletPointIconStyle"
-                android:contentDescription="@null"
-                android:src="@drawable/ic_text_dot" />
-            <TextView
-                style="@style/PrivateSpaceBulletPointTextFontStyle"
-                android:layout_toRightOf="@+id/point3"
-                android:textSize="14sp"
-                android:text="@string/private_space_gaia_education_bullet3"/>
-        </RelativeLayout>
-        <RelativeLayout
-            style="@style/PrivateSpaceSetupBulletPointLayoutStyle">
-            <ImageView
-                android:id="@+id/point4"
-                style="@style/PrivateSpaceBulletPointIconStyle"
-                android:contentDescription="@null"
-                android:src="@drawable/ic_text_dot" />
-            <TextView
-                style="@style/PrivateSpaceBulletPointTextFontStyle"
-                android:layout_toRightOf="@+id/point4"
-                android:textSize="14sp"
-                android:text="@string/private_space_gaia_education_bullet4"/>
-        </RelativeLayout>
-    </LinearLayout>
-</com.google.android.setupdesign.GlifLayout>
diff --git a/res/layout/private_space_pre_finish_delay.xml b/res/layout/private_space_wait_screen.xml
similarity index 94%
rename from res/layout/private_space_pre_finish_delay.xml
rename to res/layout/private_space_wait_screen.xml
index 3b620bf..83b4806 100644
--- a/res/layout/private_space_pre_finish_delay.xml
+++ b/res/layout/private_space_wait_screen.xml
@@ -25,5 +25,5 @@
     android:icon="@drawable/ic_private_space_icon"
     app:sudUsePartnerHeavyTheme="true"
     app:sudIllustrationType="default"
-    app:sucHeaderText="@string/private_space_pre_finish_title">
-</com.google.android.setupdesign.GlifLoadingLayout>
\ No newline at end of file
+    app:sucHeaderText="@string/private_space_wait_screen_title">
+</com.google.android.setupdesign.GlifLoadingLayout>
diff --git a/res/navigation/privatespace_main_context_nav.xml b/res/navigation/privatespace_main_context_nav.xml
index 0de5d91..3eb57d3 100644
--- a/res/navigation/privatespace_main_context_nav.xml
+++ b/res/navigation/privatespace_main_context_nav.xml
@@ -35,9 +35,6 @@
         <action
             android:id="@+id/action_set_lock_fragment"
             app:destination="@id/ps_profile_lock_fragment"/>
-        <action
-            android:id="@+id/action_account_intro_fragment"
-            app:destination="@id/ps_account_intro_fragment"/>
     </fragment>
     <fragment android:id="@+id/ps_profile_error_fragment"
               android:name="com.android.settings.privatespace.PrivateProfileCreationError"
@@ -60,9 +57,6 @@
               android:name="com.android.settings.privatespace.PrivateSpaceAccountLoginError"
               android:label="fragment_account_error">
         <action
-            android:id="@+id/action_advance_login_error"
-            app:destination="@id/ps_account_intro_fragment"/>
-        <action
             android:id="@+id/action_skip_account_login"
             app:destination="@id/ps_profile_lock_fragment"/>
     </fragment>
@@ -73,16 +67,10 @@
             android:id="@+id/action_lock_success_fragment"
             app:destination="@id/ps_pre_finish_delay_fragment"/>
     </fragment>
-    <fragment android:id="@+id/ps_account_intro_fragment"
-              android:name="com.android.settings.privatespace.PrivateSpaceGaiaEducationFragment"
-              android:label="fragment_ps_account_intro">
-        <action
-            android:id="@+id/action_account_lock_fragment"
-            app:destination="@id/ps_profile_lock_fragment"/>
-        <action
-            android:id="@+id/action_advance_login_error"
-            app:destination="@id/ps_account_error_fragment"/>
-    </fragment>
     <action android:id="@+id/action_pre_finish_delay_fragment"
             app:destination="@id/ps_pre_finish_delay_fragment"/>
+    <action android:id="@+id/action_advance_login_error"
+            app:destination="@id/ps_account_error_fragment"/>
+    <action android:id="@+id/show_set_lock_fragment"
+            app:destination="@id/ps_profile_lock_fragment"/>
 </navigation>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index ba14ce1..1f7ba9f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1380,7 +1380,7 @@
     <!-- Label for private space lock setup button to choose a new lock. [CHAR LIMIT=50] -->
     <string name="private_space_set_lock_label">Choose new lock</string>
     <!-- Title for private space setup pre completion screen to add a delay. [CHAR LIMIT=30] -->
-    <string name="private_space_pre_finish_title">Just a sec\u2026</string>
+    <string name="private_space_wait_screen_title">Just a sec\u2026</string>
     <!-- Title for private space setup success screen. [CHAR LIMIT=30] -->
     <string name="private_space_success_title">All set!</string>
     <!-- Summary for the private space setup success screen. [CHAR LIMIT=NONE] -->
@@ -1407,22 +1407,6 @@
     <string name="private_space_choose_your_password_header">Set a password for your private space</string>
     <!-- Header for private space choose your pattern screen [CHAR LIMIT=40] -->
     <string name="private_space_choose_your_pattern_header">Set a pattern for your private space</string>
-    <!-- Title for private space GAIA education screen [CHAR LIMIT=90] -->
-    <string name="private_space_gaia_education_title">Create a Google Account to help keep your data private</string>
-    <!-- Description for private space GAIA education screen [CHAR LIMIT=120] -->
-    <string name="private_space_gaia_education_description">On the next screen you can sign in to an account to use with your private space</string>
-    <!-- Sub header for private space GAIA education screen [CHAR LIMIT=120] -->
-    <string name="private_space_gaia_education_header"><b>Create a dedicated account to help stop data appearing outside private space, such as:</b></string>
-    <!-- Text for private space GAIA education screen [CHAR LIMIT=90] -->
-    <string name="private_space_gaia_education_bullet1">Synced photos, files, emails, contacts, calendar events, and other data</string>
-    <!-- Text for private space GAIA education screen [CHAR LIMIT=60] -->
-    <string name="private_space_gaia_education_bullet2">App download history and recommendations</string>
-    <!-- Text for private space GAIA education screen [CHAR LIMIT=60] -->
-    <string name="private_space_gaia_education_bullet3">Browsing history, bookmarks, and saved passwords</string>
-    <!-- Text for private space GAIA education screen [CHAR LIMIT=90] -->
-    <string name="private_space_gaia_education_bullet4">Suggested content related to your activity in private space apps</string>
-    <!-- Text for button in private space GAIA education screen to start login [CHAR LIMIT=20] -->
-    <string name="private_space_gaia_education_got_it">Got it</string>
     <!-- The title of the category for settings related to the private space lock  [CHAR LIMIT=20] -->
     <string name="private_space_category_lock">Lock</string>
     <!-- The title of the category for settings related to hiding the private space  [CHAR LIMIT=20] -->
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index 866a529..227f72d 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -675,12 +675,6 @@
             android:title="@string/immediately_destroy_activities"
             android:summary="@string/immediately_destroy_activities_summary" />
 
-        <ListPreference
-            android:key="app_process_limit"
-            android:title="@string/app_process_limit_title"
-            android:entries="@array/app_process_limit_entries"
-            android:entryValues="@array/app_process_limit_values" />
-
         <Preference
             android:key="background_check"
             android:fragment="com.android.settings.applications.appops.BackgroundCheckSummary"
diff --git a/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java b/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java
index fab6e47..b5101b7 100644
--- a/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java
+++ b/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java
@@ -43,6 +43,7 @@
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnStart;
 import com.android.settingslib.core.lifecycle.events.OnStop;
+import com.android.settingslib.utils.ThreadUtils;
 
 import java.util.Set;
 
@@ -120,7 +121,20 @@
     }
 
     @Override
-    public CharSequence getSummary() {
+    protected void refreshSummary(Preference preference) {
+        if (preference == null) {
+            return;
+        }
+
+        // Loading the hearing aids summary requires IPC call, which can block the UI thread.
+        // To reduce page loading latency, move loadSummary in the background thread.
+        ThreadUtils.postOnBackgroundThread(() -> {
+            CharSequence summary = loadSummary();
+            ThreadUtils.getUiThreadHandler().post(() -> preference.setSummary(summary));
+        });
+    }
+
+    private CharSequence loadSummary() {
         final CachedBluetoothDevice device = mHelper.getConnectedHearingAidDevice();
         if (device == null) {
             return mContext.getText(R.string.accessibility_hearingaid_not_connected_summary);
diff --git a/src/com/android/settings/applications/credentials/OWNERS b/src/com/android/settings/applications/credentials/OWNERS
index 55b89b4..b4d1f20 100644
--- a/src/com/android/settings/applications/credentials/OWNERS
+++ b/src/com/android/settings/applications/credentials/OWNERS
@@ -1,5 +1,4 @@
 # Default reviewers for this and subdirectories.
-beccahughes@google.com
 reemabajwa@google.com
 helenqin@google.com
 sgjerry@google.com
@@ -7,4 +6,4 @@
 akaphle@google.com
 duqinmei@google.com
 
-# Emergency approvers in case the above are not available
\ No newline at end of file
+# Emergency approvers in case the above are not available
diff --git a/src/com/android/settings/development/BackgroundProcessLimitPreferenceController.java b/src/com/android/settings/development/BackgroundProcessLimitPreferenceController.java
deleted file mode 100644
index 7a7d6fa..0000000
--- a/src/com/android/settings/development/BackgroundProcessLimitPreferenceController.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2017 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.development;
-
-import android.app.ActivityManager;
-import android.app.IActivityManager;
-import android.content.Context;
-import android.os.RemoteException;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.ListPreference;
-import androidx.preference.Preference;
-
-import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settingslib.R;
-import com.android.settingslib.development.DeveloperOptionsPreferenceController;
-
-public class BackgroundProcessLimitPreferenceController extends
-        DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener,
-        PreferenceControllerMixin {
-
-    private static final String APP_PROCESS_LIMIT_KEY = "app_process_limit";
-
-    private final String[] mListValues;
-    private final String[] mListSummaries;
-
-    public BackgroundProcessLimitPreferenceController(Context context) {
-        super(context);
-
-        mListValues = context.getResources().getStringArray(R.array.app_process_limit_values);
-        mListSummaries = context.getResources().getStringArray(R.array.app_process_limit_entries);
-    }
-
-    @Override
-    public String getPreferenceKey() {
-        return APP_PROCESS_LIMIT_KEY;
-    }
-
-    @Override
-    public boolean onPreferenceChange(Preference preference, Object newValue) {
-        writeAppProcessLimitOptions(newValue);
-        updateAppProcessLimitOptions();
-        return true;
-    }
-
-    @Override
-    public void updateState(Preference preference) {
-        updateAppProcessLimitOptions();
-    }
-
-    @Override
-    protected void onDeveloperOptionsSwitchDisabled() {
-        super.onDeveloperOptionsSwitchDisabled();
-        writeAppProcessLimitOptions(null);
-    }
-
-    private void updateAppProcessLimitOptions() {
-        try {
-            final int limit = getActivityManagerService().getProcessLimit();
-            int index = 0; // default
-            for (int i = 0; i < mListValues.length; i++) {
-                int val = Integer.parseInt(mListValues[i]);
-                if (val >= limit) {
-                    index = i;
-                    break;
-                }
-            }
-            final ListPreference listPreference = (ListPreference) mPreference;
-            listPreference.setValue(mListValues[index]);
-            listPreference.setSummary(mListSummaries[index]);
-        } catch (RemoteException e) {
-            // intentional no-op
-        }
-    }
-
-    private void writeAppProcessLimitOptions(Object newValue) {
-        try {
-            final int limit = newValue != null ? Integer.parseInt(newValue.toString()) : -1;
-            getActivityManagerService().setProcessLimit(limit);
-            updateAppProcessLimitOptions();
-        } catch (RemoteException e) {
-            // intentional no-op
-        }
-    }
-
-    @VisibleForTesting
-    IActivityManager getActivityManagerService() {
-        return ActivityManager.getService();
-    }
-}
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index 65a23eb..dd9a1f0 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -740,7 +740,6 @@
         controllers.add(new StrictModePreferenceController(context));
         controllers.add(new ProfileGpuRenderingPreferenceController(context));
         controllers.add(new KeepActivitiesPreferenceController(context));
-        controllers.add(new BackgroundProcessLimitPreferenceController(context));
         controllers.add(new CachedAppsFreezerPreferenceController(context));
         controllers.add(new ShowFirstCrashDialogPreferenceController(context));
         controllers.add(new AppsNotRespondingPreferenceController(context));
diff --git a/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java b/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java
index 56ce9e7..e92d999 100644
--- a/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java
+++ b/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java
@@ -507,7 +507,9 @@
                     }
                 }
             } else if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
-                if (MobileNetworkUtils.isTdscdmaSupported(mContext, mSubId)) {
+                if (mIsGlobalCdma) {
+                    enabledNetworkType = EnabledNetworks.ENABLED_NETWORKS_CDMA_CHOICES;
+                } else if (MobileNetworkUtils.isTdscdmaSupported(mContext, mSubId)) {
                     enabledNetworkType = EnabledNetworks.ENABLED_NETWORKS_TDSCDMA_CHOICES;
                 } else if (!mDisplay2gOptions && !mDisplay3gOptions) {
                     enabledNetworkType = mShow4gForLTE
@@ -521,8 +523,6 @@
                             : EnabledNetworks.ENABLED_NETWORKS_EXCEPT_GSM_CHOICES;
                 } else if (!mLteEnabled) {
                     enabledNetworkType = EnabledNetworks.ENABLED_NETWORKS_EXCEPT_LTE_CHOICES;
-                } else if (mIsGlobalCdma) {
-                    enabledNetworkType = EnabledNetworks.ENABLED_NETWORKS_CDMA_CHOICES;
                 } else {
                     enabledNetworkType = mShow4gForLTE ? EnabledNetworks.ENABLED_NETWORKS_4G_CHOICES
                             : EnabledNetworks.ENABLED_NETWORKS_CHOICES;
diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
index 1c3c78d..8b927a9 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
@@ -68,7 +68,6 @@
 import java.util.List;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
-import java.util.function.Consumer;
 
 @SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
 public class MobileNetworkSettings extends AbstractMobileNetworkSettings implements
@@ -359,23 +358,17 @@
     }
 
     private void onSubscriptionDetailChanged() {
-        if (mSubscriptionInfoEntity != null) {
-            /**
-             * Update the title when SIM stats got changed
-             */
-            final Consumer<Activity> renameTitle = activity -> {
-                if (activity != null && !activity.isFinishing()) {
-                    if (activity instanceof SettingsActivity) {
-                        ((SettingsActivity) activity).setTitle(mSubscriptionInfoEntity.uniqueName);
-                    }
-                }
-            };
-
-            ThreadUtils.postOnMainThread(() -> {
-                renameTitle.accept(getActivity());
-                redrawPreferenceControllers();
-            });
+        final SubscriptionInfoEntity subscriptionInfoEntity = mSubscriptionInfoEntity;
+        if (subscriptionInfoEntity == null) {
+            return;
         }
+        ThreadUtils.postOnMainThread(() -> {
+            if (getActivity() instanceof SettingsActivity activity && !activity.isFinishing()) {
+                // Update the title when SIM stats got changed
+                activity.setTitle(subscriptionInfoEntity.uniqueName);
+            }
+            redrawPreferenceControllers();
+        });
     }
 
     @Override
diff --git a/src/com/android/settings/password/BiometricFragment.java b/src/com/android/settings/password/BiometricFragment.java
index aeef482..02f5b86 100644
--- a/src/com/android/settings/password/BiometricFragment.java
+++ b/src/com/android/settings/password/BiometricFragment.java
@@ -25,6 +25,7 @@
 import android.multiuser.Flags;
 import android.os.Bundle;
 import android.os.CancellationSignal;
+import android.text.TextUtils;
 
 import androidx.annotation.NonNull;
 
@@ -149,6 +150,13 @@
                 .setShowEmergencyCallButton(promptInfo.isShowEmergencyCallButton())
                 .setReceiveSystemEvents(true)
                 .setComponentNameForConfirmDeviceCredentialActivity(callingActivity);
+        if (promptInfo.getLogoRes() != 0){
+            promptBuilder.setLogoRes(promptInfo.getLogoRes());
+        }
+        String logoDescription = promptInfo.getLogoDescription();
+        if (!TextUtils.isEmpty(logoDescription)) {
+            promptBuilder.setLogoDescription(logoDescription);
+        }
 
         if (android.os.Flags.allowPrivateProfile() && Flags.enablePrivateSpaceFeatures()
                 && Flags.enableBiometricsToUnlockPrivateSpace()) {
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
index 069f910..30fd619 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
@@ -20,6 +20,7 @@
 import static android.app.admin.DevicePolicyResources.Strings.Settings.CONFIRM_WORK_PROFILE_PASSWORD_HEADER;
 import static android.app.admin.DevicePolicyResources.Strings.Settings.CONFIRM_WORK_PROFILE_PATTERN_HEADER;
 import static android.app.admin.DevicePolicyResources.Strings.Settings.CONFIRM_WORK_PROFILE_PIN_HEADER;
+import static android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
 
 import android.app.Activity;
@@ -31,6 +32,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.pm.UserProperties;
 import android.content.res.Configuration;
 import android.graphics.Color;
@@ -44,6 +46,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.StorageManager;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.WindowManager;
 
@@ -65,6 +68,12 @@
 
     private static final String TAG_BIOMETRIC_FRAGMENT = "fragment";
 
+    /** Use this extra value to provide a custom logo for the biometric prompt. **/
+    public static final String CUSTOM_BIOMETRIC_PROMPT_LOGO_RES_ID_KEY = "custom_logo_res_id";
+    /** Use this extra value to provide a custom logo description for the biometric prompt. **/
+    public static final String CUSTOM_BIOMETRIC_PROMPT_LOGO_DESCRIPTION_KEY =
+            "custom_logo_description";
+
     public static class InternalActivity extends ConfirmDeviceCredentialActivity {
     }
 
@@ -202,6 +211,21 @@
         promptInfo.setDescription(mDetails);
         promptInfo.setDisallowBiometricsIfPolicyExists(mCheckDevicePolicyManager);
 
+        if (android.multiuser.Flags.enablePrivateSpaceFeatures()
+                && android.multiuser.Flags.usePrivateSpaceIconInBiometricPrompt()
+                && hasSetBiometricDialogAdvanced(mContext, getLaunchedFromUid())
+        ) {
+            int iconResId = intent.getIntExtra(CUSTOM_BIOMETRIC_PROMPT_LOGO_RES_ID_KEY, 0);
+            if (iconResId != 0) {
+                promptInfo.setLogoRes(iconResId);
+            }
+            String logoDescription = intent.getStringExtra(
+                    CUSTOM_BIOMETRIC_PROMPT_LOGO_DESCRIPTION_KEY);
+            if (!TextUtils.isEmpty(logoDescription)) {
+                promptInfo.setLogoDescription(logoDescription);
+            }
+        }
+
         final int policyType = mDevicePolicyManager.getManagedSubscriptionsPolicy().getPolicyType();
 
         if (isEffectiveUserManagedProfile
@@ -409,6 +433,14 @@
         }
     }
 
+    /**
+     * Checks if the calling uid has the permission to set biometric dialog icon and description.
+     */
+    private static boolean hasSetBiometricDialogAdvanced(@NonNull Context context, int callingUid) {
+        return context.checkPermission(SET_BIOMETRIC_DIALOG_ADVANCED, /* pid */ -1, callingUid)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
     // User could be locked while Effective user is unlocked even though the effective owns the
     // credential. Otherwise, biometric can't unlock fbe/keystore through
     // verifyTiedProfileChallenge. In such case, we also wanna show the user message that
diff --git a/src/com/android/settings/print/PrintRepository.kt b/src/com/android/settings/print/PrintRepository.kt
new file mode 100644
index 0000000..8a9182a
--- /dev/null
+++ b/src/com/android/settings/print/PrintRepository.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 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.print
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.print.PrintManager
+import android.printservice.PrintServiceInfo
+import com.android.settings.R
+import com.android.settingslib.spa.framework.util.mapItem
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+
+class PrintRepository(private val context: Context) {
+
+    private val printManager = context.getSystemService(PrintManager::class.java)!!
+    private val packageManager = context.packageManager
+
+    data class PrintServiceDisplayInfo(
+        val title: String,
+        val isEnabled: Boolean,
+        val summary: String,
+        val icon: Drawable,
+        val componentName: String,
+    )
+
+    fun printServiceDisplayInfosFlow(): Flow<List<PrintServiceDisplayInfo>> =
+        printServicesFlow()
+            .mapItem { printService -> printService.toPrintServiceDisplayInfo() }
+            .conflate()
+            .flowOn(Dispatchers.Default)
+
+    private fun PrintServiceInfo.toPrintServiceDisplayInfo() = PrintServiceDisplayInfo(
+        title = resolveInfo.loadLabel(packageManager).toString(),
+        isEnabled = isEnabled,
+        summary = context.getString(
+            if (isEnabled) R.string.print_feature_state_on else R.string.print_feature_state_off
+        ),
+        icon = resolveInfo.loadIcon(packageManager),
+        componentName = componentName.flattenToString(),
+    )
+
+    private fun printServicesFlow(): Flow<List<PrintServiceInfo>> =
+        printManager.printServicesChangeFlow()
+            .map { printManager.getPrintServices(PrintManager.ALL_SERVICES) }
+            .conflate()
+            .flowOn(Dispatchers.Default)
+
+    private companion object {
+        fun PrintManager.printServicesChangeFlow(): Flow<Unit> = callbackFlow {
+            val listener = PrintManager.PrintServicesChangeListener { trySend(Unit) }
+            addPrintServicesChangeListener(listener, null)
+            trySend(Unit)
+            awaitClose { removePrintServicesChangeListener(listener) }
+        }.conflate().flowOn(Dispatchers.Default)
+    }
+}
diff --git a/src/com/android/settings/print/PrintSettingsFragment.java b/src/com/android/settings/print/PrintSettingsFragment.java
index cd80998..ee94683 100644
--- a/src/com/android/settings/print/PrintSettingsFragment.java
+++ b/src/com/android/settings/print/PrintSettingsFragment.java
@@ -46,6 +46,7 @@
 import android.widget.Button;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 import androidx.loader.app.LoaderManager.LoaderCallbacks;
 import androidx.loader.content.AsyncTaskLoader;
@@ -54,7 +55,9 @@
 import androidx.preference.PreferenceCategory;
 
 import com.android.settings.R;
+import com.android.settings.flags.Flags;
 import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.spa.SpaActivity;
 import com.android.settingslib.search.Indexable;
 import com.android.settingslib.search.SearchIndexable;
 import com.android.settingslib.widget.AppPreference;
@@ -102,6 +105,15 @@
     }
 
     @Override
+    public void onAttach(@NonNull Context context) {
+        super.onAttach(context);
+        if (Flags.refactorPrintSettings()) {
+            SpaActivity.startSpaActivity(context, PrintSettingsPageProvider.INSTANCE.getName());
+            finish();
+        }
+    }
+
+    @Override
     protected String getLogTag() {
         return TAG;
     }
diff --git a/src/com/android/settings/print/PrintSettingsPageProvider.kt b/src/com/android/settings/print/PrintSettingsPageProvider.kt
new file mode 100644
index 0000000..aac0a5d
--- /dev/null
+++ b/src/com/android/settings/print/PrintSettingsPageProvider.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2024 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.print
+
+import android.app.settings.SettingsEnums
+import android.os.Bundle
+import androidx.annotation.VisibleForTesting
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import androidx.core.os.bundleOf
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.settings.R
+import com.android.settings.core.SubSettingLauncher
+import com.android.settings.print.PrintRepository.PrintServiceDisplayInfo
+import com.android.settings.print.PrintSettingsFragment.EXTRA_CHECKED
+import com.android.settings.print.PrintSettingsFragment.EXTRA_SERVICE_COMPONENT_NAME
+import com.android.settings.print.PrintSettingsFragment.EXTRA_TITLE
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.compose.rememberDrawablePainter
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+import com.android.settingslib.spa.widget.ui.Category
+import com.android.settingslib.spaprivileged.template.common.UserProfilePager
+
+object PrintSettingsPageProvider : SettingsPageProvider {
+    override val name = "PrintSettings"
+
+    @Composable
+    override fun Page(arguments: Bundle?) {
+        RegularScaffold(title = stringResource(R.string.print_settings)) {
+            val context = LocalContext.current
+            val printRepository = remember(context) { PrintRepository(context) }
+            UserProfilePager {
+                PrintServices(printRepository)
+            }
+        }
+    }
+
+    @Composable
+    private fun PrintServices(printRepository: PrintRepository) {
+        val printServiceDisplayInfos by remember {
+            printRepository.printServiceDisplayInfosFlow()
+        }.collectAsStateWithLifecycle(initialValue = emptyList())
+        Category(title = stringResource(R.string.print_settings_title)) {
+            for (printServiceDisplayInfo in printServiceDisplayInfos) {
+                PrintService(printServiceDisplayInfo)
+            }
+        }
+    }
+
+    @VisibleForTesting
+    @Composable
+    fun PrintService(displayInfo: PrintServiceDisplayInfo) {
+        val context = LocalContext.current
+        Preference(model = object : PreferenceModel {
+            override val title = displayInfo.title
+            override val summary = { displayInfo.summary }
+            override val icon: @Composable () -> Unit = {
+                Image(
+                    painter = rememberDrawablePainter(displayInfo.icon),
+                    contentDescription = null,
+                    modifier = Modifier.size(SettingsDimension.appIconItemSize),
+                )
+            }
+            override val onClick = {
+                SubSettingLauncher(context).apply {
+                    setDestination(PrintServiceSettingsFragment::class.qualifiedName)
+                    setArguments(
+                        bundleOf(
+                            EXTRA_CHECKED to displayInfo.isEnabled,
+                            EXTRA_TITLE to displayInfo.title,
+                            EXTRA_SERVICE_COMPONENT_NAME to displayInfo.componentName
+                        )
+                    )
+                    setSourceMetricsCategory(SettingsEnums.PRINT_SETTINGS)
+                }.launch()
+            }
+        })
+    }
+}
diff --git a/src/com/android/settings/privatespace/PrivateProfileContextHelperActivity.java b/src/com/android/settings/privatespace/PrivateProfileContextHelperActivity.java
index ef33d11..cba3b77 100644
--- a/src/com/android/settings/privatespace/PrivateProfileContextHelperActivity.java
+++ b/src/com/android/settings/privatespace/PrivateProfileContextHelperActivity.java
@@ -29,6 +29,7 @@
 
 import android.content.Intent;
 import android.os.Bundle;
+import android.os.UserHandle;
 import android.util.Log;
 
 import androidx.activity.result.ActivityResult;
@@ -70,11 +71,20 @@
         if (savedInstanceState == null) {
             int action = getIntent().getIntExtra(EXTRA_ACTION_TYPE, -1);
             if (action == ACCOUNT_LOGIN_ACTION) {
+                setContentView(R.layout.private_space_wait_screen);
                 PrivateSpaceLoginFeatureProvider privateSpaceLoginFeatureProvider =
                         FeatureFactory.getFeatureFactory().getPrivateSpaceLoginFeatureProvider();
-                if (!privateSpaceLoginFeatureProvider.initiateAccountLogin(
-                        this, mAddAccountToPrivateProfile)) {
-                    setResult(RESULT_OK);
+                UserHandle userHandle =
+                        PrivateSpaceMaintainer.getInstance(this).getPrivateProfileHandle();
+                if (userHandle != null) {
+                    if (!privateSpaceLoginFeatureProvider.initiateAccountLogin(
+                            createContextAsUser(userHandle, 0), mAddAccountToPrivateProfile)) {
+                        setResult(RESULT_OK);
+                        finish();
+                    }
+                } else {
+                    Log.w(TAG, "Private profile user handle is null");
+                    setResult(RESULT_CANCELED);
                     finish();
                 }
             } else if (action == SET_LOCK_ACTION) {
diff --git a/src/com/android/settings/privatespace/PrivateSpaceAccountLoginError.java b/src/com/android/settings/privatespace/PrivateSpaceAccountLoginError.java
index 48a5a7f..f0b960a 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceAccountLoginError.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceAccountLoginError.java
@@ -16,8 +16,12 @@
 
 package com.android.settings.privatespace;
 
+import static com.android.settings.privatespace.PrivateSpaceSetupActivity.ACCOUNT_LOGIN_ACTION;
+import static com.android.settings.privatespace.PrivateSpaceSetupActivity.EXTRA_ACTION_TYPE;
+
 import android.annotation.SuppressLint;
 import android.app.settings.SettingsEnums;
+import android.content.Intent;
 import android.os.Bundle;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -85,8 +89,7 @@
             mMetricsFeatureProvider.action(
                     getContext(),
                     SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_TRY_CREATE_ACCOUNT_AGAIN);
-            NavHostFragment.findNavController(PrivateSpaceAccountLoginError.this)
-                    .navigate(R.id.action_advance_login_error);
+            startAccountLogin();
         };
     }
 
@@ -102,4 +105,13 @@
                     .navigate(R.id.action_skip_account_login);
         };
     }
+
+    /** Start new activity in private profile to add an account to private profile */
+    private void startAccountLogin() {
+        Intent intent = new Intent(getContext(), PrivateProfileContextHelperActivity.class);
+        intent.putExtra(EXTRA_ACTION_TYPE, ACCOUNT_LOGIN_ACTION);
+        mMetricsFeatureProvider.action(
+                getContext(), SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_ACCOUNT_LOGIN_START);
+        getActivity().startActivityForResult(intent, ACCOUNT_LOGIN_ACTION);
+    }
 }
diff --git a/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java b/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java
index 00dcc46..08b5fb9 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java
@@ -20,6 +20,8 @@
 
 import static com.android.internal.app.SetScreenLockDialogActivity.LAUNCH_REASON_PRIVATE_SPACE_SETTINGS_ACCESS;
 import static com.android.settings.activityembedding.EmbeddedDeepLinkUtils.tryStartMultiPaneDeepLink;
+import static com.android.settings.password.ConfirmDeviceCredentialActivity.CUSTOM_BIOMETRIC_PROMPT_LOGO_DESCRIPTION_KEY;
+import static com.android.settings.password.ConfirmDeviceCredentialActivity.CUSTOM_BIOMETRIC_PROMPT_LOGO_RES_ID_KEY;
 
 import android.app.ActivityOptions;
 import android.app.KeyguardManager;
@@ -228,6 +230,14 @@
     private void authenticatePrivateSpaceEntry() {
         Intent credentialIntent = mPrivateSpaceMaintainer.getPrivateProfileLockCredentialIntent();
         if (credentialIntent != null) {
+            if (android.multiuser.Flags.usePrivateSpaceIconInBiometricPrompt()) {
+                credentialIntent.putExtra(CUSTOM_BIOMETRIC_PROMPT_LOGO_RES_ID_KEY,
+                        com.android.internal.R.drawable.stat_sys_private_profile_status);
+                credentialIntent.putExtra(CUSTOM_BIOMETRIC_PROMPT_LOGO_DESCRIPTION_KEY,
+                        getApplicationContext().getString(
+                                com.android.internal.R.string.private_space_biometric_prompt_title
+                        ));
+            }
             mVerifyDeviceLock.launch(credentialIntent);
         } else {
             Log.e(TAG, "verifyCredentialIntent is null even though device lock is set");
diff --git a/src/com/android/settings/privatespace/PrivateSpaceCreationFragment.java b/src/com/android/settings/privatespace/PrivateSpaceCreationFragment.java
index 1e0c65e..eb88644 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceCreationFragment.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceCreationFragment.java
@@ -16,8 +16,14 @@
 
 package com.android.settings.privatespace;
 
+import static com.android.settings.privatespace.PrivateSpaceSetupActivity.ACCOUNT_LOGIN_ACTION;
+import static com.android.settings.privatespace.PrivateSpaceSetupActivity.EXTRA_ACTION_TYPE;
+
 import android.app.settings.SettingsEnums;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.os.Bundle;
@@ -42,12 +48,31 @@
 public class PrivateSpaceCreationFragment extends InstrumentedFragment {
     private static final String TAG = "PrivateSpaceCreateFrag";
     private static final int PRIVATE_SPACE_CREATE_POST_DELAY_MS = 1000;
+    private static final int PRIVATE_SPACE_ACCOUNT_LOGIN_POST_DELAY_MS = 5000;
     private static final Handler sHandler = new Handler(Looper.getMainLooper());
     private Runnable mRunnable =
             () -> {
                 createPrivateSpace();
             };
 
+    private Runnable mAccountLoginRunnable =
+            () -> {
+                unRegisterReceiver();
+                startAccountLogin();
+            };
+
+    final BroadcastReceiver mProfileAccessReceiver = new  BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (action.equals(Intent.ACTION_PROFILE_ACCESSIBLE)) {
+                Log.i(TAG, "onReceive " + action);
+                sHandler.removeCallbacks(mAccountLoginRunnable);
+                sHandler.post(mAccountLoginRunnable);
+            }
+        }
+    };
+
     @Override
     public void onCreate(@Nullable Bundle savedInstanceState) {
         if (android.os.Flags.allowPrivateProfile()
@@ -82,6 +107,7 @@
         super.onResume();
         // Ensures screen visibility to user by introducing a 1-second delay before creating private
         // space.
+        sHandler.removeCallbacks(mRunnable);
         sHandler.postDelayed(mRunnable, PRIVATE_SPACE_CREATE_POST_DELAY_MS);
     }
 
@@ -97,8 +123,9 @@
             mMetricsFeatureProvider.action(
                     getContext(), SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_SPACE_CREATED, true);
             if (isConnectedToInternet()) {
-                NavHostFragment.findNavController(PrivateSpaceCreationFragment.this)
-                        .navigate(R.id.action_account_intro_fragment);
+                registerReceiver();
+                sHandler.postDelayed(
+                        mAccountLoginRunnable, PRIVATE_SPACE_ACCOUNT_LOGIN_POST_DELAY_MS);
             } else {
                 NavHostFragment.findNavController(PrivateSpaceCreationFragment.this)
                         .navigate(R.id.action_set_lock_fragment);
@@ -127,4 +154,25 @@
         NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
         return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
     }
+
+    /** Start new activity in private profile to add an account to private profile */
+    private void startAccountLogin() {
+        Intent intent = new Intent(getContext(), PrivateProfileContextHelperActivity.class);
+        intent.putExtra(EXTRA_ACTION_TYPE, ACCOUNT_LOGIN_ACTION);
+        mMetricsFeatureProvider.action(
+                getContext(), SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_ACCOUNT_LOGIN_START);
+        getActivity().startActivityForResult(intent, ACCOUNT_LOGIN_ACTION);
+    }
+
+    private void registerReceiver() {
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_PROFILE_ACCESSIBLE);
+        getActivity().registerReceiver(mProfileAccessReceiver, filter);
+    }
+
+    private void unRegisterReceiver() {
+        if (mProfileAccessReceiver != null) {
+            getActivity().unregisterReceiver(mProfileAccessReceiver);
+        }
+    }
 }
diff --git a/src/com/android/settings/privatespace/PrivateSpaceGaiaEducationFragment.java b/src/com/android/settings/privatespace/PrivateSpaceGaiaEducationFragment.java
deleted file mode 100644
index 6c26186..0000000
--- a/src/com/android/settings/privatespace/PrivateSpaceGaiaEducationFragment.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2024 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.privatespace;
-
-import static com.android.settings.privatespace.PrivateSpaceSetupActivity.ACCOUNT_LOGIN_ACTION;
-import static com.android.settings.privatespace.PrivateSpaceSetupActivity.EXTRA_ACTION_TYPE;
-
-import android.app.settings.SettingsEnums;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.activity.OnBackPressedCallback;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.navigation.fragment.NavHostFragment;
-
-import com.android.settings.R;
-import com.android.settings.core.InstrumentedFragment;
-
-import com.google.android.setupcompat.template.FooterBarMixin;
-import com.google.android.setupcompat.template.FooterButton;
-import com.google.android.setupdesign.GlifLayout;
-
-/** Fragment for GAIA education screen */
-public class PrivateSpaceGaiaEducationFragment extends InstrumentedFragment {
-    private static final String TAG = "PrivateSpaceGaiaEduFrag";
-
-    @Override
-    public void onCreate(@Nullable Bundle savedInstanceState) {
-        if (android.os.Flags.allowPrivateProfile()
-                && android.multiuser.Flags.enablePrivateSpaceFeatures()) {
-            super.onCreate(savedInstanceState);
-        }
-    }
-
-    @NonNull
-    @Override
-    public View onCreateView(
-            @NonNull LayoutInflater inflater,
-            @Nullable ViewGroup container,
-            @Nullable Bundle savedInstanceState) {
-        GlifLayout rootView =
-                (GlifLayout)
-                        inflater.inflate(
-                                R.layout.private_space_gaia_education_screen, container, false);
-        final FooterBarMixin mixin = rootView.getMixin(FooterBarMixin.class);
-        mixin.setPrimaryButton(
-                new FooterButton.Builder(getContext())
-                        .setText(R.string.private_space_gaia_education_got_it)
-                        .setListener(onStartLogin())
-                        .setButtonType(FooterButton.ButtonType.NEXT)
-                        .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
-                        .build());
-        mixin.setSecondaryButton(
-                new FooterButton.Builder(getContext())
-                        .setText(R.string.skip_label)
-                        .setListener(onSkip())
-                        .setButtonType(FooterButton.ButtonType.NEXT)
-                        .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Secondary)
-                        .build());
-        OnBackPressedCallback callback =
-                new OnBackPressedCallback(true /* enabled by default */) {
-                    @Override
-                    public void handleOnBackPressed() {
-                        // Handle the back button event. We intentionally don't want to allow back
-                        // button to work in this screen during the setup flow.
-                    }
-                };
-        requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
-
-        return rootView;
-    }
-
-    @Override
-    public int getMetricsCategory() {
-        return METRICS_CATEGORY_UNKNOWN;
-    }
-
-    private View.OnClickListener onSkip() {
-        return v -> {
-            NavHostFragment.findNavController(PrivateSpaceGaiaEducationFragment.this)
-                    .navigate(R.id.action_account_lock_fragment);
-        };
-    }
-
-    private View.OnClickListener onStartLogin() {
-        return v -> {
-            startAccountLogin();
-        };
-    }
-
-    /** Start new activity in private profile to add an account to private profile */
-    private void startAccountLogin() {
-        UserHandle userHandle =
-                PrivateSpaceMaintainer.getInstance(getActivity()).getPrivateProfileHandle();
-        if (userHandle != null) {
-            Intent intent = new Intent(getContext(), PrivateProfileContextHelperActivity.class);
-            intent.putExtra(EXTRA_ACTION_TYPE, ACCOUNT_LOGIN_ACTION);
-            mMetricsFeatureProvider.action(
-                    getContext(), SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_ACCOUNT_LOGIN_START);
-            getActivity().startActivityForResultAsUser(intent, ACCOUNT_LOGIN_ACTION, userHandle);
-        } else {
-            Log.w(TAG, "Private profile user handle is null");
-        }
-    }
-}
diff --git a/src/com/android/settings/privatespace/PrivateSpaceSetupActivity.java b/src/com/android/settings/privatespace/PrivateSpaceSetupActivity.java
index 4cbcac7..d67ce55 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceSetupActivity.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceSetupActivity.java
@@ -65,7 +65,7 @@
             if (resultCode == RESULT_OK) {
                 mMetricsFeatureProvider.action(
                         this, SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_ACCOUNT_LOGIN_SUCCESS, true);
-                mNavHostFragment.getNavController().navigate(R.id.action_account_lock_fragment);
+                mNavHostFragment.getNavController().navigate(R.id.show_set_lock_fragment);
             } else {
                 mMetricsFeatureProvider.action(
                         this,
diff --git a/src/com/android/settings/privatespace/SetupPreFinishDelayFragment.java b/src/com/android/settings/privatespace/SetupPreFinishDelayFragment.java
index 4f2634e..89e2db1 100644
--- a/src/com/android/settings/privatespace/SetupPreFinishDelayFragment.java
+++ b/src/com/android/settings/privatespace/SetupPreFinishDelayFragment.java
@@ -92,7 +92,7 @@
             @Nullable Bundle savedInstanceState) {
         GlifLayout rootView =
                 (GlifLayout)
-                        inflater.inflate(R.layout.private_space_pre_finish_delay, container, false);
+                        inflater.inflate(R.layout.private_space_wait_screen, container, false);
         OnBackPressedCallback callback =
                 new OnBackPressedCallback(true /* enabled by default */) {
                     @Override
diff --git a/src/com/android/settings/spa/SettingsSpaEnvironment.kt b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
index 5dbc8dc..45d238a 100644
--- a/src/com/android/settings/spa/SettingsSpaEnvironment.kt
+++ b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
@@ -19,6 +19,7 @@
 import android.content.Context
 import android.util.FeatureFlagUtils
 import com.android.settings.network.apn.ApnEditPageProvider
+import com.android.settings.print.PrintSettingsPageProvider
 import com.android.settings.spa.about.AboutPhonePageProvider
 import com.android.settings.spa.app.AllAppListPageProvider
 import com.android.settings.spa.app.AppsMainPageProvider
@@ -120,6 +121,7 @@
         BatteryOptimizationModeAppListPageProvider,
         NetworkCellularGroupProvider(),
         WifiPrivacyPageProvider,
+        PrintSettingsPageProvider,
     )
 
     override val logger = if (FeatureFlagUtils.isEnabled(
diff --git a/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt b/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
index 6c8ce6e..6466731 100644
--- a/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
+++ b/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
@@ -87,12 +87,17 @@
     }
     val phoneNumber = phoneNumber(subInfo)
     val alertDialogPresenter = rememberAlertDialogPresenter(
-        confirmButton = AlertDialogButton(stringResource(R.string.mobile_network_sim_name_rename)) {
+        confirmButton = AlertDialogButton(
+            stringResource(R.string.mobile_network_sim_name_rename),
+            titleSimName.isNotBlank()
+        ) {
             onboardingService.addItemForRenaming(
                 subInfo, if (titleSimName.isEmpty()) originalSimCarrierName else titleSimName
             )
         },
-        dismissButton = AlertDialogButton(stringResource(R.string.cancel)) {
+        dismissButton = AlertDialogButton(
+            stringResource(R.string.cancel),
+        ) {
             titleSimName = onboardingService.getSubscriptionInfoDisplayName(subInfo)
         },
         title = stringResource(R.string.sim_onboarding_label_sim_dialog_title),
@@ -107,7 +112,7 @@
                 placeholder = {Text(text = originalSimCarrierName)},
                 modifier = Modifier.fillMaxWidth().testTag("contentInput")
             ) {
-                titleSimName = if (it.matches(Regex("^\\s*$"))) originalSimCarrierName else it
+                titleSimName = it
             }
         },
     )
diff --git a/src/com/android/settings/wifi/WifiConfigController2.java b/src/com/android/settings/wifi/WifiConfigController2.java
index 1e40568..70e08eb 100644
--- a/src/com/android/settings/wifi/WifiConfigController2.java
+++ b/src/com/android/settings/wifi/WifiConfigController2.java
@@ -311,7 +311,9 @@
         mHiddenSettingsSpinner = mView.findViewById(R.id.hidden_settings);
         if (!mHideMeteredAndPrivacy && mWifiManager.isConnectedMacRandomizationSupported()) {
             mPrivacySettingsSpinner = mView.findViewById(R.id.privacy_settings);
-            mDhcpSettingsSpinner  = mView.findViewById(R.id.dhcp_settings);
+            if (Flags.androidVWifiApi()) {
+                mDhcpSettingsSpinner = mView.findViewById(R.id.dhcp_settings);
+            }
             mView.findViewById(R.id.privacy_settings_fields).setVisibility(View.VISIBLE);
         }
         mHiddenSettingsSpinner.setOnItemSelectedListener(this);
diff --git a/tests/Enable16KbTests/Android.bp b/tests/Enable16KbTests/Android.bp
index 57c6ef6..ecb0357 100644
--- a/tests/Enable16KbTests/Android.bp
+++ b/tests/Enable16KbTests/Android.bp
@@ -17,7 +17,7 @@
     default_team: "trendy_team_android_kernel",
 }
 
-android_test_helper_app {
+android_test {
     name: "test_16kb_app",
     srcs: ["test_16kb_app/src/**/*.java"],
     manifest: "test_16kb_app/test_16kb_app.xml",
diff --git a/tests/Enable16KbTests/TEST_MAPPING b/tests/Enable16KbTests/TEST_MAPPING
new file mode 100644
index 0000000..fbe6fe1
--- /dev/null
+++ b/tests/Enable16KbTests/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+  "postsubmit": [
+    {
+      "name": "Enable16KbTest"
+    }
+  ]
+}
+
diff --git a/tests/Enable16KbTests/test_16kb_app/test_16kb_app.xml b/tests/Enable16KbTests/test_16kb_app/test_16kb_app.xml
index 8fe9ad5..3ca786a 100644
--- a/tests/Enable16KbTests/test_16kb_app/test_16kb_app.xml
+++ b/tests/Enable16KbTests/test_16kb_app/test_16kb_app.xml
@@ -16,8 +16,8 @@
   -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.settings.development.test"
-          android:sharedUserId="android.uid.systemui">
+          android:installLocation="internalOnly"
+          package="com.android.settings.development.test">
     <application>
         <uses-library android:name="android.test.runner"/>
     </application>
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java
index 743f884..c521f25 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java
@@ -60,6 +60,7 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.shadow.api.Shadow;
 import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.shadows.ShadowLooper;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -132,6 +133,7 @@
         Intent intent = new Intent(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
         intent.putExtra(BluetoothHearingAid.EXTRA_STATE, BluetoothHearingAid.STATE_CONNECTED);
         sendIntent(intent);
+        ShadowLooper.idleMainLooper();
 
         assertThat(mHearingAidPreference.getSummary().toString().contentEquals(
                 "TEST_HEARING_AID_BT_DEVICE_NAME / Right only")).isTrue();
@@ -149,6 +151,7 @@
         Intent intent = new Intent(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
         intent.putExtra(BluetoothHearingAid.EXTRA_STATE, BluetoothHearingAid.STATE_CONNECTED);
         sendIntent(intent);
+        ShadowLooper.idleMainLooper();
 
         assertThat(mHearingAidPreference.getSummary().toString().contentEquals(
                 "TEST_HEARING_AID_BT_DEVICE_NAME / Left and right")).isTrue();
@@ -165,6 +168,7 @@
         Intent intent = new Intent(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED);
         intent.putExtra(BluetoothHearingAid.EXTRA_STATE, BluetoothHapClient.STATE_CONNECTED);
         sendIntent(intent);
+        ShadowLooper.idleMainLooper();
 
         assertThat(mHearingAidPreference.getSummary().toString().contentEquals(
                 "TEST_HEARING_AID_BT_DEVICE_NAME / Left only")).isTrue();
@@ -181,6 +185,7 @@
         Intent intent = new Intent(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED);
         intent.putExtra(BluetoothHearingAid.EXTRA_STATE, BluetoothHapClient.STATE_CONNECTED);
         sendIntent(intent);
+        ShadowLooper.idleMainLooper();
 
         assertThat(mHearingAidPreference.getSummary().toString().contentEquals(
                 "TEST_HEARING_AID_BT_DEVICE_NAME / Right only")).isTrue();
@@ -197,6 +202,7 @@
         Intent intent = new Intent(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED);
         intent.putExtra(BluetoothHearingAid.EXTRA_STATE, BluetoothHapClient.STATE_CONNECTED);
         sendIntent(intent);
+        ShadowLooper.idleMainLooper();
 
         assertThat(mHearingAidPreference.getSummary().toString().contentEquals(
                 "TEST_HEARING_AID_BT_DEVICE_NAME / Left and right")).isTrue();
@@ -212,6 +218,7 @@
         Intent intent = new Intent(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED);
         intent.putExtra(BluetoothHearingAid.EXTRA_STATE, BluetoothHapClient.STATE_CONNECTED);
         sendIntent(intent);
+        ShadowLooper.idleMainLooper();
 
         assertThat(mHearingAidPreference.getSummary().toString()).isEqualTo(
                 "TEST_HEARING_AID_BT_DEVICE_NAME / Left and right");
@@ -228,6 +235,7 @@
         Intent intent = new Intent(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
         intent.putExtra(BluetoothHearingAid.EXTRA_STATE, BluetoothHearingAid.STATE_CONNECTED);
         sendIntent(intent);
+        ShadowLooper.idleMainLooper();
 
         assertThat(mHearingAidPreference.getSummary().toString().contentEquals(
                 "TEST_HEARING_AID_BT_DEVICE_NAME +1 more")).isTrue();
@@ -239,6 +247,7 @@
         Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
         intent.putExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF);
         sendIntent(intent);
+        ShadowLooper.idleMainLooper();
 
         assertThat(mHearingAidPreference.getSummary()).isEqualTo(
                 mContext.getText(R.string.accessibility_hearingaid_not_connected_summary));
@@ -252,6 +261,7 @@
 
         mPreferenceController.onStart();
         mPreferenceController.onServiceConnected();
+        ShadowLooper.idleMainLooper();
 
         assertThat(mHearingAidPreference.getSummary().toString()).isEqualTo(
                 "TEST_HEARING_AID_BT_DEVICE_NAME / Left only");
@@ -265,6 +275,7 @@
 
         mPreferenceController.onStart();
         mPreferenceController.onServiceConnected();
+        ShadowLooper.idleMainLooper();
 
         assertThat(mHearingAidPreference.getSummary().toString()).isEqualTo(
                 "TEST_HEARING_AID_BT_DEVICE_NAME / Right only");
diff --git a/tests/robotests/src/com/android/settings/development/BackgroundProcessLimitPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BackgroundProcessLimitPreferenceControllerTest.java
deleted file mode 100644
index d51547e..0000000
--- a/tests/robotests/src/com/android/settings/development/BackgroundProcessLimitPreferenceControllerTest.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2017 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.development;
-
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.IActivityManager;
-import android.content.Context;
-import android.os.RemoteException;
-
-import androidx.preference.ListPreference;
-import androidx.preference.PreferenceScreen;
-
-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;
-
-@RunWith(RobolectricTestRunner.class)
-public class BackgroundProcessLimitPreferenceControllerTest {
-
-    @Mock
-    private IActivityManager mActivityManager;
-    @Mock
-    private ListPreference mPreference;
-    @Mock
-    private PreferenceScreen mScreen;
-
-    /**
-     * 0: Standard limit
-     * 1: No Background processes
-     * 2: At most 1 process
-     * 3: At most 2 processes
-     * 4: At most 3 processes
-     * 5: At most 4 processes
-     */
-    private String[] mListValues;
-    private String[] mListSummaries;
-    private Context mContext;
-    private BackgroundProcessLimitPreferenceController mController;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        mContext = RuntimeEnvironment.application;
-        mListValues = mContext.getResources()
-                .getStringArray(com.android.settingslib.R.array.app_process_limit_values);
-        mListSummaries = mContext.getResources()
-                .getStringArray(com.android.settingslib.R.array.app_process_limit_entries);
-        mController = spy(new BackgroundProcessLimitPreferenceController(mContext));
-        doReturn(mActivityManager).when(mController).getActivityManagerService();
-        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
-        mController.displayPreference(mScreen);
-    }
-
-    @Test
-    public void onPreferenceChange_noBackgroundProcessSet_shouldSetToNoBackgroundProcess()
-            throws RemoteException {
-        mController.onPreferenceChange(mPreference, mListValues[1]);
-
-        verify(mActivityManager).setProcessLimit(Integer.valueOf(mListValues[1]));
-    }
-
-    @Test
-    public void onPreferenceChange_1ProcessSet_shouldSetTo1BackgroundProcess()
-            throws RemoteException {
-        mController.onPreferenceChange(mPreference, mListValues[2]);
-
-        verify(mActivityManager).setProcessLimit(Integer.valueOf(mListValues[2]));
-    }
-
-    @Test
-    public void updateState_noBackgroundProcessSet_shouldSetPreferenceToNoBackgroundProcess()
-            throws RemoteException {
-        when(mActivityManager.getProcessLimit()).thenReturn(Integer.valueOf(mListValues[1]));
-
-        mController.updateState(mPreference);
-
-        verify(mPreference).setValue(mListValues[1]);
-        verify(mPreference).setSummary(mListSummaries[1]);
-    }
-
-    @Test
-    public void updateState_1ProcessSet_shouldSetPreference1BackgroundProcess()
-            throws RemoteException {
-        when(mActivityManager.getProcessLimit()).thenReturn(Integer.valueOf(mListValues[2]));
-
-        mController.updateState(mPreference);
-
-        verify(mPreference).setValue(mListValues[2]);
-        verify(mPreference).setSummary(mListSummaries[2]);
-    }
-
-    @Test
-    public void updateState_veryHighLimit_shouldDefaultToStandardLimit() throws RemoteException {
-        when(mActivityManager.getProcessLimit()).thenReturn(Integer.MAX_VALUE);
-
-        mController.updateState(mPreference);
-
-        verify(mPreference).setValue(mListValues[0]);
-        verify(mPreference).setSummary(mListSummaries[0]);
-    }
-
-    @Test
-    public void onDeveloperOptionsSwitchDisabled_shouldDisableAndResetPreference()
-            throws RemoteException {
-        mController.onDeveloperOptionsSwitchDisabled();
-
-        verify(mPreference).setEnabled(false);
-        verify(mActivityManager).setProcessLimit(-1);
-    }
-}
diff --git a/tests/spa_unit/src/com/android/settings/print/PrintRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/print/PrintRepositoryTest.kt
new file mode 100644
index 0000000..79d86da
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/print/PrintRepositoryTest.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2024 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.print
+
+import android.content.Context
+import android.content.pm.ResolveInfo
+import android.content.pm.ServiceInfo
+import android.graphics.drawable.Drawable
+import android.print.PrintManager
+import android.printservice.PrintServiceInfo
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+
+@RunWith(AndroidJUnit4::class)
+class PrintRepositoryTest {
+
+    private val printServiceInfo = PrintServiceInfo(
+        /* resolveInfo = */ ResolveInfo().apply { serviceInfo = MockServiceInfo },
+        /* settingsActivityName = */ "",
+        /* addPrintersActivityName = */ "",
+        /* advancedPrintOptionsActivityName = */ "",
+    )
+
+    private val mockPrintManager = mock<PrintManager> {
+        on { getPrintServices(PrintManager.ALL_SERVICES) } doReturn listOf(printServiceInfo)
+    }
+
+    private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+        on { getSystemService(PrintManager::class.java) } doReturn mockPrintManager
+    }
+
+    private val repository = PrintRepository(context)
+
+    @Test
+    fun printServiceDisplayInfosFlow_title() = runBlocking {
+        val displayInfo = repository.printServiceDisplayInfosFlow().firstWithTimeoutOrNull()!!
+            .single()
+
+        assertThat(displayInfo.title).isEqualTo(LABEL)
+    }
+
+    @Test
+    fun printServiceDisplayInfosFlow_isEnabled() = runBlocking {
+        printServiceInfo.setIsEnabled(true)
+
+        val displayInfo = repository.printServiceDisplayInfosFlow().firstWithTimeoutOrNull()!!
+            .single()
+
+        assertThat(displayInfo.isEnabled).isTrue()
+        assertThat(displayInfo.summary)
+            .isEqualTo(context.getString(R.string.print_feature_state_on))
+    }
+
+    @Test
+    fun printServiceDisplayInfosFlow_notEnabled() = runBlocking {
+        printServiceInfo.setIsEnabled(false)
+
+        val displayInfo = repository.printServiceDisplayInfosFlow().firstWithTimeoutOrNull()!!
+            .single()
+
+        assertThat(displayInfo.isEnabled).isFalse()
+        assertThat(displayInfo.summary)
+            .isEqualTo(context.getString(R.string.print_feature_state_off))
+    }
+
+    @Test
+    fun printServiceDisplayInfosFlow_componentName() = runBlocking {
+        val displayInfo = repository.printServiceDisplayInfosFlow().firstWithTimeoutOrNull()!!
+            .single()
+
+        assertThat(displayInfo.componentName).isEqualTo("$PACKAGE_NAME/$SERVICE_NAME")
+    }
+
+    private companion object {
+        const val PACKAGE_NAME = "package.name"
+        const val SERVICE_NAME = "ServiceName"
+        const val LABEL = "Label"
+        val MockServiceInfo = mock<ServiceInfo> {
+            on { loadLabel(any()) } doReturn LABEL
+            on { loadIcon(any()) } doReturn mock<Drawable>()
+        }.apply {
+            packageName = PACKAGE_NAME
+            name = SERVICE_NAME
+        }
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/print/PrintSettingsPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/print/PrintSettingsPageProviderTest.kt
new file mode 100644
index 0000000..746816b
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/print/PrintSettingsPageProviderTest.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2024 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.print
+
+import android.content.Context
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.test.isDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.android.settings.SettingsActivity
+import com.android.settings.print.PrintRepository.PrintServiceDisplayInfo
+import com.android.settings.print.PrintSettingsFragment.EXTRA_CHECKED
+import com.android.settings.print.PrintSettingsFragment.EXTRA_SERVICE_COMPONENT_NAME
+import com.android.settings.print.PrintSettingsFragment.EXTRA_TITLE
+import com.android.settings.print.PrintSettingsPageProvider.PrintService
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argThat
+import org.mockito.kotlin.doNothing
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@RunWith(AndroidJUnit4::class)
+class PrintSettingsPageProviderTest {
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+        doNothing().whenever(mock).startActivity(any())
+    }
+
+    private val displayInfo = PrintServiceDisplayInfo(
+        title = TITLE,
+        isEnabled = true,
+        summary = SUMMARY,
+        icon = context.getDrawable(R.drawable.ic_settings_print)!!,
+        componentName = "ComponentName",
+    )
+
+    @Test
+    fun printService_titleDisplayed() {
+        composeTestRule.setContent {
+            PrintService(displayInfo)
+        }
+
+        composeTestRule.onNodeWithText(TITLE).isDisplayed()
+    }
+
+    @Test
+    fun printService_summaryDisplayed() {
+        composeTestRule.setContent {
+            PrintService(displayInfo)
+        }
+
+        composeTestRule.onNodeWithText(SUMMARY).isDisplayed()
+    }
+
+    @Test
+    fun printService_onClick() {
+        composeTestRule.setContent {
+            CompositionLocalProvider(LocalContext provides context) {
+                PrintService(displayInfo)
+            }
+        }
+
+        composeTestRule.onNodeWithText(TITLE).performClick()
+
+        verify(context).startActivity(argThat {
+            val fragment = getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)
+            val arguments = getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS)!!
+            fragment == PrintServiceSettingsFragment::class.qualifiedName &&
+                arguments.getBoolean(EXTRA_CHECKED) == displayInfo.isEnabled &&
+                arguments.getString(EXTRA_TITLE) == displayInfo.title &&
+                arguments.getString(EXTRA_SERVICE_COMPONENT_NAME) == displayInfo.componentName
+        })
+    }
+
+    private companion object {
+        const val TITLE = "Title"
+        const val SUMMARY = "Summary"
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt
index 79f53ca..ad2ba55 100644
--- a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt
@@ -28,6 +28,8 @@
 import androidx.compose.ui.platform.LocalLifecycleOwner
 import androidx.compose.ui.semantics.SemanticsProperties
 import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsNotEnabled
 import androidx.compose.ui.test.hasText
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
@@ -186,7 +188,7 @@
     }
 
     @Test
-    fun showDialog_clearContent_showOriginalDisplayName() {
+    fun showDialog_clearContent_saveBtnIsDisabled() {
         preSetupContent()
 
         composeTestRule.setContent {
@@ -196,15 +198,12 @@
         composeTestRule.onNodeWithText(DISPLAY_NAME_1).performClick()
         composeTestRule.onNodeWithTag(TEXT_FIELD_INPUT).performTextClearance()
 
-        assertEquals(
-            composeTestRule.onNodeWithTag(TEXT_FIELD_INPUT)
-                .fetchSemanticsNode()
-                .config[SemanticsProperties.EditableText].text, DISPLAY_NAME_1
-        )
+        composeTestRule.onNodeWithText(context.getString(R.string.mobile_network_sim_name_rename))
+            .assertIsNotEnabled()
     }
 
     @Test
-    fun showDialog_modifyContent_showModifiedDisplayName() {
+    fun showDialog_modifyContent_showAndSaveModifiedDisplayName() {
         val inputData = "input_data"
         preSetupContent()
 
@@ -232,7 +231,7 @@
     }
 
     @Test
-    fun showDialog_onlySpaceCharContent_showAndSaveOriginalDisplayName() {
+    fun showDialog_onlySpaceCharContent_saveBtnIsDisabled() {
         val spaceChars = "        ";
         preSetupContent()
 
@@ -241,30 +240,12 @@
         }
 
         // Simulate real operation,
-        // 1. Click preference of DISPLAY_NAME_1
         composeTestRule.onNodeWithText(DISPLAY_NAME_1).performClick()
-        // 2. Input space chars to EditText view
+        composeTestRule.onNodeWithTag(TEXT_FIELD_INPUT).performTextClearance()
         composeTestRule.onNodeWithTag(TEXT_FIELD_INPUT).performTextInput(spaceChars)
-        // 3. Remove the string of DISPLAY_NAME_1 from EditText view
-        repeat(DISPLAY_NAME_1.length) {
-            composeTestRule.onNodeWithTag(TEXT_FIELD_INPUT)
-                .performKeyPress(KeyEvent(NativeKeyEvent(ACTION_DOWN, KEYCODE_FORWARD_DEL)))
-        }
 
-        // Due to this TextField with Text and EditText, it need fetch correct node to get correct
-        // content.
-        assertEquals(
-            DISPLAY_NAME_1, composeTestRule.onNodeWithTag(TEXT_FIELD_INPUT)
-                .fetchSemanticsNode()
-                .config[SemanticsProperties.EditableText].text
-        )
-
-        // Click save button
         composeTestRule.onNodeWithText(context.getString(R.string.mobile_network_sim_name_rename))
-            .performClick()
-
-        // Check preference's name is still DISPLAY_NAME_1
-        composeTestRule.onNodeWithText(DISPLAY_NAME_1).assertExists()
+            .assertIsNotEnabled()
     }
 
     fun preSetupContent() {