Merge "Inline shouldDisableMediaOutput into Settings app" into main
diff --git a/res-product/values/strings.xml b/res-product/values/strings.xml
index 4736d22..3ce29d6 100644
--- a/res-product/values/strings.xml
+++ b/res-product/values/strings.xml
@@ -738,9 +738,4 @@
     <string name="daltonizer_feature_summary" product="default">Adjust how colors display on your phone</string>
     <!-- The daltonizer feature summary display as a subtext as an item in a list. -->
     <string name="daltonizer_feature_summary" product="tablet">Adjust how colors display on your tablet</string>
-
-    <!--  Warning text about the visibility of device name on phone. [CHAR LIMIT=NONE] -->
-    <string name="about_phone_device_name_warning" product="default">Your device name is visible to apps on your phone. It may also be seen by other people when you connect to Bluetooth devices, connect to a Wi-Fi network or set up a Wi-Fi hotspot.</string>
-    <!--  Warning text about the visibility of device name on tablet. [CHAR LIMIT=NONE] -->
-    <string name="about_phone_device_name_warning" product="tablet">Your device name is visible to apps on your tablet. It may also be seen by other people when you connect to Bluetooth devices, connect to a Wi-Fi network or set up a Wi-Fi hotspot.</string>
 </resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index 51cfeb7..f20bce0 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -545,6 +545,9 @@
     <!-- Whether to show Smooth Display feature in Settings Options -->
     <bool name="config_show_smooth_display">false</bool>
 
+    <!-- Whether to show Stay awake on fold feature in Settings Options -->
+    <bool name="config_stay_awake_on_fold">false</bool>
+
     <!-- Whether to show emergency settings in top-level Settings -->
     <bool name="config_show_emergency_settings">true</bool>
 
diff --git a/res/values/strings.xml b/res/values/strings.xml
index ba4ff3c..177a5cb 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2378,6 +2378,10 @@
     <string name="display_white_balance_title">Display white balance</string>
     <!-- Display settings screen, display white balance settings summary [CHAR LIMIT=NONE] -->
     <string name="display_white_balance_summary"></string>
+    <!-- Display settings screen, setting name to enable staying awake on fold [CHAR LIMIT=30] -->
+    <string name="stay_awake_on_fold_title">Stay unlocked on fold</string>
+    <!-- Display settings screen, setting summary to enable staying awake on fold [CHAR LIMIT=NONE] -->
+    <string name="stay_awake_on_fold_summary">Keep front display unlocked when folded until screen timeout</string>
     <!-- Display settings screen, peak refresh rate settings title [CHAR LIMIT=30] -->
     <string name="peak_refresh_rate_title">Smooth Display</string>
     <!-- Display settings screen, peak refresh rate settings summary [CHAR LIMIT=NONE] -->
@@ -7061,6 +7065,9 @@
     <string name="keywords_app_pinning">screen pinning</string>
     <string name="keywords_profile_challenge">work challenge, work, profile</string>
     <string name="keywords_unification">work profile, managed profile, unify, unification, work, profile</string>
+    <string name="keywords_stay_awake_on_lock">
+        awake, sleep, do not lock, stay unlocked on fold, folding, closing, fold, close, screen off
+    </string>
     <string name="keywords_gesture">gestures</string>
     <string name="keywords_wallet">wallet</string>
     <string name="keywords_payment_settings">pay, tap, payments</string>
@@ -12211,4 +12218,6 @@
     <!-- Summary of the Live Caption enabled state. -->
     <string name="live_caption_enabled">On</string>
 
+    <!--  Warning text about the visibility of device name. [CHAR LIMIT=NONE] -->
+    <string name="about_phone_device_name_warning">Your device name is visible to apps you installed. It may also be seen by other people when you connect to Bluetooth devices, connect to a Wi-Fi network or set up a Wi-Fi hotspot.</string>
 </resources>
diff --git a/res/xml/display_settings.xml b/res/xml/display_settings.xml
index ad5236e..f94ba70 100644
--- a/res/xml/display_settings.xml
+++ b/res/xml/display_settings.xml
@@ -48,6 +48,13 @@
             settings:keywords="@string/keywords_ambient_display_screen"
             settings:controller="com.android.settings.security.screenlock.LockScreenPreferenceController"/>
 
+        <SwitchPreference
+            android:key="stay_awake_on_fold"
+            android:title="@string/stay_awake_on_fold_title"
+            android:summary="@string/stay_awake_on_fold_summary"
+            settings:keywords="@string/keywords_stay_awake_on_lock"
+            settings:controller="com.android.settings.display.StayAwakeOnFoldPreferenceController"/>
+
         <com.android.settingslib.RestrictedPreference
             android:key="screen_timeout"
             android:title="@string/screen_timeout"
diff --git a/res/xml/stylus_usi_details_fragment.xml b/res/xml/stylus_usi_details_fragment.xml
index 8a1d036..639c284 100644
--- a/res/xml/stylus_usi_details_fragment.xml
+++ b/res/xml/stylus_usi_details_fragment.xml
@@ -30,4 +30,7 @@
     <PreferenceCategory
         android:key="device_stylus"/>
 
+    <PreferenceCategory
+        android:key="stylus_usb_firmware"
+        settings:controller="com.android.settings.connecteddevice.stylus.StylusUsbFirmwareController"/>
 </PreferenceScreen>
\ No newline at end of file
diff --git a/src/com/android/settings/SetFullBackupPassword.java b/src/com/android/settings/SetFullBackupPassword.java
index b6a03d5..833ba99 100644
--- a/src/com/android/settings/SetFullBackupPassword.java
+++ b/src/com/android/settings/SetFullBackupPassword.java
@@ -25,6 +25,7 @@
 import android.util.Log;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.view.WindowManager;
 import android.widget.Button;
 import android.widget.TextView;
 import android.widget.Toast;
@@ -80,6 +81,7 @@
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
 
         mBackupManager = IBackupManager.Stub.asInterface(ServiceManager.getService("backup"));
 
diff --git a/src/com/android/settings/biometrics/face/FaceUpdater.java b/src/com/android/settings/biometrics/face/FaceUpdater.java
index 3a1f77c..57c1195 100644
--- a/src/com/android/settings/biometrics/face/FaceUpdater.java
+++ b/src/com/android/settings/biometrics/face/FaceUpdater.java
@@ -50,8 +50,9 @@
     /** Wrapper around the {@link FaceManager#enroll} method. */
     public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel,
             FaceManager.EnrollmentCallback callback, int[] disabledFeatures) {
-        mFaceManager.enroll(userId, hardwareAuthToken, cancel,
-                new NotifyingEnrollmentCallback(mContext, callback), disabledFeatures);
+        this.enroll(userId, hardwareAuthToken, cancel,
+                new NotifyingEnrollmentCallback(mContext, callback), disabledFeatures,
+                null, false);
     }
 
     /** Wrapper around the {@link FaceManager#enroll} method. */
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
index 47bd436..ba2786e 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
@@ -69,7 +69,7 @@
     private static final String ENABLE_DUAL_MODE_AUDIO =
             "persist.bluetooth.enable_dual_mode_audio";
     private static final String CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT = "le_audio_enabled_by_default";
-    private static final boolean LE_AUDIO_DEVICE_DETAIL_DEFAULT_VALUE = false;
+    private static final boolean LE_AUDIO_DEVICE_DETAIL_DEFAULT_VALUE = true;
 
     private LocalBluetoothManager mManager;
     private LocalBluetoothProfileManager mProfileManager;
