Merge "Allow background start from notif history" into main
diff --git a/aconfig/Android.bp b/aconfig/Android.bp
index 6daa90c..7b571e0 100644
--- a/aconfig/Android.bp
+++ b/aconfig/Android.bp
@@ -12,6 +12,7 @@
         "settings_experience_flag_declarations.aconfig",
         "settings_onboarding_experience_flag_declarations.aconfig",
         "settings_telephony_flag_declarations.aconfig",
+        "settings_biometrics_integration_declarations.aconfig",
     ],
 }
 
diff --git a/aconfig/settings_biometrics_integration_declarations.aconfig b/aconfig/settings_biometrics_integration_declarations.aconfig
new file mode 100644
index 0000000..bc437f2
--- /dev/null
+++ b/aconfig/settings_biometrics_integration_declarations.aconfig
@@ -0,0 +1,16 @@
+package: "com.android.settings.flags"
+
+flag {
+  name: "sfps_enroll_refinement"
+  namespace: "biometrics_integration"
+  description: "This flag controls whether the sfps pause enrollment feature should be enabled"
+  bug: "288155127"
+}
+
+flag {
+  name: "udfps_enroll_calibration"
+  namespace: "biometrics_integration"
+  description: "This flag controls whether the fps enroll calibration feature should be enabled"
+  bug: "301226085"
+}
+
diff --git a/aconfig/settings_connecteddevice_flag_declarations.aconfig b/aconfig/settings_connecteddevice_flag_declarations.aconfig
index eda2de6..450fc0d 100644
--- a/aconfig/settings_connecteddevice_flag_declarations.aconfig
+++ b/aconfig/settings_connecteddevice_flag_declarations.aconfig
@@ -6,3 +6,10 @@
   description: "Gates whether to enable subsequent pair Settings integration."
   bug: "299405720"
 }
+
+flag {
+  name: "enable_le_audio_sharing"
+  namespace: "pixel_cross_device_control"
+  description: "Gates whether to enable LE audio sharing"
+  bug: "305620450"
+}
diff --git a/res/layout/preference_compose.xml b/res/layout/preference_compose.xml
new file mode 100644
index 0000000..14cb2d7
--- /dev/null
+++ b/res/layout/preference_compose.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<androidx.compose.ui.platform.ComposeView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content" />
diff --git a/res/values-night/styles.xml b/res/values-night/styles.xml
index f69c952..a93c981 100644
--- a/res/values-night/styles.xml
+++ b/res/values-night/styles.xml
@@ -19,15 +19,4 @@
     <style name="Widget.ActionBar.Base" parent="@android:style/Widget.DeviceDefault.ActionBar.Solid">
         <item name="android:background">?android:attr/colorPrimaryDark</item>
     </style>
-
-    <style name="TextAppearance.SimConfirmDialogList" parent="@style/TextAppearance.DialogMessage">
-      <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
-      <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
-    </style>
-
-    <style name="TextAppearance.SimConfirmDialogList.Summary">
-        <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
-        <item name="android:textColor">?android:attr/textColorSecondaryInverse</item>
-    </style>
 </resources>
\ No newline at end of file
diff --git a/res/values/config.xml b/res/values/config.xml
index 145c6c5..4eba076 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -226,6 +226,9 @@
          Can be overridden for specific product builds if the target device does not support it -->
     <bool name="config_media_vibration_supported">true</bool>
 
+    <!-- Whether to show Keyboard vibration settings in the vibration and haptics screen. -->
+    <bool name="config_keyboard_vibration_supported">false</bool>
+
     <!--
         Whether or not the homepage should be powered by legacy suggestion (versus contextual cards)
         Default to true as not all devices support contextual cards.
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 515b571..a2bb40e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -259,6 +259,8 @@
     <string name="connected_device_saved_title">Saved devices</string>
     <!-- Title for fast pair device group [CHAR LIMIT=none]-->
     <string name="connected_device_fast_pair_device_title">Associated with account</string>
+    <!-- Title for previously associated fast pair device group [CHAR LIMIT=none]-->
+    <string name="connected_device_previously_associated_fast_pair_device_title">Previously used with account</string>
     <!-- Summary for preference to add a device [CHAR LIMIT=none]-->
     <string name="connected_device_add_device_summary">Bluetooth will turn on to pair</string>
     <!-- Title for other connection preferences [CHAR LIMIT=none]-->
@@ -4808,6 +4810,8 @@
     <string name="accessibility_alarm_vibration_title">Alarm vibration</string>
     <!-- Title for preference for configuring media vibrations (e.g. vibrations played together with animations, music, videos, etc). [CHAR LIMIT=NONE] -->
     <string name="accessibility_media_vibration_title">Media vibration</string>
+    <!-- Toggle for keyboard vibration. [CHAR LIMIT=NONE]-->
+    <string name="accessibility_keyboard_vibration_title">Keyboard vibration</string>
     <!-- Title for accessibility preference for configuring ring vibrations. [CHAR LIMIT=NONE] -->
     <string name="accessibility_ring_vibration_title">Ring vibration</string>
     <!-- Title for accessibility preference for configuring notification vibrations. -->
@@ -7339,6 +7343,9 @@
     <!-- List of synonyms for the nfc tag apps control [CHAR LIMIT=NONE] -->
     <string name="keywords_change_nfc_tag_apps_state">nfc, tag, reader</string>
 
+    <!-- List of synonyms for keyboard vibration settings search [CHAR LIMIT=NONE] -->
+    <string name="keywords_keyboard_vibration">keyboard, haptics, vibrate,</string>
+
     <!-- Summary for sound settings, explaining a few important settings under it [CHAR LIMIT=NONE]-->
     <string name="sound_dashboard_summary">Volume, vibration, Do Not Disturb</string>
 
diff --git a/res/xml/accessibility_vibration_intensity_settings.xml b/res/xml/accessibility_vibration_intensity_settings.xml
index d9505b5..13dc03b 100644
--- a/res/xml/accessibility_vibration_intensity_settings.xml
+++ b/res/xml/accessibility_vibration_intensity_settings.xml
@@ -77,6 +77,12 @@
             app:keywords="@string/keywords_media_vibration"
             app:controller="com.android.settings.accessibility.MediaVibrationIntensityPreferenceController" />
 
+        <SwitchPreference
+            android:key="vibration_intensity_preference_keyboard"
+            android:title="@string/accessibility_keyboard_vibration_title"
+            app:keywords="@string/keywords_keyboard_vibration"
+            app:controller="com.android.settings.accessibility.KeyboardVibrationTogglePreferenceController"/>
+
     </PreferenceCategory>
 
 </PreferenceScreen>
diff --git a/res/xml/accessibility_vibration_settings.xml b/res/xml/accessibility_vibration_settings.xml
index 5e2f923..0435cb4 100644
--- a/res/xml/accessibility_vibration_settings.xml
+++ b/res/xml/accessibility_vibration_settings.xml
@@ -77,6 +77,12 @@
             app:keywords="@string/keywords_media_vibration"
             app:controller="com.android.settings.accessibility.MediaVibrationTogglePreferenceController" />
 
+        <SwitchPreference
+            android:key="vibration_preference_keyboard"
+            android:title="@string/accessibility_keyboard_vibration_title"
+            app:keywords="@string/keywords_keyboard_vibration"
+            app:controller="com.android.settings.accessibility.KeyboardVibrationTogglePreferenceController"/>
+
     </PreferenceCategory>
 
 </PreferenceScreen>
diff --git a/res/xml/connected_devices.xml b/res/xml/connected_devices.xml
index 830ed87..0043a2d 100644
--- a/res/xml/connected_devices.xml
+++ b/res/xml/connected_devices.xml
@@ -63,7 +63,7 @@
 
     <PreferenceCategory
         android:key="fast_pair_devices"
-        android:title="@string/connected_device_fast_pair_device_title"
+        android:title="@string/connected_device_previously_associated_fast_pair_device_title"
         settings:controller=
             "com.android.settings.connecteddevice.fastpair.FastPairDevicePreferenceController">
 
diff --git a/res/xml/fast_pair_devices.xml b/res/xml/fast_pair_devices.xml
index 7e72cd7..bb8381f 100644
--- a/res/xml/fast_pair_devices.xml
+++ b/res/xml/fast_pair_devices.xml
@@ -18,7 +18,7 @@
 <PreferenceScreen
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:settings="http://schemas.android.com/apk/res-auto"
-    android:title="@string/connected_device_fast_pair_device_title">
+    android:title="@string/connected_device_previously_associated_fast_pair_device_title">
 
     <PreferenceCategory
         android:key="fast_pair_device_list"
diff --git a/res/xml/network_provider_sims_list.xml b/res/xml/network_provider_sims_list.xml
index b21341e..cc7589c 100644
--- a/res/xml/network_provider_sims_list.xml
+++ b/res/xml/network_provider_sims_list.xml
@@ -24,7 +24,7 @@
         android:title="@string/summary_placeholder"
         android:layout="@layout/preference_category_no_label"
         android:order="20"
-        settings:controller="com.android.settings.network.NetworkProviderSimsCategoryController"/>
+        settings:controller="com.android.settings.network.NetworkProviderSimListController"/>
 
     <com.android.settingslib.RestrictedPreference
         android:key="add_sim"
diff --git a/res/xml/reset_dashboard_fragment.xml b/res/xml/reset_dashboard_fragment.xml
index 08852c9..ab253ac 100644
--- a/res/xml/reset_dashboard_fragment.xml
+++ b/res/xml/reset_dashboard_fragment.xml
@@ -30,11 +30,9 @@
         android:fragment="com.android.settings.ResetNetwork" />
 
     <!-- Bluetooth and WiFi reset -->
-    <com.android.settingslib.RestrictedPreference
+    <com.android.settings.spa.preference.ComposePreference
         android:key="network_reset_bluetooth_wifi_pref"
         android:title="@string/reset_bluetooth_wifi_title"
-        settings:userRestriction="no_network_reset"
-        settings:useAdminDisabledSummary="true"
         settings:controller="com.android.settings.network.BluetoothWiFiResetPreferenceController" />
 
     <!-- Reset app preferences -->
diff --git a/src/com/android/settings/DefaultRingtonePreference.java b/src/com/android/settings/DefaultRingtonePreference.java
index 9bf626c..4c65488 100644
--- a/src/com/android/settings/DefaultRingtonePreference.java
+++ b/src/com/android/settings/DefaultRingtonePreference.java
@@ -51,16 +51,9 @@
             return;
         }
 
-        String mimeType = mUserContext.getContentResolver().getType(ringtoneUri);
-        if (mimeType == null) {
+        if (!isValidRingtoneUri(ringtoneUri)) {
             Log.e(TAG, "onSaveRingtone for URI:" + ringtoneUri
-                    + " ignored: failure to find mimeType (no access from this context?)");
-            return;
-        }
-
-        if (!(mimeType.startsWith("audio/") || mimeType.equals("application/ogg"))) {
-            Log.e(TAG, "onSaveRingtone for URI:" + ringtoneUri
-                    + " ignored: associated mimeType:" + mimeType + " is not an audio type");
+                    + " ignored: invalid ringtone Uri");
             return;
         }
 
diff --git a/src/com/android/settings/RingtonePreference.java b/src/com/android/settings/RingtonePreference.java
index 8f9c618..de5b7c3 100644
--- a/src/com/android/settings/RingtonePreference.java
+++ b/src/com/android/settings/RingtonePreference.java
@@ -16,6 +16,8 @@
 
 package com.android.settings;
 
+import android.content.ContentProvider;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.TypedArray;
@@ -23,9 +25,11 @@
 import android.media.RingtoneManager;
 import android.net.Uri;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.Settings.System;
 import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.util.Log;
 
 import androidx.preference.Preference;
 import androidx.preference.PreferenceManager;
@@ -239,4 +243,83 @@
         return true;
     }
 
+    public boolean isDefaultRingtone(Uri ringtoneUri) {
+        // null URIs are valid (None/silence)
+        return ringtoneUri == null || RingtoneManager.isDefault(ringtoneUri);
+    }
+
+    protected boolean isValidRingtoneUri(Uri ringtoneUri) {
+        if (isDefaultRingtone(ringtoneUri)) {
+            return true;
+        }
+
+        // Return early for android resource URIs
+        if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(ringtoneUri.getScheme())) {
+            return true;
+        }
+
+        String mimeType = mUserContext.getContentResolver().getType(ringtoneUri);
+        if (mimeType == null) {
+            Log.e(TAG, "isValidRingtoneUri for URI:" + ringtoneUri
+                    + " failed: failure to find mimeType (no access from this context?)");
+            return false;
+        }
+
+        if (!(mimeType.startsWith("audio/") || mimeType.equals("application/ogg")
+                || mimeType.equals("application/x-flac"))) {
+            Log.e(TAG, "isValidRingtoneUri for URI:" + ringtoneUri
+                    + " failed: associated mimeType:" + mimeType + " is not an audio type");
+            return false;
+        }
+
+        // Validate userId from URIs: content://{userId}@...
+        final int userIdFromUri = ContentProvider.getUserIdFromUri(ringtoneUri, mUserId);
+        if (userIdFromUri != mUserId) {
+            final UserManager userManager = mUserContext.getSystemService(UserManager.class);
+
+            if (!userManager.isSameProfileGroup(mUserId, userIdFromUri)) {
+                Log.e(TAG,
+                    "isValidRingtoneUri for URI:" + ringtoneUri + " failed: user " + userIdFromUri
+                        + " and user " + mUserId + " are not in the same profile group");
+                return false;
+            }
+
+            final int parentUserId;
+            final int profileUserId;
+            if (userManager.isProfile()) {
+                profileUserId = mUserId;
+                parentUserId = userIdFromUri;
+            } else {
+                parentUserId = mUserId;
+                profileUserId = userIdFromUri;
+            }
+
+            final UserHandle parent = userManager.getProfileParent(UserHandle.of(profileUserId));
+            if (parent == null || parent.getIdentifier() != parentUserId) {
+                Log.e(TAG,
+                    "isValidRingtoneUri for URI:" + ringtoneUri + " failed: user " + profileUserId
+                        + " is not a profile of user " + parentUserId);
+                return false;
+            }
+
+            // Allow parent <-> managed profile sharing, unless restricted
+            if (userManager.hasUserRestrictionForUser(
+                UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, UserHandle.of(parentUserId))) {
+                Log.e(TAG,
+                    "isValidRingtoneUri for URI:" + ringtoneUri + " failed: user " + parentUserId
+                        + " has restriction: " + UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE);
+                return false;
+            }
+
+            if (!(userManager.isManagedProfile(profileUserId) || userManager.getUserProperties(
+                    UserHandle.of(profileUserId)).isMediaSharedWithParent())) {
+                Log.e(TAG, "isValidRingtoneUri for URI:" + ringtoneUri
+                    + " failed: user " + profileUserId + " is not a cloned or managed profile");
+                return false;
+            }
+        }
+
+        return true;
+    }
+
 }
diff --git a/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceController.java b/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceController.java
new file mode 100644
index 0000000..29d5928
--- /dev/null
+++ b/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceController.java
@@ -0,0 +1,159 @@
+/*
+ * 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.accessibility;
+
+import static android.provider.Settings.System.KEYBOARD_VIBRATION_ENABLED;
+
+import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
+import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.VibrationAttributes;
+import android.os.Vibrator;
+import android.os.vibrator.Flags;
+import android.provider.Settings;
+import android.util.Log;
+
+import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+
+import com.android.settings.R;
+import com.android.settings.core.TogglePreferenceController;
+
+
+/**
+ *  A preference controller to turn on/off keyboard vibration state with a single toggle.
+ */
+public class KeyboardVibrationTogglePreferenceController extends TogglePreferenceController
+        implements DefaultLifecycleObserver {
+
+    private static final String TAG = "KeyboardVibrateControl";
+
+    private static final Uri MAIN_VIBRATION_SWITCH_URI =
+            Settings.System.getUriFor(VibrationPreferenceConfig.MAIN_SWITCH_SETTING_KEY);
+
+    private final ContentObserver mContentObserver;
+
+    private final Vibrator mVibrator;
+
+    @Nullable
+    private SwitchPreference mPreference;
+
+    public KeyboardVibrationTogglePreferenceController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
+        mVibrator = context.getSystemService(Vibrator.class);
+        mContentObserver = new ContentObserver(new Handler(/* async= */ true)) {
+            @Override
+            public void onChange(boolean selfChange, Uri uri) {
+                if (uri.equals(MAIN_VIBRATION_SWITCH_URI)) {
+                    updateState(mPreference);
+                } else {
+                    Log.w(TAG, "Unexpected uri change:" + uri);
+                }
+            }
+        };
+    }
+
+    @Override
+    public void onStart(@NonNull LifecycleOwner owner) {
+        mContext.getContentResolver().registerContentObserver(MAIN_VIBRATION_SWITCH_URI,
+                /* notifyForDescendants= */ false, mContentObserver);
+    }
+
+    @Override
+    public void onStop(@NonNull LifecycleOwner owner) {
+        mContext.getContentResolver().unregisterContentObserver(mContentObserver);
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mPreference = screen.findPreference(getPreferenceKey());
+    }
+
+    @Override
+    public void updateState(@Nullable Preference preference) {
+        if (preference != null) {
+            super.updateState(preference);
+            preference.setEnabled(isPreferenceEnabled());
+        }
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        if (Flags.keyboardCategoryEnabled()
+                && mContext.getResources().getBoolean(R.bool.config_keyboard_vibration_supported)) {
+            return AVAILABLE;
+        }
+        return UNSUPPORTED_ON_DEVICE;
+    }
+
+    @Override
+    public boolean isChecked() {
+        // Always unchecked if the preference disabled
+        return isPreferenceEnabled() && isKeyboardVibrationSwitchEnabled();
+    }
+
+    @Override
+    public boolean setChecked(boolean isChecked) {
+        final boolean success = updateKeyboardVibrationSetting(isChecked);
+        if (success && isChecked) {
+            // Play the preview vibration effect when the toggle is on.
+            final VibrationAttributes touchAttrs =
+                    VibrationPreferenceConfig.createPreviewVibrationAttributes(
+                            VibrationAttributes.USAGE_TOUCH);
+            final VibrationAttributes keyboardAttrs =
+                    new VibrationAttributes.Builder(touchAttrs)
+                            .setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
+                            .build();
+            VibrationPreferenceConfig.playVibrationPreview(mVibrator, keyboardAttrs);
+        }
+        return true;
+    }
+
+    @Override
+    public int getSliceHighlightMenuRes() {
+        return R.string.menu_key_accessibility;
+    }
+
+    private boolean isPreferenceEnabled() {
+        return VibrationPreferenceConfig.isMainVibrationSwitchEnabled(
+                mContext.getContentResolver());
+    }
+
+    private boolean isKeyboardVibrationSwitchEnabled() {
+        return Settings.System.getInt(mContext.getContentResolver(), KEYBOARD_VIBRATION_ENABLED,
+                mVibrator.isDefaultKeyboardVibrationEnabled() ? ON : OFF) == ON;
+    }
+
+    private boolean updateKeyboardVibrationSetting(boolean enable) {
+        final boolean success = Settings.System.putInt(mContext.getContentResolver(),
+                    KEYBOARD_VIBRATION_ENABLED, enable ? ON : OFF);
+        if (!success) {
+            Log.w(TAG, "Update settings database error!");
+        }
+        return success;
+    }
+}
diff --git a/src/com/android/settings/accessibility/VibrationPreferenceConfig.java b/src/com/android/settings/accessibility/VibrationPreferenceConfig.java
index b4be528..c25c38e 100644
--- a/src/com/android/settings/accessibility/VibrationPreferenceConfig.java
+++ b/src/com/android/settings/accessibility/VibrationPreferenceConfig.java
@@ -68,8 +68,19 @@
     /** Play a vibration effect with intensity just selected by the user. */
     public static void playVibrationPreview(Vibrator vibrator,
             @VibrationAttributes.Usage int vibrationUsage) {
-        vibrator.vibrate(PREVIEW_VIBRATION_EFFECT,
-                createPreviewVibrationAttributes(vibrationUsage));
+        playVibrationPreview(vibrator, createPreviewVibrationAttributes(vibrationUsage));
+    }
+
+    /**
+     * Play a vibration effect with intensity just selected by the user.
+     *
+     * @param vibrator The {@link Vibrator} used to play the vibration.
+     * @param vibrationAttributes The {@link VibrationAttributes} to indicate the
+     *        vibration information.
+     */
+    public static void playVibrationPreview(Vibrator vibrator,
+            VibrationAttributes vibrationAttributes) {
+        vibrator.vibrate(PREVIEW_VIBRATION_EFFECT, vibrationAttributes);
     }
 
     public VibrationPreferenceConfig(Context context, String settingKey,
@@ -135,7 +146,7 @@
         return mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT;
     }
 