diff --git a/src/com/android/settings/connecteddevice/stylus/StylusFeatureProvider.java b/src/com/android/settings/connecteddevice/stylus/StylusFeatureProvider.java
new file mode 100644
index 0000000..cd9e981
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/stylus/StylusFeatureProvider.java
@@ -0,0 +1,47 @@
+/*
+ * 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.connecteddevice.stylus;
+
+import android.content.Context;
+import android.hardware.usb.UsbDevice;
+
+import androidx.preference.Preference;
+
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+/** FeatureProvider for USB settings **/
+public interface StylusFeatureProvider {
+
+    /**
+     * Returns whether the current attached USB device allows firmware updates.
+     *
+     * @param usbDevice The USB device to check
+     */
+    boolean isUsbFirmwareUpdateEnabled(UsbDevice usbDevice);
+
+    /**
+     * Returns a list of preferences for the connected USB device if exists. If not, returns
+     * null. If an update is not available but firmware update feature is enabled for the device,
+     * the list will contain only the preference showing the current firmware version.
+     *
+     * @param context The context
+     */
+    @Nullable
+    List<Preference> getUsbFirmwareUpdatePreferences(Context context);
+}
diff --git a/src/com/android/settings/connecteddevice/stylus/StylusFeatureProviderImpl.java b/src/com/android/settings/connecteddevice/stylus/StylusFeatureProviderImpl.java
new file mode 100644
index 0000000..ab9d2ce
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/stylus/StylusFeatureProviderImpl.java
@@ -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.connecteddevice.stylus;
+
+import android.content.Context;
+import android.hardware.usb.UsbDevice;
+
+import androidx.preference.Preference;
+
+import java.util.List;
+
+
+/** Default implementation for StylusFeatureProvider **/
+public class StylusFeatureProviderImpl implements StylusFeatureProvider {
+    @Override
+    public boolean isUsbFirmwareUpdateEnabled(UsbDevice usbDevice) {
+        return false;
+    }
+
+    @Override
+    public List<Preference> getUsbFirmwareUpdatePreferences(Context context) {
+        return null;
+    }
+}
diff --git a/src/com/android/settings/connecteddevice/stylus/StylusUsbFirmwareController.java b/src/com/android/settings/connecteddevice/stylus/StylusUsbFirmwareController.java
new file mode 100644
index 0000000..0f79d3e
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/stylus/StylusUsbFirmwareController.java
@@ -0,0 +1,142 @@
+/*
+ * 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.connecteddevice.stylus;
+
+import android.content.Context;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Preference controller for stylus firmware updates via USB **/
+public class StylusUsbFirmwareController extends BasePreferenceController
+        implements LifecycleObserver, OnStart, OnStop {
+    private static final String TAG = StylusUsbFirmwareController.class.getSimpleName();
+    @Nullable
+    private UsbDevice mStylusUsbDevice;
+    private final UsbStylusBroadcastReceiver mUsbStylusBroadcastReceiver;
+
+    private PreferenceScreen mPreferenceScreen;
+    private PreferenceCategory mPreference;
+
+    @VisibleForTesting
+    UsbStylusBroadcastReceiver.UsbStylusConnectionListener mUsbConnectionListener =
+            (stylusUsbDevice, attached) -> {
+                refresh();
+            };
+
+    public StylusUsbFirmwareController(Context context, String key) {
+        super(context, key);
+        mUsbStylusBroadcastReceiver = new UsbStylusBroadcastReceiver(context,
+                mUsbConnectionListener);
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        mPreferenceScreen = screen;
+        refresh();
+        super.displayPreference(screen);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        // always available, preferences will be added or
+        // removed according to the connected usb device
+        return AVAILABLE;
+    }
+
+    private void refresh() {
+        if (mPreferenceScreen == null) return;
+
+        UsbDevice device = getStylusUsbDevice();
+        if (device == mStylusUsbDevice) {
+            return;
+        }
+        mStylusUsbDevice = device;
+        mPreference = mPreferenceScreen.findPreference(getPreferenceKey());
+        if (mPreference != null) {
+            mPreferenceScreen.removePreference(mPreference);
+        }
+        if (hasUsbStylusFirmwareUpdateFeature(mStylusUsbDevice)) {
+            StylusFeatureProvider featureProvider = FeatureFactory.getFactory(
+                    mContext).getStylusFeatureProvider();
+            List<Preference> preferences =
+                    featureProvider.getUsbFirmwareUpdatePreferences(mContext);
+
+            if (preferences != null) {
+                mPreference = new PreferenceCategory(mContext);
+                mPreference.setKey(getPreferenceKey());
+                mPreferenceScreen.addPreference(mPreference);
+
+                for (Preference preference : preferences) {
+                    mPreference.addPreference(preference);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void onStart() {
+        mUsbStylusBroadcastReceiver.register();
+    }
+
+    @Override
+    public void onStop() {
+        mUsbStylusBroadcastReceiver.unregister();
+    }
+
+    private UsbDevice getStylusUsbDevice() {
+        UsbManager usbManager = mContext.getSystemService(UsbManager.class);
+
+        if (usbManager == null) {
+            return null;
+        }
+
+        List<UsbDevice> devices = new ArrayList<>(usbManager.getDeviceList().values());
+        if (devices.isEmpty()) {
+            return null;
+        }
+
+        UsbDevice usbDevice = devices.get(0);
+        if (hasUsbStylusFirmwareUpdateFeature(usbDevice)) {
+            return usbDevice;
+        }
+        return null;
+    }
+
+    static boolean hasUsbStylusFirmwareUpdateFeature(UsbDevice usbDevice) {
+        if (usbDevice == null) return false;
+
+        StylusFeatureProvider featureProvider = FeatureFactory.getFactory(
+                FeatureFactory.getAppContext()).getStylusFeatureProvider();
+
+        return featureProvider.isUsbFirmwareUpdateEnabled(usbDevice);
+    }
+}
diff --git a/src/com/android/settings/connecteddevice/stylus/StylusUsiDetailsFragment.java b/src/com/android/settings/connecteddevice/stylus/StylusUsiDetailsFragment.java
index 5e68a53..ea9781e 100644
--- a/src/com/android/settings/connecteddevice/stylus/StylusUsiDetailsFragment.java
+++ b/src/com/android/settings/connecteddevice/stylus/StylusUsiDetailsFragment.java
@@ -54,7 +54,6 @@
         }
     }
 
-
     @Override
     public int getMetricsCategory() {
         return SettingsEnums.USI_DEVICE_DETAILS;
diff --git a/src/com/android/settings/connecteddevice/stylus/UsbStylusBroadcastReceiver.java b/src/com/android/settings/connecteddevice/stylus/UsbStylusBroadcastReceiver.java
new file mode 100644
index 0000000..8c8b4fd
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/stylus/UsbStylusBroadcastReceiver.java
@@ -0,0 +1,75 @@
+/*
+ * 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.connecteddevice.stylus;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+
+/** Broadcast receiver for styluses connected via USB **/
+public class UsbStylusBroadcastReceiver extends BroadcastReceiver {
+    private Context mContext;
+    private UsbStylusConnectionListener mUsbConnectionListener;
+    private boolean mListeningToUsbEvents;
+
+    public UsbStylusBroadcastReceiver(Context context,
+            UsbStylusConnectionListener usbConnectionListener) {
+        mContext = context;
+        mUsbConnectionListener = usbConnectionListener;
+    }
+
+    /** Registers the receiver. **/
+    public void register() {
+        if (!mListeningToUsbEvents) {
+            final IntentFilter intentFilter = new IntentFilter();
+            intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+            intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
+            final Intent intent = mContext.registerReceiver(this, intentFilter);
+            if (intent != null) {
+                onReceive(mContext, intent);
+            }
+            mListeningToUsbEvents = true;
+        }
+    }
+
+    /** Unregisters the receiver. **/
+    public void unregister() {
+        if (mListeningToUsbEvents) {
+            mContext.unregisterReceiver(this);
+            mListeningToUsbEvents = false;
+        }
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE, UsbDevice.class);
+        if (StylusUsbFirmwareController.hasUsbStylusFirmwareUpdateFeature(usbDevice)) {
+            mUsbConnectionListener.onUsbStylusConnectionChanged(usbDevice,
+                    intent.getAction().equals(UsbManager.ACTION_USB_DEVICE_ATTACHED));
+        }
+    }
+
+    /**
+     * Interface definition for a callback to be invoked when stylus usb connection is changed.
+     */
+    interface UsbStylusConnectionListener {
+        void onUsbStylusConnectionChanged(UsbDevice device, boolean connected);
+    }
+}
diff --git a/src/com/android/settings/development/BluetoothLeAudioDeviceDetailsPreferenceController.java b/src/com/android/settings/development/BluetoothLeAudioDeviceDetailsPreferenceController.java
index 9545728..298ced0 100644
--- a/src/com/android/settings/development/BluetoothLeAudioDeviceDetailsPreferenceController.java
+++ b/src/com/android/settings/development/BluetoothLeAudioDeviceDetailsPreferenceController.java
@@ -40,7 +40,7 @@
 
     private static final String PREFERENCE_KEY = "bluetooth_show_leaudio_device_details";
     private static final String CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT = "le_audio_enabled_by_default";
-    private static final boolean LE_AUDIO_DEVICE_DETAIL_DEFAULT_VALUE = false;
+    private static final boolean LE_AUDIO_DEVICE_DETAIL_DEFAULT_VALUE = true;
     static int sLeAudioSupportedStateCache = BluetoothStatusCodes.ERROR_UNKNOWN;
 
     @VisibleForTesting
diff --git a/src/com/android/settings/display/StayAwakeOnFoldPreferenceController.java b/src/com/android/settings/display/StayAwakeOnFoldPreferenceController.java
new file mode 100644
index 0000000..9df48f3
--- /dev/null
+++ b/src/com/android/settings/display/StayAwakeOnFoldPreferenceController.java
@@ -0,0 +1,75 @@
+/*
+ * 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.display;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.provider.Settings;
+
+import com.android.settings.R;
+import com.android.settings.core.TogglePreferenceController;
+
+/**
+ * A preference controller for the "Stay unlocked on fold" setting.
+ *
+ * This preference controller allows users to control whether or not the device
+ * stays awake when it is folded. When this setting is enabled, the device will
+ * stay awake even if the device is folded.
+ *
+ * @link android.provider.Settings.System#STAY_AWAKE_ON_FOLD
+ */
+public class StayAwakeOnFoldPreferenceController extends TogglePreferenceController {
+
+    private final Resources mResources;
+
+    public StayAwakeOnFoldPreferenceController(Context context, String key) {
+        this(context, key, context.getResources());
+    }
+
+    public StayAwakeOnFoldPreferenceController(Context context, String key, Resources resources) {
+        super(context, key);
+        mResources = resources;
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return mResources.getBoolean(R.bool.config_stay_awake_on_fold) ? AVAILABLE
+                : UNSUPPORTED_ON_DEVICE;
+    }
+
+    @Override
+    public boolean isChecked() {
+        return Settings.System.getInt(
+                mContext.getContentResolver(),
+                Settings.System.STAY_AWAKE_ON_FOLD,
+                0) == 1;
+    }
+
+    @Override
+    public boolean setChecked(boolean isChecked) {
+        final int stayUnlockedOnFold = isChecked ? 1 : 0;
+
+        return Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.STAY_AWAKE_ON_FOLD, stayUnlockedOnFold);
+    }
+
+    @Override
+    public int getSliceHighlightMenuRes() {
+        return R.string.menu_key_display;
+    }
+
+}
diff --git a/src/com/android/settings/localepicker/LocaleDialogFragment.java b/src/com/android/settings/localepicker/LocaleDialogFragment.java
index f54446a..6c37e38 100644
--- a/src/com/android/settings/localepicker/LocaleDialogFragment.java
+++ b/src/com/android/settings/localepicker/LocaleDialogFragment.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.localepicker;
 
+import static android.window.OnBackInvokedDispatcher.PRIORITY_DEFAULT;
+
 import android.app.Activity;
 import android.app.Dialog;
 import android.app.settings.SettingsEnums;
@@ -23,15 +25,17 @@
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.os.Bundle;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
+import android.window.OnBackInvokedCallback;
+import android.window.OnBackInvokedDispatcher;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.app.AlertDialog;
-import androidx.fragment.app.FragmentManager;
 
 import com.android.internal.app.LocaleStore;
 import com.android.settings.R;
@@ -53,6 +57,12 @@
     static final String ARG_SHOW_DIALOG = "arg_show_dialog";
 
     private boolean mShouldKeepDialog;
+    private AlertDialog mAlertDialog;
+    private OnBackInvokedDispatcher mBackDispatcher;
+
+    private OnBackInvokedCallback mBackCallback = () -> {
+        Log.d(TAG, "Do not back to previous page if the dialog is displaying.");
+    };
 
     public static LocaleDialogFragment newInstance() {
         return new LocaleDialogFragment();
@@ -108,9 +118,15 @@
         if (!dialogContent.mNegativeButton.isEmpty()) {
             builder.setNegativeButton(dialogContent.mNegativeButton, controller);
         }
-        AlertDialog alertDialog = builder.create();
-        alertDialog.setCanceledOnTouchOutside(false);
-        return alertDialog;
+        mAlertDialog = builder.create();
+        getOnBackInvokedDispatcher().registerOnBackInvokedCallback(PRIORITY_DEFAULT, mBackCallback);
+        mAlertDialog.setCanceledOnTouchOutside(false);
+        mAlertDialog.setOnDismissListener(dialogInterface -> {
+            mAlertDialog.getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(
+                            mBackCallback);
+        });
+
+        return mAlertDialog;
     }
 
     private static void setDialogTitle(View root, String content) {
@@ -130,6 +146,25 @@
     }
 
     @VisibleForTesting
+    public OnBackInvokedCallback getBackInvokedCallback() {
+        return mBackCallback;
+    }
+
+    @VisibleForTesting
+    public void setBackDispatcher(OnBackInvokedDispatcher dispatcher) {
+        mBackDispatcher = dispatcher;
+    }
+
+    @VisibleForTesting
+    public OnBackInvokedDispatcher getOnBackInvokedDispatcher() {
+        if (mBackDispatcher != null) {
+            return mBackDispatcher;
+        } else {
+            return mAlertDialog.getOnBackInvokedDispatcher();
+        }
+    }
+
+    @VisibleForTesting
     LocaleDialogController getLocaleDialogController(Context context,
             LocaleDialogFragment dialogFragment, LocaleListEditor parentFragment) {
         return new LocaleDialogController(context, dialogFragment, parentFragment);
@@ -155,11 +190,6 @@
             mParent = parentFragment;
         }
 
-        LocaleDialogController(@NonNull LocaleDialogFragment dialogFragment,
-                LocaleListEditor parent) {
-            this(dialogFragment.getContext(), dialogFragment, parent);
-        }
-
         @Override
         public void onClick(DialogInterface dialog, int which) {
             if (mDialogType == DIALOG_CONFIRM_SYSTEM_DEFAULT) {
diff --git a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
index 3d7976a..f703c83 100644
--- a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
+++ b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
@@ -395,10 +395,13 @@
                 // drag locale's original position to the top.
                 mDragLocale = (LocaleStore.LocaleInfo) savedInstanceState.getSerializable(
                         CFGKEY_DRAG_LOCALE);
-                mFeedItemList.removeIf(
-                        localeInfo -> TextUtils.equals(localeInfo.getId(), mDragLocale.getId()));
-                mFeedItemList.add(0, mDragLocale);
-                notifyItemRangeChanged(0, mFeedItemList.size());
+                if (mDragLocale != null) {
+                    mFeedItemList.removeIf(
+                            localeInfo -> TextUtils.equals(localeInfo.getId(),
+                                    mDragLocale.getId()));
+                    mFeedItemList.add(0, mDragLocale);
+                    notifyItemRangeChanged(0, mFeedItemList.size());
+                }
             }
         }
     }
diff --git a/src/com/android/settings/localepicker/LocaleListEditor.java b/src/com/android/settings/localepicker/LocaleListEditor.java
index ee5e672..824a8fd 100644
--- a/src/com/android/settings/localepicker/LocaleListEditor.java
+++ b/src/com/android/settings/localepicker/LocaleListEditor.java
@@ -456,7 +456,13 @@
                                 // to remove.
                                 mRemoveMode = false;
                                 mShowingRemoveDialog = false;
+                                LocaleStore.LocaleInfo firstLocale =
+                                        mAdapter.getFeedItemList().get(0);
                                 mAdapter.removeChecked();
+                                boolean isFirstRemoved =
+                                        firstLocale != mAdapter.getFeedItemList().get(0);
+                                showConfirmDialog(isFirstRemoved, isFirstRemoved ? firstLocale
+                                        : mAdapter.getFeedItemList().get(0));
                                 setRemoveMode(false);
                             }
                         })
@@ -522,22 +528,27 @@
     public boolean onTouch(View v, MotionEvent event) {
         if (event.getAction() == MotionEvent.ACTION_UP
                 || event.getAction() == MotionEvent.ACTION_CANCEL) {
-            LocaleStore.LocaleInfo localeInfo = mAdapter.getFeedItemList().get(0);
-            if (!localeInfo.getLocale().equals(LocalePicker.getLocales().get(0))) {
-                final LocaleDialogFragment localeDialogFragment =
-                        LocaleDialogFragment.newInstance();
-                Bundle args = new Bundle();
-                args.putInt(LocaleDialogFragment.ARG_DIALOG_TYPE, DIALOG_CONFIRM_SYSTEM_DEFAULT);
-                args.putSerializable(LocaleDialogFragment.ARG_TARGET_LOCALE, localeInfo);
-                localeDialogFragment.setArguments(args);
-                localeDialogFragment.show(mFragmentManager, TAG_DIALOG_CONFIRM_SYSTEM_DEFAULT);
-            } else {
-                mAdapter.doTheUpdate();
-            }
+            showConfirmDialog(false, mAdapter.getFeedItemList().get(0));
         }
         return false;
     }
 
+    private void showConfirmDialog(boolean isFirstRemoved, LocaleStore.LocaleInfo localeInfo) {
+        Locale currentSystemLocale = LocalePicker.getLocales().get(0);
+        if (!localeInfo.getLocale().equals(currentSystemLocale)) {
+            final LocaleDialogFragment localeDialogFragment =
+                    LocaleDialogFragment.newInstance();
+            Bundle args = new Bundle();
+            args.putInt(LocaleDialogFragment.ARG_DIALOG_TYPE, DIALOG_CONFIRM_SYSTEM_DEFAULT);
+            args.putSerializable(LocaleDialogFragment.ARG_TARGET_LOCALE,
+                    isFirstRemoved ? LocaleStore.getLocaleInfo(currentSystemLocale) : localeInfo);
+            localeDialogFragment.setArguments(args);
+            localeDialogFragment.show(mFragmentManager, TAG_DIALOG_CONFIRM_SYSTEM_DEFAULT);
+        } else {
+            mAdapter.doTheUpdate();
+        }
+    }
+
     // Hide the "Remove" menu if there is only one locale in the list, show it otherwise
     // This is called when the menu is first created, and then one add / remove locale
     private void updateVisibilityOfRemoveMenu() {
diff --git a/src/com/android/settings/overlay/FeatureFactory.kt b/src/com/android/settings/overlay/FeatureFactory.kt
index 9b1f0f4..f38b5da 100644
--- a/src/com/android/settings/overlay/FeatureFactory.kt
+++ b/src/com/android/settings/overlay/FeatureFactory.kt
@@ -23,6 +23,7 @@
 import com.android.settings.biometrics.face.FaceFeatureProvider
 import com.android.settings.biometrics2.factory.BiometricsRepositoryProvider
 import com.android.settings.bluetooth.BluetoothFeatureProvider
+import com.android.settings.connecteddevice.stylus.StylusFeatureProvider
 import com.android.settings.dashboard.DashboardFeatureProvider
 import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider
 import com.android.settings.deviceinfo.hardwareinfo.HardwareInfoFeatureProvider
@@ -145,6 +146,11 @@
      */
     abstract val keyboardSettingsFeatureProvider: KeyboardSettingsFeatureProvider
 
+    /**
+     * Retrieves implementation for stylus feature.
+     */
+    abstract val stylusFeatureProvider: StylusFeatureProvider
+
     companion object {
         private var _factory: FeatureFactory? = null
 
diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.kt b/src/com/android/settings/overlay/FeatureFactoryImpl.kt
index f627c4f..67671fb 100644
--- a/src/com/android/settings/overlay/FeatureFactoryImpl.kt
+++ b/src/com/android/settings/overlay/FeatureFactoryImpl.kt
@@ -33,6 +33,8 @@
 import com.android.settings.bluetooth.BluetoothFeatureProvider
 import com.android.settings.bluetooth.BluetoothFeatureProviderImpl
 import com.android.settings.connecteddevice.dock.DockUpdaterFeatureProviderImpl
+import com.android.settings.connecteddevice.stylus.StylusFeatureProvider
+import com.android.settings.connecteddevice.stylus.StylusFeatureProviderImpl
 import com.android.settings.core.instrumentation.SettingsMetricsFeatureProvider
 import com.android.settings.dashboard.DashboardFeatureProviderImpl
 import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider
@@ -173,4 +175,8 @@
     override val keyboardSettingsFeatureProvider: KeyboardSettingsFeatureProvider by lazy {
         KeyboardSettingsFeatureProviderImpl()
     }
+
+    override val stylusFeatureProvider: StylusFeatureProvider by lazy {
+        StylusFeatureProviderImpl()
+    }
 }
diff --git a/src/com/android/settings/password/ChooseLockGeneric.java b/src/com/android/settings/password/ChooseLockGeneric.java
index 4c4795c..0bf1255 100644
--- a/src/com/android/settings/password/ChooseLockGeneric.java
+++ b/src/com/android/settings/password/ChooseLockGeneric.java
@@ -33,6 +33,7 @@
 import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_DEVICE_PASSWORD_REQUIREMENT_ONLY;
 import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_IS_CALLING_APP_ADMIN;
 import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW;
 
 import android.app.Activity;
 import android.app.Dialog;
@@ -795,6 +796,9 @@
                 if (getIntent().getBooleanExtra(EXTRA_SHOW_OPTIONS_BUTTON, false)) {
                     intent.putExtra(EXTRA_SHOW_OPTIONS_BUTTON, chooseLockSkipped);
                 }
+                if (getIntent().getBooleanExtra(EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW, false)) {
+                    intent.putExtra(EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW, true);
+                }
                 intent.putExtra(EXTRA_CHOOSE_LOCK_GENERIC_EXTRAS, getIntent().getExtras());
                 // If the caller requested Gatekeeper Password Handle to be returned, we assume it
                 // came from biometric enrollment. onActivityResult will put the LockSettingsService
diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java
index e591a21..6066efb 100644
--- a/src/com/android/settings/password/ChooseLockPassword.java
+++ b/src/com/android/settings/password/ChooseLockPassword.java
@@ -65,7 +65,6 @@
 import android.text.TextUtils;
 import android.text.TextWatcher;
 import android.util.Log;
-import android.util.Pair;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -89,7 +88,6 @@
 import com.android.internal.widget.LockscreenCredential;
 import com.android.internal.widget.PasswordValidationError;
 import com.android.internal.widget.TextViewInputDisabler;
-import com.android.internal.widget.VerifyCredentialResponse;
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
 import com.android.settings.SetupWizardUtils;
@@ -236,6 +234,7 @@
         private LockscreenCredential mCurrentCredential;
         private LockscreenCredential mChosenPassword;
         private boolean mRequestGatekeeperPassword;
+        private boolean mRequestWriteRepairModePassword;
         private ImeAwareEditText mPasswordEntry;
         private TextViewInputDisabler mPasswordEntryInputDisabler;
 
@@ -565,6 +564,8 @@
                     ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
             mRequestGatekeeperPassword = intent.getBooleanExtra(
                     ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, false);
+            mRequestWriteRepairModePassword = intent.getBooleanExtra(
+                    ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW, false);
             if (savedInstanceState == null) {
                 updateStage(Stage.Introduction);
                 if (confirmCredentials) {
@@ -574,6 +575,7 @@
                             .setTitle(getString(R.string.unlock_set_unlock_launch_picker_title))
                             .setReturnCredentials(true)
                             .setRequestGatekeeperPasswordHandle(mRequestGatekeeperPassword)
+                            .setRequestWriteRepairModePassword(mRequestWriteRepairModePassword)
                             .setUserId(mUserId)
                             .show();
                 }
@@ -1034,7 +1036,10 @@
             setNextEnabled(false);
 
             mSaveAndFinishWorker = new SaveAndFinishWorker();
-            mSaveAndFinishWorker.setListener(this);
+            mSaveAndFinishWorker
+                    .setListener(this)
+                    .setRequestGatekeeperPasswordHandle(mRequestGatekeeperPassword)
+                    .setRequestWriteRepairModePassword(mRequestWriteRepairModePassword);
 
             getFragmentManager().beginTransaction().add(mSaveAndFinishWorker,
                     FRAGMENT_TAG_SAVE_AND_FINISH).commit();
@@ -1054,7 +1059,7 @@
                     (mAutoPinConfirmOption != null && mAutoPinConfirmOption.isChecked()),
                     mUserId);
 
-            mSaveAndFinishWorker.start(mLockPatternUtils, mRequestGatekeeperPassword,
+            mSaveAndFinishWorker.start(mLockPatternUtils,
                     mChosenPassword, mCurrentCredential, mUserId);
         }
 
@@ -1107,50 +1112,4 @@
             }
         }
     }
-
-    public static class SaveAndFinishWorker extends SaveChosenLockWorkerBase {
-
-        private LockscreenCredential mChosenPassword;
-        private LockscreenCredential mCurrentCredential;
-
-        public void start(LockPatternUtils utils, boolean requestGatekeeperPassword,
-                LockscreenCredential chosenPassword, LockscreenCredential currentCredential,
-                int userId) {
-            prepare(utils, requestGatekeeperPassword, userId);
-
-            mChosenPassword = chosenPassword;
-            mCurrentCredential = currentCredential != null ? currentCredential
-                    : LockscreenCredential.createNone();
-            mUserId = userId;
-
-            start();
-        }
-
-        @Override
-        protected Pair<Boolean, Intent> saveAndVerifyInBackground() {
-            final boolean success = mUtils.setLockCredential(
-                    mChosenPassword, mCurrentCredential, mUserId);
-            if (success) {
-                unifyProfileCredentialIfRequested();
-            }
-            Intent result = null;
-            if (success && mRequestGatekeeperPassword) {
-                // If a Gatekeeper Password was requested, invoke the LockSettingsService code
-                // path to return a Gatekeeper Password based on the credential that the user
-                // chose. This should only be run if the credential was successfully set.
-                final VerifyCredentialResponse response = mUtils.verifyCredential(mChosenPassword,
-                        mUserId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE);
-
-                if (!response.isMatched() || !response.containsGatekeeperPasswordHandle()) {
-                    Log.e(TAG, "critical: bad response or missing GK PW handle for known good"
-                            + " password: " + response.toString());
-                }
-
-                result = new Intent();
-                result.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE,
-                        response.getGatekeeperPasswordHandle());
-            }
-            return Pair.create(success, result);
-        }
-    }
 }