-    private static VibrationAttributes createPreviewVibrationAttributes(
+    static VibrationAttributes createPreviewVibrationAttributes(
             @VibrationAttributes.Usage int vibrationUsage) {
         return new VibrationAttributes.Builder()
                 .setUsage(vibrationUsage)
diff --git a/src/com/android/settings/biometrics/BiometricEnrollBase.java b/src/com/android/settings/biometrics/BiometricEnrollBase.java
index c9c8cff..7df1fe1 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollBase.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollBase.java
@@ -67,6 +67,7 @@
     public static final String EXTRA_FINISHED_ENROLL_FACE = "finished_enrolling_face";
     public static final String EXTRA_FINISHED_ENROLL_FINGERPRINT = "finished_enrolling_fingerprint";
     public static final String EXTRA_LAUNCHED_POSTURE_GUIDANCE = "launched_posture_guidance";
+    public static final String KEY_CALIBRATOR_UUID = "calibrator_uuid";
 
     /**
      * Used by the choose fingerprint wizard to indicate the wizard is
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
index be2a948..063d55d 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
@@ -68,10 +68,13 @@
 import com.android.settings.biometrics.BiometricEnrollSidecar;
 import com.android.settings.biometrics.BiometricUtils;
 import com.android.settings.biometrics.BiometricsEnrollEnrolling;
+import com.android.settings.biometrics.fingerprint.feature.SfpsEnrollmentFeature;
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.display.DisplayDensityUtils;
 
 import com.airbnb.lottie.LottieAnimationView;
+import com.airbnb.lottie.LottieComposition;
 import com.airbnb.lottie.LottieCompositionFactory;
 import com.airbnb.lottie.LottieProperty;
 import com.airbnb.lottie.model.KeyPath;
@@ -84,6 +87,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
+import java.util.function.Function;
 
 /**
  * Activity which handles the actual enrolling for fingerprint.
@@ -99,27 +103,22 @@
 
     private static final int PROGRESS_BAR_MAX = 10000;
 
-    private static final int STAGE_UNKNOWN = -1;
+    public static final int STAGE_UNKNOWN = -1;
     private static final int STAGE_CENTER = 0;
     private static final int STAGE_GUIDED = 1;
     private static final int STAGE_FINGERTIP = 2;
     private static final int STAGE_LEFT_EDGE = 3;
     private static final int STAGE_RIGHT_EDGE = 4;
 
-    @VisibleForTesting
-    protected static final int SFPS_STAGE_NO_ANIMATION = 0;
+    public static final int SFPS_STAGE_NO_ANIMATION = 0;
 
-    @VisibleForTesting
-    protected static final int SFPS_STAGE_CENTER = 1;
+    public static final int SFPS_STAGE_CENTER = 1;
 
-    @VisibleForTesting
-    protected static final int SFPS_STAGE_FINGERTIP = 2;
+    public static final int SFPS_STAGE_FINGERTIP = 2;
 
-    @VisibleForTesting
-    protected static final int SFPS_STAGE_LEFT_EDGE = 3;
+    public static final int SFPS_STAGE_LEFT_EDGE = 3;
 
-    @VisibleForTesting
-    protected static final int SFPS_STAGE_RIGHT_EDGE = 4;
+    public static final int SFPS_STAGE_RIGHT_EDGE = 4;
 
     @IntDef({STAGE_UNKNOWN, STAGE_CENTER, STAGE_GUIDED, STAGE_FINGERTIP, STAGE_LEFT_EDGE,
             STAGE_RIGHT_EDGE})
@@ -196,6 +195,9 @@
     private OrientationEventListener mOrientationEventListener;
     private int mPreviousRotation = 0;
 
+    @NonNull
+    private SfpsEnrollmentFeature mSfpsEnrollmentFeature = new EmptySfpsEnrollmentFeature();
+
     @VisibleForTesting
     protected boolean shouldShowLottie() {
         DisplayDensityUtils displayDensity = new DisplayDensityUtils(getApplicationContext());
@@ -244,6 +246,8 @@
             setContentView(layout);
             setDescriptionText(R.string.security_settings_udfps_enroll_start_message);
         } else if (mCanAssumeSfps) {
+            mSfpsEnrollmentFeature = FeatureFactory.getFeatureFactory()
+                    .getFingerprintFeatureProvider().getSfpsEnrollmentFeature();
             setContentView(R.layout.sfps_enroll_enrolling);
             setHelpAnimation();
         } else {
@@ -599,7 +603,8 @@
         }
         switch (getCurrentSfpsStage()) {
             case SFPS_STAGE_NO_ANIMATION:
-                setHeaderText(R.string.security_settings_fingerprint_enroll_repeat_title);
+                setHeaderText(mSfpsEnrollmentFeature
+                        .getFeaturedStageHeaderResource(SFPS_STAGE_NO_ANIMATION));
                 if (!mHaveShownSfpsNoAnimationLottie && mIllustrationLottie != null) {
                     mHaveShownSfpsNoAnimationLottie = true;
                     mIllustrationLottie.setContentDescription(
@@ -608,39 +613,48 @@
                                     0
                             )
                     );
-                    configureEnrollmentStage(R.raw.sfps_lottie_no_animation);
+                    configureEnrollmentStage(mSfpsEnrollmentFeature
+                            .getSfpsEnrollLottiePerStage(SFPS_STAGE_NO_ANIMATION));
                 }
                 break;
 
             case SFPS_STAGE_CENTER:
-                setHeaderText(R.string.security_settings_sfps_enroll_finger_center_title);
+                setHeaderText(mSfpsEnrollmentFeature
+                        .getFeaturedStageHeaderResource(SFPS_STAGE_CENTER));
                 if (!mHaveShownSfpsCenterLottie && mIllustrationLottie != null) {
                     mHaveShownSfpsCenterLottie = true;
-                    configureEnrollmentStage(R.raw.sfps_lottie_pad_center);
+                    configureEnrollmentStage(mSfpsEnrollmentFeature
+                            .getSfpsEnrollLottiePerStage(SFPS_STAGE_CENTER));
                 }
                 break;
 
             case SFPS_STAGE_FINGERTIP:
-                setHeaderText(R.string.security_settings_sfps_enroll_fingertip_title);
+                setHeaderText(mSfpsEnrollmentFeature
+                        .getFeaturedStageHeaderResource(SFPS_STAGE_FINGERTIP));
                 if (!mHaveShownSfpsTipLottie && mIllustrationLottie != null) {
                     mHaveShownSfpsTipLottie = true;
-                    configureEnrollmentStage(R.raw.sfps_lottie_tip);
+                    configureEnrollmentStage(mSfpsEnrollmentFeature
+                            .getSfpsEnrollLottiePerStage(SFPS_STAGE_FINGERTIP));
                 }
                 break;
 
             case SFPS_STAGE_LEFT_EDGE:
-                setHeaderText(R.string.security_settings_sfps_enroll_left_edge_title);
+                setHeaderText(mSfpsEnrollmentFeature
+                        .getFeaturedStageHeaderResource(SFPS_STAGE_LEFT_EDGE));
                 if (!mHaveShownSfpsLeftEdgeLottie && mIllustrationLottie != null) {
                     mHaveShownSfpsLeftEdgeLottie = true;
-                    configureEnrollmentStage(R.raw.sfps_lottie_left_edge);
+                    configureEnrollmentStage(mSfpsEnrollmentFeature
+                            .getSfpsEnrollLottiePerStage(SFPS_STAGE_LEFT_EDGE));
                 }
                 break;
 
             case SFPS_STAGE_RIGHT_EDGE:
-                setHeaderText(R.string.security_settings_sfps_enroll_right_edge_title);
+                setHeaderText(mSfpsEnrollmentFeature
+                        .getFeaturedStageHeaderResource(SFPS_STAGE_RIGHT_EDGE));
                 if (!mHaveShownSfpsRightEdgeLottie && mIllustrationLottie != null) {
                     mHaveShownSfpsRightEdgeLottie = true;
-                    configureEnrollmentStage(R.raw.sfps_lottie_right_edge);
+                    configureEnrollmentStage(mSfpsEnrollmentFeature
+                            .getSfpsEnrollLottiePerStage(SFPS_STAGE_RIGHT_EDGE));
                 }
                 break;
 
@@ -665,11 +679,16 @@
             setDescriptionText("");
         }
         LottieCompositionFactory.fromRawRes(this, lottie)
-                .addListener((c) -> {
-                    mIllustrationLottie.setComposition(c);
-                    mIllustrationLottie.setVisibility(View.VISIBLE);
-                    mIllustrationLottie.playAnimation();
-                });
+                .addListener((c) -> onLottieComposition(mIllustrationLottie, c));
+    }
+
+    private void onLottieComposition(LottieAnimationView view, LottieComposition composition) {
+        if (view == null || composition == null) {
+            return;
+        }
+        view.setComposition(composition);
+        view.setVisibility(View.VISIBLE);
+        view.playAnimation();
     }
 
     @EnrollStage
@@ -699,17 +718,8 @@
         }
 
         final int progressSteps = mSidecar.getEnrollmentSteps() - mSidecar.getEnrollmentRemaining();
-        if (progressSteps < getStageThresholdSteps(0)) {
-            return SFPS_STAGE_NO_ANIMATION;
-        } else if (progressSteps < getStageThresholdSteps(1)) {
-            return SFPS_STAGE_CENTER;
-        } else if (progressSteps < getStageThresholdSteps(2)) {
-            return SFPS_STAGE_FINGERTIP;
-        } else if (progressSteps < getStageThresholdSteps(3)) {
-            return SFPS_STAGE_LEFT_EDGE;
-        } else {
-            return SFPS_STAGE_RIGHT_EDGE;
-        }
+        return mSfpsEnrollmentFeature
+                .getCurrentSfpsEnrollStage(progressSteps, this::getStageThresholdSteps);
     }
 
     private boolean isStageHalfCompleted() {
@@ -740,22 +750,31 @@
             Log.w(TAG, "getStageThresholdSteps: Enrollment not started yet");
             return 1;
         }
-        return Math.round(mSidecar.getEnrollmentSteps()
-                * mFingerprintManager.getEnrollStageThreshold(index));
+        final float threshold = mCanAssumeSfps
+                ? mSfpsEnrollmentFeature.getEnrollStageThreshold(this, index)
+                : mFingerprintManager.getEnrollStageThreshold(index);
+        return Math.round(mSidecar.getEnrollmentSteps() * threshold);
     }
 
     @Override
     public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {
-        if (!TextUtils.isEmpty(helpString)) {
+        final CharSequence featuredString = mCanAssumeSfps
+                ? mSfpsEnrollmentFeature.getFeaturedVendorString(this, helpMsgId, helpString)
+                : helpString;
+
+        if (!TextUtils.isEmpty(featuredString)) {
             if (!(mCanAssumeUdfps || mCanAssumeSfps)) {
                 mErrorText.removeCallbacks(mTouchAgainRunnable);
             }
-            showError(helpString);
+            showError(featuredString);
 
             if (mUdfpsEnrollHelper != null) mUdfpsEnrollHelper.onEnrollmentHelp();
         }
 
         dismissTouchDialogIfSfps();
+        if (mCanAssumeSfps) {
+            mSfpsEnrollmentFeature.handleOnEnrollmentHelp(helpMsgId, featuredString, () -> this);
+        }
     }
 
     @Override
@@ -1170,4 +1189,28 @@
             return SettingsEnums.DIALOG_FINGERPRINT_ICON_TOUCH;
         }
     }
-}
\ No newline at end of file
+
+    private static class EmptySfpsEnrollmentFeature implements SfpsEnrollmentFeature {
+        private final String exceptionStr = "Assume sfps but no SfpsEnrollmentFeature impl.";
+
+        @Override
+        public int getCurrentSfpsEnrollStage(int progressSteps, Function<Integer, Integer> mapper) {
+            throw new IllegalStateException(exceptionStr);
+        }
+
+        @Override
+        public int getFeaturedStageHeaderResource(int stage) {
+            throw new IllegalStateException(exceptionStr);
+        }
+
+        @Override
+        public int getSfpsEnrollLottiePerStage(int stage) {
+            throw new IllegalStateException(exceptionStr);
+        }
+
+        @Override
+        public float getEnrollStageThreshold(@NonNull Context context, int index) {
+            throw new IllegalStateException(exceptionStr);
+        }
+    }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
index c207ec9..276845c 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.biometrics.fingerprint;
 
+import static android.text.Layout.HYPHENATION_FREQUENCY_NORMAL;
+
 import android.app.settings.SettingsEnums;
 import android.content.Intent;
 import android.content.res.Configuration;
@@ -23,21 +25,27 @@
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
 import android.util.Log;
 import android.view.OrientationEventListener;
 import android.view.Surface;
 import android.view.View;
-import android.view.View.OnClickListener;
 import android.view.accessibility.AccessibilityManager;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.lifecycle.Observer;
 
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settings.biometrics.BiometricEnrollBase;
 import com.android.settings.biometrics.BiometricEnrollSidecar;
 import com.android.settings.biometrics.BiometricUtils;
+import com.android.settings.biometrics.fingerprint.UdfpsEnrollCalibrator.Result;
+import com.android.settings.biometrics.fingerprint.UdfpsEnrollCalibrator.Status;
+import com.android.settings.flags.Flags;
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.password.ChooseLockSettingsHelper;
 import com.android.settingslib.widget.LottieColorUtils;
 import com.android.systemui.unfold.compat.ScreenSizeFoldProvider;
@@ -48,6 +56,7 @@
 import com.google.android.setupcompat.template.FooterButton;
 
 import java.util.List;
+import java.util.UUID;
 
 /**
  * Activity explaining the fingerprint sensor location for fingerprint enrollment.
@@ -74,6 +83,10 @@
     private ScreenSizeFoldProvider mScreenSizeFoldProvider;
     private boolean mIsFolded;
     private boolean mIsReverseDefaultRotation;
+    @Nullable
+    private UdfpsEnrollCalibrator mCalibrator;
+    @Nullable
+    private Observer<Status> mCalibratorStatusObserver;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -98,20 +111,13 @@
                         .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Secondary)
                         .build()
         );
+        getLayout().getHeaderTextView().setHyphenationFrequency(HYPHENATION_FREQUENCY_NORMAL);
 
         listenOrientationEvent();
 
         if (mCanAssumeUdfps) {
             setHeaderText(R.string.security_settings_udfps_enroll_find_sensor_title);
             setDescriptionText(R.string.security_settings_udfps_enroll_find_sensor_message);
-            mFooterBarMixin.setPrimaryButton(
-                    new FooterButton.Builder(this)
-                    .setText(R.string.security_settings_udfps_enroll_find_sensor_start_button)
-                    .setListener(this::onStartButtonClick)
-                    .setButtonType(FooterButton.ButtonType.NEXT)
-                    .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
-                    .build()
-            );
 
             mIllustrationLottie = findViewById(R.id.illustration_lottie);
             AccessibilityManager am = getSystemService(AccessibilityManager.class);
@@ -164,12 +170,20 @@
 
         mAnimation = null;
         if (mCanAssumeUdfps) {
-            mIllustrationLottie.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    onStartButtonClick(v);
+            if (Flags.udfpsEnrollCalibration()) {
+                mCalibrator = FeatureFactory.getFeatureFactory().getFingerprintFeatureProvider()
+                        .getUdfpsEnrollCalibrator(
+                                (savedInstanceState != null)
+                                ? savedInstanceState.getParcelable(KEY_CALIBRATOR_UUID, UUID.class)
+                                : getIntent().getSerializableExtra(KEY_CALIBRATOR_UUID, UUID.class)
+                        );
+                if (mCalibrator == null
+                        || mCalibrator.getStatusLiveData().getValue() == Status.FINISHED) {
+                    enableUdfpsLottieAndNextButton();
                 }
-            });
+            } else {
+                enableUdfpsLottieAndNextButton();
+            }
         } else if (!mCanAssumeSfps) {
             View animationView = findViewById(R.id.fingerprint_sensor_location_animation);
             if (animationView instanceof FingerprintFindSensorAnimation) {
@@ -178,6 +192,20 @@
         }
     }
 
+    private void enableUdfpsLottieAndNextButton() {
+        mFooterBarMixin.setPrimaryButton(
+                new FooterButton.Builder(this)
+                        .setText(R.string.security_settings_udfps_enroll_find_sensor_start_button)
+                        .setListener(this::onStartButtonClick)
+                        .setButtonType(FooterButton.ButtonType.NEXT)
+                        .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
+                        .build()
+        );
+        if (mIllustrationLottie != null) {
+            mIllustrationLottie.setOnClickListener(this::onStartButtonClick);
+        }
+    }
+
     private int getRotationFromDefault(int rotation) {
         if (mIsReverseDefaultRotation) {
             return (rotation + 1) % 4;
@@ -255,6 +283,11 @@
     protected void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
         outState.putBoolean(SAVED_STATE_IS_NEXT_CLICKED, mNextClicked);
+        if (Flags.udfpsEnrollCalibration()) {
+            if (mCalibrator != null) {
+                outState.putSerializable(KEY_CALIBRATOR_UUID, mCalibrator.getUuid());
+            }
+        }
     }
 
     @Override
@@ -284,6 +317,39 @@
         if (mAnimation != null) {
             mAnimation.startAnimation();
         }
+        if (Flags.udfpsEnrollCalibration()) {
+            if (mCalibrator != null) {
+                final Status current = mCalibrator.getStatusLiveData().getValue();
+                if (current == Status.PROCESSING) {
+                    if (mCalibratorStatusObserver == null) {
+                        mCalibratorStatusObserver = status -> {
+                            if (status == Status.GOT_RESULT) {
+                                onGotCalibrationResult();
+                            }
+                        };
+                    }
+                    mCalibrator.getStatusLiveData().observe(this, mCalibratorStatusObserver);
+                } else if (current == Status.GOT_RESULT) {
+                    onGotCalibrationResult();
+                }
+            }
+        }
+    }
+
+    private void onGotCalibrationResult() {
+        if (Flags.udfpsEnrollCalibration()) {
+            if (mCalibrator != null) {
+                mCalibrator.setFinished();
+                if (mCalibrator.getResult() == Result.NEED_CALIBRATION) {
+                    UdfpsEnrollCalibrationDialog.newInstance(
+                            mCalibrator.getCalibrationDialogTitleTextId(),
+                            mCalibrator.getCalibrationDialogMessageTextId(),
+                            mCalibrator.getCalibrationDialogDismissButtonTextId()
+                    ).show(getSupportFragmentManager(), "findsensor-calibration-dialog");
+                }
+            }
+            new Handler(Looper.getMainLooper()).post(this::enableUdfpsLottieAndNextButton);
+        }
     }
 
     private void stopLookingForFingerprint() {
@@ -341,6 +407,12 @@
         if (mAnimation != null) {
             mAnimation.pauseAnimation();
         }
+        if (Flags.udfpsEnrollCalibration()) {
+            if (mCalibrator != null && mCalibratorStatusObserver != null) {
+                mCalibrator.getStatusLiveData().removeObserver(mCalibratorStatusObserver);
+                mCalibratorStatusObserver = null;
+            }
+        }
     }
 
     @Override
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
index df23a5c..dc3c65e 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
@@ -45,6 +45,8 @@
 import com.android.settings.biometrics.BiometricUtils;
 import com.android.settings.biometrics.GatekeeperPasswordProvider;
 import com.android.settings.biometrics.MultiBiometricEnrollHelper;
+import com.android.settings.flags.Flags;
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.password.ChooseLockSettingsHelper;
 import com.android.settingslib.HelpUtils;
 import com.android.settingslib.RestrictedLockUtilsInternal;
@@ -55,6 +57,7 @@
 import com.google.android.setupdesign.util.DeviceHelper;
 
 import java.util.List;
+import java.util.UUID;
 
 public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
 
@@ -67,6 +70,8 @@
 
     private DevicePolicyManager mDevicePolicyManager;
     private boolean mCanAssumeUdfps;
+    @Nullable
+    private UdfpsEnrollCalibrator mCalibrator;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -85,6 +90,16 @@
 
         mDevicePolicyManager = getSystemService(DevicePolicyManager.class);
 
+        if (Flags.udfpsEnrollCalibration()) {
+            mCalibrator = FeatureFactory.getFeatureFactory().getFingerprintFeatureProvider()
+                    .getUdfpsEnrollCalibrator(
+                            (savedInstanceState != null)
+                                    ? savedInstanceState.getParcelable(
+                                        KEY_CALIBRATOR_UUID, UUID.class)
+                                    : null
+                        );
+        }
+
         final ImageView iconFingerprint = findViewById(R.id.icon_fingerprint);
         final ImageView iconDeviceLocked = findViewById(R.id.icon_device_locked);
         final ImageView iconTrashCan = findViewById(R.id.icon_trash_can);
@@ -156,6 +171,16 @@
     }
 
     @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        if (Flags.udfpsEnrollCalibration()) {
+            if (mCalibrator != null) {
+                outState.putSerializable(KEY_CALIBRATOR_UUID, mCalibrator.getUuid());
+            }
+        }
+    }
+
+    @Override
     protected void initViews() {
         setDescriptionText(getString(
                 R.string.security_settings_fingerprint_enroll_introduction_v3_message,
@@ -364,6 +389,11 @@
             intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE,
                     BiometricUtils.getGatekeeperPasswordHandle(getIntent()));
         }
+        if (Flags.udfpsEnrollCalibration()) {
+            if (mCalibrator != null) {
+                intent.putExtra(KEY_CALIBRATOR_UUID, mCalibrator.getUuid());
+            }
+        }
         return intent;
     }
 
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java b/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java
new file mode 100644
index 0000000..5a2bf8b
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java
@@ -0,0 +1,39 @@
+/*
+ * 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.biometrics.fingerprint;
+
+import androidx.annotation.Nullable;
+
+import com.android.settings.biometrics.fingerprint.feature.SfpsEnrollmentFeature;
+
+import java.util.UUID;
+
+public interface FingerprintFeatureProvider {
+    /**
+     * Gets the feature implementation of SFPS enrollment.
+     * @return the feature implementation
+     */
+    SfpsEnrollmentFeature getSfpsEnrollmentFeature();
+
+    /**
+     * Gets calibrator to calibrate the FPS before enrolling udfps
+     * @param uuid unique id for passed between different activities
+     * @return udfps calibrator
+     */
+    @Nullable
+    UdfpsEnrollCalibrator getUdfpsEnrollCalibrator(@Nullable UUID uuid);
+}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProviderImpl.java b/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProviderImpl.java
new file mode 100644
index 0000000..1baabc6
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProviderImpl.java
@@ -0,0 +1,44 @@
+/*
+ * 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.biometrics.fingerprint;
+
+import androidx.annotation.Nullable;
+
+import com.android.settings.biometrics.fingerprint.feature.SfpsEnrollmentFeature;
+import com.android.settings.biometrics.fingerprint.feature.SfpsEnrollmentFeatureImpl;
+
+import java.util.UUID;
+
+public class FingerprintFeatureProviderImpl implements FingerprintFeatureProvider {
+
+    @Nullable
+    private SfpsEnrollmentFeature mSfpsEnrollmentFeatureImpl = null;
+
+    @Override
+    public SfpsEnrollmentFeature getSfpsEnrollmentFeature() {
+        if (mSfpsEnrollmentFeatureImpl == null) {
+            mSfpsEnrollmentFeatureImpl = new SfpsEnrollmentFeatureImpl();
+        }
+        return mSfpsEnrollmentFeatureImpl;
+    }
+
+    @Nullable
+    @Override
+    public UdfpsEnrollCalibrator getUdfpsEnrollCalibrator(@Nullable UUID uuid) {
+        return null;
+    }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollCalibrationDialog.kt b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollCalibrationDialog.kt
new file mode 100644
index 0000000..892996a
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollCalibrationDialog.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.biometrics.fingerprint
+
+import android.app.Dialog
+import android.content.DialogInterface
+import android.os.Bundle
+import androidx.annotation.StringRes
+import androidx.appcompat.app.AlertDialog
+import androidx.fragment.app.DialogFragment
+import com.android.settings.R
+
+class UdfpsEnrollCalibrationDialog : DialogFragment() {
+
+    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =
+            AlertDialog.Builder(requireActivity(), R.style.Theme_AlertDialog)
+                    .setTitle(arguments!!.getInt(KEY_TITLE_TEXT_ID))
+                    .setMessage(arguments!!.getInt(KEY_MESSAGE_TEXT_ID))
+                    .setPositiveButton(arguments!!.getInt(KEY_DISMISS_BUTTON_TEXT_ID)) {
+                        dialog: DialogInterface?, _: Int -> dialog?.dismiss()
+                    }
+                    .create().also {
+                        isCancelable = false
+                    }
+
+    companion object {
+
+        private const val KEY_TITLE_TEXT_ID = "title_text_id"
+        private const val KEY_MESSAGE_TEXT_ID = "message_text_id"
+        private const val KEY_DISMISS_BUTTON_TEXT_ID = "dismiss_button_text_id"
+
+        @JvmStatic
+        fun newInstance(
+                @StringRes titleTextId: Int,
+                @StringRes messageTextId: Int,
+                @StringRes dismissButtonTextId: Int
+        ) = UdfpsEnrollCalibrationDialog().apply {
+            arguments = Bundle().apply {
+                putInt(KEY_TITLE_TEXT_ID, titleTextId)
+                putInt(KEY_MESSAGE_TEXT_ID, messageTextId)
+                putInt(KEY_DISMISS_BUTTON_TEXT_ID, dismissButtonTextId)
+            }
+        }
+    }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollCalibrator.kt b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollCalibrator.kt
new file mode 100644
index 0000000..c0626d3
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollCalibrator.kt
@@ -0,0 +1,36 @@
+package com.android.settings.biometrics.fingerprint
+
+import androidx.annotation.StringRes
+import androidx.lifecycle.LiveData
+import java.util.UUID
+
+interface UdfpsEnrollCalibrator {
+
+    enum class Status {
+        PROCESSING,
+        GOT_RESULT,
+        FINISHED,
+    }
+
+    enum class Result {
+        NEED_CALIBRATION,
+        NO_NEED_CALIBRATION,
+    }
+
+    val uuid: UUID
+
+    val statusLiveData: LiveData<Status>
+
+    val result: Result?
+
+    fun setFinished()
+
+    @get:StringRes
+    val calibrationDialogTitleTextId: Int
+
+    @get:StringRes
+    val calibrationDialogMessageTextId: Int
+
+    @get:StringRes
+    val calibrationDialogDismissButtonTextId: Int
+}
\ No newline at end of file
diff --git a/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeature.java b/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeature.java
new file mode 100644
index 0000000..a1a18e5
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeature.java
@@ -0,0 +1,79 @@
+/*
+ * 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.biometrics.fingerprint.feature;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+
+import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling;
+
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+public interface SfpsEnrollmentFeature {
+
+    /**
+     * Gets current SFPS enrollment stage.
+     * @param progressSteps current step of enrollment
+     * @param mapper a mapper to map each stage to its threshold
+     * @return current enrollment stage
+     */
+    int getCurrentSfpsEnrollStage(int progressSteps, Function<Integer, Integer> mapper);
+
+    /**
+     * Gets the vendor string by feature.
+     * @param context Context
+     * @param id An integer identifying the error message
+     * @param msg A human-readable string that can be shown in UI
+     * @return A human-readable string of specific feature
+     */
+    default CharSequence getFeaturedVendorString(Context context, int id, CharSequence msg) {
+        return msg;
+    }
+
+    /**
+     * Gets the stage header string by feature.
+     * @param stage the specific stage
+     * @return the resource id of the header text of the specific stage
+     */
+    int getFeaturedStageHeaderResource(int stage);
+
+    /**
+     * Gets the enrollment lottie resource id per stage
+     * @param stage current enrollment stage
+     * @return enrollment lottie resource id
+     */
+    int getSfpsEnrollLottiePerStage(int stage);
+
+    /**
+     * Handles extra stuffs on receiving enrollment help.
+     * @param helpMsgId help message id
+     * @param helpString help message
+     * @param enrollingSupplier supplier of enrolling context
+     */
+    default void handleOnEnrollmentHelp(int helpMsgId, CharSequence helpString,
+            Supplier<FingerprintEnrollEnrolling> enrollingSupplier) {}
+
+    /**
+     * Gets the fingerprint enrollment threshold.
+     * @param context context
+     * @param index the enrollment stage index
+     * @return threshold
+     */
+    float getEnrollStageThreshold(@NonNull Context context, int index);
+}
diff --git a/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeatureImpl.java b/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeatureImpl.java
new file mode 100644
index 0000000..5a97537
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeatureImpl.java
@@ -0,0 +1,91 @@
+/*
+ * 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.biometrics.fingerprint.feature;
+
+import static com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.SFPS_STAGE_CENTER;
+import static com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.SFPS_STAGE_FINGERTIP;
+import static com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.SFPS_STAGE_LEFT_EDGE;
+import static com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.SFPS_STAGE_NO_ANIMATION;
+import static com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.SFPS_STAGE_RIGHT_EDGE;
+import static com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.STAGE_UNKNOWN;
+
+import android.content.Context;
+import android.hardware.fingerprint.FingerprintManager;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.settings.R;
+
+import java.util.function.Function;
+
+public class SfpsEnrollmentFeatureImpl implements SfpsEnrollmentFeature {
+
+    @Nullable
+    private FingerprintManager mFingerprintManager = null;
+
+    @Override
+    public int getCurrentSfpsEnrollStage(int progressSteps, Function<Integer, Integer> mapper) {
+        if (mapper == null) {
+            return STAGE_UNKNOWN;
+        }
+        if (progressSteps < mapper.apply(0)) {
+            return SFPS_STAGE_NO_ANIMATION;
+        } else if (progressSteps < mapper.apply(1)) {
+            return SFPS_STAGE_CENTER;
+        } else if (progressSteps < mapper.apply(2)) {
+            return SFPS_STAGE_FINGERTIP;
+        } else if (progressSteps < mapper.apply(3)) {
+            return SFPS_STAGE_LEFT_EDGE;
+        } else {
+            return SFPS_STAGE_RIGHT_EDGE;
+        }
+    }
+
+    @Override
+    public int getFeaturedStageHeaderResource(int stage) {
+        return switch (stage) {
+            case SFPS_STAGE_NO_ANIMATION
+                    -> R.string.security_settings_fingerprint_enroll_repeat_title;
+            case SFPS_STAGE_CENTER -> R.string.security_settings_sfps_enroll_finger_center_title;
+            case SFPS_STAGE_FINGERTIP -> R.string.security_settings_sfps_enroll_fingertip_title;
+            case SFPS_STAGE_LEFT_EDGE -> R.string.security_settings_sfps_enroll_left_edge_title;
+            case SFPS_STAGE_RIGHT_EDGE -> R.string.security_settings_sfps_enroll_right_edge_title;
+            default -> throw new IllegalArgumentException("Invalid stage: " + stage);
+        };
+    }
+
+    @Override
+    public int getSfpsEnrollLottiePerStage(int stage) {
+        return switch (stage) {
+            case SFPS_STAGE_NO_ANIMATION -> R.raw.sfps_lottie_no_animation;
+            case SFPS_STAGE_CENTER -> R.raw.sfps_lottie_pad_center;
+            case SFPS_STAGE_FINGERTIP -> R.raw.sfps_lottie_tip;
+            case SFPS_STAGE_LEFT_EDGE -> R.raw.sfps_lottie_left_edge;
+            case SFPS_STAGE_RIGHT_EDGE -> R.raw.sfps_lottie_right_edge;
+            default -> throw new IllegalArgumentException("Invalid stage: " + stage);
+        };
+    }
+
+    @Override
+    public float getEnrollStageThreshold(@NonNull Context context, int index) {
+        if (mFingerprintManager == null) {
+            mFingerprintManager = context.getSystemService(FingerprintManager.class);
+        }
+        return mFingerprintManager.getEnrollStageThreshold(index);
+    }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollFindSensorV2Fragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollFindSensorV2Fragment.kt
index dcdcccf..0afa613 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollFindSensorV2Fragment.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollFindSensorV2Fragment.kt
@@ -34,7 +34,6 @@
 import com.google.android.setupcompat.template.FooterBarMixin
 import com.google.android.setupcompat.template.FooterButton
 import com.google.android.setupdesign.GlifLayout
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.launch
 
 private const val TAG = "FingerprintEnrollFindSensorV2Fragment"
@@ -94,12 +93,12 @@
 
       // Set up lottie or animation
       lifecycleScope.launch {
-        viewModel.showSfpsLottie.collect { (isFolded, rotation) ->
+        viewModel.sfpsLottieInfo.collect { (isFolded, rotation) ->
           setupLottie(view, getSfpsIllustrationLottieAnimation(isFolded, rotation))
         }
       }
       lifecycleScope.launch {
-        viewModel.showUdfpsLottie.collect { isAccessibilityEnabled ->
+        viewModel.udfpsLottieInfo.collect { isAccessibilityEnabled ->
           val lottieAnimation =
             if (isAccessibilityEnabled) R.raw.udfps_edu_a11y_lottie else R.raw.udfps_edu_lottie
           setupLottie(view, lottieAnimation) { viewModel.proceedToEnrolling() }
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollFindSensorViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollFindSensorViewModel.kt
index 17f8132..90aefc8 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollFindSensorViewModel.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrollFindSensorViewModel.kt
@@ -26,13 +26,12 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.combineTransform
+import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.shareIn
-import kotlinx.coroutines.flow.transform
 import kotlinx.coroutines.flow.update
 import kotlinx.coroutines.launch
 
@@ -47,41 +46,43 @@
 ) : ViewModel() {
   /** Represents the stream of sensor type. */
   val sensorType: Flow<FingerprintSensorType> =