diff --git a/src/com/android/settings/password/ChooseLockPattern.java b/src/com/android/settings/password/ChooseLockPattern.java
index a5d04cc..7569c15 100644
--- a/src/com/android/settings/password/ChooseLockPattern.java
+++ b/src/com/android/settings/password/ChooseLockPattern.java
@@ -34,7 +34,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Log;
-import android.util.Pair;
 import android.util.TypedValue;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -53,7 +52,6 @@
 import com.android.internal.widget.LockPatternView.Cell;
 import com.android.internal.widget.LockPatternView.DisplayMode;
 import com.android.internal.widget.LockscreenCredential;
-import com.android.internal.widget.VerifyCredentialResponse;
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
 import com.android.settings.SetupWizardUtils;
@@ -206,6 +204,7 @@
 
         private LockscreenCredential mCurrentCredential;
         private boolean mRequestGatekeeperPassword;
+        private boolean mRequestWriteRepairModePassword;
         protected TextView mHeaderText;
         protected LockPatternView mLockPatternView;
         protected TextView mFooterText;
@@ -563,6 +562,8 @@
                     intent.getParcelableExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
             mRequestGatekeeperPassword = intent.getBooleanExtra(
                     ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, false);
+            mRequestWriteRepairModePassword = intent.getBooleanExtra(
+                    ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW, false);
 
             if (savedInstanceState == null) {
                 if (confirmCredentials) {
@@ -576,6 +577,7 @@
                             .setTitle(getString(R.string.unlock_set_unlock_launch_picker_title))
                             .setReturnCredentials(true)
                             .setRequestGatekeeperPasswordHandle(mRequestGatekeeperPassword)
+                            .setRequestWriteRepairModePassword(mRequestWriteRepairModePassword)
                             .setUserId(mUserId)
                             .show();
 
@@ -827,7 +829,10 @@
             setRightButtonEnabled(false);
 
             mSaveAndFinishWorker = new SaveAndFinishWorker();
-            mSaveAndFinishWorker.setListener(this);
+            mSaveAndFinishWorker
+                    .setListener(this)
+                    .setRequestGatekeeperPasswordHandle(mRequestGatekeeperPassword)
+                    .setRequestWriteRepairModePassword(mRequestWriteRepairModePassword);
 
             getFragmentManager().beginTransaction().add(mSaveAndFinishWorker,
                     FRAGMENT_TAG_SAVE_AND_FINISH).commit();
@@ -843,7 +848,7 @@
                             profileCredential);
                 }
             }
-            mSaveAndFinishWorker.start(mLockPatternUtils, mRequestGatekeeperPassword,
+            mSaveAndFinishWorker.start(mLockPatternUtils,
                     mChosenPattern, mCurrentCredential, mUserId);
         }
 
@@ -867,51 +872,4 @@
             getActivity().finish();
         }
     }
-
-    public static class SaveAndFinishWorker extends SaveChosenLockWorkerBase {
-
-        private LockscreenCredential mChosenPattern;
-        private LockscreenCredential mCurrentCredential;
-
-        public void start(LockPatternUtils utils, boolean requestGatekeeperPassword,
-                LockscreenCredential chosenPattern, LockscreenCredential currentCredential,
-                int userId) {
-            prepare(utils, requestGatekeeperPassword, userId);
-
-            mCurrentCredential = currentCredential != null ? currentCredential
-                    : LockscreenCredential.createNone();
-            mChosenPattern = chosenPattern;
-            mUserId = userId;
-
-            start();
-        }
-
-        @Override
-        protected Pair<Boolean, Intent> saveAndVerifyInBackground() {
-            final int userId = mUserId;
-            final boolean success = mUtils.setLockCredential(mChosenPattern, mCurrentCredential,
-                    userId);
-            if (success) {
-                unifyProfileCredentialIfRequested();
-            }
-            Intent result = null;
-            if (success && mRequestGatekeeperPassword) {
-                // If a Gatekeeper Password was requested, invoke the LockSettingsService code
-                // path to return a Gatekeeper Password based on the credential that the user
-                // chose. This should only be run if the credential was successfully set.
-                final VerifyCredentialResponse response = mUtils.verifyCredential(mChosenPattern,
-                        userId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE);
-
-                if (!response.isMatched() || !response.containsGatekeeperPasswordHandle()) {
-                    Log.e(TAG, "critical: bad response or missing GK PW handle for known good"
-                            + " pattern: " + response.toString());
-                }
-
-                result = new Intent();
-                result.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE,
-                        response.getGatekeeperPasswordHandle());
-            }
-            return Pair.create(success, result);
-        }
-    }
 }
diff --git a/src/com/android/settings/password/ChooseLockSettingsHelper.java b/src/com/android/settings/password/ChooseLockSettingsHelper.java
index 9533314..e5fc550 100644
--- a/src/com/android/settings/password/ChooseLockSettingsHelper.java
+++ b/src/com/android/settings/password/ChooseLockSettingsHelper.java
@@ -73,6 +73,8 @@
     public static final String EXTRA_KEY_GK_PW_HANDLE = "gk_pw_handle";
     public static final String EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW =
             "request_write_repair_mode_pw";
+    public static final String EXTRA_KEY_WROTE_REPAIR_MODE_CREDENTIAL =
+            "wrote_repair_mode_credential";
 
     /**
      * When EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL and EXTRA_KEY_UNIFICATION_PROFILE_ID are
diff --git a/src/com/android/settings/password/ConfirmLockPassword.java b/src/com/android/settings/password/ConfirmLockPassword.java
index c6022b5..8d0ff14 100644
--- a/src/com/android/settings/password/ConfirmLockPassword.java
+++ b/src/com/android/settings/password/ConfirmLockPassword.java
@@ -125,7 +125,7 @@
 
     public static class ConfirmLockPasswordFragment extends ConfirmDeviceCredentialBaseFragment
             implements OnClickListener, OnEditorActionListener,
-            CredentialCheckResultTracker.Listener, SaveChosenLockWorkerBase.Listener,
+            CredentialCheckResultTracker.Listener, SaveAndFinishWorker.Listener,
             RemoteLockscreenValidationFragment.Listener {
         private static final String FRAGMENT_TAG_CHECK_LOCK_RESULT = "check_lock_result";
         private ImeAwareEditText mPasswordEntry;
@@ -633,15 +633,15 @@
                     if (mCheckBox.isChecked() && mRemoteLockscreenValidationFragment
                             .getLockscreenCredential() != null) {
                         Log.i(TAG, "Setting device screen lock to the other device's screen lock.");
-                        ChooseLockPassword.SaveAndFinishWorker saveAndFinishWorker =
-                                new ChooseLockPassword.SaveAndFinishWorker();
+                        SaveAndFinishWorker saveAndFinishWorker = new SaveAndFinishWorker();
                         getFragmentManager().beginTransaction().add(saveAndFinishWorker, null)
                                 .commit();
                         getFragmentManager().executePendingTransactions();
-                        saveAndFinishWorker.setListener(this);
+                        saveAndFinishWorker
+                                .setListener(this)
+                                .setRequestGatekeeperPasswordHandle(true);
                         saveAndFinishWorker.start(
                                 mLockPatternUtils,
-                                /* requestGatekeeperPassword= */ true,
                                 mRemoteLockscreenValidationFragment.getLockscreenCredential(),
                                 /* currentCredential= */ null,
                                 mEffectiveUserId);
diff --git a/src/com/android/settings/password/ConfirmLockPattern.java b/src/com/android/settings/password/ConfirmLockPattern.java
index a2bcb5a..ffd7c64 100644
--- a/src/com/android/settings/password/ConfirmLockPattern.java
+++ b/src/com/android/settings/password/ConfirmLockPattern.java
@@ -93,7 +93,7 @@
 
     public static class ConfirmLockPatternFragment extends ConfirmDeviceCredentialBaseFragment
             implements AppearAnimationCreator<Object>, CredentialCheckResultTracker.Listener,
-            SaveChosenLockWorkerBase.Listener, RemoteLockscreenValidationFragment.Listener {
+            SaveAndFinishWorker.Listener, RemoteLockscreenValidationFragment.Listener {
 
         private static final String FRAGMENT_TAG_CHECK_LOCK_RESULT = "check_lock_result";
 
@@ -630,15 +630,15 @@
                     if (mCheckBox.isChecked() && mRemoteLockscreenValidationFragment
                             .getLockscreenCredential() != null) {
                         Log.i(TAG, "Setting device screen lock to the other device's screen lock.");
-                        ChooseLockPattern.SaveAndFinishWorker saveAndFinishWorker =
-                                new ChooseLockPattern.SaveAndFinishWorker();
+                        SaveAndFinishWorker saveAndFinishWorker = new SaveAndFinishWorker();
                         getFragmentManager().beginTransaction().add(saveAndFinishWorker, null)
                                 .commit();
                         getFragmentManager().executePendingTransactions();
-                        saveAndFinishWorker.setListener(this);
+                        saveAndFinishWorker
+                                .setListener(this)
+                                .setRequestGatekeeperPasswordHandle(true);
                         saveAndFinishWorker.start(
                                 mLockPatternUtils,
-                                /* requestGatekeeperPassword= */ true,
                                 mRemoteLockscreenValidationFragment.getLockscreenCredential(),
                                 /* currentCredential= */ null,
                                 mEffectiveUserId);
diff --git a/src/com/android/settings/password/SaveAndFinishWorker.java b/src/com/android/settings/password/SaveAndFinishWorker.java
new file mode 100644
index 0000000..df679e5
--- /dev/null
+++ b/src/com/android/settings/password/SaveAndFinishWorker.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2015 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.password;
+
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Pair;
+import android.widget.Toast;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.fragment.app.Fragment;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
+import com.android.internal.widget.VerifyCredentialResponse;
+import com.android.settings.R;
+import com.android.settings.safetycenter.LockScreenSafetySource;
+
+/**
+ * An invisible retained worker fragment to track the AsyncWork that saves (and optionally
+ * verifies if a challenge is given) the chosen lock credential (pattern/pin/password).
+ */
+public class SaveAndFinishWorker extends Fragment {
+    private static final String TAG = "SaveAndFinishWorker";
+
+    private Listener mListener;
+    private boolean mFinished;
+    private Intent mResultData;
+
+    private LockPatternUtils mUtils;
+    private boolean mRequestGatekeeperPassword;
+    private boolean mRequestWriteRepairModePassword;
+    private boolean mWasSecureBefore;
+    private int mUserId;
+    private int mUnificationProfileId = UserHandle.USER_NULL;
+    private LockscreenCredential mUnificationProfileCredential;
+    private LockscreenCredential mChosenCredential;
+    private LockscreenCredential mCurrentCredential;
+
+    private boolean mBlocking;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setRetainInstance(true);
+    }
+
+    public SaveAndFinishWorker setListener(Listener listener) {
+        if (mListener == listener) {
+            return this;
+        }
+
+        mListener = listener;
+        if (mFinished && mListener != null) {
+            mListener.onChosenLockSaveFinished(mWasSecureBefore, mResultData);
+        }
+        return this;
+    }
+
+    @VisibleForTesting
+    void prepare(LockPatternUtils utils, LockscreenCredential chosenCredential,
+            LockscreenCredential currentCredential, int userId) {
+        mUtils = utils;
+        mUserId = userId;
+        // This will be a no-op for non managed profiles.
+        mWasSecureBefore = mUtils.isSecure(mUserId);
+        mFinished = false;
+        mResultData = null;
+
+        mChosenCredential = chosenCredential;
+        mCurrentCredential = currentCredential != null ? currentCredential
+                : LockscreenCredential.createNone();
+    }
+
+    public void start(LockPatternUtils utils, LockscreenCredential chosenCredential,
+            LockscreenCredential currentCredential, int userId) {
+        prepare(utils, chosenCredential, currentCredential, userId);
+        if (mBlocking) {
+            finish(saveAndVerifyInBackground().second);
+        } else {
+            new Task().execute();
+        }
+    }
+
+    /**
+     * Executes the save and verify work in background.
+     * @return pair where the first is a boolean confirming whether the change was successful or not
+     * and second is the Intent which has the challenge token or is null.
+     */
+    @VisibleForTesting
+    Pair<Boolean, Intent> saveAndVerifyInBackground() {
+        final int userId = mUserId;
+        if (!mUtils.setLockCredential(mChosenCredential, mCurrentCredential, userId)) {
+            return Pair.create(false, null);
+        }
+
+        unifyProfileCredentialIfRequested();
+
+        @LockPatternUtils.VerifyFlag int flags = 0;
+        if (mRequestGatekeeperPassword) {
+            // If a Gatekeeper Password was requested, invoke the LockSettingsService code
+            // path to return a Gatekeeper Password based on the credential that the user
+            // chose. This should only be run if the credential was successfully set.
+            flags |= LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE;
+        }
+        if (mRequestWriteRepairModePassword) {
+            flags |= LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW;
+        }
+        if (flags == 0) {
+            return Pair.create(true, null);
+        }
+
+        Intent result = new Intent();
+        final VerifyCredentialResponse response = mUtils.verifyCredential(mChosenCredential,
+                userId, flags);
+        if (response.isMatched()) {
+            if (mRequestGatekeeperPassword && response.containsGatekeeperPasswordHandle()) {
+                result.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE,
+                        response.getGatekeeperPasswordHandle());
+            } else if (mRequestGatekeeperPassword) {
+                Log.e(TAG, "critical: missing GK PW handle for known good credential: " + response);
+            }
+        } else {
+            Log.e(TAG, "critical: bad response for known good credential: " + response);
+        }
+        if (mRequestWriteRepairModePassword) {
+            // Notify the caller if repair mode credential is saved successfully
+            result.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_WROTE_REPAIR_MODE_CREDENTIAL,
+                    response.isMatched());
+        }
+
+        return Pair.create(true, result);
+    }
+
+    private void finish(Intent resultData) {
+        mFinished = true;
+        mResultData = resultData;
+        if (mListener != null) {
+            mListener.onChosenLockSaveFinished(mWasSecureBefore, mResultData);
+        }
+        if (mUnificationProfileCredential != null) {
+            mUnificationProfileCredential.zeroize();
+        }
+        LockScreenSafetySource.onLockScreenChange(getContext());
+    }
+
+    public SaveAndFinishWorker setRequestGatekeeperPasswordHandle(boolean value) {
+        mRequestGatekeeperPassword = value;
+        return this;
+    }
+
+    public SaveAndFinishWorker setRequestWriteRepairModePassword(boolean value) {
+        mRequestWriteRepairModePassword = value;
+        return this;
+    }
+
+    public SaveAndFinishWorker setBlocking(boolean blocking) {
+        mBlocking = blocking;
+        return this;
+    }
+
+    public SaveAndFinishWorker setProfileToUnify(
+            int profileId, LockscreenCredential credential) {
+        mUnificationProfileId = profileId;
+        mUnificationProfileCredential = credential.duplicate();
+        return this;
+    }
+
+    private void unifyProfileCredentialIfRequested() {
+        if (mUnificationProfileId != UserHandle.USER_NULL) {
+            mUtils.setSeparateProfileChallengeEnabled(mUnificationProfileId, false,
+                    mUnificationProfileCredential);
+        }
+    }
+
+    private class Task extends AsyncTask<Void, Void, Pair<Boolean, Intent>> {
+
+        @Override
+        protected Pair<Boolean, Intent> doInBackground(Void... params){
+            return saveAndVerifyInBackground();
+        }
+
+        @Override
+        protected void onPostExecute(Pair<Boolean, Intent> resultData) {
+            if (!resultData.first) {
+                Toast.makeText(getContext(), R.string.lockpassword_credential_changed,
+                        Toast.LENGTH_LONG).show();
+            }
+            finish(resultData.second);
+        }
+    }
+
+    interface Listener {
+        void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData);
+    }
+}
diff --git a/src/com/android/settings/password/SaveChosenLockWorkerBase.java b/src/com/android/settings/password/SaveChosenLockWorkerBase.java
deleted file mode 100644
index 4864941..0000000
--- a/src/com/android/settings/password/SaveChosenLockWorkerBase.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2015 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.password;
-
-import android.content.Intent;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.util.Pair;
-import android.widget.Toast;
-
-import androidx.fragment.app.Fragment;
-
-import com.android.internal.widget.LockPatternUtils;
-import com.android.internal.widget.LockscreenCredential;
-import com.android.settings.R;
-import com.android.settings.safetycenter.LockScreenSafetySource;
-
-/**
- * An invisible retained worker fragment to track the AsyncWork that saves (and optionally
- * verifies if a challenge is given) the chosen lock credential (pattern/pin/password).
- */
-abstract class SaveChosenLockWorkerBase extends Fragment {
-
-    private Listener mListener;
-    private boolean mFinished;
-    private Intent mResultData;
-
-    protected LockPatternUtils mUtils;
-    protected boolean mRequestGatekeeperPassword;
-    protected boolean mWasSecureBefore;
-    protected int mUserId;
-    protected int mUnificationProfileId = UserHandle.USER_NULL;
-    protected LockscreenCredential mUnificationProfileCredential;
-
-    private boolean mBlocking;
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setRetainInstance(true);
-    }
-
-    public void setListener(Listener listener) {
-        if (mListener == listener) {
-            return;
-        }
-
-        mListener = listener;
-        if (mFinished && mListener != null) {
-            mListener.onChosenLockSaveFinished(mWasSecureBefore, mResultData);
-        }
-    }
-
-    protected void prepare(LockPatternUtils utils, boolean requestGatekeeperPassword, int userId) {
-        mUtils = utils;
-        mUserId = userId;
-        mRequestGatekeeperPassword = requestGatekeeperPassword;
-        // This will be a no-op for non managed profiles.
-        mWasSecureBefore = mUtils.isSecure(mUserId);
-        mFinished = false;
-        mResultData = null;
-    }
-
-    protected void start() {
-        if (mBlocking) {
-            finish(saveAndVerifyInBackground().second);
-        } else {
-            new Task().execute();
-        }
-    }
-
-    /**
-     * Executes the save and verify work in background.
-     * @return pair where the first is a boolean confirming whether the change was successful or not
-     * and second is the Intent which has the challenge token or is null.
-     */
-    protected abstract Pair<Boolean, Intent> saveAndVerifyInBackground();
-
-    protected void finish(Intent resultData) {
-        mFinished = true;
-        mResultData = resultData;
-        if (mListener != null) {
-            mListener.onChosenLockSaveFinished(mWasSecureBefore, mResultData);
-        }
-        if (mUnificationProfileCredential != null) {
-            mUnificationProfileCredential.zeroize();
-        }
-        LockScreenSafetySource.onLockScreenChange(getContext());
-    }
-
-    public void setBlocking(boolean blocking) {
-        mBlocking = blocking;
-    }
-
-    public void setProfileToUnify(int profileId, LockscreenCredential credential) {
-        mUnificationProfileId = profileId;
-        mUnificationProfileCredential = credential.duplicate();
-    }
-
-    protected void unifyProfileCredentialIfRequested() {
-        if (mUnificationProfileId != UserHandle.USER_NULL) {
-            mUtils.setSeparateProfileChallengeEnabled(mUnificationProfileId, false,
-                    mUnificationProfileCredential);
-        }
-    }
-
-    private class Task extends AsyncTask<Void, Void, Pair<Boolean, Intent>> {
-
-        @Override
-        protected Pair<Boolean, Intent> doInBackground(Void... params){
-            return saveAndVerifyInBackground();
-        }
-
-        @Override
-        protected void onPostExecute(Pair<Boolean, Intent> resultData) {
-            if (!resultData.first) {
-                Toast.makeText(getContext(), R.string.lockpassword_credential_changed,
-                        Toast.LENGTH_LONG).show();
-            }
-            finish(resultData.second);
-        }
-    }
-
-    interface Listener {
-        void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData);
-    }
-}
diff --git a/src/com/android/settings/password/SetupChooseLockPattern.java b/src/com/android/settings/password/SetupChooseLockPattern.java
index 2f48fa1..560906d 100644
--- a/src/com/android/settings/password/SetupChooseLockPattern.java
+++ b/src/com/android/settings/password/SetupChooseLockPattern.java
@@ -94,12 +94,6 @@
             }
             // Show the skip button during SUW but not during Settings > Biometric Enrollment
             mSkipOrClearButton.setOnClickListener(this::onSkipOrClearButtonClick);
-
-            final View headerView = view.findViewById(R.id.sud_layout_header);
-            final ViewGroup.MarginLayoutParams lp =
-                    (ViewGroup.MarginLayoutParams) headerView.getLayoutParams();
-            lp.bottomMargin = 0;
-            view.setLayoutParams(lp);
             return view;
         }
 
diff --git a/src/com/android/settings/spa/notification/AppNotificationsListModel.kt b/src/com/android/settings/spa/notification/AppNotificationsListModel.kt
index 028b2f4..0b9b676 100644
--- a/src/com/android/settings/spa/notification/AppNotificationsListModel.kt
+++ b/src/com/android/settings/spa/notification/AppNotificationsListModel.kt
@@ -35,7 +35,7 @@
 import com.android.settingslib.spaprivileged.model.app.AppListModel
 import com.android.settingslib.spaprivileged.model.app.AppRecord
 import com.android.settingslib.spaprivileged.template.app.AppListItemModel