-    fingerprintEnrollViewModel.sensorType
-      .filterWhenEducationIsShown()
-      .shareIn(viewModelScope, SharingStarted.WhileSubscribed(), 1)
+    fingerprintEnrollViewModel.sensorType.shareIn(
+      viewModelScope,
+      SharingStarted.WhileSubscribed(),
+      1
+    )
   private val _isUdfps: Flow<Boolean> =
     sensorType.map {
       it == FingerprintSensorType.UDFPS_OPTICAL || it == FingerprintSensorType.UDFPS_ULTRASONIC
     }
   private val _isSfps: Flow<Boolean> = sensorType.map { it == FingerprintSensorType.POWER_BUTTON }
-  private val _isRearSfps: Flow<Boolean> =
-    combineTransform(_isSfps, _isUdfps) { v1, v2 -> !v1 && !v2 }
+  private val _isRearSfps: Flow<Boolean> = sensorType.map { it == FingerprintSensorType.REAR }
 
   /** Represents the stream of showing primary button. */
-  val showPrimaryButton: Flow<Boolean> = _isUdfps.transform { if (it) emit(true) }
+  val showPrimaryButton: Flow<Boolean> = _isUdfps.filter { it }
 
-  /** Represents the stream of showing sfps lottie, Pair(isFolded, rotation). */
-  val showSfpsLottie: Flow<Pair<Boolean, Int>> =
+  private val _showSfpsLottie = _isSfps.filter { it }
+  /** Represents the stream of showing sfps lottie and the information Pair(isFolded, rotation). */
+  val sfpsLottieInfo: Flow<Pair<Boolean, Int>> =
     combineTransform(
-      _isSfps,
+      _showSfpsLottie,
       foldStateViewModel.isFolded,
       orientationStateViewModel.rotation,
-    ) { isSfps, isFolded, rotation ->
-      if (isSfps) emit(Pair(isFolded, rotation))
+    ) { _, isFolded, rotation ->
+      emit(Pair(isFolded, rotation))
     }
 
-  /** Represents the stream of showing udfps lottie. */
-  val showUdfpsLottie: Flow<Boolean> =
-    combineTransform(
-      _isUdfps,
-      accessibilityViewModel.isAccessibilityEnabled,
-    ) { isUdfps, isAccessibilityEnabled ->
-      if (isUdfps) emit(isAccessibilityEnabled)
+  private val _showUdfpsLottie = _isUdfps.filter { it }
+  /** Represents the stream of showing udfps lottie and whether accessibility is enabled. */
+  val udfpsLottieInfo: Flow<Boolean> =
+    _showUdfpsLottie.combine(accessibilityViewModel.isAccessibilityEnabled) {
+      _,
+      isAccessibilityEnabled ->
+      isAccessibilityEnabled
     }
 
   /** Represents the stream of showing rfps animation. */
-  val showRfpsAnimation: Flow<Boolean> = _isRearSfps.transform { if (it) emit(true) }
+  val showRfpsAnimation: Flow<Boolean> = _isRearSfps.filter { it }
 
   private val _showErrorDialog: MutableStateFlow<Pair<Int, Boolean>?> = MutableStateFlow(null)
   /** Represents the stream of showing error dialog. */
@@ -145,16 +146,6 @@
     navigationViewModel.nextStep()
   }
 
-  // TODO: If we decide to remove previous fragment from activity, then we don't need to check
-  // whether education is shown for the flows that are subscribed by
-  // [FingerprintEnrollFindSensorV2Fragment].
-  private fun <T> Flow<T>.filterWhenEducationIsShown() =
-    combineTransform(navigationViewModel.navigationViewModel) { value, navigationViewModel ->
-      if (navigationViewModel.currStep == Education) {
-        emit(value)
-      }
-    }
-
   class FingerprintEnrollFindSensorViewModelFactory(
     private val navigationViewModel: FingerprintEnrollNavigationViewModel,
     private val fingerprintEnrollViewModel: FingerprintEnrollViewModel,
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsDataSyncController.java b/src/com/android/settings/bluetooth/BluetoothDetailsDataSyncController.java
index a8cd85f..81c5689 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsDataSyncController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsDataSyncController.java
@@ -112,6 +112,11 @@
             mPreferenceCategory.addPreference(permSyncPref);
         }
 
+        if (mAssociationId == DUMMY_ASSOCIATION_ID) {
+            permSyncPref.setVisible(false);
+            return;
+        }
+
         boolean visible = false;
         boolean checked = false;
         PermissionSyncRequest request = mCompanionDeviceManager.getPermissionSyncRequest(
diff --git a/src/com/android/settings/connecteddevice/fastpair/FastPairDeviceGroupController.java b/src/com/android/settings/connecteddevice/fastpair/FastPairDeviceGroupController.java
index ac117f1..8918f01 100644
--- a/src/com/android/settings/connecteddevice/fastpair/FastPairDeviceGroupController.java
+++ b/src/com/android/settings/connecteddevice/fastpair/FastPairDeviceGroupController.java
@@ -22,6 +22,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.util.Log;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
@@ -44,7 +45,10 @@
 public class FastPairDeviceGroupController extends BasePreferenceController
         implements PreferenceControllerMixin, DefaultLifecycleObserver, DevicePreferenceCallback {
 
+    private static final String TAG = "FastPairDeviceGroupCtr";
+
     private static final String KEY = "fast_pair_device_list";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     @VisibleForTesting PreferenceGroup mPreferenceGroup;
     private final FastPairDeviceUpdater mFastPairDeviceUpdater;
@@ -68,6 +72,7 @@
             mFastPairDeviceUpdater =
                     fastPairFeatureProvider.getFastPairDeviceUpdater(context, this);
         } else {
+            Log.d(TAG, "Flag disabled. Ignored.");
             mFastPairDeviceUpdater = null;
         }
         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
@@ -78,6 +83,10 @@
     public void onStart(@NonNull LifecycleOwner owner) {
         if (mFastPairDeviceUpdater != null) {
             mFastPairDeviceUpdater.registerCallback();
+        } else {
+            if (DEBUG) {
+                Log.d(TAG, "Callback register: Fast Pair device updater is null. Ignore.");
+            }
         }
         mContext.registerReceiver(mReceiver, mIntentFilter, Context.RECEIVER_EXPORTED_UNAUDITED);
     }
@@ -86,6 +95,10 @@
     public void onStop(@NonNull LifecycleOwner owner) {
         if (mFastPairDeviceUpdater != null) {
             mFastPairDeviceUpdater.unregisterCallback();
+        } else {
+            if (DEBUG) {
+                Log.d(TAG, "Callback unregister: Fast Pair device updater is null. Ignore.");
+            }
         }
         mContext.unregisterReceiver(mReceiver);
     }
@@ -117,14 +130,24 @@
 
     @Override
     public void onDeviceAdded(Preference preference) {
-        if (preference == null) return;
+        if (preference == null) {
+            if (DEBUG) {
+                Log.d(TAG, "onDeviceAdded receives null preference. Ignore.");
+            }
+            return;
+        }
         mPreferenceGroup.addPreference(preference);
         updatePreferenceVisibility();
     }
 
     @Override
     public void onDeviceRemoved(Preference preference) {
-        if (preference == null) return;
+        if (preference == null) {
+            if (DEBUG) {
+                Log.d(TAG, "onDeviceRemoved receives null preference. Ignore.");
+            }
+            return;
+        }
         mPreferenceGroup.removePreference(preference);
         updatePreferenceVisibility();
     }
diff --git a/src/com/android/settings/connecteddevice/fastpair/FastPairDevicePreferenceController.java b/src/com/android/settings/connecteddevice/fastpair/FastPairDevicePreferenceController.java
index 6a5b6b9..76c9d00 100644
--- a/src/com/android/settings/connecteddevice/fastpair/FastPairDevicePreferenceController.java
+++ b/src/com/android/settings/connecteddevice/fastpair/FastPairDevicePreferenceController.java
@@ -81,6 +81,7 @@
             mFastPairDeviceUpdater =
                     fastPairFeatureProvider.getFastPairDeviceUpdater(context, this);
         } else {
+            Log.d(TAG, "Flag disabled. Ignore.");
             mFastPairDeviceUpdater = null;
         }
         mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
@@ -91,6 +92,10 @@
     public void onStart(@NonNull LifecycleOwner owner) {
         if (mFastPairDeviceUpdater != null) {
             mFastPairDeviceUpdater.registerCallback();
+        } else {
+            if (DEBUG) {
+                Log.d(TAG, "Callback register: Fast Pair device updater is null. Ignore.");
+            }
         }
         mContext.registerReceiver(mReceiver, mIntentFilter, Context.RECEIVER_EXPORTED_UNAUDITED);
     }
@@ -99,6 +104,10 @@
     public void onStop(@NonNull LifecycleOwner owner) {
         if (mFastPairDeviceUpdater != null) {
             mFastPairDeviceUpdater.unregisterCallback();
+        } else {
+            if (DEBUG) {
+                Log.d(TAG, "Callback unregister: Fast Pair device updater is null. Ignore.");
+            }
         }
         mContext.unregisterReceiver(mReceiver);
     }
diff --git a/src/com/android/settings/datausage/ChartDataUsagePreferenceController.kt b/src/com/android/settings/datausage/ChartDataUsagePreferenceController.kt
index 49235b5..5149af0 100644
--- a/src/com/android/settings/datausage/ChartDataUsagePreferenceController.kt
+++ b/src/com/android/settings/datausage/ChartDataUsagePreferenceController.kt
@@ -26,6 +26,7 @@
 import androidx.preference.PreferenceScreen
 import com.android.settings.core.BasePreferenceController
 import com.android.settings.datausage.lib.INetworkCycleDataRepository