-import com.android.settingslib.spaprivileged.template.app.AppListSwitchItem
+import com.android.settingslib.spaprivileged.template.app.AppListTwoTargetSwitchItem
 import com.android.settingslib.utils.StringUtil
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
@@ -117,7 +117,7 @@
 
     @Composable
     override fun AppListItemModel<AppNotificationsRecord>.AppItem() {
-        AppListSwitchItem(
+        AppListTwoTargetSwitchItem(
             onClick = { navigateToAppNotificationSettings(app = record.app) },
             checked = record.controller.isEnabled.observeAsState(),
             changeable = produceState(initialValue = false) {
diff --git a/tests/robotests/assets/exempt_not_implementing_instrumentable b/tests/robotests/assets/exempt_not_implementing_instrumentable
index 04ef0ef..28e1e73 100644
--- a/tests/robotests/assets/exempt_not_implementing_instrumentable
+++ b/tests/robotests/assets/exempt_not_implementing_instrumentable
@@ -1,8 +1,7 @@
 com.android.settings.deletionhelper.ActivationWarningFragment
 com.android.settings.applications.appops.AppOpsCategory
 com.android.settings.CustomListPreference$CustomListPreferenceDialogFragment
-com.android.settings.password.ChooseLockPassword$SaveAndFinishWorker
-com.android.settings.password.ChooseLockPattern$SaveAndFinishWorker
+com.android.settings.password.SaveAndFinishWorker
 com.android.settings.RestrictedListPreference$RestrictedListPreferenceDialogFragment
 com.android.settings.password.ConfirmDeviceCredentialBaseFragment$LastTryDialog
 com.android.settings.password.CredentialCheckResultTracker
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusUsbFirmwareControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusUsbFirmwareControllerTest.java
new file mode 100644
index 0000000..5922016
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusUsbFirmwareControllerTest.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.connecteddevice.stylus;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+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 java.util.Collections;
+import java.util.HashMap;
+
+@RunWith(RobolectricTestRunner.class)
+public class StylusUsbFirmwareControllerTest {
+
+    private Context mContext;
+    private FakeFeatureFactory mFeatureFactory;
+    private Lifecycle mLifecycle;
+    private PreferenceScreen mScreen;
+
+    private StylusUsbFirmwareController mController;
+    @Mock
+    private StylusUsiDetailsFragment mFragment;
+    @Mock
+    private UsbManager mUsbManager;
+    private PreferenceCategory mPreferenceCategory;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = spy(RuntimeEnvironment.application);
+        mLifecycle = new Lifecycle(() -> mLifecycle);
+
+        when(mFragment.getContext()).thenReturn(mContext);
+
+        mFeatureFactory = FakeFeatureFactory.setupForTest();
+        mController = new StylusUsbFirmwareController(mContext, "stylus_usb_firmware");
+
+        PreferenceManager preferenceManager = new PreferenceManager(mContext);
+        mScreen = preferenceManager.createPreferenceScreen(mContext);
+
+        mPreferenceCategory = new PreferenceCategory(mContext);
+        mPreferenceCategory.setKey(mController.getPreferenceKey());
+    }
+
+    @Test
+    public void displayPreference_featurePresentUsbStylusAttached_preferenceAdded() {
+        attachUsbDevice();
+        enableFullStylusFeature();
+
+        mController.displayPreference(mScreen);
+
+        assertNotNull(mScreen.findPreference("stylus_usb_firmware"));
+    }
+
+    @Test
+    public void displayPreference_featureAbsentUsbStylusAttached_preferenceNotAdded() {
+        attachUsbDevice();
+        mController.mUsbConnectionListener.onUsbStylusConnectionChanged(
+                mock(UsbDevice.class), true);
+
+        mController.displayPreference(mScreen);
+
+        assertNull(mScreen.findPreference(mController.getPreferenceKey()));
+    }
+
+    @Test
+    public void onUsbStylusConnectionChanged_featurePresentUsbStylusAttached_preferenceAdded() {
+        mController.displayPreference(mScreen);
+
+        attachUsbDevice();
+        enableFullStylusFeature();
+        mController.mUsbConnectionListener.onUsbStylusConnectionChanged(
+                mock(UsbDevice.class), true);
+
+        assertNotNull(mScreen.findPreference(mController.getPreferenceKey()));
+    }
+
+    @Test
+    public void onUsbStylusConnectionChanged_featureAbsentUsbStylusAttached_preferenceRemoved() {
+        mController.displayPreference(mScreen);
+
+        attachUsbDevice();
+        mController.mUsbConnectionListener.onUsbStylusConnectionChanged(
+                mock(UsbDevice.class), true);
+
+        assertNull(mScreen.findPreference(mController.getPreferenceKey()));
+    }
+
+    @Test
+    public void hasUsbStylusFirmwareUpdateFeature_featurePresent_true() {
+        when(mFeatureFactory.getStylusFeatureProvider()
+                .isUsbFirmwareUpdateEnabled(any())).thenReturn(true);
+        attachUsbDevice();
+
+        assertTrue(StylusUsbFirmwareController
+                .hasUsbStylusFirmwareUpdateFeature(mock(UsbDevice.class)));
+    }
+
+    @Test
+    public void hasUsbStylusFirmwareUpdateFeature_featureNotPresent_false() {
+        when(mFeatureFactory.getStylusFeatureProvider()
+                .isUsbFirmwareUpdateEnabled(any())).thenReturn(false);
+        attachUsbDevice();
+
+        assertFalse(StylusUsbFirmwareController
+                .hasUsbStylusFirmwareUpdateFeature(mock(UsbDevice.class)));
+    }
+
+    private void attachUsbDevice() {
+        when(mContext.getSystemService(UsbManager.class)).thenReturn(mUsbManager);
+        HashMap<String, UsbDevice> deviceList = new HashMap<>();
+        deviceList.put("0", mock(UsbDevice.class));
+        when(mUsbManager.getDeviceList()).thenReturn(deviceList);
+    }
+
+    private void enableFullStylusFeature() {
+        when(mFeatureFactory.getStylusFeatureProvider()
+                .isUsbFirmwareUpdateEnabled(any())).thenReturn(true);
+        when(mFeatureFactory.getStylusFeatureProvider()
+                .getUsbFirmwareUpdatePreferences(any()))
+                .thenReturn(Collections.singletonList(mock(Preference.class)));
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/stylus/UsbStylusBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/connecteddevice/stylus/UsbStylusBroadcastReceiverTest.java
new file mode 100644
index 0000000..ccaefb2
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/connecteddevice/stylus/UsbStylusBroadcastReceiverTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.connecteddevice.stylus;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+
+import com.android.settings.testutils.FakeFeatureFactory;
+
+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 UsbStylusBroadcastReceiverTest {
+    private Context mContext;
+    private UsbStylusBroadcastReceiver mReceiver;
+    private FakeFeatureFactory mFeatureFactory;
+    @Mock
+    private UsbStylusBroadcastReceiver.UsbStylusConnectionListener mListener;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = RuntimeEnvironment.application;
+        mReceiver = new UsbStylusBroadcastReceiver(mContext, mListener);
+        mFeatureFactory = FakeFeatureFactory.setupForTest();
+    }
+
+    @Test
+    public void onReceive_usbDeviceAttachedStylus_invokeCallback() {
+        when(mFeatureFactory.mStylusFeatureProvider.isUsbFirmwareUpdateEnabled(any()))
+                .thenReturn(true);
+        final UsbDevice usbDevice = mock(UsbDevice.class);
+        final Intent intent = new Intent();
+        intent.setAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+        intent.putExtra(UsbManager.EXTRA_DEVICE, usbDevice);
+
+        mReceiver.onReceive(mContext, intent);
+
+        verify(mListener).onUsbStylusConnectionChanged(usbDevice, true);
+    }
+
+    @Test
+    public void onReceive_usbDeviceDetachedStylus_invokeCallback() {
+        when(mFeatureFactory.mStylusFeatureProvider.isUsbFirmwareUpdateEnabled(any()))
+                .thenReturn(true);
+        final UsbDevice usbDevice = mock(UsbDevice.class);
+        final Intent intent = new Intent();
+        intent.setAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
+        intent.putExtra(UsbManager.EXTRA_DEVICE, usbDevice);
+
+        mReceiver.onReceive(mContext, intent);
+
+        verify(mListener).onUsbStylusConnectionChanged(usbDevice, false);
+    }
+
+    @Test
+    public void onReceive_usbDeviceAttachedNotStylus_doesNotInvokeCallback() {
+        when(mFeatureFactory.mStylusFeatureProvider.isUsbFirmwareUpdateEnabled(any()))
+                .thenReturn(false);
+        final UsbDevice usbDevice = mock(UsbDevice.class);
+        final Intent intent = new Intent();
+        intent.setAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+        intent.putExtra(UsbManager.EXTRA_DEVICE, usbDevice);
+
+        mReceiver.onReceive(mContext, intent);
+
+        verifyNoMoreInteractions(mListener);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/display/StayAwakeOnFoldPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/StayAwakeOnFoldPreferenceControllerTest.java
new file mode 100644
index 0000000..c994818
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/display/StayAwakeOnFoldPreferenceControllerTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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.display;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.provider.Settings;
+
+import com.android.settings.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class StayAwakeOnFoldPreferenceControllerTest {
+
+    @Mock
+    private Resources mResources;
+    private Context mContext;
+    private StayAwakeOnFoldPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+        mResources = Mockito.mock(Resources.class);
+        mController = new StayAwakeOnFoldPreferenceController(mContext, "key", mResources);
+    }
+
+    @Test
+    public void getAvailabilityStatus_withConfigNoShow_returnUnsupported() {
+        when(mResources.getBoolean(R.bool.config_stay_awake_on_fold)).thenReturn(false);
+
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+    }
+
+    @Test
+    public void getAvailabilityStatus_withConfigNoShow_returnAvailable() {
+        when(mResources.getBoolean(R.bool.config_stay_awake_on_fold)).thenReturn(true);
+
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+    }
+
+    @Test
+    public void setChecked_enableStayAwakeOnFold_setChecked() {
+        mController.setChecked(true);
+
+        assertThat(isStayAwakeOnFoldEnabled())
+                .isTrue();
+    }
+
+    @Test
+    public void setChecked_disableStayAwakeOnFold_setUnchecked() {
+        mController.setChecked(false);
+
+        assertThat(isStayAwakeOnFoldEnabled())
+                .isFalse();
+    }
+
+    @Test
+    public void isChecked_enableStayAwakeOnFold_returnTrue() {
+        enableStayAwakeOnFoldPreference();
+
+        assertThat(mController.isChecked()).isTrue();
+    }
+
+    @Test
+    public void isChecked_disableStayAwakeOnFold_returnFalse() {
+        disableStayAwakeOnFoldPreference();
+
+        assertThat(mController.isChecked()).isFalse();
+    }
+
+    private void enableStayAwakeOnFoldPreference() {
+        Settings.System.putInt(
+                mContext.getContentResolver(),
+                Settings.System.STAY_AWAKE_ON_FOLD,
+                1);
+    }
+
+    private void disableStayAwakeOnFoldPreference() {
+        Settings.System.putInt(
+                mContext.getContentResolver(),
+                Settings.System.STAY_AWAKE_ON_FOLD,
+                0);
+    }
+
+    private boolean isStayAwakeOnFoldEnabled() {
+        return (Settings.System.getInt(
+                mContext.getContentResolver(),
+                Settings.System.STAY_AWAKE_ON_FOLD,
+                0) == 1);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/localepicker/LocaleDialogFragmentTest.java b/tests/robotests/src/com/android/settings/localepicker/LocaleDialogFragmentTest.java
new file mode 100644
index 0000000..57f2b01
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/localepicker/LocaleDialogFragmentTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.localepicker;
+
+import static com.android.settings.localepicker.LocaleDialogFragment.ARG_DIALOG_TYPE;
+import static com.android.settings.localepicker.LocaleDialogFragment.ARG_TARGET_LOCALE;
+import static com.android.settings.localepicker.LocaleDialogFragment.DIALOG_CONFIRM_SYSTEM_DEFAULT;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.os.Bundle;
+import android.window.OnBackInvokedDispatcher;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+
+import com.android.internal.app.LocaleStore;
+import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
+import com.android.settings.utils.ActivityControllerWrapper;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.Locale;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowAlertDialogCompat.class})
+public class LocaleDialogFragmentTest {
+
+    @Mock
+    private OnBackInvokedDispatcher mOnBackInvokedDispatcher;
+
+    private FragmentActivity mActivity;
+    private LocaleDialogFragment mDialogFragment;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mActivity = (FragmentActivity) ActivityControllerWrapper.setup(
+                Robolectric.buildActivity(FragmentActivity.class)).get();
+        mDialogFragment = LocaleDialogFragment.newInstance();
+        LocaleStore.LocaleInfo localeInfo = LocaleStore.getLocaleInfo(Locale.ENGLISH);
+        Bundle args = new Bundle();
+        args.putInt(ARG_DIALOG_TYPE, DIALOG_CONFIRM_SYSTEM_DEFAULT);
+        args.putSerializable(ARG_TARGET_LOCALE, localeInfo);
+        mDialogFragment.setArguments(args);
+        FragmentManager fragmentManager = mActivity.getSupportFragmentManager();
+        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
+        fragmentTransaction.add(mDialogFragment, null);
+        fragmentTransaction.commit();
+    }
+
+    @Test
+    public void onCreateDialog_onBackInvokedCallbackIsRegistered() {
+        mDialogFragment.setBackDispatcher(mOnBackInvokedDispatcher);
+        mDialogFragment.onCreateDialog(null);
+
+        verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
+                eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT), any());
+    }
+
+    @Test
+    public void onBackInvoked_dialogIsStillDisplaying() {
+        mDialogFragment.setBackDispatcher(mOnBackInvokedDispatcher);
+        AlertDialog alertDialog = (AlertDialog) mDialogFragment.onCreateDialog(null);
+        alertDialog.show();
+        assertThat(alertDialog).isNotNull();
+        assertThat(alertDialog.isShowing()).isTrue();
+
+        mOnBackInvokedDispatcher.registerOnBackInvokedCallback(
+                eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT), any());
+
+        mDialogFragment.getBackInvokedCallback().onBackInvoked();
+
+        assertThat(alertDialog.isShowing()).isTrue();
+
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/localepicker/LocaleListEditorTest.java b/tests/robotests/src/com/android/settings/localepicker/LocaleListEditorTest.java
index 147b44b..d349865 100644
--- a/tests/robotests/src/com/android/settings/localepicker/LocaleListEditorTest.java
+++ b/tests/robotests/src/com/android/settings/localepicker/LocaleListEditorTest.java
@@ -67,6 +67,7 @@
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 import org.robolectric.util.ReflectionHelpers;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -105,6 +106,8 @@
     private View mView;
     @Mock
     private IActivityManager mActivityService;
+    @Mock
+    private MetricsFeatureProvider mMetricsFeatureProvider;
 
     @Before
     public void setUp() throws Exception {
@@ -123,6 +126,8 @@
         ReflectionHelpers.setField(mLocaleListEditor, "mAdapter", mAdapter);
         ReflectionHelpers.setField(mLocaleListEditor, "mAddLanguage", mAddLanguage);
         ReflectionHelpers.setField(mLocaleListEditor, "mFragmentManager", mFragmentManager);
+        ReflectionHelpers.setField(mLocaleListEditor, "mMetricsFeatureProvider",
+                mMetricsFeatureProvider);
         when(mFragmentManager.beginTransaction()).thenReturn(mFragmentTransaction);
         FakeFeatureFactory.setupForTest();
     }
@@ -217,6 +222,38 @@
     }
 
     @Test
+    public void showConfirmDialog_systemLocaleSelected_shouldShowLocaleChangeDialog()
+            throws Exception {
+        //pre-condition
+        setUpLocaleConditions();
+        final Configuration config = new Configuration();
+        config.setLocales((LocaleList.forLanguageTags("zh-TW,en-US")));
+        when(mActivityService.getConfiguration()).thenReturn(config);
+        when(mAdapter.getFeedItemList()).thenReturn(mLocaleList);
+        when(mAdapter.getCheckedCount()).thenReturn(1);
+        when(mAdapter.getItemCount()).thenReturn(2);
+        when(mAdapter.isFirstLocaleChecked()).thenReturn(true);
+        ReflectionHelpers.setField(mLocaleListEditor, "mRemoveMode", true);
+        ReflectionHelpers.setField(mLocaleListEditor, "mShowingRemoveDialog", true);
+
+        //launch the first dialog
+        mLocaleListEditor.showRemoveLocaleWarningDialog();
+
+        final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+
+        assertThat(dialog).isNotNull();
+
+        // click the remove button
+        dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick();
+
+        assertThat(dialog.isShowing()).isFalse();
+
+        // check the second dialog is showing
+        verify(mFragmentTransaction).add(any(LocaleDialogFragment.class),
+                eq(TAG_DIALOG_CONFIRM_SYSTEM_DEFAULT));
+    }
+
+    @Test
     public void mayAppendUnicodeTags_appendUnicodeTags_success() {
         LocaleStore.LocaleInfo localeInfo = LocaleStore.fromLocale(Locale.forLanguageTag("en-US"));
 
diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java
index 7cfd6b1..12e4b8d 100644
--- a/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java
+++ b/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java
@@ -65,7 +65,6 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.Robolectric;
@@ -413,26 +412,8 @@
                 "Must be at least 6 characters");
     }
 
-    @Ignore
     @Test
-    public void processAndValidatePasswordRequirements_autoPinDisabled_defaultPinMinimumLength() {
-        DeviceConfig.setProperty(NAMESPACE_AUTO_PIN_CONFIRMATION, FLAG_ENABLE_AUTO_PIN_CONFIRMATION,
-                /* value= */ "false", /* makeDefault= */ false);
-        PasswordPolicy policy = new PasswordPolicy();
-        policy.quality = PASSWORD_QUALITY_UNSPECIFIED;
-
-        assertPasswordValidationResult(
-                /* minMetrics */ policy.getMinMetrics(),
-                /* minComplexity= */ PASSWORD_COMPLEXITY_NONE,
-                /* passwordType= */ PASSWORD_QUALITY_NUMERIC,
-                /* userEnteredPassword= */ LockscreenCredential.createPassword("11"),
-                "PIN must be at least 4 digits");
-    }
-
-    @Test
-    public void processAndValidatePasswordRequirements_autoPinEnabled_defaultPinMinimumLength() {
-        DeviceConfig.setProperty(NAMESPACE_AUTO_PIN_CONFIRMATION, FLAG_ENABLE_AUTO_PIN_CONFIRMATION,
-                /* value= */ "true", /* makeDefault= */ false);
+    public void processAndValidatePasswordRequirements_defaultPinMinimumLength() {
         PasswordPolicy policy = new PasswordPolicy();
         policy.quality = PASSWORD_QUALITY_UNSPECIFIED;
 
@@ -543,39 +524,6 @@
         assertThat(pinAutoConfirmOption.isChecked()).isFalse();
     }
 
-    @Ignore
-    @Test
-    public void autoPinConfirmOption_featureDisabled_shouldRemainInvisibleAndUnchecked() {
-        DeviceConfig.setProperty(NAMESPACE_AUTO_PIN_CONFIRMATION, FLAG_ENABLE_AUTO_PIN_CONFIRMATION,
-                /* value= */ "false", /* makeDefault= */ false);
-        ChooseLockPassword passwordActivity = setupActivityWithPinTypeAndDefaultPolicy();
-
-        ChooseLockPasswordFragment fragment = getChooseLockPasswordFragment(passwordActivity);
-        ScrollToParentEditText passwordEntry = passwordActivity.findViewById(R.id.password_entry);
-        CheckBox pinAutoConfirmOption = passwordActivity
-                .findViewById(R.id.auto_pin_confirm_enabler);
-        TextView securityMessage =
-                passwordActivity.findViewById(R.id.auto_pin_confirm_security_message);
-
-        passwordEntry.setText("1234");
-        fragment.updateUi();
-        assertThat(pinAutoConfirmOption.getVisibility()).isEqualTo(View.GONE);
-        assertThat(securityMessage.getVisibility()).isEqualTo(View.GONE);
-        assertThat(pinAutoConfirmOption.isChecked()).isFalse();
-
-        passwordEntry.setText("123456");
-        fragment.updateUi();
-        assertThat(pinAutoConfirmOption.getVisibility()).isEqualTo(View.GONE);
-        assertThat(securityMessage.getVisibility()).isEqualTo(View.GONE);
-        assertThat(pinAutoConfirmOption.isChecked()).isFalse();
-
-        passwordEntry.setText("12345678");
-        fragment.updateUi();
-        assertThat(pinAutoConfirmOption.getVisibility()).isEqualTo(View.GONE);
-        assertThat(securityMessage.getVisibility()).isEqualTo(View.GONE);
-        assertThat(pinAutoConfirmOption.isChecked()).isFalse();
-    }
-
     private ChooseLockPassword setupActivityWithPinTypeAndDefaultPolicy() {
         PasswordPolicy policy = new PasswordPolicy();
         policy.quality = PASSWORD_QUALITY_UNSPECIFIED;
diff --git a/tests/robotests/src/com/android/settings/password/SetupChooseLockPatternTest.java b/tests/robotests/src/com/android/settings/password/SetupChooseLockPatternTest.java
index 2f46986..c5e0813 100644
--- a/tests/robotests/src/com/android/settings/password/SetupChooseLockPatternTest.java
+++ b/tests/robotests/src/com/android/settings/password/SetupChooseLockPatternTest.java
@@ -28,7 +28,6 @@
 import android.os.UserHandle;
 import android.util.TypedValue;
 import android.view.View;
-import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.TextView;
 
@@ -115,14 +114,6 @@
         assertThat(button.getVisibility()).isEqualTo(View.VISIBLE);
     }
 
-    @Test
-    public void headerView_noBottomMargin() {
-        final View header = mActivity.findViewById(R.id.sud_layout_header);
-        final ViewGroup.MarginLayoutParams lp =
-                (ViewGroup.MarginLayoutParams) header.getLayoutParams();
-        assertThat(lp.bottomMargin).isEqualTo(0);
-    }
-
     private void verifyScreenLockOptionsShown() {
         final Button button = mActivity.findViewById(R.id.screen_lock_options);
         assertThat(button).isNotNull();
diff --git a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
index 0f4c255..90985dd 100644
--- a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -26,6 +26,7 @@
 import com.android.settings.biometrics.face.FaceFeatureProvider;
 import com.android.settings.biometrics2.factory.BiometricsRepositoryProvider;
 import com.android.settings.bluetooth.BluetoothFeatureProvider;
+import com.android.settings.connecteddevice.stylus.StylusFeatureProvider;
 import com.android.settings.dashboard.DashboardFeatureProvider;
 import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
 import com.android.settings.deviceinfo.hardwareinfo.HardwareInfoFeatureProvider;
@@ -91,6 +92,7 @@
     public AdvancedVpnFeatureProvider mAdvancedVpnFeatureProvider;
     public WifiFeatureProvider mWifiFeatureProvider;
     public KeyboardSettingsFeatureProvider mKeyboardSettingsFeatureProvider;
+    public StylusFeatureProvider mStylusFeatureProvider;
 
     /**
      * Call this in {@code @Before} method of the test class to use fake factory.
@@ -134,6 +136,7 @@
         mAdvancedVpnFeatureProvider = mock(AdvancedVpnFeatureProvider.class);
         mWifiFeatureProvider = mock(WifiFeatureProvider.class);
         mKeyboardSettingsFeatureProvider = mock(KeyboardSettingsFeatureProvider.class);
+        mStylusFeatureProvider = mock(StylusFeatureProvider.class);
     }
 
     @Override
@@ -288,4 +291,9 @@
     public KeyboardSettingsFeatureProvider getKeyboardSettingsFeatureProvider() {
         return mKeyboardSettingsFeatureProvider;
     }
+
+    @Override
+    public StylusFeatureProvider getStylusFeatureProvider() {
+        return mStylusFeatureProvider;
+    }
 }
diff --git a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
index f732bce..54934a7 100644
--- a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
+++ b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
@@ -24,6 +24,7 @@
 import com.android.settings.biometrics.face.FaceFeatureProvider
 import com.android.settings.biometrics2.factory.BiometricsRepositoryProvider
 import com.android.settings.bluetooth.BluetoothFeatureProvider
+import com.android.settings.connecteddevice.stylus.StylusFeatureProvider
 import com.android.settings.dashboard.DashboardFeatureProvider
 import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider
 import com.android.settings.deviceinfo.hardwareinfo.HardwareInfoFeatureProvider
@@ -139,4 +140,6 @@
         get() = TODO("Not yet implemented")
     override val keyboardSettingsFeatureProvider: KeyboardSettingsFeatureProvider
         get() = TODO("Not yet implemented")
+    override val stylusFeatureProvider: StylusFeatureProvider
+        get() = TODO("Not yet implemented")
 }
diff --git a/tests/unit/src/com/android/settings/biometrics/face/FaceUpdaterTest.java b/tests/unit/src/com/android/settings/biometrics/face/FaceUpdaterTest.java
index 66514ac..9190d0a 100644
--- a/tests/unit/src/com/android/settings/biometrics/face/FaceUpdaterTest.java
+++ b/tests/unit/src/com/android/settings/biometrics/face/FaceUpdaterTest.java
@@ -39,7 +39,6 @@
 import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -100,7 +99,9 @@
                 same(HARDWARE_AUTH_TOKEN),
                 same(CANCELLATION_SIGNAL),
                 callbackCaptor.capture(),
-                same(DISABLED_FEATURES));
+                same(DISABLED_FEATURES),
+                same(null),
+                eq(false));
         FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
 
         callback.onEnrollmentError(ERR_MSG_ID, ERR_STRING);
@@ -126,12 +127,14 @@
                 same(HARDWARE_AUTH_TOKEN),
                 same(CANCELLATION_SIGNAL),
                 callbackCaptor.capture(),
-                same(DISABLED_FEATURES));
+                same(DISABLED_FEATURES),
+                same(null),
+                eq(false));
         FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
 
         callback.onEnrollmentProgress(/* remaining= */ 0);
 
-        verify(mSafetyCenterManagerWrapper).isEnabled(mContext);
+        verify(mSafetyCenterManagerWrapper, atLeast(1)).isEnabled(mContext);
     }
 
     @Test
@@ -145,7 +148,9 @@
                 same(HARDWARE_AUTH_TOKEN),
                 same(CANCELLATION_SIGNAL),
                 callbackCaptor.capture(),
-                same(DISABLED_FEATURES));
+                same(DISABLED_FEATURES),
+                same(null),
+                eq(false));
         FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
 
         callback.onEnrollmentProgress(/* remaining= */ 1);
@@ -153,7 +158,6 @@
         verify(mSafetyCenterManagerWrapper, never()).isEnabled(any());
     }
 
-    @Ignore("b/282413778")
     @Test
     public void enroll_secondVersion_onEnrollmentCallbacks_triggerGivenCallback() {
         ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
@@ -182,7 +186,6 @@
                 .onEnrollmentFrame(HELP_CODE, HELP_MESSAGE, CELL, STAGE, PAN, TILT, DISTANCE);
     }
 
-    @Ignore("b/282413778")
     @Test
     public void enroll_secondVersion_onEnrollmentSuccess_invokedInteractionWithSafetyCenter() {
         ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
@@ -204,7 +207,6 @@
         verify(mSafetyCenterManagerWrapper).isEnabled(mContext);
     }
 
-    @Ignore("b/282413778")
     @Test
     public void enroll_secondVersion_onEnrollmentNotYetFinished_didntInvokeInteractionWithSafetyCenter() {
         ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
diff --git a/tests/unit/src/com/android/settings/password/SaveAndFinishWorkerTest.java b/tests/unit/src/com/android/settings/password/SaveAndFinishWorkerTest.java
new file mode 100644
index 0000000..88e3150
--- /dev/null
+++ b/tests/unit/src/com/android/settings/password/SaveAndFinishWorkerTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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.password;
+
+import static com.google.common.truth.Truth.assertThat;
+
+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 androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
+import com.android.internal.widget.VerifyCredentialResponse;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SaveAndFinishWorkerTest {
+    @Test
+    public void testSetRequestWriteRepairModePassword_setLockCredentialFail() {
+        int userId = 0;
+        int flags = LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW;
+        var chosenCredential = LockscreenCredential.createPassword("1234");
+        var currentCredential = LockscreenCredential.createNone();
+        var worker = new SaveAndFinishWorker();
+        var lpu = mock(LockPatternUtils.class);
+
+        when(lpu.setLockCredential(chosenCredential, currentCredential, userId)).thenReturn(false);
+
+        worker.setRequestWriteRepairModePassword(true);
+        worker.prepare(lpu, chosenCredential, currentCredential, userId);
+        var result = worker.saveAndVerifyInBackground();
+
+        verify(lpu).setLockCredential(chosenCredential, currentCredential, userId);
+        verify(lpu, never()).verifyCredential(chosenCredential, userId, flags);
+        assertThat(result.first).isFalse();
+    }
+
+    @Test
+    public void testSetRequestWriteRepairModePassword_verifyCredentialFail() {
+        int userId = 0;
+        int flags = LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW;
+        var chosenCredential = LockscreenCredential.createPassword("1234");
+        var currentCredential = LockscreenCredential.createNone();
+        var worker = new SaveAndFinishWorker();
+        var lpu = mock(LockPatternUtils.class);
+        var response = VerifyCredentialResponse.fromError();
+
+        when(lpu.setLockCredential(chosenCredential, currentCredential, userId)).thenReturn(true);
+        when(lpu.verifyCredential(chosenCredential, userId, flags)).thenReturn(response);
+
+        worker.setRequestWriteRepairModePassword(true);
+        worker.prepare(lpu, chosenCredential, currentCredential, userId);
+        var result = worker.saveAndVerifyInBackground();
+
+        verify(lpu).setLockCredential(chosenCredential, currentCredential, userId);
+        verify(lpu).verifyCredential(chosenCredential, userId, flags);
+        assertThat(result.first).isTrue();
+        assertThat(result.second.getBooleanExtra(
+                ChooseLockSettingsHelper.EXTRA_KEY_WROTE_REPAIR_MODE_CREDENTIAL, true))
+                .isFalse();
+    }
+
+    @Test
+    public void testSetRequestWriteRepairModePassword_verifyCredentialSucceed() {
+        int userId = 0;
+        int flags = LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW;
+        var chosenCredential = LockscreenCredential.createPassword("1234");
+        var currentCredential = LockscreenCredential.createNone();
+        var worker = new SaveAndFinishWorker();
+        var lpu = mock(LockPatternUtils.class);
+        var response = new VerifyCredentialResponse.Builder().build();
+
+        when(lpu.setLockCredential(chosenCredential, currentCredential, userId)).thenReturn(true);
+        when(lpu.verifyCredential(chosenCredential, userId, flags)).thenReturn(response);
+
+        worker.setRequestWriteRepairModePassword(true);
+        worker.prepare(lpu, chosenCredential, currentCredential, userId);
+        var result = worker.saveAndVerifyInBackground();
+
+        verify(lpu).setLockCredential(chosenCredential, currentCredential, userId);
+        verify(lpu).verifyCredential(chosenCredential, userId, flags);
+        assertThat(result.first).isTrue();
+        assertThat(result.second.getBooleanExtra(
+                ChooseLockSettingsHelper.EXTRA_KEY_WROTE_REPAIR_MODE_CREDENTIAL, false))
+                .isTrue();
+    }
+
+    @Test
+    public void testSetRequestWriteRepairModePassword_verifyCredentialSucceed_noGkPwHandle() {
+        int userId = 0;
+        int flags = LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW
+                | LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE;
+        var chosenCredential = LockscreenCredential.createPassword("1234");
+        var currentCredential = LockscreenCredential.createNone();
+        var worker = new SaveAndFinishWorker();
+        var lpu = mock(LockPatternUtils.class);
+        var response = new VerifyCredentialResponse.Builder().build();
+
+        when(lpu.setLockCredential(chosenCredential, currentCredential, userId)).thenReturn(true);
+        when(lpu.verifyCredential(chosenCredential, userId, flags)).thenReturn(response);
+
+        worker.setRequestWriteRepairModePassword(true);
+        worker.setRequestGatekeeperPasswordHandle(true);
+        worker.prepare(lpu, chosenCredential, currentCredential, userId);
+        var result = worker.saveAndVerifyInBackground();
+
+        verify(lpu).setLockCredential(chosenCredential, currentCredential, userId);
+        verify(lpu).verifyCredential(chosenCredential, userId, flags);
+        assertThat(result.first).isTrue();
+        assertThat(result.second.getBooleanExtra(
+                ChooseLockSettingsHelper.EXTRA_KEY_WROTE_REPAIR_MODE_CREDENTIAL, false))
+                .isTrue();
+        assertThat(result.second.getLongExtra(
+                ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, -1))
+                .isEqualTo(-1);
+    }
+}
diff --git a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
index e0d3eca..79804e3 100644
--- a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -26,6 +26,7 @@
 import com.android.settings.biometrics.face.FaceFeatureProvider;
 import com.android.settings.biometrics2.factory.BiometricsRepositoryProvider;
 import com.android.settings.bluetooth.BluetoothFeatureProvider;
+import com.android.settings.connecteddevice.stylus.StylusFeatureProvider;
 import com.android.settings.dashboard.DashboardFeatureProvider;
 import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
 import com.android.settings.deviceinfo.hardwareinfo.HardwareInfoFeatureProvider;
@@ -90,6 +91,7 @@
     public AdvancedVpnFeatureProvider mAdvancedVpnFeatureProvider;
     public WifiFeatureProvider mWifiFeatureProvider;
     public KeyboardSettingsFeatureProvider mKeyboardSettingsFeatureProvider;
+    public StylusFeatureProvider mStylusFeatureProvider;
 
     /**
      * Call this in {@code @Before} method of the test class to use fake factory.
@@ -133,6 +135,7 @@
         mAdvancedVpnFeatureProvider = mock(AdvancedVpnFeatureProvider.class);
         mWifiFeatureProvider = mock(WifiFeatureProvider.class);
         mKeyboardSettingsFeatureProvider = mock(KeyboardSettingsFeatureProvider.class);
+        mStylusFeatureProvider = mock(StylusFeatureProvider.class);
     }
 
     @Override
@@ -287,4 +290,9 @@
     public KeyboardSettingsFeatureProvider getKeyboardSettingsFeatureProvider() {
         return mKeyboardSettingsFeatureProvider;
     }
+
+    @Override
+    public StylusFeatureProvider getStylusFeatureProvider() {
+        return mStylusFeatureProvider;
+    }
 }