+import com.android.settings.datausage.lib.NetworkCycleChartData
 import com.android.settings.datausage.lib.NetworkCycleDataRepository
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
@@ -72,6 +73,7 @@
 
     fun update(startTime: Long, endTime: Long) {
         preference.setTime(startTime, endTime)
+        preference.setNetworkCycleData(NetworkCycleChartData.AllZero)
         lifecycleScope.launch {
             val chartData = withContext(Dispatchers.Default) {
                 repository.queryChartData(startTime, endTime)
diff --git a/src/com/android/settings/datausage/lib/AppDataUsageDetailsRepository.kt b/src/com/android/settings/datausage/lib/AppDataUsageDetailsRepository.kt
index 94801ef..cd3372f 100644
--- a/src/com/android/settings/datausage/lib/AppDataUsageDetailsRepository.kt
+++ b/src/com/android/settings/datausage/lib/AppDataUsageDetailsRepository.kt
@@ -20,11 +20,8 @@
 import android.content.Context
 import android.net.NetworkTemplate
 import android.util.Range
-import androidx.annotation.VisibleForTesting
 import com.android.settings.datausage.lib.AppDataUsageRepository.Companion.withSdkSandboxUids
-import kotlinx.coroutines.async
-import kotlinx.coroutines.awaitAll
-import kotlinx.coroutines.coroutineScope
+import com.android.settingslib.spa.framework.util.asyncMap
 
 interface IAppDataUsageDetailsRepository {
     suspend fun queryDetailsForCycles(): List<NetworkUsageDetailsData>
@@ -37,32 +34,24 @@
     uids: List<Int>,
     private val networkCycleDataRepository: INetworkCycleDataRepository =
         NetworkCycleDataRepository(context, template),
+    private val networkStatsRepository: NetworkStatsRepository =
+        NetworkStatsRepository(context, template),
 ) : IAppDataUsageDetailsRepository {
     private val withSdkSandboxUids = withSdkSandboxUids(uids)
-    private val networkStatsRepository = NetworkStatsRepository(context, template)
 
-    override suspend fun queryDetailsForCycles(): List<NetworkUsageDetailsData> = coroutineScope {
-        getCycles().map {
-            async {
-                queryDetails(it)
-            }
-        }.awaitAll().filter { it.totalUsage > 0 }
-    }
+    override suspend fun queryDetailsForCycles(): List<NetworkUsageDetailsData> =
+        getCycles().asyncMap { queryDetails(it) }.filter { it.totalUsage > 0 }
 
     private fun getCycles(): List<Range<Long>> =
         cycles?.zipWithNext { endTime, startTime -> Range(startTime, endTime) }
             ?: networkCycleDataRepository.getCycles()
 
     private fun queryDetails(range: Range<Long>): NetworkUsageDetailsData {
-        var totalUsage = 0L
-        var foregroundUsage = 0L
-        for (uid in withSdkSandboxUids) {
-            val usage = getUsage(range, uid, NetworkStats.Bucket.STATE_ALL)
-            if (usage > 0L) {
-                totalUsage += usage
-                foregroundUsage += getUsage(range, uid, NetworkStats.Bucket.STATE_FOREGROUND)
-            }
-        }
+        val buckets = networkStatsRepository.queryBuckets(range.lower, range.upper)
+            .filter { it.uid in withSdkSandboxUids }
+        val totalUsage = buckets.sumOf { it.bytes }
+        val foregroundUsage =
+            buckets.filter { it.state == NetworkStats.Bucket.STATE_FOREGROUND }.sumOf { it.bytes }
         return NetworkUsageDetailsData(
             range = range,
             totalUsage = totalUsage,
@@ -70,8 +59,4 @@
             backgroundUsage = totalUsage - foregroundUsage,
         )
     }
-
-    @VisibleForTesting
-    fun getUsage(range: Range<Long>, uid: Int, state: Int): Long =
-        networkStatsRepository.queryAggregateForUid(range, uid, state)?.usage ?: 0
 }
diff --git a/src/com/android/settings/datausage/lib/AppDataUsageRepository.kt b/src/com/android/settings/datausage/lib/AppDataUsageRepository.kt
index 930737f..1844a7a 100644
--- a/src/com/android/settings/datausage/lib/AppDataUsageRepository.kt
+++ b/src/com/android/settings/datausage/lib/AppDataUsageRepository.kt
@@ -41,7 +41,7 @@
     private val networkStatsRepository = NetworkStatsRepository(context, template)
 
     fun getAppPercent(carrierId: Int?, startTime: Long, endTime: Long): List<Pair<AppItem, Int>> {
-        val buckets = networkStatsRepository.querySummary(startTime, endTime)
+        val buckets = networkStatsRepository.queryBuckets(startTime, endTime)
         return getAppPercent(carrierId, buckets)
     }
 
diff --git a/src/com/android/settings/datausage/lib/AppDataUsageSummaryRepository.kt b/src/com/android/settings/datausage/lib/AppDataUsageSummaryRepository.kt
index 5579de0..b723e27 100644
--- a/src/com/android/settings/datausage/lib/AppDataUsageSummaryRepository.kt
+++ b/src/com/android/settings/datausage/lib/AppDataUsageSummaryRepository.kt
@@ -20,9 +20,7 @@
 import android.net.NetworkTemplate
 import com.android.settings.datausage.lib.AppDataUsageRepository.Companion.withSdkSandboxUids
 import com.android.settings.datausage.lib.NetworkStatsRepository.Companion.AllTimeRange
-import kotlinx.coroutines.async
-import kotlinx.coroutines.awaitAll
-import kotlinx.coroutines.coroutineScope
+import com.android.settingslib.spa.framework.util.asyncMap
 
 interface IAppDataUsageSummaryRepository {
     suspend fun querySummary(uid: Int): NetworkUsageData?
@@ -35,11 +33,8 @@
         NetworkStatsRepository(context, template),
 ) : IAppDataUsageSummaryRepository {
 
-    override suspend fun querySummary(uid: Int): NetworkUsageData? = coroutineScope {
-        withSdkSandboxUids(listOf(uid)).map { uid ->
-            async {
-                networkStatsRepository.queryAggregateForUid(range = AllTimeRange, uid = uid)
-            }
-        }.awaitAll().filterNotNull().aggregate()
-    }
+    override suspend fun querySummary(uid: Int): NetworkUsageData? =
+        withSdkSandboxUids(listOf(uid)).asyncMap {
+            networkStatsRepository.queryAggregateForUid(range = AllTimeRange, uid = it)
+        }.filterNotNull().aggregate()
 }
diff --git a/src/com/android/settings/datausage/lib/NetworkCycleChartData.kt b/src/com/android/settings/datausage/lib/NetworkCycleChartData.kt
index fd3c504..4e73190 100644
--- a/src/com/android/settings/datausage/lib/NetworkCycleChartData.kt
+++ b/src/com/android/settings/datausage/lib/NetworkCycleChartData.kt
@@ -26,6 +26,11 @@
     val dailyUsage: List<NetworkUsageData>,
 ) {
     companion object {
+        val AllZero = NetworkCycleChartData(
+            total = NetworkUsageData.AllZero,
+            dailyUsage = emptyList(),
+        )
+
         val BUCKET_DURATION = 1.days
     }
 }
diff --git a/src/com/android/settings/datausage/lib/NetworkStatsRepository.kt b/src/com/android/settings/datausage/lib/NetworkStatsRepository.kt
index 22f9dd0..f2e18f2 100644
--- a/src/com/android/settings/datausage/lib/NetworkStatsRepository.kt
+++ b/src/com/android/settings/datausage/lib/NetworkStatsRepository.kt
@@ -54,7 +54,7 @@
         0
     }
 
-    fun querySummary(startTime: Long, endTime: Long): List<Bucket> = try {
+    fun queryBuckets(startTime: Long, endTime: Long): List<Bucket> = try {
         networkStatsManager.querySummary(template, startTime, endTime).convertToBuckets()
     } catch (e: Exception) {
         Log.e(TAG, "Exception querySummary", e)
@@ -69,13 +69,14 @@
         data class Bucket(
             val uid: Int,
             val bytes: Long,
+            val state: Int = NetworkStats.Bucket.STATE_ALL,
         )
 
         private fun NetworkStats.convertToBuckets(): List<Bucket> = use {
             val buckets = mutableListOf<Bucket>()
             val bucket = NetworkStats.Bucket()
             while (getNextBucket(bucket)) {
-                buckets += Bucket(uid = bucket.uid, bytes = bucket.bytes)
+                buckets += Bucket(uid = bucket.uid, bytes = bucket.bytes, state = bucket.state)
             }
             buckets
         }
diff --git a/src/com/android/settings/datausage/lib/NetworkUsageData.kt b/src/com/android/settings/datausage/lib/NetworkUsageData.kt
index 93cde5f..f9d83d5 100644
--- a/src/com/android/settings/datausage/lib/NetworkUsageData.kt
+++ b/src/com/android/settings/datausage/lib/NetworkUsageData.kt
@@ -43,8 +43,14 @@
     fun getDataUsedString(context: Context): String =
         context.getString(R.string.data_used_template, formatUsage(context))
 
-    private companion object {
-        const val DATE_FORMAT = DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_ABBREV_MONTH
+    companion object {
+        val AllZero = NetworkUsageData(
+            startTime = 0L,
+            endTime = 0L,
+            usage = 0L,
+        )
+
+        private const val DATE_FORMAT = DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_ABBREV_MONTH
     }
 }
 
diff --git a/src/com/android/settings/deviceinfo/StorageWizardMigrateConfirm.java b/src/com/android/settings/deviceinfo/StorageWizardMigrateConfirm.java
index ce17418..a6dd732 100644
--- a/src/com/android/settings/deviceinfo/StorageWizardMigrateConfirm.java
+++ b/src/com/android/settings/deviceinfo/StorageWizardMigrateConfirm.java
@@ -30,6 +30,7 @@
 import android.view.View;
 import android.widget.Toast;
 
+import com.android.internal.widget.LockPatternUtils;
 import com.android.settings.R;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.password.ChooseLockSettingsHelper;
@@ -97,10 +98,19 @@
     @Override
     public void onNavigateNext(View view) {
         // Ensure that all users are unlocked so that we can move their data
+        final LockPatternUtils lpu = new LockPatternUtils(this);
         if (StorageManager.isFileEncrypted()) {
             for (UserInfo user : getSystemService(UserManager.class).getUsers()) {
-                if (!StorageManager.isUserKeyUnlocked(user.id)) {
-                    Log.d(TAG, "User " + user.id + " is currently locked; requesting unlock");
+                if (StorageManager.isUserKeyUnlocked(user.id)) {
+                    continue;
+                }
+                if (!lpu.isSecure(user.id)) {
+                    Log.d(TAG, "Unsecured user " + user.id + " is currently locked; attempting "
+                            + "automatic unlock");
+                    lpu.unlockUserKeyIfUnsecured(user.id);
+                } else {
+                    Log.d(TAG, "Secured user " + user.id + " is currently locked; requesting "
+                            + "manual unlock");
                     final CharSequence description = TextUtils.expandTemplate(
                             getText(R.string.storage_wizard_move_unlock), user.name);
                     final ChooseLockSettingsHelper.Builder builder =
diff --git a/src/com/android/settings/deviceinfo/StorageWizardMoveConfirm.java b/src/com/android/settings/deviceinfo/StorageWizardMoveConfirm.java
index da96104..bf16ab0 100644
--- a/src/com/android/settings/deviceinfo/StorageWizardMoveConfirm.java
+++ b/src/com/android/settings/deviceinfo/StorageWizardMoveConfirm.java
@@ -33,6 +33,7 @@
 import android.view.View;
 
 import com.android.internal.util.Preconditions;
+import com.android.internal.widget.LockPatternUtils;
 import com.android.settings.R;
 import com.android.settings.password.ChooseLockSettingsHelper;
 
@@ -79,10 +80,19 @@
     @Override
     public void onNavigateNext(View view) {
         // Ensure that all users are unlocked so that we can move their data
+        final LockPatternUtils lpu = new LockPatternUtils(this);
         if (StorageManager.isFileEncrypted()) {
             for (UserInfo user : getSystemService(UserManager.class).getUsers()) {
-                if (!StorageManager.isUserKeyUnlocked(user.id)) {
-                    Log.d(TAG, "User " + user.id + " is currently locked; requesting unlock");
+                if (StorageManager.isUserKeyUnlocked(user.id)) {
+                    continue;
+                }
+                if (!lpu.isSecure(user.id)) {
+                    Log.d(TAG, "Unsecured user " + user.id + " is currently locked; attempting "
+                            + "automatic unlock");
+                    lpu.unlockUserKeyIfUnsecured(user.id);
+                } else {
+                    Log.d(TAG, "Secured user " + user.id + " is currently locked; requesting "
+                            + "manual unlock");
                     final CharSequence description = TextUtils.expandTemplate(
                             getText(R.string.storage_wizard_move_unlock), user.name);
                     final ChooseLockSettingsHelper.Builder builder =
diff --git a/src/com/android/settings/display/BrightnessLevelPreferenceController.java b/src/com/android/settings/display/BrightnessLevelPreferenceController.java
index b6a0ca3..ac1a1cb 100644
--- a/src/com/android/settings/display/BrightnessLevelPreferenceController.java
+++ b/src/com/android/settings/display/BrightnessLevelPreferenceController.java
@@ -14,6 +14,7 @@
 package com.android.settings.display;
 
 import static android.content.Intent.ACTION_SHOW_BRIGHTNESS_DIALOG;
+import static android.content.Intent.EXTRA_BRIGHTNESS_DIALOG_IS_FULL_WIDTH;
 
 import static com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MAX;
 import static com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MIN;
@@ -53,7 +54,6 @@
     private static final String TAG = "BrightnessPrefCtrl";
     private static final String KEY_BRIGHTNESS = "brightness";
     private static final Uri BRIGHTNESS_ADJ_URI;
-
     private final ContentResolver mContentResolver;
     private final Handler mHandler = new Handler(Looper.getMainLooper());
     private final DisplayManager mDisplayManager;
@@ -141,6 +141,7 @@
         final Intent intent = new Intent(ACTION_SHOW_BRIGHTNESS_DIALOG);
         intent.putExtra(SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE,
                 SettingsTransitionHelper.TransitionType.TRANSITION_NONE);
+        intent.putExtra(EXTRA_BRIGHTNESS_DIALOG_IS_FULL_WIDTH, true);
 
         // Start activity in the same task and pass fade animations
         final ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext,
diff --git a/src/com/android/settings/fuelgauge/BatteryUtils.java b/src/com/android/settings/fuelgauge/BatteryUtils.java
index 171c76b..3b958ae 100644
--- a/src/com/android/settings/fuelgauge/BatteryUtils.java
+++ b/src/com/android/settings/fuelgauge/BatteryUtils.java
@@ -74,6 +74,7 @@
  * Utils for battery operation
  */
 public class BatteryUtils {
+    public static final int UID_ZERO = 0;
     public static final int UID_NULL = -1;
     public static final int SDK_NULL = -1;
     /** Special UID value for data usage by removed apps. */
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntry.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntry.java
index 971ada9..9d7b629 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntry.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntry.java
@@ -283,7 +283,9 @@
     /** Whether the current BatteryDiffEntry is uninstalled app or not. */
     public boolean isUninstalledEntry() {
         final String packageName = getPackageName();
-        if (TextUtils.isEmpty(packageName) || isSystemEntry()) {
+        if (TextUtils.isEmpty(packageName) || isSystemEntry()
+                // Some special package UIDs could be 0. Those packages are not installed by users.
+                || mUid == BatteryUtils.UID_ZERO) {
             return false;
         }
 
diff --git a/src/com/android/settings/network/BluetoothWiFiResetPreferenceController.java b/src/com/android/settings/network/BluetoothWiFiResetPreferenceController.java
deleted file mode 100644
index f0f5d73..0000000
--- a/src/com/android/settings/network/BluetoothWiFiResetPreferenceController.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.network;
-
-import android.app.ProgressDialog;
-import android.app.settings.SettingsEnums;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.os.Looper;
-import android.text.TextUtils;
-import android.util.Log;
-import android.widget.Toast;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.appcompat.app.AlertDialog;
-import androidx.preference.Preference;
-
-import com.android.settings.R;
-import com.android.settings.ResetNetworkRequest;
-import com.android.settings.core.BasePreferenceController;
-import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * This is to show a preference regarding resetting Bluetooth and Wi-Fi.
- */
-public class BluetoothWiFiResetPreferenceController extends BasePreferenceController
-        implements DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
-
-    private static final String TAG = "BtWiFiResetPreferenceController";
-
-    private final NetworkResetRestrictionChecker mRestrictionChecker;
-
-    private DialogInterface mResetDialog;
-    private ProgressDialog mProgressDialog;
-    private ExecutorService mExecutorService;
-
-    /**
-     * Constructer.
-     * @param context Context
-     * @param preferenceKey is the key for Preference
-     */
-    public BluetoothWiFiResetPreferenceController(Context context, String preferenceKey) {
-        super(context, preferenceKey);
-
-        // restriction check
-        mRestrictionChecker = new NetworkResetRestrictionChecker(context);
-    }
-
-    @Override
-    public int getAvailabilityStatus() {
-        return mRestrictionChecker.hasUserRestriction() ?
-                CONDITIONALLY_UNAVAILABLE : AVAILABLE;
-    }
-
-    @Override
-    public boolean handlePreferenceTreeClick(Preference preference) {
-        if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) {
-            return false;
-        }
-        buildResetDialog(preference);
-        return true;
-    }
-
-    /**
-     * This is a pop-up dialog showing detail of this reset option.
-     */
-    void buildResetDialog(Preference preference) {
-        if (mResetDialog != null) {
-            return;
-        }
-        mResetDialog = new AlertDialog.Builder(mContext)
-                .setTitle(R.string.reset_bluetooth_wifi_title)
-                .setMessage(R.string.reset_bluetooth_wifi_desc)
-                .setPositiveButton(R.string.reset_bluetooth_wifi_button_text, this)
-                .setNegativeButton(R.string.cancel, null /* OnClickListener */)
-                .setOnDismissListener(this)
-                .show();
-    }
-
-    public void onDismiss(DialogInterface dialog) {
-        if (mResetDialog == dialog) {
-            mResetDialog = null;
-        }
-    }
-
-    /**
-     * User pressed confirmation button, for starting reset operation.
-     */
-    public void onClick(DialogInterface dialog, int which) {
-        if (mResetDialog != dialog) {
-            return;
-        }
-
-        // User confirm the reset operation
-        MetricsFeatureProvider provider = FeatureFactory.getFeatureFactory()
-                .getMetricsFeatureProvider();
-        provider.action(mContext, SettingsEnums.RESET_BLUETOOTH_WIFI_CONFIRM, true);
-
-        // Non-cancelable progress dialog
-        mProgressDialog = getProgressDialog(mContext);
-        mProgressDialog.show();
-
-        // Run reset in background thread
-        mExecutorService = Executors.newSingleThreadExecutor();
-        mExecutorService.execute(() -> {
-            final AtomicReference<Exception> exceptionDuringReset =
-                    new AtomicReference<Exception>();
-            try {
-                resetOperation().run();
-            } catch (Exception exception) {
-                exceptionDuringReset.set(exception);
-            }
-            mContext.getMainExecutor().execute(() -> endOfReset(exceptionDuringReset.get()));
-        });
-    }
-
-    @VisibleForTesting
-    protected ProgressDialog getProgressDialog(Context context) {
-        final ProgressDialog progressDialog = new ProgressDialog(context);
-        progressDialog.setIndeterminate(true);
-        progressDialog.setCancelable(false);
-        progressDialog.setMessage(
-                context.getString(R.string.main_clear_progress_text));
-        return progressDialog;
-    }
-
-    @VisibleForTesting
-    protected Runnable resetOperation() throws Exception {
-        if (SubscriptionUtil.isSimHardwareVisible(mContext)) {
-            return new ResetNetworkRequest(
-                    ResetNetworkRequest.RESET_WIFI_MANAGER |
-                    ResetNetworkRequest.RESET_WIFI_P2P_MANAGER |
-                    ResetNetworkRequest.RESET_BLUETOOTH_MANAGER)
-                .toResetNetworkOperationBuilder(mContext, Looper.getMainLooper())
-                .build();
-        }
-
-        /**
-         * For device without SIMs visible to the user
-         */
-        return new ResetNetworkRequest(
-                ResetNetworkRequest.RESET_CONNECTIVITY_MANAGER |
-                ResetNetworkRequest.RESET_VPN_MANAGER |
-                ResetNetworkRequest.RESET_WIFI_MANAGER |
-                ResetNetworkRequest.RESET_WIFI_P2P_MANAGER |
-                ResetNetworkRequest.RESET_BLUETOOTH_MANAGER)
-            .toResetNetworkOperationBuilder(mContext, Looper.getMainLooper())
-            .resetTelephonyAndNetworkPolicyManager(ResetNetworkRequest.ALL_SUBSCRIPTION_ID)
-            .build();
-    }
-
-    @VisibleForTesting
-    protected void endOfReset(Exception exceptionDuringReset) {
-        if (mExecutorService != null) {
-            mExecutorService.shutdown();
-            mExecutorService = null;
-        }
-        if (mProgressDialog != null) {
-            mProgressDialog.dismiss();
-            mProgressDialog = null;
-        }
-        if (exceptionDuringReset == null) {
-            Toast.makeText(mContext, R.string.reset_bluetooth_wifi_complete_toast,
-                    Toast.LENGTH_SHORT).show();
-        } else {
-            Log.e(TAG, "Exception during reset", exceptionDuringReset);
-        }
-    }
-}
diff --git a/src/com/android/settings/network/BluetoothWiFiResetPreferenceController.kt b/src/com/android/settings/network/BluetoothWiFiResetPreferenceController.kt
new file mode 100644
index 0000000..2047ed9
--- /dev/null
+++ b/src/com/android/settings/network/BluetoothWiFiResetPreferenceController.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network
+
+import android.app.settings.SettingsEnums
+import android.content.Context
+import android.os.Looper
+import android.os.UserManager
+import android.util.Log
+import android.widget.Toast
+import androidx.annotation.VisibleForTesting
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.res.stringResource
+import com.android.settings.R
+import com.android.settings.ResetNetworkRequest
+import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
+import com.android.settings.spa.preference.ComposePreferenceController
+import com.android.settingslib.spa.widget.dialog.AlertDialogButton
+import com.android.settingslib.spa.widget.dialog.rememberAlertDialogPresenter
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
+import com.android.settingslib.spaprivileged.template.preference.RestrictedPreference
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * This is to show a preference regarding resetting Bluetooth and Wi-Fi.
+ */
+class BluetoothWiFiResetPreferenceController(context: Context, preferenceKey: String) :
+    ComposePreferenceController(context, preferenceKey) {
+
+    private val restrictionChecker = NetworkResetRestrictionChecker(context)
+
+    override fun getAvailabilityStatus() =
+        if (restrictionChecker.hasUserRestriction()) CONDITIONALLY_UNAVAILABLE else AVAILABLE
+
+    @Composable
+    override fun Content() {
+        val coroutineScope = rememberCoroutineScope()
+        val dialogPresenter = rememberAlertDialogPresenter(
+            confirmButton = AlertDialogButton(
+                text = stringResource(R.string.reset_bluetooth_wifi_button_text),
+            ) { reset(coroutineScope) },
+            dismissButton = AlertDialogButton(text = stringResource(R.string.cancel)),
+            title = stringResource(R.string.reset_bluetooth_wifi_title),
+        ) {
+            Text(stringResource(R.string.reset_bluetooth_wifi_desc))
+        }
+
+        RestrictedPreference(
+            model = object : PreferenceModel {
+                override val title = stringResource(R.string.reset_bluetooth_wifi_title)
+                override val onClick = dialogPresenter::open
+            },
+            restrictions = Restrictions(keys = listOf(UserManager.DISALLOW_NETWORK_RESET)),
+        )
+    }
+
+    /**
+     * User pressed confirmation button, for starting reset operation.
+     */
+    private fun reset(coroutineScope: CoroutineScope) {
+        // User confirm the reset operation
+        featureFactory.metricsFeatureProvider
+            .action(mContext, SettingsEnums.RESET_BLUETOOTH_WIFI_CONFIRM, true)
+
+        // Run reset in background thread
+        coroutineScope.launch {
+            try {
+                withContext(Dispatchers.Default) {
+                    resetOperation().run()
+                }
+            } catch (e: Exception) {
+                Log.e(TAG, "Exception during reset", e)
+                return@launch
+            }
+            Toast.makeText(
+                mContext,
+                R.string.reset_bluetooth_wifi_complete_toast,
+                Toast.LENGTH_SHORT,
+            ).show()
+        }
+    }
+
+    @VisibleForTesting
+    fun resetOperation(): Runnable = if (SubscriptionUtil.isSimHardwareVisible(mContext)) {
+        ResetNetworkRequest(
+            ResetNetworkRequest.RESET_WIFI_MANAGER or
+                ResetNetworkRequest.RESET_WIFI_P2P_MANAGER or
+                ResetNetworkRequest.RESET_BLUETOOTH_MANAGER
+        )
+            .toResetNetworkOperationBuilder(mContext, Looper.getMainLooper())
+    } else {  // For device without SIMs visible to the user
+        ResetNetworkRequest(
+            ResetNetworkRequest.RESET_CONNECTIVITY_MANAGER or
+                ResetNetworkRequest.RESET_VPN_MANAGER or
+                ResetNetworkRequest.RESET_WIFI_MANAGER or
+                ResetNetworkRequest.RESET_WIFI_P2P_MANAGER or
+                ResetNetworkRequest.RESET_BLUETOOTH_MANAGER
+        )
+            .toResetNetworkOperationBuilder(mContext, Looper.getMainLooper())
+            .resetTelephonyAndNetworkPolicyManager(ResetNetworkRequest.ALL_SUBSCRIPTION_ID)
+    }.build()
+
+    private companion object {
+        private const val TAG = "BluetoothWiFiResetPref"
+    }
+}
diff --git a/src/com/android/settings/network/EraseEuiccDataController.java b/src/com/android/settings/network/EraseEuiccDataController.java
index 3dc3ab5..9892f0d 100644
--- a/src/com/android/settings/network/EraseEuiccDataController.java
+++ b/src/com/android/settings/network/EraseEuiccDataController.java
@@ -18,10 +18,12 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.os.UserManager;
 import android.text.TextUtils;
 
 import androidx.preference.Preference;
 
+import com.android.settings.Utils;
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.network.telephony.MobileNetworkUtils;
@@ -33,8 +35,11 @@
 public class EraseEuiccDataController extends BasePreferenceController {
     private ResetDashboardFragment mHostFragment;
 
+    private final UserManager mUm;
+
     public EraseEuiccDataController(Context context, String preferenceKey) {
         super(context, preferenceKey);
+        mUm = context.getSystemService(UserManager.class);
     }
 
     public void setFragment(ResetDashboardFragment hostFragment) {
@@ -52,10 +57,12 @@
 
     @Override
     public int getAvailabilityStatus() {
-        return SubscriptionUtil.isSimHardwareVisible(mContext) &&
-                (!MobileNetworkUtils.isMobileNetworkUserRestricted(mContext)) &&
-                mContext.getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_TELEPHONY_EUICC) ? AVAILABLE_UNSEARCHABLE
-                : UNSUPPORTED_ON_DEVICE;
+        boolean isAllowedUser = (mUm.isAdminUser() || Utils.isDemoUser(mContext))
+                && !MobileNetworkUtils.isMobileNetworkUserRestricted(mContext);
+        boolean hasEuiccFeature = mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_TELEPHONY_EUICC);
+        return SubscriptionUtil.isSimHardwareVisible(mContext)
+                && isAllowedUser
+                && hasEuiccFeature ? AVAILABLE_UNSEARCHABLE : UNSUPPORTED_ON_DEVICE;
     }
 }
diff --git a/src/com/android/settings/network/MobileNetworkListFragment.java b/src/com/android/settings/network/MobileNetworkListFragment.java
index d7d241a..3de05af 100644
--- a/src/com/android/settings/network/MobileNetworkListFragment.java
+++ b/src/com/android/settings/network/MobileNetworkListFragment.java
@@ -19,26 +19,19 @@
 import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.os.UserManager;
-import android.provider.SearchIndexableResource;
 
-import androidx.annotation.VisibleForTesting;
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.settings.R;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.network.telephony.MobileNetworkUtils;
 import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.search.SearchIndexable;
 
-import java.util.ArrayList;
-import java.util.List;
-
 @SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
 public class MobileNetworkListFragment extends DashboardFragment {
     private static final String LOG_TAG = "NetworkListFragment";
 
-    static final String KEY_PREFERENCE_CATEGORY_SIM = "provider_model_sim_category";
     private static final String KEY_ADD_SIM = "add_sim";
 
     @Override
@@ -68,34 +61,8 @@
         return SettingsEnums.MOBILE_NETWORK_LIST;
     }
 
-    @Override
-    protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
-        final List<AbstractPreferenceController> controllers = new ArrayList<>();
-        if (!SubscriptionUtil.isSimHardwareVisible(getContext())) {
-            finish();
-            return controllers;
-        }
-
-        NetworkProviderSimsCategoryController simCategoryPrefCtrl =
-                new NetworkProviderSimsCategoryController(context, KEY_PREFERENCE_CATEGORY_SIM,
-                        getSettingsLifecycle(), this);
-        controllers.add(simCategoryPrefCtrl);
-
-        return controllers;
-    }
-
     public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
-            new BaseSearchIndexProvider() {
-
-                @Override
-                public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
-                        boolean enabled) {
-                    final ArrayList<SearchIndexableResource> result = new ArrayList<>();
-                    final SearchIndexableResource sir = new SearchIndexableResource(context);
-                    sir.xmlResId = R.xml.network_provider_sims_list;
-                    result.add(sir);
-                    return result;
-                }
+            new BaseSearchIndexProvider(R.xml.network_provider_sims_list) {
 
                 @Override
                 protected boolean isPageSearchEnabled(Context context) {
diff --git a/src/com/android/settings/network/NetworkProviderSimListController.java b/src/com/android/settings/network/NetworkProviderSimListController.java
index 89cb73c..02b1980 100644
--- a/src/com/android/settings/network/NetworkProviderSimListController.java
+++ b/src/com/android/settings/network/NetworkProviderSimListController.java
@@ -16,69 +16,60 @@
 
 package com.android.settings.network;
 
-import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE;
-import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
-
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.telephony.SubscriptionManager;
 import android.util.ArrayMap;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
+import androidx.lifecycle.DefaultLifecycleObserver;
 import androidx.lifecycle.LifecycleOwner;
-import androidx.lifecycle.OnLifecycleEvent;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceCategory;
 import androidx.preference.PreferenceScreen;
 
 import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
 import com.android.settings.network.telephony.MobileNetworkUtils;
 import com.android.settingslib.RestrictedPreference;
-import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
-public class NetworkProviderSimListController extends AbstractPreferenceController implements
-        LifecycleObserver, MobileNetworkRepository.MobileNetworkCallback,
+public class NetworkProviderSimListController extends BasePreferenceController implements
+        DefaultLifecycleObserver, MobileNetworkRepository.MobileNetworkCallback,
         DefaultSubscriptionReceiver.DefaultSubscriptionListener {
-    private static final String TAG = "NetworkProviderSimListCtrl";
-    private static final String KEY_PREFERENCE_CATEGORY_SIM = "provider_model_sim_category";
-    private static final String KEY_PREFERENCE_SIM = "provider_model_sim_list";
 
-    private SubscriptionManager mSubscriptionManager;
+    private final SubscriptionManager mSubscriptionManager;
+    @Nullable
     private PreferenceCategory mPreferenceCategory;
     private Map<Integer, RestrictedPreference> mPreferences;
-    private LifecycleOwner mLifecycleOwner;
-    private MobileNetworkRepository mMobileNetworkRepository;
+    private final MobileNetworkRepository mMobileNetworkRepository;
     private List<SubscriptionInfoEntity> mSubInfoEntityList = new ArrayList<>();
-    private DefaultSubscriptionReceiver mDataSubscriptionChangedReceiver;
+    private final DefaultSubscriptionReceiver mDataSubscriptionChangedReceiver;
 
-    public NetworkProviderSimListController(Context context, Lifecycle lifecycle,
-            LifecycleOwner lifecycleOwner) {
-        super(context);
+    public NetworkProviderSimListController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
         mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
         mPreferences = new ArrayMap<>();
-        mLifecycleOwner = lifecycleOwner;
         mMobileNetworkRepository = MobileNetworkRepository.getInstance(context);
         mDataSubscriptionChangedReceiver = new DefaultSubscriptionReceiver(context, this);
-        lifecycle.addObserver(this);
     }
 
-    @OnLifecycleEvent(ON_RESUME)
-    public void onResume() {
-        mMobileNetworkRepository.addRegister(mLifecycleOwner, this,
+    @Override
+    public void onResume(@NonNull LifecycleOwner owner) {
+        mMobileNetworkRepository.addRegister(owner, this,
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
         mMobileNetworkRepository.updateEntity();
         mDataSubscriptionChangedReceiver.registerReceiver();
     }
 
-    @OnLifecycleEvent(ON_PAUSE)
-    public void onPause() {
+    @Override
+    public void onPause(@NonNull LifecycleOwner owner) {
         mMobileNetworkRepository.removeRegister(this);
         mDataSubscriptionChangedReceiver.unRegisterReceiver();
     }
@@ -86,7 +77,7 @@
     @Override
     public void displayPreference(PreferenceScreen screen) {
         super.displayPreference(screen);
-        mPreferenceCategory = screen.findPreference(KEY_PREFERENCE_CATEGORY_SIM);
+        mPreferenceCategory = screen.findPreference(getPreferenceKey());
         update();
     }
 
@@ -154,31 +145,22 @@
     }
 
     @Override
-    public boolean isAvailable() {
-        if (!getAvailablePhysicalSubscriptions().isEmpty()) {
-            return true;
-        }
-        return false;
+    public int getAvailabilityStatus() {
+        return getAvailablePhysicalSubscriptions().isEmpty()
+                ? CONDITIONALLY_UNAVAILABLE : AVAILABLE;
     }
 
     @VisibleForTesting
     protected List<SubscriptionInfoEntity> getAvailablePhysicalSubscriptions() {
-        List<SubscriptionInfoEntity> subList = new ArrayList<>();
-        for (SubscriptionInfoEntity info : mSubInfoEntityList) {
-            subList.add(info);
-        }
-        return subList;
-    }
-
-    @Override
-    public String getPreferenceKey() {
-        return KEY_PREFERENCE_SIM;
+        return new ArrayList<>(mSubInfoEntityList);
     }
 
     @Override
     public void onAvailableSubInfoChanged(List<SubscriptionInfoEntity> subInfoEntityList) {
         mSubInfoEntityList = subInfoEntityList;
-        mPreferenceCategory.setVisible(isAvailable());
+        if (mPreferenceCategory != null) {
+            mPreferenceCategory.setVisible(isAvailable());
+        }
         update();
     }
 
diff --git a/src/com/android/settings/network/NetworkProviderSimsCategoryController.java b/src/com/android/settings/network/NetworkProviderSimsCategoryController.java
deleted file mode 100644
index f983e62..0000000
--- a/src/com/android/settings/network/NetworkProviderSimsCategoryController.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.network;
-
-import android.content.Context;
-import android.util.Log;
-
-import androidx.lifecycle.LifecycleOwner;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceCategory;
-import androidx.preference.PreferenceScreen;
-
-import com.android.settings.R;
-import com.android.settings.widget.PreferenceCategoryController;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-import com.android.settingslib.core.lifecycle.LifecycleObserver;
-
-public class NetworkProviderSimsCategoryController extends PreferenceCategoryController implements
-        LifecycleObserver {
-    private static final String LOG_TAG = "NetworkProviderSimsCategoryController";
-    private static final String KEY_PREFERENCE_CATEGORY_SIM = "provider_model_sim_category";
-    private NetworkProviderSimListController mNetworkProviderSimListController;
-    private PreferenceCategory mPreferenceCategory;
-
-    public NetworkProviderSimsCategoryController(Context context, String key, Lifecycle lifecycle,
-            LifecycleOwner lifecycleOwner) {
-        super(context, key);
-        mNetworkProviderSimListController =
-                new NetworkProviderSimListController(mContext, lifecycle, lifecycleOwner);
-    }
-
-    @Override
-    public int getAvailabilityStatus() {
-        if (mNetworkProviderSimListController == null
-                || !mNetworkProviderSimListController.isAvailable()) {
-            return CONDITIONALLY_UNAVAILABLE;
-        } else {
-            return AVAILABLE;
-        }
-    }
-
-    @Override
-    public void displayPreference(PreferenceScreen screen) {
-        super.displayPreference(screen);
-        mNetworkProviderSimListController.displayPreference(screen);
-        mPreferenceCategory = screen.findPreference(KEY_PREFERENCE_CATEGORY_SIM);
-        if (mPreferenceCategory == null) {
-            Log.d(LOG_TAG, "displayPreference(), Can not find the category.");
-            return;
-        }
-        mPreferenceCategory.setVisible(isAvailable());
-    }
-}
diff --git a/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java b/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java
index 9fb80a5..2a355ad 100644
--- a/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java
+++ b/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java
@@ -29,9 +29,12 @@
 import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
 import androidx.lifecycle.LifecycleObserver;
 import androidx.lifecycle.OnLifecycleEvent;
 import androidx.preference.ListPreference;
+import androidx.preference.ListPreferenceDialogFragmentCompat;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
 
@@ -67,6 +70,7 @@
     private SubscriptionsChangeListener mSubscriptionsListener;
     private int mCallState = TelephonyManager.CALL_STATE_IDLE;
     private PhoneCallStateTelephonyCallback mTelephonyCallback;
+    private FragmentManager mFragmentManager;
 
     public EnabledNetworkModePreferenceController(Context context, String key) {
         super(context, key);
@@ -151,7 +155,16 @@
         listPreference.setEntryValues(mBuilder.getEntryValues());
         listPreference.setValue(Integer.toString(mBuilder.getSelectedEntryValue()));
         listPreference.setSummary(mBuilder.getSummary());
-        listPreference.setEnabled(isCallStateIdle());
+        boolean listPreferenceEnabled = isCallStateIdle();
+        listPreference.setEnabled(listPreferenceEnabled);
+        if (!listPreferenceEnabled) {
+            // If dialog is already opened when ListPreference disabled, dismiss them.
+            for (Fragment fragment : mFragmentManager.getFragments()) {
+                if (fragment instanceof ListPreferenceDialogFragmentCompat) {
+                    ((ListPreferenceDialogFragmentCompat) fragment).dismiss();
+                }
+            }
+        }
     }
 
     @Override
@@ -169,8 +182,9 @@
         return false;
     }
 
-    void init(int subId) {
+    void init(int subId, FragmentManager fragmentManager) {
         mSubId = subId;
+        mFragmentManager = fragmentManager;
         mTelephonyManager = mContext.getSystemService(TelephonyManager.class)
                 .createForSubscriptionId(mSubId);
         mBuilder = new PreferenceEntriesBuilder(mContext, mSubId);
diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
index 452ce49..afc1b7e 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
@@ -260,7 +260,7 @@
         use(CarrierPreferenceController.class).init(mSubId);
         use(DataUsagePreferenceController.class).init(mSubId);
         use(PreferredNetworkModePreferenceController.class).init(mSubId);
-        use(EnabledNetworkModePreferenceController.class).init(mSubId);
+        use(EnabledNetworkModePreferenceController.class).init(mSubId, getParentFragmentManager());
         use(DataServiceSetupPreferenceController.class).init(mSubId);
         use(Enable2gPreferenceController.class).init(mSubId);
         use(CarrierWifiTogglePreferenceController.class).init(getLifecycle(), mSubId);
diff --git a/src/com/android/settings/notification/app/NotificationSoundPreference.java b/src/com/android/settings/notification/app/NotificationSoundPreference.java
index 136b21f..b55f9bd 100644
--- a/src/com/android/settings/notification/app/NotificationSoundPreference.java
+++ b/src/com/android/settings/notification/app/NotificationSoundPreference.java
@@ -25,10 +25,13 @@
 import android.os.AsyncTask;
 import android.util.AttributeSet;
 
+import android.util.Log;
 import com.android.settings.R;
 import com.android.settings.RingtonePreference;
 
 public class NotificationSoundPreference extends RingtonePreference {
+    private static final String TAG = "NotificationSoundPreference";
+
     private Uri mRingtone;
 
     public NotificationSoundPreference(Context context, AttributeSet attrs) {
@@ -50,8 +53,13 @@
     public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
         if (data != null) {
             Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
-            setRingtone(uri);
-            callChangeListener(uri);
+            if (isValidRingtoneUri(uri)) {
+                setRingtone(uri);
+                callChangeListener(uri);
+            } else {
+                Log.e(TAG, "onActivityResult for URI:" + uri
+                    + " ignored: invalid ringtone Uri");
+            }
         }
 
         return true;
diff --git a/src/com/android/settings/overlay/FeatureFactory.kt b/src/com/android/settings/overlay/FeatureFactory.kt
index 7645076..ac689d9 100644
--- a/src/com/android/settings/overlay/FeatureFactory.kt
+++ b/src/com/android/settings/overlay/FeatureFactory.kt
@@ -21,6 +21,7 @@
 import com.android.settings.accounts.AccountFeatureProvider
 import com.android.settings.applications.ApplicationFeatureProvider
 import com.android.settings.biometrics.face.FaceFeatureProvider
+import com.android.settings.biometrics.fingerprint.FingerprintFeatureProvider
 import com.android.settings.biometrics2.factory.BiometricsRepositoryProvider
 import com.android.settings.bluetooth.BluetoothFeatureProvider
 import com.android.settings.connecteddevice.fastpair.FastPairFeatureProvider
@@ -104,9 +105,17 @@
      */
     abstract val bluetoothFeatureProvider: BluetoothFeatureProvider
 
+    /**
+     * Retrieves implementation for Face feature.
+     */
     abstract val faceFeatureProvider: FaceFeatureProvider
 
     /**
+     * Retrieves implementation for Fingerprint feature.
+     */
+    abstract val fingerprintFeatureProvider: FingerprintFeatureProvider
+
+    /**
      * Gets implementation for Biometrics repository provider.
      */
     abstract val biometricsRepositoryProvider: BiometricsRepositoryProvider
diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.kt b/src/com/android/settings/overlay/FeatureFactoryImpl.kt
index 0afe9f4..7f991b7 100644
--- a/src/com/android/settings/overlay/FeatureFactoryImpl.kt
+++ b/src/com/android/settings/overlay/FeatureFactoryImpl.kt
@@ -29,6 +29,8 @@
 import com.android.settings.applications.ApplicationFeatureProviderImpl
 import com.android.settings.biometrics.face.FaceFeatureProvider
 import com.android.settings.biometrics.face.FaceFeatureProviderImpl
+import com.android.settings.biometrics.fingerprint.FingerprintFeatureProvider
+import com.android.settings.biometrics.fingerprint.FingerprintFeatureProviderImpl
 import com.android.settings.biometrics2.factory.BiometricsRepositoryProviderImpl
 import com.android.settings.bluetooth.BluetoothFeatureProvider
 import com.android.settings.bluetooth.BluetoothFeatureProviderImpl
@@ -145,6 +147,10 @@
 
     override val faceFeatureProvider: FaceFeatureProvider by lazy { FaceFeatureProviderImpl() }
 
+    override val fingerprintFeatureProvider: FingerprintFeatureProvider by lazy {
+        FingerprintFeatureProviderImpl()
+    }
+
     override val biometricsRepositoryProvider by lazy { BiometricsRepositoryProviderImpl() }
 
     override val wifiTrackerLibProvider: WifiTrackerLibProvider by lazy {
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java b/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java
index 43d8440..ea00f7f 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java
@@ -419,6 +419,10 @@
         }
     }
 
+    protected void clearResetErrorRunnable() {
+        mHandler.removeCallbacks(mResetErrorRunnable);
+    }
+
     protected void validateGuess(LockscreenCredential credentialGuess) {
         mRemoteLockscreenValidationFragment.validateLockscreenGuess(
                 mRemoteLockscreenValidationClient, credentialGuess,
diff --git a/src/com/android/settings/password/ConfirmLockPassword.java b/src/com/android/settings/password/ConfirmLockPassword.java
index b203015..b139ae9 100644
--- a/src/com/android/settings/password/ConfirmLockPassword.java
+++ b/src/com/android/settings/password/ConfirmLockPassword.java
@@ -666,6 +666,7 @@
         }
 
         private void handleAttemptLockout(long elapsedRealtimeDeadline) {
+            clearResetErrorRunnable();
             mCountdownTimer = new CountDownTimer(
                     elapsedRealtimeDeadline - SystemClock.elapsedRealtime(),
                     LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS) {
diff --git a/src/com/android/settings/password/ConfirmLockPattern.java b/src/com/android/settings/password/ConfirmLockPattern.java
index 3afb60e..6e3ad30 100644
--- a/src/com/android/settings/password/ConfirmLockPattern.java
+++ b/src/com/android/settings/password/ConfirmLockPattern.java
@@ -697,6 +697,7 @@
         }
 
         private void handleAttemptLockout(long elapsedRealtimeDeadline) {
+            clearResetErrorRunnable();
             updateStage(Stage.LockedOut);
             long elapsedRealtime = SystemClock.elapsedRealtime();
             mCountdownTimer = new CountDownTimer(
diff --git a/src/com/android/settings/password/SetNewPasswordActivity.java b/src/com/android/settings/password/SetNewPasswordActivity.java
index c02e23b..bb3c6df 100644
--- a/src/com/android/settings/password/SetNewPasswordActivity.java
+++ b/src/com/android/settings/password/SetNewPasswordActivity.java
@@ -121,7 +121,9 @@
 
     @Override
     public void launchChooseLock(Bundle chooseLockFingerprintExtras) {
-        Intent intent = new Intent(this, SetupChooseLockGeneric.class);
+        final boolean isInSetupWizard = WizardManagerHelper.isAnySetupWizard(getIntent());
+        Intent intent = isInSetupWizard ? new Intent(this, SetupChooseLockGeneric.class)
+                : new Intent(this, ChooseLockGeneric.class);
         intent.setAction(mNewPasswordAction);
         intent.putExtras(chooseLockFingerprintExtras);
         if (mCallerAppName != null) {
diff --git a/src/com/android/settings/password/SetupChooseLockGeneric.java b/src/com/android/settings/password/SetupChooseLockGeneric.java
index eade7cf..7c0769e 100644
--- a/src/com/android/settings/password/SetupChooseLockGeneric.java
+++ b/src/com/android/settings/password/SetupChooseLockGeneric.java
@@ -256,12 +256,6 @@
             return InternalSetupChooseLockGenericFragment.class;
         }
 
-        @Override
-        protected boolean isToolbarEnabled() {
-            // Hide the action bar from this page.
-            return false;
-        }
-
         public static class InternalSetupChooseLockGenericFragment
                 extends ChooseLockGenericFragment {
             @Override
diff --git a/src/com/android/settings/spa/app/ResetAppPreferences.kt b/src/com/android/settings/spa/app/ResetAppPreferences.kt
index 12dd709..34c4145 100644
--- a/src/com/android/settings/spa/app/ResetAppPreferences.kt
+++ b/src/com/android/settings/spa/app/ResetAppPreferences.kt
@@ -16,7 +16,6 @@
 
 package com.android.settings.spa.app
 
-import android.os.UserHandle
 import android.os.UserManager
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
@@ -37,10 +36,7 @@
     RestrictedMenuItem(
         text = stringResource(R.string.reset_app_preferences),
         restrictions = remember {
-            Restrictions(
-                userId = UserHandle.myUserId(),
-                keys = listOf(UserManager.DISALLOW_APPS_CONTROL),
-            )
+            Restrictions(keys = listOf(UserManager.DISALLOW_APPS_CONTROL))
         },
         onClick = onClick,
     )
diff --git a/src/com/android/settings/spa/preference/ComposePreference.kt b/src/com/android/settings/spa/preference/ComposePreference.kt
new file mode 100644
index 0000000..aec85a9
--- /dev/null
+++ b/src/com/android/settings/spa/preference/ComposePreference.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.spa.preference
+
+import android.content.Context
+import android.util.AttributeSet
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.ViewCompositionStrategy
+import androidx.preference.Preference
+import androidx.preference.PreferenceViewHolder
+import com.android.settings.R
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+
+class ComposePreference @JvmOverloads constructor(
+    context: Context,
+    attrs: AttributeSet? = null,
+    defStyleAttr: Int = 0,
+    defStyleRes: Int = 0,
+) : Preference(context, attrs, defStyleAttr, defStyleRes) {
+    var content: @Composable () -> Unit = {}
+
+    init {
+        layoutResource = R.layout.preference_compose
+    }
+
+    override fun onBindViewHolder(holder: PreferenceViewHolder) {
+        super.onBindViewHolder(holder)
+        holder.isDividerAllowedAbove = false
+        holder.isDividerAllowedBelow = false
+
+        (holder.itemView as ComposeView).apply {
+            setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
+            setContent {
+                SettingsTheme {
+                    content()
+                }
+            }
+        }
+    }
+}
diff --git a/src/com/android/settings/spa/preference/ComposePreferenceController.kt b/src/com/android/settings/spa/preference/ComposePreferenceController.kt
new file mode 100644
index 0000000..3ddb66b
--- /dev/null
+++ b/src/com/android/settings/spa/preference/ComposePreferenceController.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.spa.preference
+
+import android.content.Context
+import androidx.compose.runtime.Composable
+import androidx.preference.PreferenceScreen
+import com.android.settings.core.BasePreferenceController
+
+abstract class ComposePreferenceController(context: Context, preferenceKey: String) :
+    BasePreferenceController(context, preferenceKey) {
+
+    private lateinit var preference: ComposePreference
+
+    override fun displayPreference(screen: PreferenceScreen) {
+        super.displayPreference(screen)
+        preference = screen.findPreference(preferenceKey)!!
+        preference.content = { Content() }
+    }
+
+    @Composable
+    abstract fun Content()
+}
diff --git a/tests/robotests/AndroidManifest.xml b/tests/robotests/AndroidManifest.xml
index 22fce4f..95fbfa6 100644
--- a/tests/robotests/AndroidManifest.xml
+++ b/tests/robotests/AndroidManifest.xml
@@ -21,6 +21,8 @@
           package="com.android.settings">
     <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
 
-    <application/>
+    <application>
+        <activity android:name="com.android.settings.security.TestActivity" android:exported="true" />
+    </application>
 
 </manifest>
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragmentTest.java
index de8ae60..995d74f 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragmentTest.java
@@ -43,7 +43,6 @@
 import com.google.common.collect.ImmutableList;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
@@ -79,60 +78,46 @@
     }
 
     @Test
-    @Ignore
     public void onCreate_afterSuccessfullyLaunch_shouldBeFinished() {
         final Intent intent = new Intent();
         intent.putExtra(Intent.EXTRA_COMPONENT_NAME, COMPONENT_NAME);
-        mFragmentController = FragmentController.of(new AccessibilityDetailsSettingsFragment(),
-                intent);
 
-        AccessibilityDetailsSettingsFragment fragment = mFragmentController.create().get();
+        AccessibilityDetailsSettingsFragment fragment = startFragment(intent);
 
         assertThat(fragment.getActivity().isFinishing()).isTrue();
     }
 
     @Test
-    @Ignore
     public void onCreate_hasValidExtraComponentName_launchExpectedFragment() {
         Intent intent = new Intent();
         intent.putExtra(Intent.EXTRA_COMPONENT_NAME, COMPONENT_NAME);
-        mFragmentController = FragmentController.of(new AccessibilityDetailsSettingsFragment(),
-                intent);
 
-        AccessibilityDetailsSettingsFragment fragment = mFragmentController.create().get();
+        AccessibilityDetailsSettingsFragment fragment = startFragment(intent);
 
         assertStartActivityWithExpectedFragment(fragment.getActivity(),
                 ToggleAccessibilityServicePreferenceFragment.class.getName());
     }
 
     @Test
-    @Ignore
     public void onCreate_hasInvalidExtraComponentName_launchAccessibilitySettings() {
         Intent intent = new Intent();
         intent.putExtra(Intent.EXTRA_COMPONENT_NAME, PACKAGE_NAME + "/.service");
-        mFragmentController = FragmentController.of(new AccessibilityDetailsSettingsFragment(),
-                intent);
 
-        AccessibilityDetailsSettingsFragment fragment = mFragmentController.create().get();
+        AccessibilityDetailsSettingsFragment fragment = startFragment(intent);
 
         assertStartActivityWithExpectedFragment(fragment.getActivity(),
                 AccessibilitySettings.class.getName());
     }
 
     @Test
-    @Ignore
     public void onCreate_hasNoExtraComponentName_launchAccessibilitySettings() {
-        mFragmentController = FragmentController.of(new AccessibilityDetailsSettingsFragment(),
-                new Intent());
-
-        AccessibilityDetailsSettingsFragment fragment = mFragmentController.create().get();
+        AccessibilityDetailsSettingsFragment fragment = startFragment(/* intent= */ null);
 
         assertStartActivityWithExpectedFragment(fragment.getActivity(),
                 AccessibilitySettings.class.getName());
     }
 
     @Test
-    @Ignore
     public void onCreate_extraComponentNameIsDisallowed_launchAccessibilitySettings() {
         Intent intent = new Intent();
         intent.putExtra(Intent.EXTRA_COMPONENT_NAME, COMPONENT_NAME);
@@ -140,47 +125,38 @@
                 DevicePolicyManager.class);
         ((ShadowDevicePolicyManager) Shadows.shadowOf(dpm)).setPermittedAccessibilityServices(
                 ImmutableList.of());
-        mFragmentController = FragmentController.of(new AccessibilityDetailsSettingsFragment(),
-                intent);
 
-        AccessibilityDetailsSettingsFragment fragment = mFragmentController.create().get();
+        AccessibilityDetailsSettingsFragment fragment = startFragment(intent);
 
         assertStartActivityWithExpectedFragment(fragment.getActivity(),
                 AccessibilitySettings.class.getName());
     }
 
     @Test
-    @Ignore
     public void onCreate_magnificationComponentName_launchMagnificationFragment() {
         Intent intent = new Intent();
         intent.putExtra(Intent.EXTRA_COMPONENT_NAME,
                 MAGNIFICATION_COMPONENT_NAME.flattenToString());
-        mFragmentController = FragmentController.of(new AccessibilityDetailsSettingsFragment(),
-                intent);
 
-        AccessibilityDetailsSettingsFragment fragment = mFragmentController.create().get();
+        AccessibilityDetailsSettingsFragment fragment = startFragment(intent);
 
         assertStartActivityWithExpectedFragment(fragment.getActivity(),
                 ToggleScreenMagnificationPreferenceFragment.class.getName());
     }
 
     @Test
-    @Ignore
     public void onCreate_accessibilityButton_launchAccessibilityButtonFragment() {
         Intent intent = new Intent();
         intent.putExtra(Intent.EXTRA_COMPONENT_NAME,
                 ACCESSIBILITY_BUTTON_COMPONENT_NAME.flattenToString());
-        mFragmentController = FragmentController.of(new AccessibilityDetailsSettingsFragment(),
-                intent);
 
-        AccessibilityDetailsSettingsFragment fragment = mFragmentController.create().get();
+        AccessibilityDetailsSettingsFragment fragment = startFragment(intent);
 
         assertStartActivityWithExpectedFragment(fragment.getActivity(),
                 AccessibilityButtonFragment.class.getName());
     }
 
     @Test
-    @Ignore
     public void onCreate_hearingAidsComponentName_launchAccessibilityHearingAidsFragment() {
         FeatureFlagUtils.setEnabled(mContext,
                 FeatureFlagUtils.SETTINGS_ACCESSIBILITY_HEARING_AID_PAGE, true);
@@ -188,25 +164,30 @@
         intent.putExtra(Intent.EXTRA_COMPONENT_NAME,
                 ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
 
-        mFragmentController = FragmentController.of(new AccessibilityDetailsSettingsFragment(),
-                intent);
-
-        AccessibilityDetailsSettingsFragment fragment = mFragmentController.create().get();
+        AccessibilityDetailsSettingsFragment fragment = startFragment(intent);
 
         assertStartActivityWithExpectedFragment(fragment.getActivity(),
                 AccessibilityHearingAidsFragment.class.getName());
     }
 
     @Test
-    @Ignore
     public void getMetricsCategory_returnsCorrectCategory() {
-        mFragmentController = FragmentController.of(new AccessibilityDetailsSettingsFragment());
-        AccessibilityDetailsSettingsFragment fragment = mFragmentController.create().get();
+
+        AccessibilityDetailsSettingsFragment fragment = startFragment(/* intent= */ null);
 
         assertThat(fragment.getMetricsCategory()).isEqualTo(
                 SettingsEnums.ACCESSIBILITY_DETAILS_SETTINGS);
     }
 
+    private AccessibilityDetailsSettingsFragment startFragment(Intent intent) {
+        mFragmentController = FragmentController.of(
+                new AccessibilityDetailsSettingsFragment(), intent)
+                .create()
+                .visible();
+
+        return mFragmentController.get();
+    }
+
     private AccessibilityServiceInfo getMockAccessibilityServiceInfo() {
         final ApplicationInfo applicationInfo = new ApplicationInfo();
         final ServiceInfo serviceInfo = new ServiceInfo();
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java
index 8e0cfc1..db6f43b 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java
@@ -19,52 +19,46 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
 
 import static java.util.Collections.singletonList;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.AccessibilityShortcutInfo;
-import android.app.AppOpsManager;
+import android.app.Application;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.database.ContentObserver;
 import android.os.Build;
-import android.os.Bundle;
 import android.provider.Settings;
 import android.view.accessibility.AccessibilityManager;
 
-import androidx.fragment.app.FragmentActivity;
-import androidx.preference.PreferenceManager;
+import androidx.fragment.app.Fragment;
 import androidx.test.core.app.ApplicationProvider;
 
 import com.android.internal.accessibility.util.AccessibilityUtils;
 import com.android.internal.content.PackageMonitor;
 import com.android.settings.R;
+import com.android.settings.SettingsActivity;
 import com.android.settings.testutils.XmlTestUtils;
+import com.android.settings.testutils.shadow.ShadowApplicationPackageManager;
 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
 import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
-import com.android.settings.testutils.shadow.ShadowFragment;
 import com.android.settings.testutils.shadow.ShadowUserManager;
 import com.android.settingslib.RestrictedPreference;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
-import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.search.SearchIndexableRaw;
+import com.android.settingslib.testutils.shadow.ShadowColorDisplayManager;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -74,21 +68,25 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 import org.robolectric.RobolectricTestRunner;
+import org.robolectric.android.controller.ActivityController;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadow.api.Shadow;
 import org.robolectric.shadows.ShadowAccessibilityManager;
+import org.robolectric.shadows.ShadowContentResolver;
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 
 /** Test for {@link AccessibilitySettings}. */
 @RunWith(RobolectricTestRunner.class)
 @Config(shadows = {
-        ShadowBluetoothUtils.class,
         ShadowBluetoothAdapter.class,
-        ShadowFragment.class,
+        ShadowUserManager.class,
+        ShadowColorDisplayManager.class,
+        ShadowApplicationPackageManager.class,
 })
 public class AccessibilitySettingsTest {
     private static final String PACKAGE_NAME = "com.android.test";
@@ -103,50 +101,34 @@
 
     @Rule
     public final MockitoRule mocks = MockitoJUnit.rule();
-    @Spy
     private final Context mContext = ApplicationProvider.getApplicationContext();
     @Spy
     private final AccessibilityServiceInfo mServiceInfo = getMockAccessibilityServiceInfo(
             PACKAGE_NAME, CLASS_NAME);
-    @Spy
-    private final AccessibilitySettings mFragment = new AccessibilitySettings();
     @Mock
     private AccessibilityShortcutInfo mShortcutInfo;
-    @Mock
-    private FragmentActivity mActivity;
-    @Mock
-    private ContentResolver mContentResolver;
-    @Mock
-    private PreferenceManager mPreferenceManager;
     private ShadowAccessibilityManager mShadowAccessibilityManager;
     @Mock
-    private AppOpsManager mAppOpsManager;
-    @Mock
     private LocalBluetoothManager mLocalBluetoothManager;
-
-    private Lifecycle mLifecycle;
+    private ActivityController<SettingsActivity> mActivityController;
+    private AccessibilitySettings mFragment;
 
     @Before
     public void setup() {
         mShadowAccessibilityManager = Shadow.extract(AccessibilityManager.getInstance(mContext));
         mShadowAccessibilityManager.setInstalledAccessibilityServiceList(new ArrayList<>());
-        when(mFragment.getContext()).thenReturn(mContext);
-        when(mFragment.getActivity()).thenReturn(mActivity);
-        when(mActivity.getContentResolver()).thenReturn(mContentResolver);
-        when(mFragment.getPreferenceManager()).thenReturn(mPreferenceManager);
-        when(mFragment.getPreferenceManager().getContext()).thenReturn(mContext);
         mContext.setTheme(androidx.appcompat.R.style.Theme_AppCompat);
-        when(mContext.getSystemService(AppOpsManager.class)).thenReturn(mAppOpsManager);
-        when(mAppOpsManager.noteOpNoThrow(eq(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS),
-                anyInt(), anyString())).thenReturn(AppOpsManager.MODE_ALLOWED);
-        mLifecycle = new Lifecycle(() -> mLifecycle);
-        when(mFragment.getSettingsLifecycle()).thenReturn(mLifecycle);
         ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
         setMockAccessibilityShortcutInfo(mShortcutInfo);
+
+        Intent intent = new Intent();
+        intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT,
+                AccessibilitySettings.class.getName());
+
+        mActivityController = ActivityController.of(new SettingsActivity(), intent);
     }
 
     @Test
-    @Ignore
     public void getNonIndexableKeys_existInXmlLayout() {
         final List<String> niks = AccessibilitySettings.SEARCH_INDEX_DATA_PROVIDER
                 .getNonIndexableKeys(mContext);
@@ -157,16 +139,15 @@
     }
 
     @Test
-    @Ignore
     public void getRawDataToIndex_isNull() {
         final List<SearchIndexableRaw> indexableRawList =
-                AccessibilitySettings.SEARCH_INDEX_DATA_PROVIDER.getRawDataToIndex(mContext, true);
+                AccessibilitySettings.SEARCH_INDEX_DATA_PROVIDER
+                        .getRawDataToIndex(mContext, true);
 
         assertThat(indexableRawList).isNull();
     }
 
     @Test
-    @Ignore
     public void getServiceSummary_serviceCrash_showsStopped() {
         mServiceInfo.crashed = true;
 
@@ -178,7 +159,6 @@
     }
 
     @Test
-    @Ignore
     public void getServiceSummary_invisibleToggle_shortcutEnabled_showsOnSummary() {
         setInvisibleToggleFragmentType(mServiceInfo);
         doReturn(DEFAULT_SUMMARY).when(mServiceInfo).loadSummary(any());
@@ -194,7 +174,6 @@
     }
 
     @Test
-    @Ignore
     public void getServiceSummary_invisibleToggle_shortcutDisabled_showsOffSummary() {
         setInvisibleToggleFragmentType(mServiceInfo);
         setShortcutEnabled(mServiceInfo.getComponentName(), false);
@@ -210,7 +189,6 @@
     }
 
     @Test
-    @Ignore
     public void getServiceSummary_enableServiceShortcutOn_showsServiceEnabledShortcutOn() {
         doReturn(EMPTY_STRING).when(mServiceInfo).loadSummary(any());
         setShortcutEnabled(mServiceInfo.getComponentName(), true);
@@ -223,7 +201,6 @@
     }
 
     @Test
-    @Ignore
     public void getServiceSummary_enableServiceShortcutOff_showsServiceEnabledShortcutOff() {
         doReturn(EMPTY_STRING).when(mServiceInfo).loadSummary(any());
         setShortcutEnabled(mServiceInfo.getComponentName(), false);
@@ -236,7 +213,6 @@
     }
 
     @Test
-    @Ignore
     public void getServiceSummary_disableServiceShortcutOff_showsDisabledShortcutOff() {
         doReturn(EMPTY_STRING).when(mServiceInfo).loadSummary(any());
         setShortcutEnabled(mServiceInfo.getComponentName(), false);
@@ -249,7 +225,6 @@
     }
 
     @Test
-    @Ignore
     public void getServiceSummary_disableServiceShortcutOn_showsDisabledShortcutOn() {
         doReturn(EMPTY_STRING).when(mServiceInfo).loadSummary(any());
         setShortcutEnabled(mServiceInfo.getComponentName(), true);
@@ -262,7 +237,6 @@
     }
 
     @Test
-    @Ignore
     public void getServiceSummary_enableServiceShortcutOffAndHasSummary_showsEnabledSummary() {
         setShortcutEnabled(mServiceInfo.getComponentName(), false);
         doReturn(DEFAULT_SUMMARY).when(mServiceInfo).loadSummary(any());
@@ -277,7 +251,6 @@
     }
 
     @Test
-    @Ignore
     public void getServiceSummary_enableServiceShortcutOnAndHasSummary_showsEnabledSummary() {
         doReturn(DEFAULT_SUMMARY).when(mServiceInfo).loadSummary(any());
         setShortcutEnabled(mServiceInfo.getComponentName(), true);
@@ -292,7 +265,6 @@
     }
 
     @Test
-    @Ignore
     public void getServiceSummary_disableServiceShortcutOnAndHasSummary_showsDisabledSummary() {
         doReturn(DEFAULT_SUMMARY).when(mServiceInfo).loadSummary(any());
         setShortcutEnabled(mServiceInfo.getComponentName(), true);
@@ -307,7 +279,6 @@
     }
 
     @Test
-    @Ignore
     public void getServiceSummary_disableServiceShortcutOffAndHasSummary_showsDisabledSummary() {
         setShortcutEnabled(mServiceInfo.getComponentName(), false);
         doReturn(DEFAULT_SUMMARY).when(mServiceInfo).loadSummary(any());
@@ -322,7 +293,6 @@
     }
 
     @Test
-    @Ignore
     public void getServiceDescription_serviceCrash_showsStopped() {
         mServiceInfo.crashed = true;
 
@@ -334,7 +304,6 @@
     }
 
     @Test
-    @Ignore
     public void getServiceDescription_haveDescription_showsDescription() {
         doReturn(DEFAULT_DESCRIPTION).when(mServiceInfo).loadDescription(any());
 
@@ -345,42 +314,66 @@
     }
 
     @Test
-    @Ignore
-    @Config(shadows = {ShadowFragment.class, ShadowUserManager.class})
     public void onCreate_haveRegisterToSpecificUrisAndActions() {
-        mFragment.onAttach(mContext);
+        setupFragment();
 
-        mFragment.onCreate(Bundle.EMPTY);
-
-        verify(mContentResolver).registerContentObserver(
-                eq(Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS)),
-                anyBoolean(),
-                any(AccessibilitySettingsContentObserver.class));
-        verify(mContentResolver).registerContentObserver(eq(Settings.Secure.getUriFor(
-                        Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)), anyBoolean(),
-                any(AccessibilitySettingsContentObserver.class));
-        verify(mActivity, atLeast(1)).registerReceiver(
-                any(PackageMonitor.class), any(), isNull(), any());
+        ShadowContentResolver shadowContentResolver = shadowOf(mContext.getContentResolver());
+        Collection<ContentObserver> a11yButtonTargetsObservers =
+                shadowContentResolver.getContentObservers(
+                        Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS));
+        Collection<ContentObserver> a11yShortcutTargetServiceObservers =
+                shadowContentResolver.getContentObservers(Settings.Secure.getUriFor(
+                        Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE));
+        List<BroadcastReceiver> broadcastReceivers =
+                shadowOf((Application) ApplicationProvider.getApplicationContext())
+                        .getRegisteredReceivers()
+                        .stream().map(wrapper -> wrapper.broadcastReceiver).toList();
+        assertThat(
+                a11yButtonTargetsObservers.stream()
+                        .anyMatch(contentObserver ->
+                                contentObserver instanceof AccessibilitySettingsContentObserver))
+                .isTrue();
+        assertThat(
+                a11yShortcutTargetServiceObservers.stream()
+                        .anyMatch(contentObserver ->
+                                contentObserver instanceof AccessibilitySettingsContentObserver))
+                .isTrue();
+        assertThat(broadcastReceivers.stream().anyMatch(
+                broadcastReceiver -> broadcastReceiver instanceof PackageMonitor)).isTrue();
     }
 
     @Test
-    @Ignore
-    @Config(shadows = {ShadowFragment.class, ShadowUserManager.class})
     public void onDestroy_unregisterObserverAndReceiver() {
         setupFragment();
-        mFragment.onPause();
-        mFragment.onStop();
 
-        mFragment.onDestroy();
+        mActivityController.pause().stop().destroy();
 
-        verify(mContentResolver).unregisterContentObserver(
-                any(AccessibilitySettingsContentObserver.class));
-        verify(mActivity).unregisterReceiver(any(PackageMonitor.class));
+        ShadowContentResolver shadowContentResolver = shadowOf(mContext.getContentResolver());
+        Collection<ContentObserver> a11yButtonTargetsObservers =
+                shadowContentResolver.getContentObservers(
+                        Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS));
+        Collection<ContentObserver> a11yShortcutTargetServiceObservers =
+                shadowContentResolver.getContentObservers(Settings.Secure.getUriFor(
+                        Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE));
+        List<BroadcastReceiver> broadcastReceivers =
+                shadowOf((Application) ApplicationProvider.getApplicationContext())
+                        .getRegisteredReceivers()
+                        .stream().map(wrapper -> wrapper.broadcastReceiver).toList();
+        assertThat(
+                a11yButtonTargetsObservers.stream()
+                        .anyMatch(contentObserver ->
+                                contentObserver instanceof AccessibilitySettingsContentObserver))
+                .isFalse();
+        assertThat(
+                a11yShortcutTargetServiceObservers.stream()
+                        .anyMatch(contentObserver ->
+                                contentObserver instanceof AccessibilitySettingsContentObserver))
+                .isFalse();
+        assertThat(broadcastReceivers.stream().anyMatch(
+                broadcastReceiver -> broadcastReceiver instanceof PackageMonitor)).isFalse();
     }
 
     @Test
-    @Ignore
-    @Config(shadows = {ShadowFragment.class, ShadowUserManager.class})
     public void onContentChanged_updatePreferenceInForeground_preferenceUpdated() {
         setupFragment();
         mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
@@ -396,8 +389,6 @@
     }
 
     @Test
-    @Ignore
-    @Config(shadows = {ShadowFragment.class, ShadowUserManager.class})
     public void onContentChanged_updatePreferenceInBackground_preferenceUpdated() {
         setupFragment();
         mFragment.onPause();
@@ -417,8 +408,6 @@
     }
 
     @Test
-    @Ignore
-    @Config(shadows = {ShadowFragment.class, ShadowUserManager.class})
     public void testAccessibilityMenuInSystem_IncludedInInteractionControl() {
         mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
                 List.of(getMockAccessibilityServiceInfo(
@@ -433,8 +422,6 @@
     }
 
     @Test
-    @Ignore
-    @Config(shadows = {ShadowFragment.class, ShadowUserManager.class})
     public void testAccessibilityMenuInSystem_NoPrefWhenNotInstalled() {
         mShadowAccessibilityManager.setInstalledAccessibilityServiceList(List.of());
         setupFragment();
@@ -487,10 +474,14 @@
     }
 
     private void setupFragment() {
-        mFragment.onAttach(mContext);
-        mFragment.onCreate(Bundle.EMPTY);
-        mFragment.onStart();
-        mFragment.onResume();
+        mActivityController.create().start().resume();
+        Fragment fragment = mActivityController.get().getSupportFragmentManager().findFragmentById(
+                R.id.main_content);
+
+        assertThat(fragment).isNotNull();
+        assertThat(fragment).isInstanceOf(AccessibilitySettings.class);
+
+        mFragment = (AccessibilitySettings) fragment;
     }
 
     private void setShortcutEnabled(ComponentName componentName, boolean enabled) {
diff --git a/tests/robotests/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceControllerTest.java
new file mode 100644
index 0000000..6bf83c6
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceControllerTest.java
@@ -0,0 +1,172 @@
+/*
+ * 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.accessibility;
+
+import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
+import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
+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.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.vibrator.Flags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link KeyboardVibrationTogglePreferenceController}. */
+@RunWith(RobolectricTestRunner.class)
+public class KeyboardVibrationTogglePreferenceControllerTest {
+
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+
+    private Context mContext;
+    private Resources mResources;
+    private KeyboardVibrationTogglePreferenceController mController;
+
+    private SwitchPreference mPreference;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(ApplicationProvider.getApplicationContext());
+        mResources = spy(mContext.getResources());
+        when(mContext.getResources()).thenReturn(mResources);
+        mController = new KeyboardVibrationTogglePreferenceController(mContext, "preferenceKey");
+        mPreference = new SwitchPreference(mContext);
+        when(mPreferenceScreen.findPreference(
+                mController.getPreferenceKey())).thenReturn(mPreference);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void getAvailabilityStatus_featureSupported_available() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
+        when(mResources.getBoolean(R.bool.config_keyboard_vibration_supported)).thenReturn(true);
+
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+    }
+
+    @Test
+    public void getAvailabilityStatus_featureNotSupported_unavailable() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
+        when(mResources.getBoolean(R.bool.config_keyboard_vibration_supported)).thenReturn(false);
+
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+    }
+
+    @Test
+    public void getAvailabilityStatus_keyboardCategoryDisabled_unavailable() {
+        mSetFlagsRule.disableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
+        when(mResources.getBoolean(R.bool.config_keyboard_vibration_supported)).thenReturn(true);
+
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+    }
+
+    @Test
+    public void updateState_mainVibrateDisabled_shouldReturnFalseForCheckedAndEnabled() {
+        updateSystemSetting(VibrationPreferenceConfig.MAIN_SWITCH_SETTING_KEY, OFF);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isEnabled()).isFalse();
+        assertThat(mPreference.isChecked()).isFalse();
+    }
+
+    @Test
+    public void updateState_mainVibrateEnabled_shouldReturnTrueForEnabled() {
+        updateSystemSetting(VibrationPreferenceConfig.MAIN_SWITCH_SETTING_KEY, ON);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isEnabled()).isTrue();
+    }
+
+    @Test
+    public void isChecked_keyboardVibrateEnabled_shouldReturnTrue() {
+        updateSystemSetting(VibrationPreferenceConfig.MAIN_SWITCH_SETTING_KEY, ON);
+        updateSystemSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, ON);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isChecked()).isTrue();
+    }
+
+    @Test
+    public void isChecked_keyboardVibrateDisabled_shouldReturnFalse() {
+        updateSystemSetting(VibrationPreferenceConfig.MAIN_SWITCH_SETTING_KEY, ON);
+        updateSystemSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, OFF);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isChecked()).isFalse();
+    }
+
+    @Test
+    public void setChecked_checked_updateSettings() throws Settings.SettingNotFoundException {
+        // set an off state initially
+        updateSystemSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, OFF);
+
+        assertThat(readSystemSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED)).isEqualTo(OFF);
+
+        mController.setChecked(true);
+
+        assertThat(readSystemSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED)).isEqualTo(ON);
+    }
+
+    @Test
+    public void setChecked_unchecked_updateSettings() throws Settings.SettingNotFoundException {
+        // set an on state initially
+        updateSystemSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, ON);
+
+        assertThat(readSystemSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED)).isEqualTo(ON);
+
+        mController.setChecked(false);
+
+        assertThat(readSystemSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED)).isEqualTo(OFF);
+    }
+
+    private void updateSystemSetting(String key, int value) {
+        Settings.System.putInt(mContext.getContentResolver(), key, value);
+    }
+
+    private int readSystemSetting(String key) throws Settings.SettingNotFoundException {
+        return Settings.System.getInt(mContext.getContentResolver(), key);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java
index 72f1ab8..adf76f4 100644
--- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java
@@ -19,6 +19,7 @@
 import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON;
 import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_REAR;
 import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
+import static android.text.Layout.HYPHENATION_FREQUENCY_NORMAL;
 
 import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED;
 import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_SKIP;
@@ -49,6 +50,7 @@
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.view.View;
+import android.widget.TextView;
 
 import androidx.fragment.app.Fragment;
 
@@ -62,6 +64,7 @@
 import com.google.android.setupcompat.template.FooterBarMixin;
 import com.google.android.setupcompat.template.FooterButton;
 import com.google.android.setupdesign.GlifLayout;
+import com.google.android.setupdesign.template.HeaderMixin;
 
 import org.junit.After;
 import org.junit.Before;
@@ -568,6 +571,15 @@
         assertThat(appliedThemes.contains("SetupWizardPartnerResource")).isTrue();
     }
 
+    @Test
+    public void fingerprintEnrollFindSensor_setHyphenationFrequencyNormalOnHeader() {
+        setupActivity_onUdfpsDevice();
+        PartnerCustomizationLayout layout = mActivity.findViewById(R.id.setup_wizard_layout);
+        final TextView textView = layout.getMixin(HeaderMixin.class).getTextView();
+
+        assertThat(textView.getHyphenationFrequency()).isEqualTo(HYPHENATION_FREQUENCY_NORMAL);
+    }
+
     private void triggerEnrollProgressAndError_onRearDevice() {
         EnrollmentCallback enrollmentCallback = verifyAndCaptureEnrollmentCallback();
         enrollmentCallback.onEnrollmentProgress(123);
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsDataSyncControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsDataSyncControllerTest.java
index 799f7fc..5d6fe31 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsDataSyncControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsDataSyncControllerTest.java
@@ -92,6 +92,14 @@
     }
 
     @Test
+    public void refresh_noAssociations_checkPreferenceInvisible() {
+        mController.mAssociationId = DUMMY_ASSOCIATION_ID;
+        mController.refresh();
+
+        assertThat(mPermSyncPreference.isVisible()).isFalse();
+    }
+
+    @Test
     public void refresh_permSyncNull_checkPreferenceInvisible() {
         mPermissionSyncRequest = null;
         when(mCompanionDeviceManager.getPermissionSyncRequest(ASSOCIATION_ID)).thenReturn(
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntryTest.java
index 2cafadb..ae726b7 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntryTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntryTest.java
@@ -62,6 +62,8 @@
     private static final int UNINSTALLED_UID = 101;
     private static final String PACKAGE_NAME = "com.android.testing";
     private static final String UNINSTALLED_PACKAGE_NAME = "com.android.testing.uninstalled";
+    private static final String UID_ZERO_PACKAGE_NAME = "com.android.testing.uid.zero";
+
 
     private Context mContext;
 
@@ -89,6 +91,9 @@
         doReturn(BatteryUtils.UID_NULL)
                 .when(mMockPackageManager)
                 .getPackageUid(UNINSTALLED_PACKAGE_NAME, PackageManager.GET_META_DATA);
+        doReturn(BatteryUtils.UID_ZERO)
+                .when(mMockPackageManager)
+                .getPackageUid(UID_ZERO_PACKAGE_NAME, PackageManager.GET_META_DATA);
         BatteryDiffEntry.clearCache();
     }
 
@@ -443,6 +448,18 @@
     }
 
     @Test
+    public void testIsUninstalledEntry_uidZero_returnFalse() throws Exception {
+        final ContentValues values =
+                getContentValuesWithType(ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
+        values.put(BatteryHistEntry.KEY_UID, BatteryUtils.UID_ZERO);
+        values.put(BatteryHistEntry.KEY_PACKAGE_NAME, PACKAGE_NAME);
+        final BatteryDiffEntry entry = createBatteryDiffEntry(10, new BatteryHistEntry(values));
+
+        assertThat(entry.isSystemEntry()).isFalse();
+        assertThat(entry.isUninstalledEntry()).isFalse();
+    }
+
+    @Test
     public void testIsUninstalledEntry_uninstalledApp_returnTrue() throws Exception {
         doReturn(BatteryUtils.UID_NULL)
                 .when(mMockPackageManager)
diff --git a/tests/robotests/src/com/android/settings/network/BluetoothWiFiResetPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/BluetoothWiFiResetPreferenceControllerTest.java
deleted file mode 100644
index 3aea4a8..0000000
--- a/tests/robotests/src/com/android/settings/network/BluetoothWiFiResetPreferenceControllerTest.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing `permissions and
- * limitations under the License.
- */
-
-package com.android.settings.network;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.net.ConnectivityManager;
-import android.os.UserManager;
-import android.telephony.TelephonyManager;
-
-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.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.shadows.ShadowToast;
-
-@RunWith(RobolectricTestRunner.class)
-public class BluetoothWiFiResetPreferenceControllerTest {
-
-    private static final String PREFERENCE_KEY = "network_reset_bluetooth_wifi_pref";
-
-    @Mock
-    private UserManager mUserManager;
-    @Mock
-    private Resources mResources;
-    @Mock
-    private ConnectivityManager mConnectivityManager;
-    @Mock
-    private TelephonyManager mTelephonyManager;
-
-    private Context mContext;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mContext = spy(RuntimeEnvironment.application);
-        when(mContext.getResources()).thenReturn(mResources);
-
-        mockService(Context.CONNECTIVITY_SERVICE, ConnectivityManager.class,
-                mConnectivityManager);
-        mockService(Context.TELEPHONY_SERVICE, TelephonyManager.class, mTelephonyManager);
-    }
-
-    @Test
-    public void getAvailabilityStatus_returnAvailable_asOwnerUser() {
-        mockService(Context.USER_SERVICE, UserManager.class, mUserManager);
-        doReturn(true).when(mUserManager).isAdminUser();
-
-        BluetoothWiFiResetPreferenceController target =
-                new BluetoothWiFiResetPreferenceController(mContext, PREFERENCE_KEY);
-
-        assertThat(target.getAvailabilityStatus()).isEqualTo(
-                BluetoothWiFiResetPreferenceController.AVAILABLE);
-    }
-
-    @Test
-    public void resetOperation_notResetConnectivity_onDeviceWithSimVisible() {
-        mockService(Context.CONNECTIVITY_SERVICE, ConnectivityManager.class,
-                mConnectivityManager);
-        when(mResources.getBoolean(R.bool.config_show_sim_info)).thenReturn(true);
-
-        BluetoothWiFiResetPreferenceController target =
-                new BluetoothWiFiResetPreferenceController(mContext, PREFERENCE_KEY);
-
-        try {
-            target.resetOperation().run();
-        } catch (Exception exception) {}
-        verify(mConnectivityManager, never()).factoryReset();
-    }
-
-    @Test
-    public void endOfReset_toastMessage_whenSuccess() {
-        String testText = "reset_bluetooth_wifi_complete_toast";
-        when(mResources.getString(R.string.reset_bluetooth_wifi_complete_toast)).thenReturn(testText);
-        BluetoothWiFiResetPreferenceController target =
-                new BluetoothWiFiResetPreferenceController(mContext, PREFERENCE_KEY);
-
-        target.endOfReset(null);
-
-        assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(testText);
-    }
-
-    private <T> void mockService(String serviceName, Class<T> serviceClass, T service) {
-        when(mContext.getSystemServiceName(serviceClass)).thenReturn(serviceName);
-        when(mContext.getSystemService(serviceName)).thenReturn(service);
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/security/MemtagPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/security/MemtagPreferenceControllerTest.java
index e3fc3cc..7d2c6dd 100644
--- a/tests/robotests/src/com/android/settings/security/MemtagPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/security/MemtagPreferenceControllerTest.java
@@ -24,11 +24,9 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.content.Context;
-import android.os.Bundle;
 
-import androidx.fragment.app.FragmentActivity;
-import androidx.fragment.app.FragmentContainerView;
-import androidx.test.rule.ActivityTestRule;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
 
 import com.android.settings.R;
 import com.android.settings.testutils.shadow.ShadowDeviceConfig;
@@ -37,11 +35,11 @@
 import com.android.settingslib.testutils.shadow.ShadowInteractionJankMonitor;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowSystemProperties;
 
@@ -57,8 +55,8 @@
     private final String mMemtagSupportedProperty = "ro.arm64.memtag.bootctl_supported";
 
     @Rule
-    public ActivityTestRule<TestActivity> mActivityTestRule =
-            new ActivityTestRule<>(TestActivity.class);
+    public ActivityScenarioRule<TestActivity> mActivityScenario =
+                        new ActivityScenarioRule<>(TestActivity.class);
 
     private MemtagPage mMemtagPage;
     private MemtagPreferenceController mController;
@@ -70,17 +68,18 @@
     @Before
     public void setUp() {
         ShadowSystemProperties.override(mMemtagSupportedProperty, "true");
-
-        mContext = RuntimeEnvironment.application;
+        mContext = ApplicationProvider.getApplicationContext();
         mMemtagPage = new MemtagPage();
-        mActivity = mActivityTestRule.getActivity();
-        mActivity
-                .getSupportFragmentManager()
-                .beginTransaction()
-                .add(TestActivity.CONTAINER_VIEW_ID, mMemtagPage)
-                .commit();
-        mController = new MemtagPreferenceController(mContext, FRAGMENT_TAG);
-        mController.setFragment(mMemtagPage);
+        System.out.println("Activity: " + mActivity);
+        mActivityScenario.getScenario().onActivity(a -> {
+            a.getSupportFragmentManager()
+                    .beginTransaction()
+                    .add(TestActivity.CONTAINER_VIEW_ID, mMemtagPage)
+                    .commitNow();
+            mController = new MemtagPreferenceController(a, FRAGMENT_TAG);
+            mController.setFragment(mMemtagPage);
+        });
+        System.out.println("Committed");
     }
 
     @Test
@@ -135,6 +134,7 @@
     }
 
     @Test
+    @Ignore
     public void setChecked_isChecked_doesNotShowDialog() {
         ZygoteShadow.setSupportsMemoryTagging(false);
         mController.setChecked(false);
@@ -142,6 +142,7 @@
     }
 
     @Test
+    @Ignore
     public void setChecked_isUnchecked_doesNotShowDialog() {
         ZygoteShadow.setSupportsMemoryTagging(true);
         mController.setChecked(true);
@@ -155,18 +156,4 @@
         mController.updateState(preference);
         assertThat(preference.isDisabledByAdmin()).isTrue();
     }
-
-    private static final class TestActivity extends FragmentActivity {
-
-        private static final int CONTAINER_VIEW_ID = 1234;
-
-        @Override
-        protected void onCreate(Bundle bundle) {
-            super.onCreate(bundle);
-
-            FragmentContainerView contentView = new FragmentContainerView(this);
-            contentView.setId(CONTAINER_VIEW_ID);
-            setContentView(contentView);
-        }
-    }
 }
diff --git a/tests/robotests/src/com/android/settings/security/TestActivity.java b/tests/robotests/src/com/android/settings/security/TestActivity.java
new file mode 100644
index 0000000..70b5cf5
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/security/TestActivity.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.security;
+
+import android.os.Bundle;
+
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentContainerView;
+
+public final class TestActivity extends FragmentActivity {
+
+    static final int CONTAINER_VIEW_ID = 1234;
+
+    @Override
+    protected void onCreate(Bundle bundle) {
+        super.onCreate(bundle);
+
+        FragmentContainerView contentView = new FragmentContainerView(this);
+        contentView.setId(CONTAINER_VIEW_ID);
+        setContentView(contentView);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowApplicationPackageManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowApplicationPackageManager.java
index 7ff2e5d..e6b0307 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowApplicationPackageManager.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowApplicationPackageManager.java
@@ -16,9 +16,12 @@
 
 package com.android.settings.testutils.shadow;
 
+import android.annotation.NonNull;
 import android.app.ApplicationPackageManager;
 import android.content.pm.PackageInfo;
+import android.os.IRemoteCallback;
 
+import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
 
 import java.util.ArrayList;
@@ -49,4 +52,10 @@
         }
         return packages;
     }
+
+    @Implementation
+    public void registerPackageMonitorCallback(@NonNull IRemoteCallback callback, int userId) {}
+
+    @Implementation
+    public void unregisterPackageMonitorCallback(@NonNull IRemoteCallback callback) {}
 }
diff --git a/tests/robotests/testutils/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/testutils/com/android/settings/testutils/FakeFeatureFactory.java
index 52a5f24..9156cae 100644
--- a/tests/robotests/testutils/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/robotests/testutils/com/android/settings/testutils/FakeFeatureFactory.java
@@ -24,6 +24,7 @@
 import com.android.settings.accounts.AccountFeatureProvider;
 import com.android.settings.applications.ApplicationFeatureProvider;
 import com.android.settings.biometrics.face.FaceFeatureProvider;
+import com.android.settings.biometrics.fingerprint.FingerprintFeatureProvider;
 import com.android.settings.biometrics2.factory.BiometricsRepositoryProvider;
 import com.android.settings.bluetooth.BluetoothFeatureProvider;
 import com.android.settings.connecteddevice.fastpair.FastPairFeatureProvider;
@@ -80,6 +81,7 @@
     public final AccountFeatureProvider mAccountFeatureProvider;
     public final BluetoothFeatureProvider mBluetoothFeatureProvider;
     public final FaceFeatureProvider mFaceFeatureProvider;
+    public final FingerprintFeatureProvider mFingerprintFeatureProvider;
     public final BiometricsRepositoryProvider mBiometricsRepositoryProvider;
 
     public PanelFeatureProvider panelFeatureProvider;
@@ -132,6 +134,7 @@
         panelFeatureProvider = mock(PanelFeatureProvider.class);
         mBluetoothFeatureProvider = mock(BluetoothFeatureProvider.class);
         mFaceFeatureProvider = mock(FaceFeatureProvider.class);
+        mFingerprintFeatureProvider = mock(FingerprintFeatureProvider.class);
         mBiometricsRepositoryProvider = mock(BiometricsRepositoryProvider.class);
         wifiTrackerLibProvider = mock(WifiTrackerLibProvider.class);
         securitySettingsFeatureProvider = mock(SecuritySettingsFeatureProvider.class);
@@ -257,6 +260,11 @@
     }
 
     @Override
+    public FingerprintFeatureProvider getFingerprintFeatureProvider() {
+        return mFingerprintFeatureProvider;
+    }
+
+    @Override
     public BiometricsRepositoryProvider getBiometricsRepositoryProvider() {
         return mBiometricsRepositoryProvider;
     }
diff --git a/tests/spa_unit/src/com/android/settings/datausage/lib/AppDataUsageDetailsRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/datausage/lib/AppDataUsageDetailsRepositoryTest.kt
index 7072b46..85431a4 100644
--- a/tests/spa_unit/src/com/android/settings/datausage/lib/AppDataUsageDetailsRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/datausage/lib/AppDataUsageDetailsRepositoryTest.kt
@@ -16,7 +16,7 @@
 
 package com.android.settings.datausage.lib
 
-import android.app.usage.NetworkStats.Bucket
+import android.app.usage.NetworkStats
 import android.content.Context
 import android.net.NetworkTemplate
 import android.util.Range
@@ -28,8 +28,8 @@
 import org.junit.runner.RunWith
 import org.mockito.kotlin.doReturn
 import org.mockito.kotlin.mock
-import org.mockito.kotlin.spy
-import org.mockito.kotlin.whenever
+import org.mockito.kotlin.stub
+import com.android.settings.datausage.lib.NetworkStatsRepository.Companion.Bucket
 
 @RunWith(AndroidJUnit4::class)
 class AppDataUsageDetailsRepositoryTest {
@@ -41,58 +41,78 @@
         on { getCycles() } doReturn listOf(Range(CYCLE1_END_TIME, CYCLE2_END_TIME))
     }
 
+    private val networkStatsRepository = mock<NetworkStatsRepository>()
+
     @Test
     fun queryDetailsForCycles_hasCycles(): Unit = runBlocking {
-        val range = Range(CYCLE1_START_TIME, CYCLE1_END_TIME)
-        val repository = spy(
-            AppDataUsageDetailsRepository(
-                context = context,
-                cycles = listOf(CYCLE1_END_TIME, CYCLE1_START_TIME),
-                template = template,
-                uids = listOf(UID),
-                networkCycleDataRepository = networkCycleDataRepository,
+        networkStatsRepository.stub {
+            on { queryBuckets(CYCLE1_START_TIME, CYCLE1_END_TIME) } doReturn listOf(
+                Bucket(
+                    uid = UID,
+                    state = NetworkStats.Bucket.STATE_DEFAULT,
+                    bytes = BACKGROUND_USAGE,
+                ),
+                Bucket(
+                    uid = UID,
+                    state = NetworkStats.Bucket.STATE_FOREGROUND,
+                    bytes = FOREGROUND_USAGE,
+                ),
             )
-        ) {
-            doReturn(ALL_USAGE).whenever(mock).getUsage(range, UID, Bucket.STATE_ALL)
-            doReturn(FOREGROUND_USAGE).whenever(mock).getUsage(range, UID, Bucket.STATE_FOREGROUND)
         }
+        val repository = AppDataUsageDetailsRepository(
+            context = context,
+            cycles = listOf(CYCLE1_END_TIME, CYCLE1_START_TIME),
+            template = template,
+            uids = listOf(UID),
+            networkCycleDataRepository = networkCycleDataRepository,
+            networkStatsRepository = networkStatsRepository,
+        )
 
         val detailsForCycles = repository.queryDetailsForCycles()
 
         assertThat(detailsForCycles).containsExactly(
             NetworkUsageDetailsData(
-                range = range,
-                totalUsage = ALL_USAGE,
+                range = Range(CYCLE1_START_TIME, CYCLE1_END_TIME),
+                totalUsage = BACKGROUND_USAGE + FOREGROUND_USAGE,
                 foregroundUsage = FOREGROUND_USAGE,
-                backgroundUsage = ALL_USAGE - FOREGROUND_USAGE,
+                backgroundUsage = BACKGROUND_USAGE,
             )
         )
     }
 
     @Test
     fun queryDetailsForCycles_defaultCycles(): Unit = runBlocking {
-        val range = Range(CYCLE1_END_TIME, CYCLE2_END_TIME)
-        val repository = spy(
-            AppDataUsageDetailsRepository(
-                context = context,
-                cycles = null,
-                template = template,
-                uids = listOf(UID),
-                networkCycleDataRepository = networkCycleDataRepository,
+        networkStatsRepository.stub {
+            on { queryBuckets(CYCLE1_END_TIME, CYCLE2_END_TIME) } doReturn listOf(
+                Bucket(
+                    uid = UID,
+                    state = NetworkStats.Bucket.STATE_DEFAULT,
+                    bytes = BACKGROUND_USAGE,
+                ),
+                Bucket(
+                    uid = UID,
+                    state = NetworkStats.Bucket.STATE_FOREGROUND,
+                    bytes = FOREGROUND_USAGE,
+                ),
             )
-        ) {
-            doReturn(ALL_USAGE).whenever(mock).getUsage(range, UID, Bucket.STATE_ALL)
-            doReturn(FOREGROUND_USAGE).whenever(mock).getUsage(range, UID, Bucket.STATE_FOREGROUND)
         }
+        val repository = AppDataUsageDetailsRepository(
+            context = context,
+            cycles = null,
+            template = template,
+            uids = listOf(UID),
+            networkCycleDataRepository = networkCycleDataRepository,
+            networkStatsRepository = networkStatsRepository,
+        )
 
         val detailsForCycles = repository.queryDetailsForCycles()
 
         assertThat(detailsForCycles).containsExactly(
             NetworkUsageDetailsData(
-                range = range,
-                totalUsage = ALL_USAGE,
+                range = Range(CYCLE1_END_TIME, CYCLE2_END_TIME),
+                totalUsage = BACKGROUND_USAGE + FOREGROUND_USAGE,
                 foregroundUsage = FOREGROUND_USAGE,
-                backgroundUsage = ALL_USAGE - FOREGROUND_USAGE,
+                backgroundUsage = BACKGROUND_USAGE,
             )
         )
     }
@@ -103,7 +123,7 @@
         const val CYCLE2_END_TIME = 1695566666000L
         const val UID = 10000
 
-        const val ALL_USAGE = 10L
+        const val BACKGROUND_USAGE = 8L
         const val FOREGROUND_USAGE = 2L
     }
 }
diff --git a/tests/spa_unit/src/com/android/settings/network/BluetoothWiFiResetPreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/network/BluetoothWiFiResetPreferenceControllerTest.kt
new file mode 100644
index 0000000..210a0c7
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/BluetoothWiFiResetPreferenceControllerTest.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network
+
+import android.bluetooth.BluetoothAdapter
+import android.bluetooth.BluetoothManager
+import android.content.Context
+import android.content.res.Resources
+import android.net.ConnectivityManager
+import android.net.NetworkPolicyManager
+import android.net.VpnManager
+import android.os.UserManager
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.android.settings.core.BasePreferenceController.AVAILABLE
+import com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.stub
+import org.mockito.kotlin.verify
+
+@RunWith(AndroidJUnit4::class)
+class BluetoothWiFiResetPreferenceControllerTest {
+
+    private val mockUserManager = mock<UserManager>()
+    private val mockBluetoothAdapter = mock<BluetoothAdapter>()
+    private val mockBluetoothManager = mock<BluetoothManager> {
+        on { adapter } doReturn mockBluetoothAdapter
+    }
+    private val mockConnectivityManager = mock<ConnectivityManager>()
+    private val mockNetworkPolicyManager = mock<NetworkPolicyManager>()
+    private val mockVpnManager = mock<VpnManager>()
+    private val mockResources = mock<Resources>()
+
+    private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+        on { getSystemService(Context.USER_SERVICE) } doReturn mockUserManager
+        on { getSystemService(Context.BLUETOOTH_SERVICE) } doReturn mockBluetoothManager
+        on { getSystemService(Context.CONNECTIVITY_SERVICE) } doReturn mockConnectivityManager
+        on { getSystemService(Context.NETWORK_POLICY_SERVICE) } doReturn mockNetworkPolicyManager
+        on { getSystemService(Context.VPN_MANAGEMENT_SERVICE) } doReturn mockVpnManager
+        on { resources } doReturn mockResources
+    }
+
+    private val controller = BluetoothWiFiResetPreferenceController(context, TEST_KEY)
+
+    @Test
+    fun getAvailabilityStatus_isAdminUser_returnAvailable() {
+        mockUserManager.stub {
+            on { isAdminUser } doReturn true
+        }
+
+        val availabilityStatus = controller.getAvailabilityStatus()
+
+        assertThat(availabilityStatus).isEqualTo(AVAILABLE)
+    }
+
+    @Test
+    fun getAvailabilityStatus_notAdminUser_returnConditionallyUnavailable() {
+        mockUserManager.stub {
+            on { isAdminUser } doReturn false
+        }
+
+        val availabilityStatus = controller.getAvailabilityStatus()
+
+        assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE)
+    }
+
+    @Test
+    fun resetOperation_resetBluetooth() {
+        controller.resetOperation().run()
+
+        verify(mockBluetoothAdapter).clearBluetooth()
+    }
+
+    @Test
+    fun resetOperation_onDeviceWithSimVisible_notResetConnectivity() {
+        mockResources.stub {
+            on { getBoolean(R.bool.config_show_sim_info) } doReturn true
+        }
+
+        controller.resetOperation().run()
+
+        verify(mockConnectivityManager, never()).factoryReset()
+    }
+
+    @Test
+    fun resetOperation_onDeviceWithSimInvisible_resetVpn() {
+        mockResources.stub {
+            on { getBoolean(R.bool.config_show_sim_info) } doReturn false
+        }
+
+        controller.resetOperation().run()
+
+        verify(mockVpnManager).factoryReset()
+    }
+
+    private companion object {
+        const val TEST_KEY = "test_key"
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/InteractAcrossProfilesDetailsPreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/InteractAcrossProfilesDetailsPreferenceTest.kt
index f4489c6..e123389 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/InteractAcrossProfilesDetailsPreferenceTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/InteractAcrossProfilesDetailsPreferenceTest.kt
@@ -40,6 +40,7 @@
 import com.android.settingslib.spaprivileged.framework.common.crossProfileApps
 import org.junit.After
 import org.junit.Before
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -116,6 +117,7 @@
         composeTestRule.waitUntilExists(hasText(SUMMARY))
     }
 
+    @Ignore
     @Test
     fun whenClick_startActivity() {
         mockCanConfig(true)
diff --git a/tests/spa_unit/src/com/android/settings/spa/preference/ComposePreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/spa/preference/ComposePreferenceControllerTest.kt
new file mode 100644
index 0000000..36817d1
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/preference/ComposePreferenceControllerTest.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.spa.preference
+
+import android.content.Context
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.preference.PreferenceManager
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class ComposePreferenceControllerTest {
+
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    private val controller = object : ComposePreferenceController(
+        context = context,
+        preferenceKey = TEST_KEY,
+    ) {
+        override fun getAvailabilityStatus() = AVAILABLE
+
+        @Composable
+        override fun Content() {
+            Text(TEXT)
+        }
+    }
+
+    private val preference = ComposePreference(context).apply {
+        key = TEST_KEY
+    }
+
+    private val preferenceScreen = PreferenceManager(context).createPreferenceScreen(context)
+        .apply { addPreference(preference) }
+
+    @Test
+    fun displayPreference() {
+        controller.displayPreference(preferenceScreen)
+
+        composeTestRule.setContent {
+            preference.content()
+        }
+        composeTestRule.onNodeWithText(TEXT).assertIsDisplayed()
+    }
+
+    private companion object {
+        const val TEST_KEY = "test_key"
+        const val TEXT = "Text"
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/spa/preference/ComposePreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/preference/ComposePreferenceTest.kt
new file mode 100644
index 0000000..28bde3a
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/preference/ComposePreferenceTest.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.spa.preference
+
+import android.content.Context
+import androidx.compose.material3.Text
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.preference.PreferenceViewHolder
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class ComposePreferenceTest {
+
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    private val preference = ComposePreference(context)
+
+    private val composeView = ComposeView(context)
+
+    @Test
+    fun onBindViewHolder() {
+        preference.content = {
+            Text(TEXT)
+        }
+
+        preference.onBindViewHolder(PreferenceViewHolder.createInstanceForTests(composeView))
+
+        composeTestRule.setContent {
+            composeView.Content()
+        }
+        composeTestRule.onNodeWithText(TEXT).assertIsDisplayed()
+    }
+
+    private companion object {
+        const val TEXT = "Text"
+    }
+}
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 95f25ad..54299eb 100644
--- a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
+++ b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
@@ -22,6 +22,7 @@
 import com.android.settings.accounts.AccountFeatureProvider
 import com.android.settings.applications.ApplicationFeatureProvider
 import com.android.settings.biometrics.face.FaceFeatureProvider
+import com.android.settings.biometrics.fingerprint.FingerprintFeatureProvider
 import com.android.settings.biometrics2.factory.BiometricsRepositoryProvider
 import com.android.settings.bluetooth.BluetoothFeatureProvider
 import com.android.settings.connecteddevice.fastpair.FastPairFeatureProvider
@@ -120,6 +121,8 @@
         get() = TODO("Not yet implemented")
     override val faceFeatureProvider: FaceFeatureProvider
         get() = TODO("Not yet implemented")
+    override val fingerprintFeatureProvider: FingerprintFeatureProvider
+        get() = TODO("Not yet implemented")
     override val biometricsRepositoryProvider: BiometricsRepositoryProvider
         get() = TODO("Not yet implemented")
     override val wifiTrackerLibProvider: WifiTrackerLibProvider
diff --git a/tests/unit/src/com/android/settings/DefaultRingtonePreferenceTest.java b/tests/unit/src/com/android/settings/DefaultRingtonePreferenceTest.java
index 7877684..c580fc5 100644
--- a/tests/unit/src/com/android/settings/DefaultRingtonePreferenceTest.java
+++ b/tests/unit/src/com/android/settings/DefaultRingtonePreferenceTest.java
@@ -16,16 +16,20 @@
 
 package com.android.settings;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.ContentInterface;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.media.RingtoneManager;
+import android.content.pm.UserProperties;
 import android.net.Uri;
+import android.os.UserHandle;
+import android.os.UserManager;
 
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -34,17 +38,22 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 /** Unittest for DefaultRingtonePreference. */
 @RunWith(AndroidJUnit4.class)
 public class DefaultRingtonePreferenceTest {
 
+    private static final int OWNER_USER_ID = 1;
+    private static final int OTHER_USER_ID = 10;
+    private static final int INVALID_RINGTONE_TYPE = 0;
     private DefaultRingtonePreference mDefaultRingtonePreference;
 
     @Mock
     private ContentResolver mContentResolver;
     @Mock
+    private UserManager mUserManager;
     private Uri mRingtoneUri;
 
     @Before
@@ -52,14 +61,29 @@
         MockitoAnnotations.initMocks(this);
 
         Context context = spy(ApplicationProvider.getApplicationContext());
-        doReturn(mContentResolver).when(context).getContentResolver();
+        mContentResolver = ContentResolver.wrap(Mockito.mock(ContentInterface.class));
+        when(context.getContentResolver()).thenReturn(mContentResolver);
 
         mDefaultRingtonePreference = spy(new DefaultRingtonePreference(context, null /* attrs */));
         doReturn(context).when(mDefaultRingtonePreference).getContext();
+
+        // Use INVALID_RINGTONE_TYPE to return early in RingtoneManager.setActualDefaultRingtoneUri
         when(mDefaultRingtonePreference.getRingtoneType())
-                .thenReturn(RingtoneManager.TYPE_RINGTONE);
-        mDefaultRingtonePreference.setUserId(1);
+                .thenReturn(INVALID_RINGTONE_TYPE);
+
+        mDefaultRingtonePreference.setUserId(OWNER_USER_ID);
         mDefaultRingtonePreference.mUserContext = context;
+        when(mDefaultRingtonePreference.isDefaultRingtone(any(Uri.class))).thenReturn(false);
+
+        when(context.getSystemServiceName(UserManager.class)).thenReturn(Context.USER_SERVICE);
+        when(context.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+
+        UserProperties userProperties = new UserProperties.Builder().setMediaSharedWithParent(false)
+                .build();
+        when(mUserManager.getUserProperties(UserHandle.of(OTHER_USER_ID))).thenReturn(
+                userProperties);
+
+        mRingtoneUri = Uri.parse("content://none");
     }
 
     @Test
@@ -79,4 +103,53 @@
 
         verify(mDefaultRingtonePreference, never()).setActualDefaultRingtoneUri(mRingtoneUri);
     }
+
+    @Test
+    public void onSaveRingtone_notManagedProfile_shouldNotSetRingtone() {
+        mRingtoneUri = Uri.parse("content://" + OTHER_USER_ID + "@ringtone");
+        when(mContentResolver.getType(mRingtoneUri)).thenReturn("audio/*");
+        when(mUserManager.isSameProfileGroup(OWNER_USER_ID, OTHER_USER_ID)).thenReturn(true);
+        when(mUserManager.getProfileParent(UserHandle.of(OTHER_USER_ID))).thenReturn(
+                UserHandle.of(OWNER_USER_ID));
+        when(mUserManager.isManagedProfile(OTHER_USER_ID)).thenReturn(false);
+
+        mDefaultRingtonePreference.onSaveRingtone(mRingtoneUri);
+
+        verify(mDefaultRingtonePreference, never()).setActualDefaultRingtoneUri(mRingtoneUri);
+    }
+
+    @Test
+    public void onSaveRingtone_notSameUser_shouldNotSetRingtone() {
+        mRingtoneUri = Uri.parse("content://" + OTHER_USER_ID + "@ringtone");
+        when(mContentResolver.getType(mRingtoneUri)).thenReturn("audio/*");
+        when(mUserManager.isSameProfileGroup(OWNER_USER_ID, OTHER_USER_ID)).thenReturn(false);
+
+        mDefaultRingtonePreference.onSaveRingtone(mRingtoneUri);
+
+        verify(mDefaultRingtonePreference, never()).setActualDefaultRingtoneUri(mRingtoneUri);
+    }
+
+    @Test
+    public void onSaveRingtone_isManagedProfile_shouldSetRingtone() {
+        mRingtoneUri = Uri.parse("content://" + OTHER_USER_ID + "@ringtone");
+        when(mContentResolver.getType(mRingtoneUri)).thenReturn("audio/*");
+        when(mUserManager.isSameProfileGroup(OWNER_USER_ID, OTHER_USER_ID)).thenReturn(true);
+        when(mUserManager.getProfileParent(UserHandle.of(OTHER_USER_ID))).thenReturn(
+                UserHandle.of(OWNER_USER_ID));
+        when(mUserManager.isManagedProfile(OTHER_USER_ID)).thenReturn(true);
+
+        mDefaultRingtonePreference.onSaveRingtone(mRingtoneUri);
+
+        verify(mDefaultRingtonePreference).setActualDefaultRingtoneUri(mRingtoneUri);
+    }
+
+    @Test
+    public void onSaveRingtone_defaultUri_shouldSetRingtone() {
+        mRingtoneUri = Uri.parse("default_ringtone");
+        when(mDefaultRingtonePreference.isDefaultRingtone(any(Uri.class))).thenReturn(true);
+
+        mDefaultRingtonePreference.onSaveRingtone(mRingtoneUri);
+
+        verify(mDefaultRingtonePreference).setActualDefaultRingtoneUri(mRingtoneUri);
+    }
 }
diff --git a/tests/unit/src/com/android/settings/fingerprint2/enrollment/viewmodel/FingerprintEnrollFindSensorViewModelV2Test.kt b/tests/unit/src/com/android/settings/fingerprint2/enrollment/viewmodel/FingerprintEnrollFindSensorViewModelV2Test.kt
new file mode 100644
index 0000000..509b0ed
--- /dev/null
+++ b/tests/unit/src/com/android/settings/fingerprint2/enrollment/viewmodel/FingerprintEnrollFindSensorViewModelV2Test.kt
@@ -0,0 +1,255 @@
+/*
+ * 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.fingerprint2.enrollment.viewmodel
+
+import android.content.Context
+import android.content.res.Configuration
+import android.view.accessibility.AccessibilityManager
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule
+import androidx.test.core.app.ApplicationProvider
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.AccessibilityViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.Education
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollFindSensorViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollNavigationViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintGatekeeperViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FoldStateViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.NextStepViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.OrientationStateViewModel
+import com.android.settings.testutils2.FakeFingerprintManagerInteractor
+import com.android.systemui.biometrics.shared.model.FingerprintSensor
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.resetMain
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoJUnitRunner
+
+/** consistent with [ScreenSizeFoldProvider.INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP] */
+private const val INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP = 600
+
+@RunWith(MockitoJUnitRunner::class)
+class FingerprintEnrollFindSensorViewModelV2Test {
+  @JvmField @Rule var rule = MockitoJUnit.rule()
+  @get:Rule val instantTaskRule = InstantTaskExecutorRule()
+
+  private var backgroundDispatcher = StandardTestDispatcher()
+  private var testScope = TestScope(backgroundDispatcher)
+  private lateinit var fakeFingerprintManagerInteractor: FakeFingerprintManagerInteractor
+  private lateinit var gatekeeperViewModel: FingerprintGatekeeperViewModel
+  private lateinit var enrollViewModel: FingerprintEnrollViewModel
+  private lateinit var navigationViewModel: FingerprintEnrollNavigationViewModel
+  private lateinit var accessibilityViewModel: AccessibilityViewModel
+  private lateinit var foldStateViewModel: FoldStateViewModel
+  private lateinit var orientationStateViewModel: OrientationStateViewModel
+  private lateinit var underTest: FingerprintEnrollFindSensorViewModel
+  private val context: Context = ApplicationProvider.getApplicationContext()
+  private val accessibilityManager: AccessibilityManager =
+    context.getSystemService(AccessibilityManager::class.java)!!
+
+  @Before
+  fun setup() {
+    backgroundDispatcher = StandardTestDispatcher()
+    testScope = TestScope(backgroundDispatcher)
+    Dispatchers.setMain(backgroundDispatcher)
+
+    fakeFingerprintManagerInteractor = FakeFingerprintManagerInteractor()
+    gatekeeperViewModel =
+      FingerprintGatekeeperViewModel.FingerprintGatekeeperViewModelFactory(
+          null,
+          fakeFingerprintManagerInteractor
+        )
+        .create(FingerprintGatekeeperViewModel::class.java)
+    navigationViewModel =
+      FingerprintEnrollNavigationViewModel.FingerprintEnrollNavigationViewModelFactory(
+          backgroundDispatcher,
+          fakeFingerprintManagerInteractor,
+          gatekeeperViewModel,
+          canSkipConfirm = true,
+        )
+        .create(FingerprintEnrollNavigationViewModel::class.java)
+    enrollViewModel =
+      FingerprintEnrollViewModel.FingerprintEnrollViewModelFactory(
+          fakeFingerprintManagerInteractor,
+          backgroundDispatcher
+        )
+        .create(FingerprintEnrollViewModel::class.java)
+    accessibilityViewModel =
+      AccessibilityViewModel.AccessibilityViewModelFactory(accessibilityManager)
+        .create(AccessibilityViewModel::class.java)
+    foldStateViewModel =
+      FoldStateViewModel.FoldStateViewModelFactory(context).create(FoldStateViewModel::class.java)
+    orientationStateViewModel =
+      OrientationStateViewModel.OrientationViewModelFactory(context)
+        .create(OrientationStateViewModel::class.java)
+    underTest =
+      FingerprintEnrollFindSensorViewModel.FingerprintEnrollFindSensorViewModelFactory(
+          navigationViewModel,
+          enrollViewModel,
+          gatekeeperViewModel,
+          accessibilityViewModel,
+          foldStateViewModel,
+          orientationStateViewModel
+        )
+        .create(FingerprintEnrollFindSensorViewModel::class.java)
+
+    // Navigate to Education page
+    navigationViewModel.nextStep()
+  }
+  @After
+  fun tearDown() {
+    Dispatchers.resetMain()
+  }
+
+  // TODO(b/305094585): test enroll() logic
+
+  @Test
+  fun currentStepIsEducation() =
+    testScope.runTest {
+      var step: NextStepViewModel? = null
+      val job = launch {
+        navigationViewModel.navigationViewModel.collectLatest { step = it.currStep }
+      }
+      advanceUntilIdle()
+      assertThat(step).isEqualTo(Education)
+      job.cancel()
+    }
+
+  @Test
+  fun udfpsLottieInfo() =
+    testScope.runTest {
+      fakeFingerprintManagerInteractor.sensorProp =
+        FingerprintSensor(
+          0 /* sensorId */,
+          SensorStrength.STRONG,
+          5,
+          FingerprintSensorType.UDFPS_OPTICAL
+        )
+
+      var udfpsLottieInfo: Boolean? = null
+      val job = launch { underTest.udfpsLottieInfo.collect { udfpsLottieInfo = it } }
+
+      advanceUntilIdle()
+      assertThat(udfpsLottieInfo).isNotNull()
+      job.cancel()
+    }
+
+  @Test
+  fun sfpsLottieInfoWhenFolded() =
+    testScope.runTest {
+      var isFolded = false
+      var rotation: Int = -1
+      val job = launch {
+        underTest.sfpsLottieInfo.collect {
+          isFolded = it.first
+          rotation = it.second
+        }
+      }
+
+      val config = createConfiguration(isFolded = true)
+      foldStateViewModel.onConfigurationChange(config)
+      advanceUntilIdle()
+      assertThat(isFolded).isTrue()
+      assertThat(rotation).isEqualTo(context.display!!.rotation)
+      job.cancel()
+    }
+
+  @Test
+  fun sfpsLottieInfoWhenUnFolded() =
+    testScope.runTest {
+      var isFolded = false
+      var rotation: Int = -1
+      val job = launch {
+        underTest.sfpsLottieInfo.collect {
+          isFolded = it.first
+          rotation = it.second
+        }
+      }
+
+      val config = createConfiguration(isFolded = false)
+      foldStateViewModel.onConfigurationChange(config)
+      advanceUntilIdle()
+      assertThat(isFolded).isFalse()
+      assertThat(rotation).isEqualTo(context.display!!.rotation)
+      job.cancel()
+    }
+
+  @Test
+  fun rfpsAnimation() =
+    testScope.runTest {
+      fakeFingerprintManagerInteractor.sensorProp =
+        FingerprintSensor(0 /* sensorId */, SensorStrength.STRONG, 5, FingerprintSensorType.REAR)
+
+      var showRfpsAnimation: Boolean? = null
+      val job = launch { underTest.showRfpsAnimation.collect { showRfpsAnimation = it } }
+
+      advanceUntilIdle()
+      assertThat(showRfpsAnimation).isTrue()
+      job.cancel()
+    }
+
+  @Test
+  fun showPrimaryButton_ifUdfps() =
+    testScope.runTest {
+      fakeFingerprintManagerInteractor.sensorProp =
+        FingerprintSensor(
+          0 /* sensorId */,
+          SensorStrength.STRONG,
+          5,
+          FingerprintSensorType.UDFPS_OPTICAL
+        )
+
+      var showPrimaryButton: Boolean? = null
+      val job = launch { underTest.showPrimaryButton.collect { showPrimaryButton = it } }
+
+      advanceUntilIdle()
+      assertThat(showPrimaryButton).isTrue()
+      job.cancel()
+    }
+
+  @Test
+  fun doesNotShowPrimaryButton_ifNonUdfps() =
+    testScope.runTest {
+      var showPrimaryButton: Boolean? = null
+      val job = launch { underTest.showPrimaryButton.collect { showPrimaryButton = it } }
+
+      advanceUntilIdle()
+      assertThat(showPrimaryButton).isNull()
+      job.cancel()
+    }
+
+  private fun createConfiguration(isFolded: Boolean): Configuration {
+    val config = Configuration()
+    config.smallestScreenWidthDp =
+      if (isFolded) INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP - 1
+      else INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP + 1
+    return config
+  }
+}
diff --git a/tests/unit/src/com/android/settings/network/NetworkProviderSimListControllerTest.java b/tests/unit/src/com/android/settings/network/NetworkProviderSimListControllerTest.java
index c4e0f64..a98f83b 100644
--- a/tests/unit/src/com/android/settings/network/NetworkProviderSimListControllerTest.java
+++ b/tests/unit/src/com/android/settings/network/NetworkProviderSimListControllerTest.java
@@ -16,19 +16,16 @@
 
 package com.android.settings.network;
 
-import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
-import static com.google.common.truth.Truth.assertThat;
-
 import static androidx.lifecycle.Lifecycle.Event;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
-import android.graphics.drawable.Drawable;
 import android.os.Looper;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -36,18 +33,15 @@
 
 import androidx.lifecycle.LifecycleOwner;
 import androidx.lifecycle.LifecycleRegistry;
-import androidx.preference.PreferenceManager;
-import androidx.preference.Preference;
 import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceManager;
 import androidx.preference.PreferenceScreen;
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
-import com.android.settings.R;
 import com.android.settings.testutils.ResourcesUtils;
 import com.android.settingslib.RestrictedPreference;
-import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
 
 import org.junit.Before;
@@ -83,8 +77,6 @@
     @Mock
     private SubscriptionManager mSubscriptionManager;
     @Mock
-    private Lifecycle mLifecycle;
-    @Mock
     private LifecycleOwner mLifecycleOwner;
     private LifecycleRegistry mLifecycleRegistry;
 
@@ -100,12 +92,10 @@
      * Mock the NetworkProviderSimListController that allows one to set a default voice,
      * SMS and mobile data subscription ID.
      */
-    @SuppressWarnings("ClassCanBeStatic")
-    private class MockNetworkProviderSimListController extends
-            com.android.settings.network.NetworkProviderSimListController {
-        public MockNetworkProviderSimListController(Context context, Lifecycle lifecycle,
-        LifecycleOwner lifecycleOwner) {
-            super(context, lifecycle, lifecycleOwner);
+    private static class MockNetworkProviderSimListController
+            extends NetworkProviderSimListController {
+        MockNetworkProviderSimListController(Context context, String preferenceKey) {
+            super(context, preferenceKey);
         }
 
         private List<SubscriptionInfoEntity> mSubscriptionInfoEntity;
@@ -136,8 +126,7 @@
         mPreference.setKey(KEY_PREFERENCE_SIM_LIST);
         mPreferenceCategory = new PreferenceCategory(mContext);
         mPreferenceCategory.setKey(KEY_PREFERENCE_CATEGORY_SIM);
-        mController = new MockNetworkProviderSimListController(mContext, mLifecycle,
-                mLifecycleOwner);
+        mController = new MockNetworkProviderSimListController(mContext, "test_key");
         mLifecycleRegistry = new LifecycleRegistry(mLifecycleOwner);
         when(mLifecycleOwner.getLifecycle()).thenReturn(mLifecycleRegistry);
     }
diff --git a/tests/unit/src/com/android/settings/network/NetworkProviderSimsCategoryControllerTest.java b/tests/unit/src/com/android/settings/network/NetworkProviderSimsCategoryControllerTest.java
deleted file mode 100644
index dc17e91..0000000
--- a/tests/unit/src/com/android/settings/network/NetworkProviderSimsCategoryControllerTest.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.network;
-
-import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.os.Looper;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-
-import com.android.settings.testutils.ResourcesUtils;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-
-import androidx.lifecycle.LifecycleOwner;
-import androidx.preference.PreferenceCategory;
-import androidx.preference.PreferenceManager;
-import androidx.preference.PreferenceScreen;
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-
-//TODO: Remove NetworkProviderSimsCategoryControllerTest once it is removed in the b/244769887.
-@RunWith(AndroidJUnit4.class)
-public class NetworkProviderSimsCategoryControllerTest {
-
-    private static final String KEY_PREFERENCE_CATEGORY_SIM = "provider_model_sim_category";
-    private static final String SUB_1 = "SUB_1";
-    private static final String SUB_2 = "SUB_2";
-    private static final int SUB_ID_1 = 1;
-    private static final int SUB_ID_2 = 2;
-
-    @Mock
-    private Lifecycle mLifecycle;
-    @Mock
-    private SubscriptionInfo mSubscriptionInfo1;
-    @Mock
-    private SubscriptionInfo mSubscriptionInfo2;
-
-    private Context mContext;
-    private NetworkProviderSimsCategoryController mCategoryController;
-    private PreferenceManager mPreferenceManager;
-    private PreferenceScreen mPreferenceScreen;
-    private PreferenceCategory mPreferenceCategory;
-    private LifecycleOwner mLifecycleOwner;
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        mContext = spy(ApplicationProvider.getApplicationContext());
-
-        if (Looper.myLooper() == null) {
-            Looper.prepare();
-        }
-
-        mLifecycleOwner = () -> mLifecycle;
-        mPreferenceManager = new PreferenceManager(mContext);
-        mPreferenceScreen = mPreferenceManager.createPreferenceScreen(mContext);
-        mPreferenceCategory = new PreferenceCategory(mContext);
-        mPreferenceCategory.setKey(KEY_PREFERENCE_CATEGORY_SIM);
-        mPreferenceScreen.addPreference(mPreferenceCategory);
-
-        mCategoryController = new NetworkProviderSimsCategoryController(
-                mContext, KEY_PREFERENCE_CATEGORY_SIM, mLifecycle, mLifecycleOwner);
-    }
-
-    @Ignore
-    @Test
-    public void getAvailabilityStatus_returnUnavailable() {
-        SubscriptionUtil.setAvailableSubscriptionsForTesting(new ArrayList<>());
-
-        assertThat(mCategoryController.getAvailabilityStatus()).isEqualTo(
-                CONDITIONALLY_UNAVAILABLE);
-    }
-
-    @Ignore
-    @Test
-    public void displayPreference_isVisible() {
-        setUpSubscriptionInfoForPhysicalSim(SUB_ID_1, SUB_1, mSubscriptionInfo1);
-        SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(mSubscriptionInfo1));
-        mCategoryController.displayPreference(mPreferenceScreen);
-
-        assertEquals(mPreferenceCategory.isVisible(), true);
-    }
-
-    @Ignore
-    @Test
-    public void updateState_setTitle_withTwoPhysicalSims_returnSims() {
-        setUpSubscriptionInfoForPhysicalSim(SUB_ID_1, SUB_1, mSubscriptionInfo1);
-        setUpSubscriptionInfoForPhysicalSim(SUB_ID_2, SUB_2, mSubscriptionInfo2);
-        SubscriptionUtil.setAvailableSubscriptionsForTesting(
-                Arrays.asList(mSubscriptionInfo1, mSubscriptionInfo2));
-
-        mCategoryController.displayPreference(mPreferenceScreen);
-        mCategoryController.updateState(mPreferenceCategory);
-
-        assertThat(mPreferenceCategory.getPreferenceCount()).isEqualTo(2);
-        assertThat(mPreferenceCategory.getTitle()).isEqualTo(
-                ResourcesUtils.getResourcesString(mContext, "provider_network_settings_title"));
-    }
-
-    @Ignore
-    @Test
-    public void updateState_setTitle_withOnePhysicalSim_returnSim() {
-        setUpSubscriptionInfoForPhysicalSim(SUB_ID_1, SUB_1, mSubscriptionInfo1);
-        SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(mSubscriptionInfo1));
-
-        mCategoryController.displayPreference(mPreferenceScreen);
-        mCategoryController.updateState(mPreferenceCategory);
-
-        assertThat(mPreferenceCategory.getPreferenceCount()).isEqualTo(1);
-        assertThat(mPreferenceCategory.getTitle()).isEqualTo(
-                ResourcesUtils.getResourcesString(mContext, "sim_category_title"));
-    }
-
-    private void setUpSubscriptionInfoForPhysicalSim(int subId, String displayName,
-            SubscriptionInfo subscriptionInfo) {
-        when(subscriptionInfo.isEmbedded()).thenReturn(false);
-        when(subscriptionInfo.getSubscriptionId()).thenReturn(subId);
-        when(subscriptionInfo.getDisplayName()).thenReturn(displayName);
-    }
-
-}
diff --git a/tests/unit/src/com/android/settings/network/EnabledNetworkModePreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceControllerTest.java
similarity index 96%
rename from tests/unit/src/com/android/settings/network/EnabledNetworkModePreferenceControllerTest.java
rename to tests/unit/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceControllerTest.java
index 7f170b5..1b337ca 100644
--- a/tests/unit/src/com/android/settings/network/EnabledNetworkModePreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceControllerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -46,6 +46,7 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 
+import androidx.fragment.app.FragmentManager;
 import androidx.lifecycle.LifecycleOwner;
 import androidx.preference.ListPreference;
 import androidx.preference.PreferenceManager;
@@ -64,6 +65,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Collections;
+
 @RunWith(AndroidJUnit4.class)
 public class EnabledNetworkModePreferenceControllerTest {
     private static final int SUB_ID = 2;
@@ -80,6 +83,8 @@
     private CarrierConfigCache mCarrierConfigCache;
     @Mock
     private ServiceState mServiceState;
+    @Mock
+    private FragmentManager mFragmentManager;
 
     private PersistableBundle mPersistableBundle;
     private EnabledNetworkModePreferenceController mController;
@@ -111,7 +116,8 @@
         mController = new EnabledNetworkModePreferenceController(mContext, KEY);
         mockAllowedNetworkTypes(ALLOWED_ALL_NETWORK_TYPE);
         mockAccessFamily(TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA);
-        mController.init(SUB_ID);
+        when(mFragmentManager.getFragments()).thenReturn(Collections.emptyList());
+        mController.init(SUB_ID, mFragmentManager);
         mPreference.setKey(mController.getPreferenceKey());
     }
 
@@ -205,7 +211,7 @@
     public void updateState_5gWorldPhone_GlobalHasNr() {
         mockAllowedNetworkTypes(ALLOWED_ALL_NETWORK_TYPE);
         mockAccessFamily(TelephonyManager.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA);
-        mController.init(SUB_ID);
+        mController.init(SUB_ID, mFragmentManager);
         mPersistableBundle.putBoolean(CarrierConfigManager.KEY_WORLD_MODE_ENABLED_BOOL, true);
 
         mController.updateState(mPreference);
@@ -221,7 +227,7 @@
         mockAllowedNetworkTypes(ALLOWED_ALL_NETWORK_TYPE);
         mockEnabledNetworkMode(TelephonyManagerConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA);
         mockAccessFamily(TelephonyManager.NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA);
-        mController.init(SUB_ID);
+        mController.init(SUB_ID, mFragmentManager);
 
         // NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA = NR | LTE | RAF_TD_SCDMA | GSM | WCDMA
         when(mTelephonyManager.getAllowedNetworkTypesForReason(
@@ -241,7 +247,7 @@
         mockEnabledNetworkMode(TelephonyManagerConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA);
         mockAccessFamily(TelephonyManager.NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA);
         mockAllowedNetworkTypes(DISABLED_5G_NETWORK_TYPE);
-        mController.init(SUB_ID);
+        mController.init(SUB_ID, mFragmentManager);
 
         // NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA = NR | LTE | RAF_TD_SCDMA | GSM | WCDMA
         when(mTelephonyManager.getAllowedNetworkTypesForReason(
@@ -261,7 +267,7 @@
         mockEnabledNetworkMode(TelephonyManagerConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA);
         mockAccessFamily(TelephonyManager.NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA);
         mockAllowedNetworkTypes(DISABLED_5G_NETWORK_TYPE);
-        mController.init(SUB_ID);
+        mController.init(SUB_ID, mFragmentManager);
 
         // NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA = NR | LTE | RAF_TD_SCDMA | GSM | WCDMA
         when(mTelephonyManager.getAllowedNetworkTypesForReason(
@@ -281,7 +287,7 @@
         mockAccessFamily(TelephonyManager.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA);
         mockAllowedNetworkTypes(DISABLED_5G_NETWORK_TYPE);
 
-        mController.init(SUB_ID);
+        mController.init(SUB_ID, mFragmentManager);
 
         // NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA = LTE | CDMA | EVDO | GSM | WCDMA
         when(mTelephonyManager.getAllowedNetworkTypesForReason(
@@ -304,7 +310,7 @@
     public void updateState_GlobalDisAllowed5g_GlobalWithoutNR() {
         mockAccessFamily(TelephonyManager.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA);
         mockAllowedNetworkTypes(DISABLED_5G_NETWORK_TYPE);
-        mController.init(SUB_ID);
+        mController.init(SUB_ID, mFragmentManager);
         mPersistableBundle.putBoolean(CarrierConfigManager.KEY_WORLD_MODE_ENABLED_BOOL, true);
 
         // NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA = NR | LTE | CDMA | EVDO | GSM | WCDMA
@@ -324,7 +330,7 @@
     public void updateState_GlobalDisAllowed5g_SelectOnGlobal() {
         mockAccessFamily(TelephonyManager.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA);
         mockAllowedNetworkTypes(DISABLED_5G_NETWORK_TYPE);
-        mController.init(SUB_ID);
+        mController.init(SUB_ID, mFragmentManager);
         mPersistableBundle.putBoolean(CarrierConfigManager.KEY_WORLD_MODE_ENABLED_BOOL, true);
 
         // NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA = NR | LTE | CDMA | EVDO | GSM | WCDMA
@@ -497,7 +503,7 @@
             mPersistableBundle.putBoolean(CarrierConfigManager.KEY_PREFER_2G_BOOL, true);
             mPersistableBundle.putBoolean(CarrierConfigManager.KEY_LTE_ENABLED_BOOL, true);
         }
-        mController.init(SUB_ID);
+        mController.init(SUB_ID, mFragmentManager);
     }
 
     private void mockAllowedNetworkTypes(long allowedNetworkType) {
diff --git a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
index a3a92a3..b5062a0 100644
--- a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -24,6 +24,7 @@
 import com.android.settings.accounts.AccountFeatureProvider;
 import com.android.settings.applications.ApplicationFeatureProvider;
 import com.android.settings.biometrics.face.FaceFeatureProvider;
+import com.android.settings.biometrics.fingerprint.FingerprintFeatureProvider;
 import com.android.settings.biometrics2.factory.BiometricsRepositoryProvider;
 import com.android.settings.bluetooth.BluetoothFeatureProvider;
 import com.android.settings.connecteddevice.fastpair.FastPairFeatureProvider;
@@ -79,6 +80,7 @@
     public final AccountFeatureProvider mAccountFeatureProvider;
     public final BluetoothFeatureProvider mBluetoothFeatureProvider;
     public final FaceFeatureProvider mFaceFeatureProvider;
+    public final FingerprintFeatureProvider mFingerprintFeatureProvider;
     public final BiometricsRepositoryProvider mBiometricsRepositoryProvider;
 
     public PanelFeatureProvider panelFeatureProvider;
@@ -131,6 +133,7 @@
         panelFeatureProvider = mock(PanelFeatureProvider.class);
         mBluetoothFeatureProvider = mock(BluetoothFeatureProvider.class);
         mFaceFeatureProvider = mock(FaceFeatureProvider.class);
+        mFingerprintFeatureProvider = mock(FingerprintFeatureProvider.class);
         mBiometricsRepositoryProvider = mock(BiometricsRepositoryProvider.class);
         wifiTrackerLibProvider = mock(WifiTrackerLibProvider.class);
         securitySettingsFeatureProvider = mock(SecuritySettingsFeatureProvider.class);
@@ -256,6 +259,11 @@
     }
 
     @Override
+    public FingerprintFeatureProvider getFingerprintFeatureProvider() {
+        return mFingerprintFeatureProvider;
+    }
+
+    @Override
     public BiometricsRepositoryProvider getBiometricsRepositoryProvider() {
         return mBiometricsRepositoryProvider;
     }