Merge "Update the limit of split screen dialog strings." into udc-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 21b3fa4..f537415 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2785,6 +2785,7 @@
             </intent-filter>
             <intent-filter>
                 <action android:name="com.android.settings.action.SETTINGS" />
+                <action android:name="com.android.intent.action.SHOW_CONTRAST_DIALOG" />
             </intent-filter>
             <meta-data android:name="com.android.settings.order" android:value="-40"/>
             <meta-data android:name="com.android.settings.category"
diff --git a/res/drawable/ic_settings_safety_center.xml b/res/drawable/ic_settings_safety_center.xml
index e817b71..46684d1 100644
--- a/res/drawable/ic_settings_safety_center.xml
+++ b/res/drawable/ic_settings_safety_center.xml
@@ -19,13 +19,11 @@
     android:viewportWidth="24"
     android:viewportHeight="24"
     android:tint="?android:attr/textColorPrimary">
-
+  <group>
+    <clip-path
+        android:pathData="M4,2h16v20h-16z"/>
     <path
-        android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
-    <path
-        android:fillColor="#000000"
-        android:pathData="M12,4.14l6,2.25v4.71c0,2.12-0.62,4-1.88,5.74c-1.13,1.55-2.48,2.56-4.12,3.09c-1.64-0.52-2.99-1.54-4.12-3.09 C6.62,15.1,6,13.22,6,11.1V6.39L12,4.14 M12,2L4,5v6.1c0,2.53,0.75,4.84,2.26,6.91C7.77,20.09,9.68,21.42,12,22 c2.32-0.58,4.23-1.91,5.74-3.99C19.25,15.94,20,13.63,20,11.1V5L12,2L12,2z" />
-    <path
-        android:fillColor="#000000"
-        android:pathData="M 10.95 15.55 L 7.42 12.02 L 8.85 10.6 L 10.95 12.7 L 15.17 8.47 L 16.6 9.9 Z" />
-</vector>
\ No newline at end of file
+        android:pathData="M18.92,4.4L12.56,2.1C12.38,2.03 12.19,2 12,2C11.81,2 11.62,2.03 11.44,2.1L5.08,4.4C4.43,4.63 4,5.25 4,5.94V10.32C4.02,11.07 4.07,11.79 4.17,12.54C4.64,15.72 6.44,19.33 11.37,21.85C11.57,21.95 11.78,22 12,22C12.22,22 12.43,21.95 12.63,21.85C13.08,21.62 13.5,21.37 13.9,21.12C14.04,21.05 14.18,20.96 14.32,20.86C17.98,18.43 19.41,15.32 19.82,12.54C19.92,11.8 19.98,11.07 19.99,10.32V5.94C19.99,5.25 19.56,4.64 18.91,4.4H18.92ZM12.25,19.78C12.1,19.87 11.9,19.87 11.74,19.78C8.5,17.97 6.62,15.43 6.15,12.27C6.06,11.59 6.01,10.94 6,10.32V6.55C6,6.34 6.13,6.15 6.33,6.08L8.26,5.38C8.11,5.89 8.03,6.44 8.03,7.04C8.04,8.91 9.03,10.68 10.7,11.8C11.15,12.08 12.39,12.89 12.78,13.19C13.27,13.57 13.95,14.21 14.26,14.74C15.29,16.52 14.26,18.46 13.1,19.27C12.83,19.45 12.55,19.62 12.25,19.79V19.78ZM17.85,12.24C17.66,13.49 17.26,14.63 16.65,15.68C16.57,15.04 16.37,14.38 15.99,13.74C15.42,12.75 14.33,11.86 14.01,11.61C13.47,11.19 11.94,10.22 11.79,10.12C10.69,9.39 10.04,8.23 10.03,6.99C10.03,5.01 11.25,4.34 11.98,4.12C12.08,4.09 12.19,4.1 12.29,4.13L17.68,6.08C17.88,6.15 18.01,6.34 18.01,6.55V10.28C18,10.94 17.95,11.59 17.86,12.24H17.85Z"
+        android:fillColor="#000000"/>
+  </group>
+</vector>
diff --git a/res/layout/locale_order_list.xml b/res/layout/locale_order_list.xml
index acde145..5c1db15 100644
--- a/res/layout/locale_order_list.xml
+++ b/res/layout/locale_order_list.xml
@@ -14,11 +14,11 @@
      limitations under the License.
 -->
 
-<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layoutDirection="locale"
-            android:textDirection="locale">
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:layout_width="match_parent"
+             android:layout_height="match_parent"
+             android:layoutDirection="locale"
+             android:textDirection="locale">
 
     <LinearLayout
         android:layout_width="match_parent"
@@ -48,4 +48,4 @@
 
     </LinearLayout>
 
-</androidx.core.widget.NestedScrollView>
+</FrameLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index aa64923..5a5bc27 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4946,6 +4946,8 @@
     <string name="keywords_color_correction">adjust color </string>
     <!-- List of synonyms used in the settings search bar to find the “Color inversion”. [CHAR LIMIT=NONE] -->
     <string name="keywords_color_inversion">turn screen dark, turn screen light</string>
+    <!-- List of synonyms used in the settings search bar to find the “Contrast”. [CHAR LIMIT=NONE] -->
+    <string name="keywords_contrast">color contrast</string>
     <!-- List of synonyms used in the settings search bar to find the “Accessibility Menu”. [CHAR LIMIT=NONE] -->
     <string name="keywords_accessibility_menu"></string>
     <!-- List of synonyms used in the settings search bar to find the “Switch Access”. [CHAR LIMIT=NONE] -->
@@ -11522,9 +11524,12 @@
     <!-- Summary for UWB preference. [CHAR_LIMIT=NONE]-->
     <string name="uwb_settings_summary">Helps identify the relative position of nearby devices that have UWB</string>
 
-    <!-- Summary for UWB preference when airplane mode is disabled. [CHAR_LIMIT=NONE]-->
+    <!-- Summary for UWB preference when airplane mode is enabled. [CHAR_LIMIT=NONE]-->
     <string name="uwb_settings_summary_airplane_mode">Turn off airplane mode to use UWB </string>
 
+    <!-- Summary for UWB preference when UWB is unavailable due to regulatory requirements. [CHAR_LIMIT=NONE]-->
+    <string name="uwb_settings_summary_no_uwb_regulatory">UWB is unavailable in the current location</string>
+
     <!-- Label for the camera use toggle [CHAR LIMIT=40] -->
     <string name="camera_toggle_title">Camera access</string>
     <!-- Label for the camera use toggle [CHAR LIMIT=40] -->
@@ -12021,6 +12026,15 @@
     <!-- Button to close the dialog without saving in screen flash color selection dialog. [CHAR LIMIT=20] -->
     <string name="color_selector_dialog_cancel">Cancel</string>
 
+    <!-- Title for the contrast preference fragment [CHAR LIMIT=35] -->
+    <string name="contrast_title">Contrast</string>
+    <!-- 'Standard' contrast option [CHAR LIMIT=20] -->
+    <string name="contrast_standard">Standard</string>
+    <!-- 'Medium' contrast option [CHAR LIMIT=20] -->
+    <string name="contrast_medium">Medium</string>
+    <!-- 'High' contrast option [CHAR LIMIT=20] -->
+    <string name="contrast_high">High</string>
+
     <!-- Warning message when we try to dock an app not supporting multiple instances split into multiple sides [CHAR LIMIT=NONE] -->
     <string name="dock_multi_instances_not_supported_text">"This app can only be opened in 1 window"</string>
 </resources>
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index 3b514b7..9e1dbad 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -515,6 +515,12 @@
             android:title="@string/transparent_navigation_bar"
             android:summary="@string/transparent_navigation_bar_summary" />
 
+        <Preference
+            android:key="contrast_preference"
+            android:title="@string/contrast_title"
+            android:persistent="false"
+            settings:keywords="@string/keywords_contrast" />
+
     </PreferenceCategory>
 
     <PreferenceCategory
diff --git a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java
index b1b420d..695ea0d 100644
--- a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java
+++ b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.biometrics2.ui.viewmodel;
 
+import static android.hardware.fingerprint.FingerprintManager.ENROLL_ENROLL;
+
 import static com.android.settings.biometrics2.ui.model.EnrollmentProgress.INITIAL_REMAINING;
 import static com.android.settings.biometrics2.ui.model.EnrollmentProgress.INITIAL_STEPS;
 
@@ -63,7 +65,6 @@
     private final int mUserId;
 
     private final FingerprintUpdater mFingerprintUpdater;
-    private final MessageDisplayController mMessageDisplayController;
     @Nullable private CancellationSignal mCancellationSignal = null;
     private final EnrollmentCallback mEnrollmentCallback = new EnrollmentCallback() {
 
@@ -81,6 +82,9 @@
 
         @Override
         public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {
+            if (DEBUG) {
+                Log.d(TAG, "onEnrollmentHelp(" + helpMsgId + ", " + helpString + ")");
+            }
             mHelpMessageLiveData.postValue(new EnrollmentStatusMessage(helpMsgId, helpString));
         }
 
@@ -113,20 +117,6 @@
         super(application);
         mFingerprintUpdater = fingerprintUpdater;
         mUserId = userId;
-
-        final Resources res = application.getResources();
-        mMessageDisplayController =
-                res.getBoolean(R.bool.enrollment_message_display_controller_flag)
-                        ? new MessageDisplayController(
-                                application.getMainThreadHandler(),
-                                mEnrollmentCallback,
-                                SystemClock.elapsedRealtimeClock(),
-                                res.getInteger(R.integer.enrollment_help_minimum_time_display),
-                                res.getInteger(R.integer.enrollment_progress_minimum_time_display),
-                                res.getBoolean(R.bool.enrollment_progress_priority_over_help),
-                                res.getBoolean(R.bool.enrollment_prioritize_acquire_messages),
-                                res.getInteger(R.integer.enrollment_collect_time))
-                        : null;
     }
 
     public void setToken(byte[] token) {
@@ -195,9 +185,24 @@
         mErrorMessageLiveData.setValue(null);
 
         mCancellationSignal = new CancellationSignal();
-        mFingerprintUpdater.enroll(mToken, mCancellationSignal, mUserId,
-                mMessageDisplayController != null ? mMessageDisplayController : mEnrollmentCallback,
-                reason);
+
+        final Resources res = getApplication().getResources();
+        if (reason == ENROLL_ENROLL
+                && res.getBoolean(R.bool.enrollment_message_display_controller_flag)) {
+            final EnrollmentCallback callback = new MessageDisplayController(
+                    getApplication().getMainThreadHandler(),
+                    mEnrollmentCallback,
+                    SystemClock.elapsedRealtimeClock(),
+                    res.getInteger(R.integer.enrollment_help_minimum_time_display),
+                    res.getInteger(R.integer.enrollment_progress_minimum_time_display),
+                    res.getBoolean(R.bool.enrollment_progress_priority_over_help),
+                    res.getBoolean(R.bool.enrollment_prioritize_acquire_messages),
+                    res.getInteger(R.integer.enrollment_collect_time));
+            mFingerprintUpdater.enroll(mToken, mCancellationSignal, mUserId, callback, reason);
+        } else {
+            mFingerprintUpdater.enroll(mToken, mCancellationSignal, mUserId, mEnrollmentCallback,
+                    reason);
+        }
         return true;
     }
 
diff --git a/src/com/android/settings/dashboard/profileselector/ProfileFragmentBridge.java b/src/com/android/settings/dashboard/profileselector/ProfileFragmentBridge.java
index 2bcc02e..1e5145a 100644
--- a/src/com/android/settings/dashboard/profileselector/ProfileFragmentBridge.java
+++ b/src/com/android/settings/dashboard/profileselector/ProfileFragmentBridge.java
@@ -22,6 +22,7 @@
 import com.android.settings.applications.manageapplications.ManageApplications;
 import com.android.settings.deviceinfo.StorageDashboardFragment;
 import com.android.settings.inputmethod.AvailableVirtualKeyboardFragment;
+import com.android.settings.inputmethod.NewKeyboardLayoutEnabledLocalesFragment;
 import com.android.settings.location.LocationServices;
 
 import java.util.Map;
@@ -49,5 +50,7 @@
                 ProfileSelectStorageFragment.class.getName());
         FRAGMENT_MAP.put(AvailableVirtualKeyboardFragment.class.getName(),
                 ProfileSelectKeyboardFragment.class.getName());
+        FRAGMENT_MAP.put(NewKeyboardLayoutEnabledLocalesFragment.class.getName(),
+                ProfileSelectPhysicalKeyboardFragment.class.getName());
     }
 }
diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectPhysicalKeyboardFragment.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectPhysicalKeyboardFragment.java
new file mode 100644
index 0000000..3c1546e
--- /dev/null
+++ b/src/com/android/settings/dashboard/profileselector/ProfileSelectPhysicalKeyboardFragment.java
@@ -0,0 +1,72 @@
+/*
+ * 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.dashboard.profileselector;
+
+import android.hardware.input.InputDeviceIdentifier;
+import android.os.Bundle;
+import android.provider.Settings;
+
+import androidx.fragment.app.Fragment;
+
+import com.android.settings.R;
+import com.android.settings.inputmethod.NewKeyboardLayoutEnabledLocalesFragment;
+
+/**
+ * When current user has work profile, this fragment used following fragments to represent the
+ * enabled IMEs keyboard layout settings page.
+ *
+ * <p>{@link NewKeyboardLayoutEnabledLocalesFragment} used to show both of personal/work user
+ * enabled IMEs and their physical keyboard layouts.</p>
+ */
+public final class ProfileSelectPhysicalKeyboardFragment extends ProfileSelectFragment {
+
+    private InputDeviceIdentifier mInputDeviceIdentifier;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        Bundle arguments = getArguments();
+        mInputDeviceIdentifier =
+                arguments.getParcelable(Settings.EXTRA_INPUT_DEVICE_IDENTIFIER);
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.keyboard_settings_enabled_locales_list;
+    }
+
+    @Override
+    public Fragment[] getFragments() {
+        final Bundle personalOnly = new Bundle();
+        personalOnly.putInt(EXTRA_PROFILE, ProfileType.PERSONAL);
+        final Fragment personalFragment = new NewKeyboardLayoutEnabledLocalesFragment();
+        personalOnly.putParcelable(
+                Settings.EXTRA_INPUT_DEVICE_IDENTIFIER, mInputDeviceIdentifier);
+        personalFragment.setArguments(personalOnly);
+
+        final Bundle workOnly = new Bundle();
+        workOnly.putInt(EXTRA_PROFILE, ProfileType.WORK);
+        final Fragment workFragment = new NewKeyboardLayoutEnabledLocalesFragment();
+        workOnly.putParcelable(Settings.EXTRA_INPUT_DEVICE_IDENTIFIER, mInputDeviceIdentifier);
+        workFragment.setArguments(workOnly);
+
+        return new Fragment[]{
+                personalFragment,
+                workFragment
+        };
+    }
+}
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index 3180a79..68fe08e 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -19,6 +19,7 @@
 import static android.service.quicksettings.TileService.ACTION_QS_TILE_PREFERENCES;
 
 import android.app.Activity;
+import android.app.UiModeManager;
 import android.app.settings.SettingsEnums;
 import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothAdapter;
@@ -63,6 +64,7 @@
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.actionbar.SearchMenuController;
+import com.android.settings.theme.ContrastPreferenceController;
 import com.android.settings.widget.SettingsMainSwitchBar;
 import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.core.lifecycle.Lifecycle;
@@ -685,6 +687,8 @@
         controllers.add(new IngressRateLimitPreferenceController((context)));
         controllers.add(new BackAnimationPreferenceController(context, fragment));
         controllers.add(new PhantomProcessPreferenceController(context));
+        controllers.add(new ContrastPreferenceController(
+                context, context.getSystemService(UiModeManager.class)));
 
         return controllers;
     }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java
index 0be6c98..2121c60 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java
@@ -23,6 +23,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.Log;
 import android.view.View;
 import android.widget.AdapterView;
@@ -48,9 +49,11 @@
 import com.android.settingslib.core.lifecycle.events.OnResume;
 import com.android.settingslib.widget.FooterPreference;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /** Controller for battery usage breakdown preference group. */
 public class BatteryUsageBreakdownController extends BasePreferenceController
@@ -61,6 +64,7 @@
     private static final String SPINNER_PREFERENCE_KEY = "battery_usage_spinner";
     private static final String APP_LIST_PREFERENCE_KEY = "app_list";
     private static final String PACKAGE_NAME_NONE = "none";
+    private static final List<BatteryDiffEntry> EMPTY_ENTRY_LIST = new ArrayList<>();
 
     private static int sUiMode = Configuration.UI_MODE_NIGHT_UNDEFINED;
 
@@ -183,7 +187,7 @@
                         if (mSpinnerPosition != position) {
                             mSpinnerPosition = position;
                             mHandler.post(() -> {
-                                removeAndCacheAllPreferences();
+                                removeAndCacheAllUnusedPreferences();
                                 addAllPreferences();
                                 mMetricsFeatureProvider.action(
                                         mPrefContext,
@@ -238,27 +242,34 @@
     private void showSpinnerAndAppList() {
         if (mBatteryDiffData == null) {
             mHandler.post(() -> {
-                removeAndCacheAllPreferences();
+                removeAndCacheAllUnusedPreferences();
             });
             return;
         }
         mSpinnerPreference.setVisible(true);
         mAppListPreferenceGroup.setVisible(true);
         mHandler.post(() -> {
-            removeAndCacheAllPreferences();
+            removeAndCacheAllUnusedPreferences();
             addAllPreferences();
         });
     }
 
+    private List<BatteryDiffEntry> getBatteryDiffEntries() {
+        if (mBatteryDiffData == null) {
+            return EMPTY_ENTRY_LIST;
+        }
+        return mSpinnerPosition == 0
+                ? mBatteryDiffData.getAppDiffEntryList()
+                : mBatteryDiffData.getSystemDiffEntryList();
+    }
+
     @VisibleForTesting
     void addAllPreferences() {
         if (mBatteryDiffData == null) {
             return;
         }
         final long start = System.currentTimeMillis();
-        final List<BatteryDiffEntry> entries = mSpinnerPosition == 0
-                ? mBatteryDiffData.getAppDiffEntryList()
-                : mBatteryDiffData.getSystemDiffEntryList();
+        final List<BatteryDiffEntry> entries = getBatteryDiffEntries();
         int prefIndex = mAppListPreferenceGroup.getPreferenceCount();
         for (BatteryDiffEntry entry : entries) {
             boolean isAdded = false;
@@ -272,7 +283,6 @@
             PowerGaugePreference pref = mAppListPreferenceGroup.findPreference(prefKey);
             if (pref != null) {
                 isAdded = true;
-                Log.w(TAG, "preference should be removed for:" + entry.getPackageName());
             } else {
                 pref = (PowerGaugePreference) mPreferenceCache.get(prefKey);
             }
@@ -301,16 +311,25 @@
     }
 
     @VisibleForTesting
-    void removeAndCacheAllPreferences() {
+    void removeAndCacheAllUnusedPreferences() {
+        List<BatteryDiffEntry> entries = getBatteryDiffEntries();
+        Set<String> entryKeySet = new ArraySet<>();
+        for (BatteryDiffEntry entry : entries) {
+            entryKeySet.add(entry.getKey());
+        }
+
         final int prefsCount = mAppListPreferenceGroup.getPreferenceCount();
-        for (int index = 0; index < prefsCount; index++) {
+        for (int index = prefsCount - 1; index >= 0; index--) {
             final Preference pref = mAppListPreferenceGroup.getPreference(index);
-            if (TextUtils.isEmpty(pref.getKey())) {
+            if (entryKeySet.contains(pref.getKey())) {
+                // The pref is still used, don't remove.
                 continue;
             }
-            mPreferenceCache.put(pref.getKey(), pref);
+            if (!TextUtils.isEmpty(pref.getKey())) {
+                mPreferenceCache.put(pref.getKey(), pref);
+            }
+            mAppListPreferenceGroup.removePreference(pref);
         }
-        mAppListPreferenceGroup.removeAll();
     }
 
     @VisibleForTesting
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
index 6270eb3..ec1a4be 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
@@ -1446,31 +1446,39 @@
         final int workProfileUserId =
                 userHandle != null ? userHandle.getIdentifier() : Integer.MIN_VALUE;
         // Each time slot usage diff data =
-        //     Math.abs(timestamp[i+2] data - timestamp[i+1] data) +
-        //     Math.abs(timestamp[i+1] data - timestamp[i] data);
-        // since we want to aggregate every two hours data into a single time slot.
+        //     sum(Math.abs(timestamp[i+1] data - timestamp[i] data));
+        // since we want to aggregate every hour usage diff data into a single time slot.
         for (int dailyIndex = 0; dailyIndex < hourlyBatteryLevelsPerDay.size(); dailyIndex++) {
             final Map<Integer, BatteryDiffData> dailyDiffMap = new ArrayMap<>();
             resultMap.put(dailyIndex, dailyDiffMap);
             if (hourlyBatteryLevelsPerDay.get(dailyIndex) == null) {
                 continue;
             }
-            final List<Long> timestamps = hourlyBatteryLevelsPerDay.get(dailyIndex).getTimestamps();
-            for (int hourlyIndex = 0; hourlyIndex < timestamps.size() - 1; hourlyIndex++) {
+            final List<Long> hourlyTimestamps =
+                    hourlyBatteryLevelsPerDay.get(dailyIndex).getTimestamps();
+            for (int hourlyIndex = 0; hourlyIndex < hourlyTimestamps.size() - 1; hourlyIndex++) {
+                final Long startTimestamp = hourlyTimestamps.get(hourlyIndex);
+                final Long endTimestamp = hourlyTimestamps.get(hourlyIndex + 1);
+                final long slotDuration = endTimestamp - startTimestamp;
+                List<Map<String, BatteryHistEntry>> slotBatteryHistoryList = new ArrayList<>();
+                for (Long timestamp = startTimestamp; timestamp <= endTimestamp;
+                        timestamp += DateUtils.HOUR_IN_MILLIS) {
+                    slotBatteryHistoryList.add(
+                            batteryHistoryMap.getOrDefault(timestamp, EMPTY_BATTERY_MAP));
+                }
                 final BatteryDiffData hourlyBatteryDiffData =
                         insertHourlyUsageDiffDataPerSlot(
                                 context,
                                 currentUserId,
                                 workProfileUserId,
-                                hourlyIndex,
-                                timestamps,
+                                slotDuration,
                                 systemAppsPackageNames,
                                 systemAppsUids,
                                 appUsagePeriodMap == null
                                         || appUsagePeriodMap.get(dailyIndex) == null
                                         ? null
                                         : appUsagePeriodMap.get(dailyIndex).get(hourlyIndex),
-                                batteryHistoryMap);
+                                slotBatteryHistoryList);
                 dailyDiffMap.put(hourlyIndex, hourlyBatteryDiffData);
             }
         }
@@ -1508,54 +1516,42 @@
             final Context context,
             final int currentUserId,
             final int workProfileUserId,
-            final int currentIndex,
-            final List<Long> timestamps,
+            final long slotDuration,
             final Set<String> systemAppsPackageNames,
             final Set<Integer> systemAppsUids,
             final Map<Long, Map<String, List<AppUsagePeriod>>> appUsageMap,
-            final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
+            final List<Map<String, BatteryHistEntry>> slotBatteryHistoryList) {
         final List<BatteryDiffEntry> appEntries = new ArrayList<>();
         final List<BatteryDiffEntry> systemEntries = new ArrayList<>();
 
-        final Long currentTimestamp = timestamps.get(currentIndex);
-        final Long nextTimestamp = currentTimestamp + DateUtils.HOUR_IN_MILLIS;
-        final Long nextTwoTimestamp = nextTimestamp + DateUtils.HOUR_IN_MILLIS;
-        // Fetches BatteryHistEntry data from corresponding time slot.
-        final Map<String, BatteryHistEntry> currentBatteryHistMap =
-                batteryHistoryMap.getOrDefault(currentTimestamp, EMPTY_BATTERY_MAP);
-        final Map<String, BatteryHistEntry> nextBatteryHistMap =
-                batteryHistoryMap.getOrDefault(nextTimestamp, EMPTY_BATTERY_MAP);
-        final Map<String, BatteryHistEntry> nextTwoBatteryHistMap =
-                batteryHistoryMap.getOrDefault(nextTwoTimestamp, EMPTY_BATTERY_MAP);
-        // We should not get the empty list since we have at least one fake data to record
-        // the battery level and status in each time slot, the empty list is used to
-        // represent there is no enough data to apply interpolation arithmetic.
-        if (currentBatteryHistMap.isEmpty()
-                || nextBatteryHistMap.isEmpty()
-                || nextTwoBatteryHistMap.isEmpty()) {
-            return null;
-        }
-
         // Collects all keys in these three time slot records as all populations.
         final Set<String> allBatteryHistEntryKeys = new ArraySet<>();
-        allBatteryHistEntryKeys.addAll(currentBatteryHistMap.keySet());
-        allBatteryHistEntryKeys.addAll(nextBatteryHistMap.keySet());
-        allBatteryHistEntryKeys.addAll(nextTwoBatteryHistMap.keySet());
+        for (Map<String, BatteryHistEntry> slotBatteryHistMap : slotBatteryHistoryList) {
+            if (slotBatteryHistMap.isEmpty()) {
+                // We should not get the empty list since we have at least one fake data to record
+                // the battery level and status in each time slot, the empty list is used to
+                // represent there is no enough data to apply interpolation arithmetic.
+                return null;
+            }
+            allBatteryHistEntryKeys.addAll(slotBatteryHistMap.keySet());
+        }
 
         // Calculates all packages diff usage data in a specific time slot.
         for (String key : allBatteryHistEntryKeys) {
             if (key == null) {
                 continue;
             }
-            final BatteryHistEntry currentEntry =
-                    currentBatteryHistMap.getOrDefault(key, EMPTY_BATTERY_HIST_ENTRY);
-            final BatteryHistEntry nextEntry =
-                    nextBatteryHistMap.getOrDefault(key, EMPTY_BATTERY_HIST_ENTRY);
-            final BatteryHistEntry nextTwoEntry =
-                    nextTwoBatteryHistMap.getOrDefault(key, EMPTY_BATTERY_HIST_ENTRY);
 
-            final BatteryHistEntry selectedBatteryEntry =
-                    selectBatteryHistEntry(currentEntry, nextEntry, nextTwoEntry);
+            BatteryHistEntry selectedBatteryEntry = null;
+            final List<BatteryHistEntry> batteryHistEntries = new ArrayList<>();
+            for (Map<String, BatteryHistEntry> slotBatteryHistMap : slotBatteryHistoryList) {
+                BatteryHistEntry entry =
+                        slotBatteryHistMap.getOrDefault(key, EMPTY_BATTERY_HIST_ENTRY);
+                batteryHistEntries.add(entry);
+                if (selectedBatteryEntry == null && entry != EMPTY_BATTERY_HIST_ENTRY) {
+                    selectedBatteryEntry = entry;
+                }
+            }
             if (selectedBatteryEntry == null) {
                 continue;
             }
@@ -1568,41 +1564,45 @@
             }
 
             // Cumulative values is a specific time slot for a specific app.
-            long foregroundUsageTimeInMs =
-                    getDiffValue(
-                            currentEntry.mForegroundUsageTimeInMs,
-                            nextEntry.mForegroundUsageTimeInMs,
-                            nextTwoEntry.mForegroundUsageTimeInMs);
-            long backgroundUsageTimeInMs =
-                    getDiffValue(
-                            currentEntry.mBackgroundUsageTimeInMs,
-                            nextEntry.mBackgroundUsageTimeInMs,
-                            nextTwoEntry.mBackgroundUsageTimeInMs);
-            double consumePower =
-                    getDiffValue(
-                            currentEntry.mConsumePower,
-                            nextEntry.mConsumePower,
-                            nextTwoEntry.mConsumePower);
-            double foregroundUsageConsumePower =
-                    getDiffValue(
-                            currentEntry.mForegroundUsageConsumePower,
-                            nextEntry.mForegroundUsageConsumePower,
-                            nextTwoEntry.mForegroundUsageConsumePower);
-            double foregroundServiceUsageConsumePower =
-                    getDiffValue(
-                            currentEntry.mForegroundServiceUsageConsumePower,
-                            nextEntry.mForegroundServiceUsageConsumePower,
-                            nextTwoEntry.mForegroundServiceUsageConsumePower);
-            double backgroundUsageConsumePower =
-                    getDiffValue(
-                            currentEntry.mBackgroundUsageConsumePower,
-                            nextEntry.mBackgroundUsageConsumePower,
-                            nextTwoEntry.mBackgroundUsageConsumePower);
-            double cachedUsageConsumePower =
-                    getDiffValue(
-                            currentEntry.mCachedUsageConsumePower,
-                            nextEntry.mCachedUsageConsumePower,
-                            nextTwoEntry.mCachedUsageConsumePower);
+            long foregroundUsageTimeInMs = 0;
+            long backgroundUsageTimeInMs = 0;
+            double consumePower = 0;
+            double foregroundUsageConsumePower = 0;
+            double foregroundServiceUsageConsumePower = 0;
+            double backgroundUsageConsumePower = 0;
+            double cachedUsageConsumePower = 0;
+            for (int i = 0; i < batteryHistEntries.size() - 1; i++) {
+                final BatteryHistEntry currentEntry = batteryHistEntries.get(i);
+                final BatteryHistEntry nextEntry = batteryHistEntries.get(i + 1);
+                foregroundUsageTimeInMs +=
+                        getDiffValue(
+                                currentEntry.mForegroundUsageTimeInMs,
+                                nextEntry.mForegroundUsageTimeInMs);
+                backgroundUsageTimeInMs +=
+                        getDiffValue(
+                                currentEntry.mBackgroundUsageTimeInMs,
+                                nextEntry.mBackgroundUsageTimeInMs);
+                consumePower +=
+                        getDiffValue(
+                                currentEntry.mConsumePower,
+                                nextEntry.mConsumePower);
+                foregroundUsageConsumePower +=
+                        getDiffValue(
+                                currentEntry.mForegroundUsageConsumePower,
+                                nextEntry.mForegroundUsageConsumePower);
+                foregroundServiceUsageConsumePower +=
+                        getDiffValue(
+                                currentEntry.mForegroundServiceUsageConsumePower,
+                                nextEntry.mForegroundServiceUsageConsumePower);
+                backgroundUsageConsumePower +=
+                        getDiffValue(
+                                currentEntry.mBackgroundUsageConsumePower,
+                                nextEntry.mBackgroundUsageConsumePower);
+                cachedUsageConsumePower +=
+                        getDiffValue(
+                                currentEntry.mCachedUsageConsumePower,
+                                nextEntry.mCachedUsageConsumePower);
+            }
             // Excludes entry since we don't have enough data to calculate.
             if (foregroundUsageTimeInMs == 0
                     && backgroundUsageTimeInMs == 0
@@ -1613,13 +1613,13 @@
             // will apply the interpolation arithmetic.
             final float totalUsageTimeInMs =
                     foregroundUsageTimeInMs + backgroundUsageTimeInMs;
-            if (totalUsageTimeInMs > TOTAL_HOURLY_TIME_THRESHOLD) {
-                final float ratio = TOTAL_HOURLY_TIME_THRESHOLD / totalUsageTimeInMs;
+            if (totalUsageTimeInMs > slotDuration) {
+                final float ratio = slotDuration / totalUsageTimeInMs;
                 if (sDebug) {
                     Log.w(TAG, String.format("abnormal usage time %d|%d for:\n%s",
                             Duration.ofMillis(foregroundUsageTimeInMs).getSeconds(),
                             Duration.ofMillis(backgroundUsageTimeInMs).getSeconds(),
-                            currentEntry));
+                            selectedBatteryEntry));
                 }
                 foregroundUsageTimeInMs =
                         Math.round(foregroundUsageTimeInMs * ratio);
@@ -1634,14 +1634,14 @@
 
             // Compute the screen on time and make sure it won't exceed the threshold.
             final long screenOnTime = Math.min(
-                    (long) TOTAL_HOURLY_TIME_THRESHOLD,
+                    (long) slotDuration,
                     getScreenOnTime(
                             appUsageMap,
                             selectedBatteryEntry.mUserId,
                             selectedBatteryEntry.mPackageName));
             // Make sure the background + screen-on time will not exceed the threshold.
             backgroundUsageTimeInMs = Math.min(
-                    backgroundUsageTimeInMs, (long) TOTAL_HOURLY_TIME_THRESHOLD - screenOnTime);
+                    backgroundUsageTimeInMs, (long) slotDuration - screenOnTime);
             final BatteryDiffEntry currentBatteryDiffEntry = new BatteryDiffEntry(
                     context,
                     foregroundUsageTimeInMs,
@@ -1948,23 +1948,12 @@
         return calendar.getTimeInMillis();
     }
 
-    private static long getDiffValue(long v1, long v2, long v3) {
-        return (v2 > v1 ? v2 - v1 : 0) + (v3 > v2 ? v3 - v2 : 0);
+    private static long getDiffValue(long v1, long v2) {
+        return v2 > v1 ? v2 - v1 : 0;
     }
 
-    private static double getDiffValue(double v1, double v2, double v3) {
-        return (v2 > v1 ? v2 - v1 : 0) + (v3 > v2 ? v3 - v2 : 0);
-    }
-
-    @Nullable
-    private static BatteryHistEntry selectBatteryHistEntry(
-            final BatteryHistEntry... batteryHistEntries) {
-        for (BatteryHistEntry entry : batteryHistEntries) {
-            if (entry != null && entry != EMPTY_BATTERY_HIST_ENTRY) {
-                return entry;
-            }
-        }
-        return null;
+    private static double getDiffValue(double v1, double v2) {
+        return v2 > v1 ? v2 - v1 : 0;
     }
 
     private static Set<String> getSystemAppsPackageNames(Context context) {
diff --git a/src/com/android/settings/inputmethod/NewKeyboardLayoutEnabledLocalesFragment.java b/src/com/android/settings/inputmethod/NewKeyboardLayoutEnabledLocalesFragment.java
index 3344f4e..054ce61 100644
--- a/src/com/android/settings/inputmethod/NewKeyboardLayoutEnabledLocalesFragment.java
+++ b/src/com/android/settings/inputmethod/NewKeyboardLayoutEnabledLocalesFragment.java
@@ -23,6 +23,7 @@
 import android.hardware.input.KeyboardLayout;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.util.Log;
 import android.view.InputDevice;
 import android.view.inputmethod.InputMethodInfo;
@@ -34,8 +35,10 @@
 import androidx.preference.PreferenceScreen;
 
 import com.android.settings.R;
+import com.android.settings.Utils;
 import com.android.settings.core.SubSettingLauncher;
 import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
 import com.android.settings.inputmethod.NewKeyboardSettingsUtils.KeyboardInfo;
 
 import java.util.ArrayList;
@@ -57,6 +60,39 @@
     private ArrayList<KeyboardInfo> mKeyboardInfoList = new ArrayList<>();
 
     @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+
+        mContext = context;
+        final int profileType = getArguments().getInt(ProfileSelectFragment.EXTRA_PROFILE);
+        final int currentUserId = UserHandle.myUserId();
+        final int newUserId;
+        final UserManager userManager = mContext.getSystemService(UserManager.class);
+
+        switch (profileType) {
+            case ProfileSelectFragment.ProfileType.WORK: {
+                // If the user is a managed profile user, use currentUserId directly. Or get the
+                // managed profile userId instead.
+                newUserId = userManager.isManagedProfile()
+                        ? currentUserId : Utils.getManagedProfileId(userManager, currentUserId);
+                break;
+            }
+            case ProfileSelectFragment.ProfileType.PERSONAL: {
+                final UserHandle primaryUser = userManager.getPrimaryUser().getUserHandle();
+                newUserId = primaryUser.getIdentifier();
+                break;
+            }
+            default:
+                newUserId = currentUserId;
+        }
+
+        mUserId = newUserId;
+        mIm = mContext.getSystemService(InputManager.class);
+        mImm = mContext.getSystemService(InputMethodManager.class);
+        mInputDeviceId = -1;
+    }
+
+    @Override
     public void onActivityCreated(final Bundle icicle) {
         super.onActivityCreated(icicle);
         Bundle arguments = getArguments();
@@ -74,13 +110,39 @@
         }
         final String title = inputDevice.getName();
         getActivity().setTitle(title);
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        mIm.registerInputDeviceListener(this, null);
+        InputDevice inputDevice =
+                NewKeyboardSettingsUtils.getInputDevice(mIm, mInputDeviceIdentifier);
+        if (inputDevice == null) {
+            getActivity().finish();
+            return;
+        }
+        mInputDeviceId = inputDevice.getId();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
         updateCheckedState();
     }
 
+    @Override
+    public void onStop() {
+        super.onStop();
+        mIm.unregisterInputDeviceListener(this);
+        mInputDeviceId = -1;
+    }
+
     private void updateCheckedState() {
         if (NewKeyboardSettingsUtils.getInputDevice(mIm, mInputDeviceIdentifier) == null) {
             return;
         }
+
         PreferenceScreen preferenceScreen = getPreferenceScreen();
         preferenceScreen.removeAll();
         List<InputMethodInfo> infoList = mImm.getEnabledInputMethodListAsUser(mUserId);
@@ -95,7 +157,7 @@
         for (InputMethodInfo info : infoList) {
             mKeyboardInfoList.clear();
             List<InputMethodSubtype> subtypes =
-                    mImm.getEnabledInputMethodSubtypeList(info, true);
+                    mImm.getEnabledInputMethodSubtypeListAsUser(info.getId(), true, mUserId);
             for (InputMethodSubtype subtype : subtypes) {
                 if (subtype.isSuitableForPhysicalKeyboardLayoutMapping()) {
                     mapLanguageWithLayout(info, subtype);
@@ -189,42 +251,6 @@
     }
 
     @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        mContext = getContext();
-        mIm = mContext.getSystemService(InputManager.class);
-        mImm = mContext.getSystemService(InputMethodManager.class);
-        mInputDeviceId = -1;
-        mUserId = UserHandle.myUserId();
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        mIm.registerInputDeviceListener(this, null);
-        InputDevice inputDevice =
-                NewKeyboardSettingsUtils.getInputDevice(mIm, mInputDeviceIdentifier);
-        if (inputDevice == null) {
-            getActivity().finish();
-            return;
-        }
-        mInputDeviceId = inputDevice.getId();
-    }
-
-    @Override
-    public void onStop() {
-        super.onStop();
-        mIm.unregisterInputDeviceListener(this);
-        mInputDeviceId = -1;
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        updateCheckedState();
-    }
-
-    @Override
     protected String getLogTag() {
         return TAG;
     }
diff --git a/src/com/android/settings/localepicker/LocaleListEditor.java b/src/com/android/settings/localepicker/LocaleListEditor.java
index bdb9295..6317f24 100644
--- a/src/com/android/settings/localepicker/LocaleListEditor.java
+++ b/src/com/android/settings/localepicker/LocaleListEditor.java
@@ -329,6 +329,7 @@
         list.setLayoutManager(llm);
 
         list.setHasFixedSize(true);
+        list.setNestedScrollingEnabled(false);
         mAdapter.setRecyclerView(list);
         list.setAdapter(mAdapter);
 
diff --git a/src/com/android/settings/network/AirplaneModePreferenceController.java b/src/com/android/settings/network/AirplaneModePreferenceController.java
index 720a334..5b4ce1b 100644
--- a/src/com/android/settings/network/AirplaneModePreferenceController.java
+++ b/src/com/android/settings/network/AirplaneModePreferenceController.java
@@ -79,7 +79,7 @@
 
     @Override
     public boolean handlePreferenceTreeClick(Preference preference) {
-        if (KEY_AIRPLANE_MODE.equals(preference.getKey())
+        if (KEY_AIRPLANE_MODE.equals(preference.getKey()) && isAvailable()
                 && mAirplaneModeEnabler.isInEcmMode()) {
             // In ECM mode launch ECM app dialog
             if (mFragment != null) {
@@ -141,12 +141,14 @@
 
     @Override
     public void onDestroy() {
-        mAirplaneModeEnabler.close();
+        if (isAvailable()) {
+            mAirplaneModeEnabler.close();
+        }
     }
 
 
     public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        if (requestCode == REQUEST_CODE_EXIT_ECM) {
+        if (requestCode == REQUEST_CODE_EXIT_ECM && isAvailable()) {
             final boolean isChoiceYes = (resultCode == Activity.RESULT_OK);
             // Set Airplane mode based on the return value and checkbox state
             mAirplaneModeEnabler.setAirplaneModeInECM(isChoiceYes,
@@ -156,7 +158,7 @@
 
     @Override
     public boolean isChecked() {
-        return mAirplaneModeEnabler.isAirplaneModeOn();
+        return isAvailable() && mAirplaneModeEnabler.isAirplaneModeOn();
     }
 
     @Override
@@ -164,7 +166,9 @@
         if (isChecked() == isChecked) {
             return false;
         }
-        mAirplaneModeEnabler.setAirplaneMode(isChecked);
+        if (isAvailable()) {
+            mAirplaneModeEnabler.setAirplaneMode(isChecked);
+        }
         return true;
     }
 
diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
index ce83053..83d2117 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
@@ -351,9 +351,14 @@
     }
 
     @Override
+    public void onPause() {
+        mMobileNetworkRepository.removeRegister(this);
+        super.onPause();
+    }
+
+    @Override
     public void onDestroy() {
         super.onDestroy();
-        mMobileNetworkRepository.removeRegister(this);
     }
 
     @VisibleForTesting
diff --git a/src/com/android/settings/nfc/NfcEnabler.java b/src/com/android/settings/nfc/NfcEnabler.java
index 88482d5..fe8762b8 100644
--- a/src/com/android/settings/nfc/NfcEnabler.java
+++ b/src/com/android/settings/nfc/NfcEnabler.java
@@ -18,9 +18,6 @@
 
 import android.content.Context;
 import android.nfc.NfcAdapter;
-import android.provider.Settings;
-
-import androidx.annotation.VisibleForTesting;
 
 import com.android.settingslib.widget.MainSwitchPreference;
 
@@ -41,7 +38,7 @@
         switch (newState) {
             case NfcAdapter.STATE_OFF:
                 mPreference.updateStatus(false);
-                mPreference.setEnabled(isToggleable());
+                mPreference.setEnabled(true);
                 break;
             case NfcAdapter.STATE_ON:
                 mPreference.updateStatus(true);
@@ -57,15 +54,4 @@
                 break;
         }
     }
-
-    @VisibleForTesting
-    boolean isToggleable() {
-        if (NfcPreferenceController.isToggleableInAirplaneMode(mContext)
-                || !NfcPreferenceController.shouldTurnOffNFCInAirplaneMode(mContext)) {
-            return true;
-        }
-        final int airplaneMode = Settings.Global.getInt(
-                mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0);
-        return airplaneMode != 1;
-    }
 }
diff --git a/src/com/android/settings/nfc/NfcPreferenceController.java b/src/com/android/settings/nfc/NfcPreferenceController.java
index 483fead..2ba00c6 100644
--- a/src/com/android/settings/nfc/NfcPreferenceController.java
+++ b/src/com/android/settings/nfc/NfcPreferenceController.java
@@ -21,7 +21,6 @@
 import android.content.IntentFilter;
 import android.net.Uri;
 import android.nfc.NfcAdapter;
-import android.provider.Settings;
 import android.util.Log;
 import android.widget.Switch;
 
@@ -128,18 +127,6 @@
         }
     }
 
-    public static boolean shouldTurnOffNFCInAirplaneMode(Context context) {
-        final String airplaneModeRadios = Settings.Global.getString(context.getContentResolver(),
-                Settings.Global.AIRPLANE_MODE_RADIOS);
-        return airplaneModeRadios != null && airplaneModeRadios.contains(Settings.Global.RADIO_NFC);
-    }
-
-    public static boolean isToggleableInAirplaneMode(Context context) {
-        final String toggleable = Settings.Global.getString(context.getContentResolver(),
-                Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
-        return toggleable != null && toggleable.contains(Settings.Global.RADIO_NFC);
-    }
-
     /**
      * Listener for background changes to NFC.
      *
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java b/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java
index 1bb6df0..f4cfabc 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java
@@ -17,6 +17,7 @@
 // TODO (b/35202196): move this class out of the root of the package.
 package com.android.settings.password;
 
+import static android.app.Activity.RESULT_FIRST_USER;
 import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LOCK_ATTEMPTS_FAILED;
 
 import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
@@ -24,7 +25,6 @@
 import android.annotation.Nullable;
 import android.app.Dialog;
 import android.app.KeyguardManager;
-import android.app.RemoteLockscreenValidationResult;
 import android.app.RemoteLockscreenValidationSession;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.ManagedSubscriptionsPolicy;
@@ -38,7 +38,6 @@
 import android.os.Handler;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.service.remotelockscreenvalidation.IRemoteLockscreenValidationCallback;
 import android.service.remotelockscreenvalidation.RemoteLockscreenValidationClient;
 import android.telecom.TelecomManager;
 import android.text.TextUtils;
@@ -55,14 +54,11 @@
 
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockscreenCredential;
-import com.android.security.SecureBox;
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settings.core.InstrumentedFragment;
 
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.PublicKey;
+import com.google.android.setupdesign.GlifLayout;
 
 /**
  * Base fragment to be shared for PIN/Pattern/Password confirmation fragments.
@@ -89,9 +85,13 @@
     /** Time we wait before clearing a wrong input attempt (e.g. pattern) and the error message. */
     protected static final long CLEAR_WRONG_ATTEMPT_TIMEOUT_MS = 3000;
 
+    protected static final String FRAGMENT_TAG_REMOTE_LOCKSCREEN_VALIDATION =
+            "remote_lockscreen_validation";
+
     protected boolean mReturnCredentials = false;
     protected boolean mReturnGatekeeperPassword = false;
     protected boolean mForceVerifyPath = false;
+    protected GlifLayout mGlifLayout;
     protected CheckBox mCheckBox;
     protected Button mCancelButton;
     /** Button allowing managed profile password reset, null when is not shown. */
@@ -109,8 +109,8 @@
     protected BiometricManager mBiometricManager;
     @Nullable protected RemoteLockscreenValidationSession mRemoteLockscreenValidationSession;
     /** Credential saved so the credential can be set for device if remote validation passes */
-    @Nullable protected LockscreenCredential mDeviceCredentialGuess;
     @Nullable protected RemoteLockscreenValidationClient mRemoteLockscreenValidationClient;
+    protected RemoteLockscreenValidationFragment mRemoteLockscreenValidationFragment;
 
     private boolean isInternalActivity() {
         return (getActivity() instanceof ConfirmLockPassword.InternalActivity)
@@ -136,8 +136,8 @@
                     FeatureFlagUtils.SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION)) {
                 mRemoteValidation = true;
             } else {
-                Log.e(TAG, "Remote device credential validation not enabled.");
-                getActivity().finish();
+                onRemoteLockscreenValidationFailure(
+                        "Remote lockscreen validation not enabled.");
             }
         }
         if (mRemoteValidation) {
@@ -146,23 +146,31 @@
                     RemoteLockscreenValidationSession.class);
             if (mRemoteLockscreenValidationSession == null
                     || mRemoteLockscreenValidationSession.getRemainingAttempts() == 0) {
-                Log.e(TAG, "RemoteLockscreenValidationSession is null or "
+                onRemoteLockscreenValidationFailure("RemoteLockscreenValidationSession is null or "
                         + "no more attempts for remote lockscreen validation.");
-                getActivity().finish();
             }
 
             ComponentName remoteLockscreenValidationServiceComponent =
                     intent.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME, ComponentName.class);
             if (remoteLockscreenValidationServiceComponent == null) {
-                Log.e(TAG, "RemoteLockscreenValidationService ComponentName is null");
-                getActivity().finish();
+                onRemoteLockscreenValidationFailure(
+                        "RemoteLockscreenValidationService ComponentName is null");
             }
             mRemoteLockscreenValidationClient = RemoteLockscreenValidationClient
                     .create(getContext(), remoteLockscreenValidationServiceComponent);
             if (!mRemoteLockscreenValidationClient.isServiceAvailable()) {
-                Log.e(TAG, String.format("RemoteLockscreenValidationService at %s is not available",
+                onRemoteLockscreenValidationFailure(String.format(
+                        "RemoteLockscreenValidationService at %s is not available",
                         remoteLockscreenValidationServiceComponent.getClassName()));
-                getActivity().finish();
+            }
+
+            mRemoteLockscreenValidationFragment =
+                    (RemoteLockscreenValidationFragment) getFragmentManager()
+                            .findFragmentByTag(FRAGMENT_TAG_REMOTE_LOCKSCREEN_VALIDATION);
+            if (mRemoteLockscreenValidationFragment == null) {
+                mRemoteLockscreenValidationFragment = new RemoteLockscreenValidationFragment();
+                getFragmentManager().beginTransaction().add(mRemoteLockscreenValidationFragment,
+                        FRAGMENT_TAG_REMOTE_LOCKSCREEN_VALIDATION).commit();
             }
         }
 
@@ -194,8 +202,10 @@
         mCancelButton.setOnClickListener(v -> {
             if (hasAlternateButton) {
                 getActivity().setResult(KeyguardManager.RESULT_ALTERNATE);
+                getActivity().finish();
+            } else if (mRemoteValidation) {
+                onRemoteLockscreenValidationFailure("Forgot lockscreen credential button pressed.");
             }
-            getActivity().finish();
         });
         setupForgotButtonIfManagedProfile(view);
 
@@ -299,17 +309,11 @@
         if (mRemoteLockscreenValidationClient != null) {
             mRemoteLockscreenValidationClient.disconnect();
         }
-        if (mDeviceCredentialGuess != null) {
-            mDeviceCredentialGuess.zeroize();
-        }
         super.onDestroy();
     }
 
     protected abstract void authenticationSucceeded();
 
-    protected abstract void onRemoteDeviceCredentialValidationResult(
-            RemoteLockscreenValidationResult result);
-
     public void prepareEnterAnimation() {
     }
 
@@ -411,43 +415,33 @@
     }
 
     protected void validateGuess(LockscreenCredential credentialGuess) {
-        if (mCheckBox.isChecked()) {
-            // Keep credential in memory since user wants to set guess as screen lock.
-            mDeviceCredentialGuess = credentialGuess;
-        } else if (mDeviceCredentialGuess != null) {
-            mDeviceCredentialGuess.zeroize();
-        }
-
-        mRemoteLockscreenValidationClient.validateLockscreenGuess(
-                encryptDeviceCredentialGuess(credentialGuess.getCredential()),
-                new IRemoteLockscreenValidationCallback.Stub() {
-                    @Override
-                    public void onSuccess(RemoteLockscreenValidationResult result) {
-                        mHandler.post(()->onRemoteDeviceCredentialValidationResult(result));
-                    }
-
-                    @Override
-                    public void onFailure(String message) {
-                        Log.e(TAG, "A failure occurred while trying "
-                                + "to validate lockscreen guess: " + message);
-                        mHandler.post(()->getActivity().finish());
-                    }
-                });
+        mRemoteLockscreenValidationFragment.validateLockscreenGuess(
+                mRemoteLockscreenValidationClient, credentialGuess,
+                mRemoteLockscreenValidationSession.getSourcePublicKey(), mCheckBox.isChecked());
     }
 
-    private byte[] encryptDeviceCredentialGuess(byte[] guess) {
-        try {
-            byte[] encodedPublicKey = mRemoteLockscreenValidationSession.getSourcePublicKey();
-            PublicKey publicKey = SecureBox.decodePublicKey(encodedPublicKey);
-            return SecureBox.encrypt(
-                    publicKey,
-                    /* sharedSecret= */ null,
-                    LockPatternUtils.ENCRYPTED_REMOTE_CREDENTIALS_HEADER,
-                    guess);
-        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
-            Log.w(TAG, "Error encrypting device credential guess. Returning empty byte[].", e);
-            return new byte[0];
+    protected void updateRemoteLockscreenValidationViews() {
+        if (!mRemoteValidation || mRemoteLockscreenValidationFragment == null) {
+            return;
         }
+
+        boolean enable = mRemoteLockscreenValidationFragment.isRemoteValidationInProgress();
+        mGlifLayout.setProgressBarShown(enable);
+        mCheckBox.setEnabled(!enable);
+        mCancelButton.setEnabled(!enable);
+    }
+
+    /**
+     * Finishes the activity with result code {@link android.app.Activity#RESULT_FIRST_USER}
+     * after logging the error message.
+     * @param message Optional message to log.
+     */
+    public void onRemoteLockscreenValidationFailure(String message) {
+        if (!TextUtils.isEmpty(message)) {
+            Log.w(TAG, message);
+        }
+        getActivity().setResult(RESULT_FIRST_USER);
+        getActivity().finish();
     }
 
     protected abstract void onShowError();
diff --git a/src/com/android/settings/password/ConfirmLockPassword.java b/src/com/android/settings/password/ConfirmLockPassword.java
index 81bd8c2..03b89f2 100644
--- a/src/com/android/settings/password/ConfirmLockPassword.java
+++ b/src/com/android/settings/password/ConfirmLockPassword.java
@@ -71,8 +71,6 @@
 import com.android.settingslib.animation.AppearAnimationUtils;
 import com.android.settingslib.animation.DisappearAnimationUtils;
 
-import com.google.android.setupdesign.GlifLayout;
-
 import java.util.ArrayList;
 
 public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
@@ -127,7 +125,8 @@
 
     public static class ConfirmLockPasswordFragment extends ConfirmDeviceCredentialBaseFragment
             implements OnClickListener, OnEditorActionListener,
-            CredentialCheckResultTracker.Listener, SaveChosenLockWorkerBase.Listener {
+            CredentialCheckResultTracker.Listener, SaveChosenLockWorkerBase.Listener,
+            RemoteLockscreenValidationFragment.Listener {
         private static final String FRAGMENT_TAG_CHECK_LOCK_RESULT = "check_lock_result";
         private ImeAwareEditText mPasswordEntry;
         private TextViewInputDisabler mPasswordEntryInputDisabler;
@@ -140,7 +139,6 @@
         private AppearAnimationUtils mAppearAnimationUtils;
         private DisappearAnimationUtils mDisappearAnimationUtils;
         private boolean mIsManagedProfile;
-        private GlifLayout mGlifLayout;
         private CharSequence mCheckBoxLabel;
 
         // required constructor for fragments
@@ -255,6 +253,7 @@
                             ? R.string.lockpassword_forgot_password
                             : R.string.lockpassword_forgot_pin);
                 }
+                updateRemoteLockscreenValidationViews();
             }
 
             if (mForgotButton != null) {
@@ -405,6 +404,9 @@
                 mCountdownTimer = null;
             }
             mCredentialCheckResultTracker.setListener(null);
+            if (mRemoteLockscreenValidationFragment != null) {
+                mRemoteLockscreenValidationFragment.setListener(null, /* handler= */ null);
+            }
         }
 
         @Override
@@ -426,6 +428,9 @@
                         mLockPatternUtils.getCurrentFailedPasswordAttempts(mEffectiveUserId));
             }
             mCredentialCheckResultTracker.setListener(this);
+            if (mRemoteLockscreenValidationFragment != null) {
+                mRemoteLockscreenValidationFragment.setListener(this, mHandler);
+            }
         }
 
         @Override
@@ -436,13 +441,17 @@
         private void updatePasswordEntry() {
             final boolean isLockedOut =
                     mLockPatternUtils.getLockoutAttemptDeadline(mEffectiveUserId) != 0;
-            mPasswordEntry.setEnabled(!isLockedOut);
-            mPasswordEntryInputDisabler.setInputEnabled(!isLockedOut);
-            if (isLockedOut) {
-                mImm.hideSoftInputFromWindow(mPasswordEntry.getWindowToken(), 0 /*flags*/);
-            } else {
+            final boolean isRemoteLockscreenValidationInProgress =
+                    mRemoteLockscreenValidationFragment != null
+                    && mRemoteLockscreenValidationFragment.isRemoteValidationInProgress();
+            boolean shouldEnableInput = !isLockedOut && !isRemoteLockscreenValidationInProgress;
+            mPasswordEntry.setEnabled(shouldEnableInput);
+            mPasswordEntryInputDisabler.setInputEnabled(shouldEnableInput);
+            if (shouldEnableInput) {
                 mPasswordEntry.scheduleShowSoftInput();
                 mPasswordEntry.requestFocus();
+            } else {
+                mImm.hideSoftInputFromWindow(mPasswordEntry.getWindowToken(), /* flags= */0);
             }
         }
 
@@ -472,7 +481,8 @@
 
             if (mRemoteValidation) {
                 validateGuess(credential);
-                mGlifLayout.setProgressBarShown(true);
+                updateRemoteLockscreenValidationViews();
+                updatePasswordEntry();
                 return;
             }
 
@@ -604,14 +614,15 @@
         }
 
         @Override
-        protected void onRemoteDeviceCredentialValidationResult(
+        public void onRemoteLockscreenValidationResult(
                 RemoteLockscreenValidationResult result) {
             switch (result.getResultCode()) {
                 case RemoteLockscreenValidationResult.RESULT_GUESS_VALID:
-                    if (mCheckBox.isChecked()) {
+                    if (mCheckBox.isChecked() && mRemoteLockscreenValidationFragment
+                            .getLockscreenCredential() != null) {
+                        Log.i(TAG, "Setting device screen lock to the other device's screen lock.");
                         ChooseLockPassword.SaveAndFinishWorker saveAndFinishWorker =
                                 new ChooseLockPassword.SaveAndFinishWorker();
-                        Log.i(TAG, "Setting device screen lock to the other device's screen lock.");
                         getFragmentManager().beginTransaction().add(saveAndFinishWorker, null)
                                 .commit();
                         getFragmentManager().executePendingTransactions();
@@ -619,14 +630,14 @@
                         saveAndFinishWorker.start(
                                 mLockPatternUtils,
                                 /* requestGatekeeperPassword= */ true,
-                                mDeviceCredentialGuess,
+                                mRemoteLockscreenValidationFragment.getLockscreenCredential(),
                                 /* currentCredential= */ null,
                                 mEffectiveUserId);
-                        return;
+                    } else {
+                        mCredentialCheckResultTracker.setResult(/* matched= */ true, new Intent(),
+                                /* timeoutMs= */ 0, mEffectiveUserId);
                     }
-                    mCredentialCheckResultTracker.setResult(/* matched= */ true, new Intent(),
-                            /* timeoutMs= */ 0, mEffectiveUserId);
-                    break;
+                    return;
                 case RemoteLockscreenValidationResult.RESULT_GUESS_INVALID:
                     mCredentialCheckResultTracker.setResult(/* matched= */ false, new Intent(),
                             /* timeoutMs= */ 0, mEffectiveUserId);
@@ -636,12 +647,15 @@
                             (int) result.getTimeoutMillis(), mEffectiveUserId);
                     break;
                 case RemoteLockscreenValidationResult.RESULT_NO_REMAINING_ATTEMPTS:
-                    getActivity().finish();
-                    break;
                 case RemoteLockscreenValidationResult.RESULT_SESSION_EXPIRED:
-                    getActivity().finish();
+                    onRemoteLockscreenValidationFailure(String.format(
+                            "Cannot continue remote lockscreen validation. ResultCode=%d",
+                            result.getResultCode()));
+                    break;
             }
-            mGlifLayout.setProgressBarShown(false);
+            updateRemoteLockscreenValidationViews();
+            updatePasswordEntry();
+            mRemoteLockscreenValidationFragment.clearLockscreenCredential();
         }
 
         @Override
@@ -701,21 +715,18 @@
         }
 
         /**
-         * Callback for when the device credential guess used for remote validation was set as the
-         * current device's device credential.
+         * Callback for when the current device's lockscreen was set to the guess used for
+         * remote lockscreen validation.
          */
         @Override
         public void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData) {
-            if (mDeviceCredentialGuess != null) {
-                mDeviceCredentialGuess.zeroize();
-            }
+            Log.i(TAG, "Device lockscreen has been set to remote device's lockscreen.");
+            mRemoteLockscreenValidationFragment.clearLockscreenCredential();
 
             Intent result = new Intent();
             if (mRemoteValidation && containsGatekeeperPasswordHandle(resultData)) {
                 result.putExtra(EXTRA_KEY_GK_PW_HANDLE, getGatekeeperPasswordHandle(resultData));
             }
-
-            mGlifLayout.setProgressBarShown(false);
             mCredentialCheckResultTracker.setResult(/* matched= */ true, result,
                     /* timeoutMs= */ 0, mEffectiveUserId);
         }
diff --git a/src/com/android/settings/password/ConfirmLockPattern.java b/src/com/android/settings/password/ConfirmLockPattern.java
index 7c21739..7db25fd 100644
--- a/src/com/android/settings/password/ConfirmLockPattern.java
+++ b/src/com/android/settings/password/ConfirmLockPattern.java
@@ -59,8 +59,6 @@
 import com.android.settingslib.animation.AppearAnimationUtils;
 import com.android.settingslib.animation.DisappearAnimationUtils;
 
-import com.google.android.setupdesign.GlifLayout;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -97,7 +95,7 @@
 
     public static class ConfirmLockPatternFragment extends ConfirmDeviceCredentialBaseFragment
             implements AppearAnimationCreator<Object>, CredentialCheckResultTracker.Listener,
-            SaveChosenLockWorkerBase.Listener {
+            SaveChosenLockWorkerBase.Listener, RemoteLockscreenValidationFragment.Listener {
 
         private static final String FRAGMENT_TAG_CHECK_LOCK_RESULT = "check_lock_result";
 
@@ -107,7 +105,6 @@
         private boolean mDisappearing = false;
         private CountDownTimer mCountdownTimer;
 
-        private GlifLayout mGlifLayout;
         private View mSudContent;
 
         // caller-supplied text for various prompts
@@ -239,6 +236,7 @@
                 if (mCancelButton != null && TextUtils.isEmpty(mAlternateButtonText)) {
                     mCancelButton.setText(R.string.lockpassword_forgot_pattern);
                 }
+                updateRemoteLockscreenValidationViews();
             }
 
             if (mForgotButton != null) {
@@ -259,6 +257,9 @@
                 mCountdownTimer.cancel();
             }
             mCredentialCheckResultTracker.setListener(null);
+            if (mRemoteLockscreenValidationFragment != null) {
+                mRemoteLockscreenValidationFragment.setListener(null, /* handler= */ null);
+            }
         }
 
         @Override
@@ -281,6 +282,12 @@
                 updateStage(Stage.NeedToUnlock);
             }
             mCredentialCheckResultTracker.setListener(this);
+            if (mRemoteLockscreenValidationFragment != null) {
+                mRemoteLockscreenValidationFragment.setListener(this, mHandler);
+                if (mRemoteLockscreenValidationFragment.isRemoteValidationInProgress()) {
+                    mLockPatternView.setEnabled(false);
+                }
+            }
         }
 
         @Override
@@ -502,7 +509,7 @@
 
                 if (mRemoteValidation) {
                     validateGuess(credential);
-                    mGlifLayout.setProgressBarShown(true);
+                    updateRemoteLockscreenValidationViews();
                     return;
                 }
 
@@ -617,11 +624,12 @@
         }
 
         @Override
-        protected void onRemoteDeviceCredentialValidationResult(
+        public void onRemoteLockscreenValidationResult(
                 RemoteLockscreenValidationResult result) {
             switch (result.getResultCode()) {
                 case RemoteLockscreenValidationResult.RESULT_GUESS_VALID:
-                    if (mCheckBox.isChecked()) {
+                    if (mCheckBox.isChecked() && mRemoteLockscreenValidationFragment
+                            .getLockscreenCredential() != null) {
                         Log.i(TAG, "Setting device screen lock to the other device's screen lock.");
                         ChooseLockPattern.SaveAndFinishWorker saveAndFinishWorker =
                                 new ChooseLockPattern.SaveAndFinishWorker();
@@ -632,14 +640,14 @@
                         saveAndFinishWorker.start(
                                 mLockPatternUtils,
                                 /* requestGatekeeperPassword= */ true,
-                                mDeviceCredentialGuess,
+                                mRemoteLockscreenValidationFragment.getLockscreenCredential(),
                                 /* currentCredential= */ null,
                                 mEffectiveUserId);
-                        return;
+                    } else {
+                        mCredentialCheckResultTracker.setResult(/* matched= */ true, new Intent(),
+                                /* timeoutMs= */ 0, mEffectiveUserId);
                     }
-                    mCredentialCheckResultTracker.setResult(/* matched= */ true, new Intent(),
-                            /* timeoutMs= */ 0, mEffectiveUserId);
-                    break;
+                    return;
                 case RemoteLockscreenValidationResult.RESULT_GUESS_INVALID:
                     mCredentialCheckResultTracker.setResult(/* matched= */ false, new Intent(),
                             /* timeoutMs= */ 0, mEffectiveUserId);
@@ -649,12 +657,14 @@
                             (int) result.getTimeoutMillis(), mEffectiveUserId);
                     break;
                 case RemoteLockscreenValidationResult.RESULT_NO_REMAINING_ATTEMPTS:
-                    getActivity().finish();
-                    break;
                 case RemoteLockscreenValidationResult.RESULT_SESSION_EXPIRED:
-                    getActivity().finish();
+                    onRemoteLockscreenValidationFailure(String.format(
+                            "Cannot continue remote lockscreen validation. ResultCode=%d",
+                            result.getResultCode()));
+                    break;
             }
-            mGlifLayout.setProgressBarShown(false);
+            updateRemoteLockscreenValidationViews();
+            mRemoteLockscreenValidationFragment.clearLockscreenCredential();
         }
 
         @Override
@@ -728,21 +738,18 @@
         }
 
         /**
-         * Callback for when the device credential guess used for remote validation was set as the
-         * current device's device credential.
+         * Callback for when the current device's lockscreen to the guess used for
+         * remote lockscreen validation.
          */
         @Override
         public void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData) {
-            if (mDeviceCredentialGuess != null) {
-                mDeviceCredentialGuess.zeroize();
-            }
+            Log.i(TAG, "Device lockscreen has been set to remote device's lockscreen.");
+            mRemoteLockscreenValidationFragment.clearLockscreenCredential();
 
             Intent result = new Intent();
             if (mRemoteValidation && containsGatekeeperPasswordHandle(resultData)) {
                 result.putExtra(EXTRA_KEY_GK_PW_HANDLE, getGatekeeperPasswordHandle(resultData));
             }
-
-            mGlifLayout.setProgressBarShown(false);
             mCredentialCheckResultTracker.setResult(/* matched= */ true, result,
                     /* timeoutMs= */ 0, mEffectiveUserId);
         }
diff --git a/src/com/android/settings/password/RemoteLockscreenValidationFragment.java b/src/com/android/settings/password/RemoteLockscreenValidationFragment.java
new file mode 100644
index 0000000..5819376
--- /dev/null
+++ b/src/com/android/settings/password/RemoteLockscreenValidationFragment.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.password;
+
+import android.app.RemoteLockscreenValidationResult;
+import android.os.Bundle;
+import android.os.Handler;
+import android.service.remotelockscreenvalidation.IRemoteLockscreenValidationCallback;
+import android.service.remotelockscreenvalidation.RemoteLockscreenValidationClient;
+import android.util.Log;
+
+import androidx.fragment.app.Fragment;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
+import com.android.security.SecureBox;
+
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+
+/**
+ * A fragment used to hold state for remote lockscreen validation.
+ * If the original listener is ever re-created, the new listener must be set again using
+ * {@link #setListener} so that the validation result does not get handled by the old listener.
+ */
+public class RemoteLockscreenValidationFragment extends Fragment {
+
+    private static final String TAG = RemoteLockscreenValidationFragment.class.getSimpleName();
+
+    private Listener mListener;
+    private Handler mHandler;
+    private boolean mIsInProgress;
+    private RemoteLockscreenValidationResult mResult;
+    private String mErrorMessage;
+    private LockscreenCredential mLockscreenCredential;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setRetainInstance(true);
+    }
+
+    @Override
+    public void onDestroy() {
+        clearLockscreenCredential();
+        if (mResult != null && mErrorMessage != null) {
+            Log.w(TAG, "Unprocessed remote lockscreen validation result");
+        }
+        super.onDestroy();
+    }
+
+    /**
+     * @return {@code true} if remote lockscreen guess validation has started or
+     * the validation result has not yet been handled.
+     */
+    public boolean isRemoteValidationInProgress() {
+        return mIsInProgress;
+    }
+
+    /**
+     * Sets the listener and handler that will handle the result of remote lockscreen validation.
+     * Unprocessed results or failures will be handled after the listener is set.
+     */
+    public void setListener(Listener listener, Handler handler) {
+        if (mListener == listener) {
+            return;
+        }
+
+        mListener = listener;
+        mHandler = handler;
+
+        if (mResult != null) {
+            handleResult();
+        } else if (mErrorMessage != null) {
+            handleFailure();
+        }
+    }
+
+    /**
+     * @return {@link LockscreenCredential} if it was cached in {@link #validateLockscreenGuess}.
+     */
+    public LockscreenCredential getLockscreenCredential() {
+        return mLockscreenCredential;
+    }
+
+    /**
+     * Clears the {@link LockscreenCredential} if it was cached in {@link #validateLockscreenGuess}.
+     */
+    public void clearLockscreenCredential() {
+        if (mLockscreenCredential != null) {
+            mLockscreenCredential.zeroize();
+            mLockscreenCredential = null;
+        }
+    }
+
+    /**
+     * Validates the lockscreen guess on the remote device.
+     * @param remoteLockscreenValidationClient the client that should be used to send the guess to
+     *                                         for validation
+     * @param guess the {@link LockscreenCredential} guess that the user entered
+     * @param encryptionKey the key that should be used to encrypt the guess before validation
+     * @param shouldCacheGuess whether to cache to guess so it can be used to set the current
+     *                         device's lockscreen after validation succeeds.
+     */
+    public void validateLockscreenGuess(
+            RemoteLockscreenValidationClient remoteLockscreenValidationClient,
+            LockscreenCredential guess, byte[] encryptionKey, boolean shouldCacheGuess) {
+        if (shouldCacheGuess) {
+            mLockscreenCredential = guess;
+        }
+
+        remoteLockscreenValidationClient.validateLockscreenGuess(
+                encryptDeviceCredentialGuess(guess.getCredential(), encryptionKey),
+                new IRemoteLockscreenValidationCallback.Stub() {
+                    @Override
+                    public void onSuccess(RemoteLockscreenValidationResult result) {
+                        mResult = result;
+                        handleResult();
+                    }
+
+                    @Override
+                    public void onFailure(String message) {
+                        mErrorMessage = message;
+                        handleFailure();
+                    }
+                });
+        mIsInProgress = true;
+    }
+
+    private byte[] encryptDeviceCredentialGuess(byte[] guess, byte[] encryptionKey) {
+        try {
+            PublicKey publicKey = SecureBox.decodePublicKey(encryptionKey);
+            return SecureBox.encrypt(
+                    publicKey,
+                    /* sharedSecret= */ null,
+                    LockPatternUtils.ENCRYPTED_REMOTE_CREDENTIALS_HEADER,
+                    guess);
+        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
+            Log.w(TAG, "Error encrypting device credential guess. Returning empty byte[].", e);
+            return new byte[0];
+        }
+    }
+
+    private void handleResult() {
+        if (mHandler != null) {
+            mHandler.post(()-> {
+                if (mListener == null || mResult == null) {
+                    return;
+                }
+                mIsInProgress = false;
+                mListener.onRemoteLockscreenValidationResult(mResult);
+                mResult = null;
+            });
+        }
+    }
+
+    private void handleFailure() {
+        if (mHandler != null) {
+            mHandler.post(()-> {
+                if (mListener == null || mErrorMessage == null) {
+                    return;
+                }
+                mIsInProgress = false;
+                mListener.onRemoteLockscreenValidationFailure(
+                        String.format("Remote lockscreen validation failed: %s", mErrorMessage));
+                mErrorMessage = null;
+            });
+        }
+    }
+
+    interface Listener {
+        void onRemoteLockscreenValidationResult(RemoteLockscreenValidationResult result);
+        void onRemoteLockscreenValidationFailure(String message);
+    }
+}
diff --git a/src/com/android/settings/security/MemtagHelper.java b/src/com/android/settings/security/MemtagHelper.java
index b04dcf9..a09a219 100644
--- a/src/com/android/settings/security/MemtagHelper.java
+++ b/src/com/android/settings/security/MemtagHelper.java
@@ -17,6 +17,7 @@
 package com.android.settings.security;
 
 import android.os.SystemProperties;
+import android.text.TextUtils;
 
 import com.android.internal.os.Zygote;
 import com.android.settings.R;
@@ -25,18 +26,15 @@
 import java.util.Arrays;
 
 public class MemtagHelper {
+    public static final String DEVICE_CONFIG_PROP =
+            "persist.device_config.runtime_native_boot.bootloader_override";
+
     public static boolean isForcedOff() {
-        return "force_off"
-                .equals(
-                        SystemProperties.get(
-                                "persist.device_config.memory_safety_native_boot.bootloader_override"));
+        return TextUtils.equals("force_on", SystemProperties.get(DEVICE_CONFIG_PROP));
     }
 
     public static boolean isForcedOn() {
-        return "force_on"
-                .equals(
-                        SystemProperties.get(
-                                "persist.device_config.memory_safety_native_boot.bootloader_override"));
+        return TextUtils.equals("force_on", SystemProperties.get(DEVICE_CONFIG_PROP));
     }
 
     public static boolean isChecked() {
diff --git a/src/com/android/settings/theme/ContrastPreferenceController.kt b/src/com/android/settings/theme/ContrastPreferenceController.kt
new file mode 100644
index 0000000..7f3844a
--- /dev/null
+++ b/src/com/android/settings/theme/ContrastPreferenceController.kt
@@ -0,0 +1,66 @@
+/*
+ * 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.theme
+
+import android.app.UiModeManager
+import android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_HIGH
+import android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_MEDIUM
+import android.app.UiModeManager.ContrastUtils.toContrastLevel
+import android.content.Context
+import android.content.Intent
+import android.os.UserHandle
+import android.text.TextUtils
+import androidx.preference.Preference
+import com.android.internal.annotations.VisibleForTesting
+import com.android.settings.R
+import com.android.settings.core.BasePreferenceController
+
+/**
+ * Controller that opens the contrast dialog and updates the text describing the current contrast
+ */
+class ContrastPreferenceController(
+        private val context: Context,
+        private val uiModeManager: UiModeManager) : BasePreferenceController(context, KEY) {
+
+    companion object {
+        @VisibleForTesting
+        const val KEY = "contrast_preference"
+    }
+
+    override fun getAvailabilityStatus(): Int {
+        return AVAILABLE
+    }
+
+    override fun handlePreferenceTreeClick(preference: Preference): Boolean {
+        if (TextUtils.equals(preference.key, preferenceKey)) {
+            val intent = Intent(Intent.ACTION_SHOW_CONTRAST_DIALOG)
+            context.startActivityAsUser(intent, UserHandle(UserHandle.USER_CURRENT))
+            return true
+        }
+        return false
+    }
+
+    override fun getSummary(): CharSequence = getSummary(toContrastLevel(uiModeManager.contrast))
+
+    @VisibleForTesting
+    fun getSummary(contrast: Int): String {
+        return when (contrast) {
+            CONTRAST_LEVEL_HIGH -> context.getString(R.string.contrast_high)
+            CONTRAST_LEVEL_MEDIUM -> context.getString(R.string.contrast_medium)
+            else -> context.getString(R.string.contrast_standard)
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/users/TimeoutToDockUserPreferenceController.java b/src/com/android/settings/users/TimeoutToDockUserPreferenceController.java
index 57e8d40..322a4ab 100644
--- a/src/com/android/settings/users/TimeoutToDockUserPreferenceController.java
+++ b/src/com/android/settings/users/TimeoutToDockUserPreferenceController.java
@@ -20,6 +20,7 @@
 
 import android.content.Context;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.Settings;
 
 import androidx.preference.PreferenceScreen;
@@ -34,6 +35,8 @@
  * automatically switch to the designated Dock User when the device is docked.
  */
 public class TimeoutToDockUserPreferenceController extends BasePreferenceController {
+    private final UserManager mUserManager;
+
     private final String[] mEntries;
     private final String[] mValues;
 
@@ -41,6 +44,8 @@
             String preferenceKey) {
         super(context, preferenceKey);
 
+        mUserManager = context.getSystemService(UserManager.class);
+
         mEntries = mContext.getResources().getStringArray(
                 com.android.settings.R.array.switch_to_dock_user_when_docked_timeout_entries);
         mValues = mContext.getResources().getStringArray(
@@ -62,9 +67,10 @@
             return UNSUPPORTED_ON_DEVICE;
         }
 
-        // Multi-user feature disabled by user.
+        // Multi-user feature disabled by user, or user switching blocked on the user.
         if (Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.USER_SWITCHER_ENABLED, 0) != 1) {
+                Settings.Global.USER_SWITCHER_ENABLED, 0) != 1
+                || mUserManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH)) {
             return CONDITIONALLY_UNAVAILABLE;
         }
 
diff --git a/src/com/android/settings/uwb/UwbPreferenceController.java b/src/com/android/settings/uwb/UwbPreferenceController.java
index fb0836d..7f19765 100644
--- a/src/com/android/settings/uwb/UwbPreferenceController.java
+++ b/src/com/android/settings/uwb/UwbPreferenceController.java
@@ -16,6 +16,11 @@
 
 package com.android.settings.uwb;
 
+import static android.uwb.UwbManager.AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_REGULATION;
+import static android.uwb.UwbManager.AdapterStateCallback.STATE_DISABLED;
+import static android.uwb.UwbManager.AdapterStateCallback.STATE_ENABLED_ACTIVE;
+import static android.uwb.UwbManager.AdapterStateCallback.STATE_ENABLED_INACTIVE;
+
 import static androidx.lifecycle.Lifecycle.Event.ON_START;
 import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
 
@@ -25,7 +30,7 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.os.Handler;
-import android.provider.Settings;
+import android.os.HandlerExecutor;
 import android.uwb.UwbManager;
 import android.uwb.UwbManager.AdapterStateCallback;
 
@@ -39,52 +44,68 @@
 import com.android.settings.core.TogglePreferenceController;
 
 import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
 
 /** Controller for "UWB" toggle. */
 public class UwbPreferenceController extends TogglePreferenceController implements
-        AdapterStateCallback, LifecycleObserver {
-    @VisibleForTesting
-    static final String KEY_UWB_SETTINGS = "uwb_settings";
-    @VisibleForTesting
-    UwbManager mUwbManager;
-    @VisibleForTesting
-    boolean mAirplaneModeOn;
-    @VisibleForTesting
+        LifecycleObserver {
+    private final UwbManager mUwbManager;
+    private final UwbUtils mUwbUtils;
+    private boolean mAirplaneModeOn;
+    private /* @AdapterStateCallback.State */ int mState;
+    private /* @AdapterStateCallback.StateChangedReason */ int mStateReason;
     private final BroadcastReceiver mAirplaneModeChangedReceiver;
+    private final AdapterStateCallback mAdapterStateCallback;
     private final Executor mExecutor;
     private final Handler mHandler;
     private Preference mPreference;
 
-    public UwbPreferenceController(Context context, String key) {
+    @VisibleForTesting
+    public UwbPreferenceController(Context context, String key, UwbUtils uwbUtils) {
         super(context, key);
-        mExecutor = Executors.newSingleThreadExecutor();
         mHandler = new Handler(context.getMainLooper());
+        mExecutor = new HandlerExecutor(mHandler);
+        mUwbUtils = uwbUtils;
         if (isUwbSupportedOnDevice()) {
             mUwbManager = context.getSystemService(UwbManager.class);
-        }
-        mAirplaneModeOn = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
-        mAirplaneModeChangedReceiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                mAirplaneModeOn = Settings.Global.getInt(mContext.getContentResolver(),
-                        Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
+            mAirplaneModeChangedReceiver = new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    mAirplaneModeOn = mUwbUtils.isAirplaneModeOn(mContext);
+                    updateState(mPreference);
+                }
+            };
+            mAdapterStateCallback = (state, reason) -> {
+                mState = state;
+                mStateReason = reason;
                 updateState(mPreference);
-            }
-        };
+            };
+        } else {
+            mUwbManager = null;
+            mAirplaneModeChangedReceiver = null;
+            mAdapterStateCallback = null;
+        }
+    }
+
+    public UwbPreferenceController(Context context, String key) {
+        this(context, key, new UwbUtils());
     }
 
     public boolean isUwbSupportedOnDevice() {
         return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_UWB);
     }
 
+    private boolean isUwbDisabledDueToRegulatory() {
+        return mState == STATE_DISABLED && mStateReason == STATE_CHANGED_REASON_SYSTEM_REGULATION;
+    }
+
     @Override
     public int getAvailabilityStatus() {
         if (!isUwbSupportedOnDevice()) {
             return UNSUPPORTED_ON_DEVICE;
         } else if (mAirplaneModeOn) {
             return DISABLED_DEPENDENT_SETTING;
+        } else if (isUwbDisabledDueToRegulatory()) {
+            return CONDITIONALLY_UNAVAILABLE;
         } else {
             return AVAILABLE;
         }
@@ -101,14 +122,11 @@
         if (!isUwbSupportedOnDevice()) {
             return false;
         }
-        int state = mUwbManager.getAdapterState();
-        return state == STATE_ENABLED_ACTIVE || state == STATE_ENABLED_INACTIVE;
+        return mState == STATE_ENABLED_ACTIVE || mState == STATE_ENABLED_INACTIVE;
     }
 
     @Override
     public boolean setChecked(boolean isChecked) {
-        mAirplaneModeOn = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
         if (isUwbSupportedOnDevice()) {
             if (mAirplaneModeOn) {
                 mUwbManager.setUwbEnabled(false);
@@ -119,32 +137,25 @@
         return true;
     }
 
-    @Override
-    public void onStateChanged(int state, int reason) {
-        Runnable runnable = () -> updateState(mPreference);
-        mHandler.post(runnable);
-    }
-
     /** Called when activity starts being displayed to user. */
     @OnLifecycleEvent(ON_START)
     public void onStart() {
         if (isUwbSupportedOnDevice()) {
-            mUwbManager.registerAdapterStateCallback(mExecutor, this);
-        }
-        if (mAirplaneModeChangedReceiver != null) {
+            mState = mUwbManager.getAdapterState();
+            mStateReason = AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN;
+            mAirplaneModeOn = mUwbUtils.isAirplaneModeOn(mContext);
+            mUwbManager.registerAdapterStateCallback(mExecutor, mAdapterStateCallback);
             mContext.registerReceiver(mAirplaneModeChangedReceiver,
-                    new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
+                    new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED), null, mHandler);
+            refreshSummary(mPreference);
         }
-        refreshSummary(mPreference);
     }
 
     /** Called when activity stops being displayed to user. */
     @OnLifecycleEvent(ON_STOP)
     public void onStop() {
         if (isUwbSupportedOnDevice()) {
-            mUwbManager.unregisterAdapterStateCallback(this);
-        }
-        if (mAirplaneModeChangedReceiver != null) {
+            mUwbManager.unregisterAdapterStateCallback(mAdapterStateCallback);
             mContext.unregisterReceiver(mAirplaneModeChangedReceiver);
         }
     }
@@ -153,13 +164,16 @@
     public void updateState(Preference preference) {
         super.updateState(preference);
         preference.setEnabled(!mAirplaneModeOn);
-        refreshSummary(preference);
+        refreshSummary(mPreference);
     }
 
     @Override
     public CharSequence getSummary() {
         if (mAirplaneModeOn) {
             return mContext.getResources().getString(R.string.uwb_settings_summary_airplane_mode);
+        } else if (isUwbDisabledDueToRegulatory()) {
+            return mContext.getResources().getString(
+                    R.string.uwb_settings_summary_no_uwb_regulatory);
         } else {
             return mContext.getResources().getString(R.string.uwb_settings_summary);
         }
diff --git a/src/com/android/settings/uwb/UwbUtils.java b/src/com/android/settings/uwb/UwbUtils.java
new file mode 100644
index 0000000..940e1af
--- /dev/null
+++ b/src/com/android/settings/uwb/UwbUtils.java
@@ -0,0 +1,34 @@
+/*
+ * 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.uwb;
+
+import android.content.Context;
+import android.provider.Settings;
+
+/**
+ * Utils to help mock static methods in {@link UwbPreferenceController}.
+ */
+public class UwbUtils {
+    /**
+     * Returns whether airplane mode is on or off.
+     */
+    public boolean isAirplaneModeOn(Context context) {
+        return Settings.Global.getInt(context.getContentResolver(),
+                Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
+    }
+}
+
diff --git a/tests/robotests/Android.bp b/tests/robotests/Android.bp
index 9cc8439..94febff 100644
--- a/tests/robotests/Android.bp
+++ b/tests/robotests/Android.bp
@@ -42,6 +42,7 @@
         "androidx.test.core",
         "androidx.test.runner",
         "androidx.test.ext.junit",
+        "frameworks-base-testutils",
         "guava",
         "jsr305",
         "settings-contextual-card-protos-lite",
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDevicesControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDevicesControllerTest.java
index fc3e6c0..f4fa397 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDevicesControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDevicesControllerTest.java
@@ -394,7 +394,7 @@
 
         assertThat(buttonsPref.isChecked()).isEqualTo(false);
         assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
-                Secure.STYLUS_BUTTONS_ENABLED, -1)).isEqualTo(0);
+                Secure.STYLUS_BUTTONS_ENABLED, -1)).isEqualTo(1);
     }
 
     private void showScreen(StylusDevicesController controller) {
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java
index 37f05bc..16fec09 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java
@@ -55,6 +55,7 @@
 @RunWith(RobolectricTestRunner.class)
 public final class BatteryUsageBreakdownControllerTest {
     private static final String PREF_KEY = "pref_key";
+    private static final String PREF_KEY2 = "pref_key2";
     private static final String PREF_SUMMARY = "fake preference summary";
 
     @Mock
@@ -175,7 +176,24 @@
     }
 
     @Test
-    public void removeAndCacheAllPreferences_buildCacheAndRemoveAllPreference() {
+    public void removeAndCacheAllUnusedPreferences_removePerf_buildCacheAndRemoveAllPreference() {
+        doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount();
+        doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).getPreference(0);
+        doReturn(PREF_KEY2).when(mBatteryHistEntry).getKey();
+        doReturn(PREF_KEY).when(mPowerGaugePreference).getKey();
+        doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).findPreference(PREF_KEY);
+        // Ensures the testing data is correct.
+        assertThat(mBatteryUsageBreakdownController.mPreferenceCache).isEmpty();
+
+        mBatteryUsageBreakdownController.removeAndCacheAllUnusedPreferences();
+
+        assertThat(mBatteryUsageBreakdownController.mPreferenceCache.get(PREF_KEY))
+                .isEqualTo(mPowerGaugePreference);
+        verify(mAppListPreferenceGroup).removePreference(mPowerGaugePreference);
+    }
+
+    @Test
+    public void removeAndCacheAllUnusedPreferences_keepPerf_KeepAllPreference() {
         doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount();
         doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).getPreference(0);
         doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
@@ -184,11 +202,10 @@
         // Ensures the testing data is correct.
         assertThat(mBatteryUsageBreakdownController.mPreferenceCache).isEmpty();
 
-        mBatteryUsageBreakdownController.removeAndCacheAllPreferences();
+        mBatteryUsageBreakdownController.removeAndCacheAllUnusedPreferences();
 
-        assertThat(mBatteryUsageBreakdownController.mPreferenceCache.get(PREF_KEY))
-                .isEqualTo(mPowerGaugePreference);
-        verify(mAppListPreferenceGroup).removeAll();
+        verify(mAppListPreferenceGroup, never()).removePreference(any());
+        assertThat(mBatteryUsageBreakdownController.mPreferenceCache).isEmpty();
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/nfc/NfcEnablerTest.java b/tests/robotests/src/com/android/settings/nfc/NfcEnablerTest.java
index cc45190..5299bbd 100644
--- a/tests/robotests/src/com/android/settings/nfc/NfcEnablerTest.java
+++ b/tests/robotests/src/com/android/settings/nfc/NfcEnablerTest.java
@@ -16,15 +16,12 @@
 
 package com.android.settings.nfc;
 
-import static com.google.common.truth.Truth.assertThat;
-
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
-import android.content.ContentResolver;
 import android.content.Context;
 import android.nfc.NfcAdapter;
-import android.provider.Settings;
 
 import com.android.settingslib.widget.MainSwitchPreference;
 
@@ -53,54 +50,13 @@
     }
 
     @Test
-    public void isToggleable_AirplaneModeOff_shouldReturnTrue() {
-        final ContentResolver contentResolver = mContext.getContentResolver();
-        Settings.Global.putInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0);
-        Settings.Global.putString(contentResolver,
-            Settings.Global.AIRPLANE_MODE_RADIOS, Settings.Global.RADIO_NFC);
-        Settings.Global.putString(contentResolver,
-            Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS, Settings.Global.RADIO_NFC);
-
-        assertThat(mNfcEnabler.isToggleable()).isTrue();
-    }
-
-    @Test
-    public void isToggleable_AirplaneModeOnNfcNotInAirplaneModeRadio_shouldReturnTrue() {
-        final ContentResolver contentResolver = mContext.getContentResolver();
-        Settings.Global.putInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, 1);
-        Settings.Global.putString(contentResolver, Settings.Global.AIRPLANE_MODE_RADIOS, "");
-
-        assertThat(mNfcEnabler.isToggleable()).isTrue();
-    }
-
-    @Test
-    public void isToggleable_AirplaneModeOnNfcToggleable_shouldReturnTrue() {
-        final ContentResolver contentResolver = mContext.getContentResolver();
-        Settings.Global.putInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, 1);
-        Settings.Global.putString(contentResolver,
-            Settings.Global.AIRPLANE_MODE_RADIOS, Settings.Global.RADIO_NFC);
-        Settings.Global.putString(contentResolver,
-            Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS, Settings.Global.RADIO_NFC);
-
-        assertThat(mNfcEnabler.isToggleable()).isTrue();
-    }
-
-    @Test
-    public void isToggleable_AirplaneModeOnNfcNotToggleable_shouldReturnFalse() {
-        final ContentResolver contentResolver = mContext.getContentResolver();
-        Settings.Global.putInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, 1);
-        Settings.Global.putString(contentResolver,
-            Settings.Global.AIRPLANE_MODE_RADIOS, Settings.Global.RADIO_NFC);
-        Settings.Global.putString(contentResolver,
-                Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS, "");
-
-        assertThat(mNfcEnabler.isToggleable()).isFalse();
-    }
-
-    @Test
     public void handleNfcStateChanged_stateOff_shouldCheckIfPreferenceEnableState() {
         mNfcEnabler.handleNfcStateChanged(NfcAdapter.STATE_OFF);
+        verify(mNfcPreference).updateStatus(false);
+        verify(mNfcPreference).setEnabled(true);
 
-        verify(mNfcEnabler).isToggleable();
+        mNfcEnabler.handleNfcStateChanged(NfcAdapter.STATE_ON);
+        verify(mNfcPreference).updateStatus(true);
+        verify(mNfcPreference, times(2)).setEnabled(true);
     }
 }
diff --git a/tests/robotests/src/com/android/settings/nfc/NfcPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/nfc/NfcPreferenceControllerTest.java
index 3ca553b..4db51aa 100644
--- a/tests/robotests/src/com/android/settings/nfc/NfcPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/nfc/NfcPreferenceControllerTest.java
@@ -28,7 +28,6 @@
 import android.nfc.NfcAdapter;
 import android.nfc.NfcManager;
 import android.os.UserManager;
-import android.provider.Settings;
 
 import androidx.preference.PreferenceScreen;
 
@@ -189,44 +188,6 @@
     }
 
     @Test
-    public void isToggleableInAirplaneMode_containNfc_shouldReturnTrue() {
-        Settings.Global.putString(mContext.getContentResolver(),
-                Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS,
-                Settings.Global.RADIO_NFC);
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.AIRPLANE_MODE_ON, 1);
-
-        assertThat(NfcPreferenceController.isToggleableInAirplaneMode(mContext)).isTrue();
-    }
-
-    @Test
-    public void isToggleableInAirplaneMode_withoutNfc_shouldReturnFalse() {
-        Settings.Global.putString(mContext.getContentResolver(),
-                Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS,
-                "null");
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.AIRPLANE_MODE_ON, 1);
-
-        assertThat(NfcPreferenceController.isToggleableInAirplaneMode(mContext)).isFalse();
-    }
-
-    @Test
-    public void shouldTurnOffNFCInAirplaneMode_airplaneModeRadiosContainsNfc_shouldReturnTrue() {
-        Settings.Global.putString(mContext.getContentResolver(),
-                Settings.Global.AIRPLANE_MODE_RADIOS, Settings.Global.RADIO_NFC);
-
-        assertThat(NfcPreferenceController.shouldTurnOffNFCInAirplaneMode(mContext)).isTrue();
-    }
-
-    @Test
-    public void shouldTurnOffNFCInAirplaneMode_airplaneModeRadiosWithoutNfc_shouldReturnFalse() {
-        Settings.Global.putString(mContext.getContentResolver(),
-                Settings.Global.AIRPLANE_MODE_RADIOS, "");
-
-        assertThat(NfcPreferenceController.shouldTurnOffNFCInAirplaneMode(mContext)).isFalse();
-    }
-
-    @Test
     public void ncfSliceWorker_nfcBroadcast_noExtra_sliceDoesntUpdate() {
         final NfcSliceWorker worker = spy(
                 new NfcSliceWorker(mContext, mNfcController.getSliceUri()));
diff --git a/tests/robotests/src/com/android/settings/password/ConfirmLockPasswordTest.java b/tests/robotests/src/com/android/settings/password/ConfirmLockPasswordTest.java
index 77a013d..d26c33b 100644
--- a/tests/robotests/src/com/android/settings/password/ConfirmLockPasswordTest.java
+++ b/tests/robotests/src/com/android/settings/password/ConfirmLockPasswordTest.java
@@ -197,7 +197,7 @@
         verify(mCredentialCheckResultTracker).setResult(
                 eq(true), any(), eq(0), eq(fragment.mEffectiveUserId));
         assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isTrue();
-        assertThat(fragment.mDeviceCredentialGuess).isNotNull();
+        assertThat(fragment.mRemoteLockscreenValidationFragment.getLockscreenCredential()).isNull();
     }
 
     @Test
@@ -223,7 +223,7 @@
         verify(mCredentialCheckResultTracker).setResult(
                 eq(true), any(), eq(0), eq(fragment.mEffectiveUserId));
         assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isFalse();
-        assertThat(fragment.mDeviceCredentialGuess).isNull();
+        assertThat(fragment.mRemoteLockscreenValidationFragment.getLockscreenCredential()).isNull();
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/password/ConfirmLockPatternTest.java b/tests/robotests/src/com/android/settings/password/ConfirmLockPatternTest.java
index 4374e38..2ed7988 100644
--- a/tests/robotests/src/com/android/settings/password/ConfirmLockPatternTest.java
+++ b/tests/robotests/src/com/android/settings/password/ConfirmLockPatternTest.java
@@ -177,7 +177,7 @@
         verify(mCredentialCheckResultTracker).setResult(
                 eq(true), any(), eq(0), eq(fragment.mEffectiveUserId));
         assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isTrue();
-        assertThat(fragment.mDeviceCredentialGuess).isNotNull();
+        assertThat(fragment.mRemoteLockscreenValidationFragment.getLockscreenCredential()).isNull();
     }
 
     @Test
@@ -203,7 +203,7 @@
         verify(mCredentialCheckResultTracker).setResult(
                 eq(true), any(), eq(0), eq(fragment.mEffectiveUserId));
         assertThat(mLockPatternUtils.isSecure(fragment.mEffectiveUserId)).isFalse();
-        assertThat(fragment.mDeviceCredentialGuess).isNull();
+        assertThat(fragment.mRemoteLockscreenValidationFragment.getLockscreenCredential()).isNull();
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/security/MemtagHelperTest.java b/tests/robotests/src/com/android/settings/security/MemtagHelperTest.java
index 4ef7de4..e3be8e2 100644
--- a/tests/robotests/src/com/android/settings/security/MemtagHelperTest.java
+++ b/tests/robotests/src/com/android/settings/security/MemtagHelperTest.java
@@ -33,8 +33,6 @@
 public class MemtagHelperTest {
     private final String mMemtagProperty = "arm64.memtag.bootctl";
     private final String mMemtagSupportedProperty = "ro.arm64.memtag.bootctl_settings_toggle";
-    private final String mDeviceConfigOverride =
-            "persist.device_config.memory_safety_native_boot.bootloader_override";
 
     @Test
     public void isChecked_empty_isFalse() {
@@ -80,7 +78,7 @@
 
     @Test
     public void getAvailabilityStatus_isForcedOff_isDISABLED_DEPENDENT_SETTING() {
-        ShadowSystemProperties.override(mDeviceConfigOverride, "force_off");
+        ShadowSystemProperties.override(MemtagHelper.DEVICE_CONFIG_PROP, "force_off");
         ShadowSystemProperties.override(mMemtagSupportedProperty, "true");
         assertThat(MemtagHelper.getAvailabilityStatus())
                 .isEqualTo(BasePreferenceController.DISABLED_DEPENDENT_SETTING);
@@ -88,7 +86,7 @@
 
     @Test
     public void getAvailabilityStatus_isForcedOn_isDISABLED_DEPENDENT_SETTING() {
-        ShadowSystemProperties.override(mDeviceConfigOverride, "force_on");
+        ShadowSystemProperties.override(MemtagHelper.DEVICE_CONFIG_PROP, "force_on");
         ShadowSystemProperties.override(mMemtagSupportedProperty, "true");
         assertThat(MemtagHelper.getAvailabilityStatus())
                 .isEqualTo(BasePreferenceController.DISABLED_DEPENDENT_SETTING);
@@ -96,7 +94,7 @@
 
     @Test
     public void getAvailabilityStatus_isUnsupported_isUNSUPPORTED_ON_DEVICE() {
-        ShadowSystemProperties.override(mDeviceConfigOverride, "");
+        ShadowSystemProperties.override(MemtagHelper.DEVICE_CONFIG_PROP, "");
         ShadowSystemProperties.override(mMemtagSupportedProperty, "false");
         assertThat(MemtagHelper.getAvailabilityStatus())
                 .isEqualTo(BasePreferenceController.UNSUPPORTED_ON_DEVICE);
@@ -127,7 +125,7 @@
     @Config(shadows = {ZygoteShadow.class})
     public void getSummary_memtagAndZygoteSupportsMemoryTagging_memtag_on() {
         ZygoteShadow.setSupportsMemoryTagging(true);
-        ShadowSystemProperties.override(mDeviceConfigOverride, "");
+        ShadowSystemProperties.override(MemtagHelper.DEVICE_CONFIG_PROP, "");
         ShadowSystemProperties.override(mMemtagProperty, "memtag");
         assertThat(MemtagHelper.getSummary()).isEqualTo(R.string.memtag_on);
     }
@@ -136,7 +134,7 @@
     @Config(shadows = {ZygoteShadow.class})
     public void getSummary_noMemtagAndZygoteSupportsMemoryTagging_memtag_off_pending() {
         ZygoteShadow.setSupportsMemoryTagging(true);
-        ShadowSystemProperties.override(mDeviceConfigOverride, "");
+        ShadowSystemProperties.override(MemtagHelper.DEVICE_CONFIG_PROP, "");
         ShadowSystemProperties.override(mMemtagProperty, "");
         assertThat(MemtagHelper.getSummary()).isEqualTo(R.string.memtag_off_pending);
     }
@@ -145,7 +143,7 @@
     @Config(shadows = {ZygoteShadow.class})
     public void getSummary_noMemtagAndNoZygoteSupportsMemoryTagging_memtag_off() {
         ZygoteShadow.setSupportsMemoryTagging(false);
-        ShadowSystemProperties.override(mDeviceConfigOverride, "");
+        ShadowSystemProperties.override(MemtagHelper.DEVICE_CONFIG_PROP, "");
         ShadowSystemProperties.override(mMemtagProperty, "");
         assertThat(MemtagHelper.getSummary()).isEqualTo(R.string.memtag_off);
     }
@@ -154,7 +152,7 @@
     @Config(shadows = {ZygoteShadow.class})
     public void getSummary_memtagAndNoZygoteSupportsMemoryTagging_memtag_on_pending() {
         ZygoteShadow.setSupportsMemoryTagging(false);
-        ShadowSystemProperties.override(mDeviceConfigOverride, "");
+        ShadowSystemProperties.override(MemtagHelper.DEVICE_CONFIG_PROP, "");
         ShadowSystemProperties.override(mMemtagProperty, "memtag");
         assertThat(MemtagHelper.getSummary()).isEqualTo(R.string.memtag_on_pending);
     }
@@ -163,7 +161,7 @@
     @Config(shadows = {ZygoteShadow.class})
     public void getSummary_forceOffOverride_memtag_force_off() {
         ZygoteShadow.setSupportsMemoryTagging(false);
-        ShadowSystemProperties.override(mDeviceConfigOverride, "force_off");
+        ShadowSystemProperties.override(MemtagHelper.DEVICE_CONFIG_PROP, "force_off");
         ShadowSystemProperties.override(mMemtagProperty, "memtag");
         assertThat(MemtagHelper.getSummary()).isEqualTo(R.string.memtag_force_off);
     }
@@ -172,20 +170,20 @@
     @Config(shadows = {ZygoteShadow.class})
     public void getSummary_forceOffOverride_memtag_force_on() {
         ZygoteShadow.setSupportsMemoryTagging(false);
-        ShadowSystemProperties.override(mDeviceConfigOverride, "force_on");
+        ShadowSystemProperties.override(MemtagHelper.DEVICE_CONFIG_PROP, "force_on");
         ShadowSystemProperties.override(mMemtagProperty, "memtag");
         assertThat(MemtagHelper.getSummary()).isEqualTo(R.string.memtag_force_on);
     }
 
     @Test
     public void isForcedOn_forceOnOverride_isTrue() {
-        ShadowSystemProperties.override(mDeviceConfigOverride, "force_on");
+        ShadowSystemProperties.override(MemtagHelper.DEVICE_CONFIG_PROP, "force_on");
         assertThat(MemtagHelper.isForcedOn()).isTrue();
     }
 
     @Test
     public void isForcedOff_forceOffOverride_isTrue() {
-        ShadowSystemProperties.override(mDeviceConfigOverride, "force_off");
+        ShadowSystemProperties.override(MemtagHelper.DEVICE_CONFIG_PROP, "force_off");
         assertThat(MemtagHelper.isForcedOff()).isTrue();
     }
 }
diff --git a/tests/robotests/src/com/android/settings/theme/ContrastPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/theme/ContrastPreferenceControllerTest.java
new file mode 100644
index 0000000..dbd3372
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/theme/ContrastPreferenceControllerTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.theme;
+
+import static android.app.UiModeManager.ContrastUtils;
+import static android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_HIGH;
+import static android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_MEDIUM;
+import static android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_STANDARD;
+import static android.provider.Settings.Secure.CONTRAST_LEVEL;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.UiModeManager;
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.preference.Preference;
+import androidx.test.core.app.ApplicationProvider;
+
+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.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.stream.Stream;
+
+@RunWith(RobolectricTestRunner.class)
+public class ContrastPreferenceControllerTest {
+
+    @Rule
+    public MockitoRule mocks = MockitoJUnit.rule();
+
+    private ContrastPreferenceController mController;
+
+    @Mock
+    private UiModeManager mMockUiModeManager;
+    private Context mContext;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mContext = ApplicationProvider.getApplicationContext();
+        mController = new ContrastPreferenceController(mContext, mMockUiModeManager);
+    }
+
+    @Test
+    public void controllerIsAvailable() {
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+    }
+
+    @Test
+    public void testHandlePreferenceTreeClick() {
+        Preference preference = new Preference(mContext);
+        preference.setKey(ContrastPreferenceController.KEY);
+        assertThat(mController.handlePreferenceTreeClick(preference)).isTrue();
+
+        Preference otherPreference = new Preference(mContext);
+        otherPreference.setKey("wrong key");
+        assertThat(mController.handlePreferenceTreeClick(otherPreference)).isFalse();
+    }
+
+    @Test
+    public void controllerSummary() {
+        float initialContrast = mContext.getSystemService(UiModeManager.class).getContrast();
+        try {
+            allContrastValues().forEach(contrastLevel -> {
+                float contrast = ContrastUtils.fromContrastLevel(contrastLevel);
+                clearInvocations(mMockUiModeManager);
+                when(mMockUiModeManager.getContrast()).thenReturn(contrast);
+                String summary = mController.getSummary().toString();
+                verify(mMockUiModeManager).getContrast();
+                assertThat(summary).isEqualTo(mController.getSummary(contrastLevel));
+            });
+        } finally {
+            putContrastInSettings(initialContrast);
+        }
+    }
+
+    private static Stream<Integer> allContrastValues() {
+        return Stream.of(CONTRAST_LEVEL_STANDARD, CONTRAST_LEVEL_MEDIUM, CONTRAST_LEVEL_HIGH);
+    }
+
+    private void putContrastInSettings(float contrast) {
+        Settings.Secure.putFloat(mContext.getContentResolver(), CONTRAST_LEVEL, contrast);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/users/TimeoutToDockUserPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/users/TimeoutToDockUserPreferenceControllerTest.java
index 2e7e2d7..3d8e893 100644
--- a/tests/robotests/src/com/android/settings/users/TimeoutToDockUserPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/users/TimeoutToDockUserPreferenceControllerTest.java
@@ -27,6 +27,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.Settings;
 
 import androidx.test.core.app.ApplicationProvider;
@@ -41,6 +42,8 @@
 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.annotation.Config;
 
@@ -51,6 +54,9 @@
     private Resources mResources;
     private TimeoutToDockUserPreferenceController mController;
 
+    @Mock
+    private UserManager mUserManager;
+
     private static final String FAKE_PREFERENCE_KEY = "timeout_to_dock_user_preference";
 
     private String[] mEntries;
@@ -58,9 +64,12 @@
 
     @Before
     public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
         mContext = spy(ApplicationProvider.getApplicationContext());
         mResources = spy(mContext.getResources());
         doReturn(mResources).when(mContext).getResources();
+        when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
 
         mEntries = mResources.getStringArray(
                 R.array.switch_to_dock_user_when_docked_timeout_entries);
@@ -78,6 +87,9 @@
         Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.USER_SWITCHER_ENABLED,
                 1);
 
+        // User switching not blocked.
+        when(mUserManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH)).thenReturn(false);
+
         // Set to user 1;
         ShadowUserHandle.setUid(1);
     }
@@ -107,6 +119,14 @@
     }
 
     @Test
+    public void getAvailabilityStatus_userSwitchingBlocked_returnConditionallyUnavailable() {
+        when(mUserManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH)).thenReturn(true);
+
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(
+                BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
+    }
+
+    @Test
     public void getAvailabilityStatus_isCurrentlyMainUser_returnDisabledForUser() {
         when(Utils.canCurrentUserDream(mContext)).thenReturn(true);
 
diff --git a/tests/robotests/src/com/android/settings/uwb/UwbPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/uwb/UwbPreferenceControllerTest.java
index 94d797a..23aca51 100644
--- a/tests/robotests/src/com/android/settings/uwb/UwbPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/uwb/UwbPreferenceControllerTest.java
@@ -16,110 +16,157 @@
 
 package com.android.settings.uwb;
 
+import static android.uwb.UwbManager.AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_POLICY;
+import static android.uwb.UwbManager.AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_REGULATION;
+import static android.uwb.UwbManager.AdapterStateCallback.STATE_DISABLED;
+import static android.uwb.UwbManager.AdapterStateCallback.STATE_ENABLED_ACTIVE;
+import static android.uwb.UwbManager.AdapterStateCallback.STATE_ENABLED_INACTIVE;
+
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.test.TestLooper;
 import android.uwb.UwbManager;
 
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
 import com.android.settings.core.BasePreferenceController;
 
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
 
 /** Unit tests for UWB preference toggle. */
 @RunWith(RobolectricTestRunner.class)
 public class UwbPreferenceControllerTest {
+    private static final String TEST_SUMMARY = "uwb";
+    private static final String TEST_AIRPLANE_SUMMARY = "apm_uwb";
+    private static final String TEST_NO_UWB_REGULATORY_SUMMARY = "regulatory_uwb";
     @Rule
     public MockitoRule rule = MockitoJUnit.rule();
 
+    @Mock
     private Context mContext;
+    @Mock
     private PackageManager mPackageManager;
     private UwbPreferenceController mController;
-
+    private ArgumentCaptor<UwbManager.AdapterStateCallback> mAdapterStateCallbackArgumentCaptor =
+            ArgumentCaptor.forClass(UwbManager.AdapterStateCallback.class);
+    private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverArgumentCaptor =
+            ArgumentCaptor.forClass(BroadcastReceiver.class);
+    private TestLooper mTestLooper;
     @Mock
     private UwbManager mUwbManager;
+    @Mock
+    private UwbUtils mUwbUtils;
+    @Mock
+    private Preference mPreference;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+    @Mock
+    private Resources mResources;
 
     @Before
-    public void setUp() {
-        mContext = spy(RuntimeEnvironment.application);
-        mPackageManager = spy(mContext.getPackageManager());
-        mController = new UwbPreferenceController(mContext, "uwb_settings");
-        mController.mUwbManager = mUwbManager;
-    }
-
-    @Test
-    public void getAvailabilityStatus_uwbDisabled_shouldReturnDisabled() {
+    public void setUp() throws Exception {
+        mTestLooper = new TestLooper();
         doReturn(mPackageManager).when(mContext).getPackageManager();
         doReturn(true).when(mPackageManager)
                 .hasSystemFeature(PackageManager.FEATURE_UWB);
-        mController.mAirplaneModeOn = true;
+        when(mResources.getString(R.string.uwb_settings_summary))
+                .thenReturn(TEST_SUMMARY);
+        when(mResources.getString(R.string.uwb_settings_summary_airplane_mode))
+                .thenReturn(TEST_AIRPLANE_SUMMARY);
+        when(mResources.getString(R.string.uwb_settings_summary_no_uwb_regulatory))
+                .thenReturn(TEST_NO_UWB_REGULATORY_SUMMARY);
+        when(mContext.getMainLooper()).thenReturn(mTestLooper.getLooper());
+        when(mContext.getSystemService(UwbManager.class)).thenReturn(mUwbManager);
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mUwbUtils.isAirplaneModeOn(any())).thenReturn(false);
+        doReturn(STATE_ENABLED_ACTIVE).when(mUwbManager).getAdapterState();
+        mController = new UwbPreferenceController(mContext, "uwb_settings", mUwbUtils);
+        when(mPreferenceScreen.findPreference(anyString())).thenReturn(mPreference);
+        mController.displayPreference(mPreferenceScreen);
+    }
 
+    private void startControllerAndCaptureCallbacks() {
+        mController.onStart();
+        verify(mContext).registerReceiver(
+                mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any());
+        verify(mUwbManager).registerAdapterStateCallback(
+                any(), mAdapterStateCallbackArgumentCaptor.capture());
+    }
+
+    @Test
+    public void getAvailabilityStatus_uwbDisabled_shouldReturnDisabled() throws Exception {
+        when(mUwbUtils.isAirplaneModeOn(any())).thenReturn(true);
+        startControllerAndCaptureCallbacks();
         assertThat(mController.getAvailabilityStatus())
                 .isEqualTo(BasePreferenceController.DISABLED_DEPENDENT_SETTING);
     }
 
     @Test
-    public void getAvailabilityStatus_uwbShown_shouldReturnAvailable() {
-        doReturn(mPackageManager).when(mContext).getPackageManager();
-        doReturn(true).when(mPackageManager)
-                .hasSystemFeature(PackageManager.FEATURE_UWB);
-        mController.mAirplaneModeOn = false;
-
+    public void getAvailabilityStatus_uwbShown_shouldReturnAvailable() throws Exception {
+        when(mUwbUtils.isAirplaneModeOn(any())).thenReturn(false);
+        startControllerAndCaptureCallbacks();
         assertThat(mController.getAvailabilityStatus())
                 .isEqualTo(BasePreferenceController.AVAILABLE);
     }
 
     @Test
     public void getAvailabilityStatus_uwbNotShown_shouldReturnUnsupported() {
-        doReturn(mPackageManager).when(mContext).getPackageManager();
         doReturn(false).when(mPackageManager)
                 .hasSystemFeature(PackageManager.FEATURE_UWB);
 
+        mController.onStart();
+        verify(mContext, never()).registerReceiver(any(), any(), any(), any());
+        verify(mUwbManager, never()).registerAdapterStateCallback(any(), any());
         assertThat(mController.getAvailabilityStatus())
                 .isEqualTo(BasePreferenceController.UNSUPPORTED_ON_DEVICE);
     }
 
     @Test
     public void isChecked_uwbEnabled_shouldReturnTrue() {
-        doReturn(mPackageManager).when(mContext).getPackageManager();
-        doReturn(true).when(mPackageManager)
-                .hasSystemFeature(PackageManager.FEATURE_UWB);
-        doReturn(mController.STATE_ENABLED_ACTIVE).when(mUwbManager).getAdapterState();
+        doReturn(STATE_ENABLED_ACTIVE).when(mUwbManager).getAdapterState();
 
+        startControllerAndCaptureCallbacks();
         assertThat(mController.isChecked()).isTrue();
     }
 
     @Test
     public void isChecked_uwbDisabled_shouldReturnFalse() {
-        doReturn(mPackageManager).when(mContext).getPackageManager();
-        doReturn(true).when(mPackageManager)
-                .hasSystemFeature(PackageManager.FEATURE_UWB);
-        doReturn(mController.STATE_DISABLED).when(mUwbManager).getAdapterState();
+        doReturn(STATE_DISABLED).when(mUwbManager).getAdapterState();
 
+        startControllerAndCaptureCallbacks();
         assertThat(mController.isChecked()).isFalse();
     }
 
     @Test
     public void setChecked_uwbDisabled_shouldEnableUwb() {
         clearInvocations(mUwbManager);
-        doReturn(mPackageManager).when(mContext).getPackageManager();
-        doReturn(true).when(mPackageManager)
-                .hasSystemFeature(PackageManager.FEATURE_UWB);
 
+        startControllerAndCaptureCallbacks();
         mController.setChecked(true);
 
         verify(mUwbManager).setUwbEnabled(true);
@@ -129,14 +176,65 @@
     @Test
     public void setChecked_uwbEnabled_shouldDisableUwb() {
         clearInvocations(mUwbManager);
-        doReturn(mPackageManager).when(mContext).getPackageManager();
-        doReturn(true).when(mPackageManager)
-                .hasSystemFeature(PackageManager.FEATURE_UWB);
 
+        startControllerAndCaptureCallbacks();
         mController.setChecked(false);
 
         verify(mUwbManager).setUwbEnabled(false);
         verify(mUwbManager, never()).setUwbEnabled(true);
     }
+
+    @Test
+    public void updateStateAndSummary_uwbDisabledAndEnabled() {
+        startControllerAndCaptureCallbacks();
+        clearInvocations(mUwbManager, mPreference);
+
+        mAdapterStateCallbackArgumentCaptor.getValue().onStateChanged(
+                STATE_DISABLED, STATE_CHANGED_REASON_SYSTEM_POLICY);
+
+        verify(mPreference).setEnabled(true);
+        assertThat(mController.isChecked()).isFalse();
+        verify(mPreference, times(2)).setSummary(TEST_SUMMARY);
+
+        mAdapterStateCallbackArgumentCaptor.getValue().onStateChanged(
+                STATE_ENABLED_INACTIVE, STATE_CHANGED_REASON_SYSTEM_POLICY);
+
+        verify(mPreference, times(2)).setEnabled(true);
+        assertThat(mController.isChecked()).isTrue();
+        verify(mPreference, times(4)).setSummary(TEST_SUMMARY);
+    }
+
+    @Test
+    public void updateStateAndSummary_apmEnabledAndDisabled() {
+        startControllerAndCaptureCallbacks();
+        clearInvocations(mUwbManager, mPreference);
+
+        when(mUwbUtils.isAirplaneModeOn(any())).thenReturn(true);
+        mBroadcastReceiverArgumentCaptor.getValue().onReceive(
+                mock(Context.class), mock(Intent.class));
+
+        verify(mPreference).setEnabled(false);
+        verify(mPreference, times(2)).setSummary(TEST_AIRPLANE_SUMMARY);
+
+        when(mUwbUtils.isAirplaneModeOn(any())).thenReturn(false);
+        mBroadcastReceiverArgumentCaptor.getValue().onReceive(
+                mock(Context.class), mock(Intent.class));
+
+        verify(mPreference).setEnabled(true);
+        verify(mPreference, times(2)).setSummary(TEST_SUMMARY);
+    }
+
+    @Test
+    public void updateStateAndSummary_uwbDisabledDueToRegulatory() {
+        startControllerAndCaptureCallbacks();
+        clearInvocations(mUwbManager, mPreference);
+
+        mAdapterStateCallbackArgumentCaptor.getValue().onStateChanged(
+                STATE_DISABLED, STATE_CHANGED_REASON_SYSTEM_REGULATION);
+
+        assertThat(mController.getAvailabilityStatus())
+                .isEqualTo(BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
+        verify(mPreference, times(2)).setSummary(TEST_NO_UWB_REGULATORY_SUMMARY);
+    }
 }
 
diff --git a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java
index 323618a..6190c5e 100644
--- a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java
+++ b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java
@@ -21,6 +21,8 @@
 import static android.hardware.fingerprint.FingerprintManager.EnrollReason;
 import static android.hardware.fingerprint.FingerprintManager.EnrollmentCallback;
 
+import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -29,18 +31,24 @@
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.only;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.Application;
 import android.content.res.Resources;
 import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
 
+import androidx.annotation.NonNull;
 import androidx.lifecycle.LiveData;
+import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
-import com.android.settings.R;
 import com.android.settings.biometrics.fingerprint.FingerprintUpdater;
+import com.android.settings.biometrics.fingerprint.MessageDisplayController;
 import com.android.settings.biometrics2.ui.model.EnrollmentProgress;
 import com.android.settings.biometrics2.ui.model.EnrollmentStatusMessage;
 import com.android.settings.testutils.InstantTaskExecutorRule;
@@ -68,12 +76,18 @@
     private FingerprintEnrollProgressViewModel mViewModel;
     private final TestWrapper<CancellationSignal> mCancellationSignalWrapper = new TestWrapper<>();
     private final TestWrapper<EnrollmentCallback> mCallbackWrapper = new TestWrapper<>();
+    private int mEnrollmentMessageDisplayControllerFlagResId;
 
     @Before
     public void setUp() {
+        mEnrollmentMessageDisplayControllerFlagResId = ApplicationProvider.getApplicationContext()
+                .getResources().getIdentifier("enrollment_message_display_controller_flag", "bool",
+                        SETTINGS_PACKAGE_NAME);
+
         when(mApplication.getResources()).thenReturn(mResources);
-        when(mResources.getBoolean(R.bool.enrollment_message_display_controller_flag))
-                .thenReturn(false);
+
+        // Not use MessageDisplayController by default
+        when(mResources.getBoolean(mEnrollmentMessageDisplayControllerFlagResId)).thenReturn(false);
         mViewModel = new FingerprintEnrollProgressViewModel(mApplication, mFingerprintUpdater,
                 TEST_USER_ID);
 
@@ -88,7 +102,7 @@
     }
 
     @Test
-    public void testStartEnrollment() {
+    public void testStartFindSensor() {
         @EnrollReason final int enrollReason = ENROLL_FIND_SENSOR;
         final byte[] token = new byte[] { 1, 2, 3 };
         mViewModel.setToken(token);
@@ -99,6 +113,54 @@
         assertThat(ret).isTrue();
         verify(mFingerprintUpdater, only()).enroll(eq(token), any(CancellationSignal.class),
                 eq(TEST_USER_ID), any(EnrollmentCallback.class), eq(enrollReason));
+        assertThat(mCallbackWrapper.mValue instanceof MessageDisplayController).isFalse();
+    }
+
+    @Test
+    public void testStartEnrolling() {
+        @EnrollReason final int enrollReason = ENROLL_ENROLL;
+        final byte[] token = new byte[] { 1, 2, 3 };
+        mViewModel.setToken(token);
+
+        // Start enrollment
+        final boolean ret = mViewModel.startEnrollment(enrollReason);
+
+        assertThat(ret).isTrue();
+        verify(mFingerprintUpdater, only()).enroll(eq(token), any(CancellationSignal.class),
+                eq(TEST_USER_ID), any(EnrollmentCallback.class), eq(enrollReason));
+        assertThat(mCallbackWrapper.mValue instanceof MessageDisplayController).isFalse();
+    }
+
+    @Test
+    public void testStartEnrollingWithMessageDisplayController() {
+        // Enable MessageDisplayController and mock handler for it
+        when(mResources.getBoolean(mEnrollmentMessageDisplayControllerFlagResId)).thenReturn(true);
+        when(mApplication.getMainThreadHandler()).thenReturn(new TestHandler());
+
+        @EnrollReason final int enrollReason = ENROLL_ENROLL;
+        final byte[] token = new byte[] { 1, 2, 3 };
+        mViewModel.setToken(token);
+
+        // Start enrollment
+        final boolean ret = mViewModel.startEnrollment(enrollReason);
+
+        assertThat(ret).isTrue();
+        verify(mFingerprintUpdater, only()).enroll(eq(token), any(CancellationSignal.class),
+                eq(TEST_USER_ID), any(MessageDisplayController.class), eq(enrollReason));
+        assertThat(mCallbackWrapper.mValue).isNotNull();
+
+        assertThat(mCallbackWrapper.mValue instanceof MessageDisplayController).isTrue();
+        final EnrollmentCallback callback1 = mCallbackWrapper.mValue;
+
+        // Cancel and start again
+        mViewModel.cancelEnrollment();
+        mViewModel.startEnrollment(enrollReason);
+
+        // Shall not use the same MessageDisplayController
+        verify(mFingerprintUpdater, times(2)).enroll(eq(token), any(CancellationSignal.class),
+                eq(TEST_USER_ID), any(MessageDisplayController.class), eq(enrollReason));
+        assertThat(mCallbackWrapper.mValue).isNotNull();
+        assertThat(callback1).isNotEqualTo(mCallbackWrapper.mValue);
     }
 
     @Test
@@ -163,6 +225,48 @@
     }
 
     @Test
+    public void testProgressUpdateWithMessageDisplayController() {
+        // Enable MessageDisplayController and mock handler for it
+        when(mResources.getBoolean(mEnrollmentMessageDisplayControllerFlagResId)).thenReturn(true);
+        when(mApplication.getMainThreadHandler()).thenReturn(new TestHandler());
+
+        mViewModel.setToken(new byte[] { 1, 2, 3 });
+
+        // Start enrollment
+        final boolean ret = mViewModel.startEnrollment(ENROLL_ENROLL);
+        assertThat(ret).isTrue();
+        assertThat(mCallbackWrapper.mValue).isNotNull();
+
+        // Test default value
+        final LiveData<EnrollmentProgress> progressLiveData = mViewModel.getProgressLiveData();
+        EnrollmentProgress progress = progressLiveData.getValue();
+        assertThat(progress).isNotNull();
+        assertThat(progress.getSteps()).isEqualTo(-1);
+        assertThat(progress.getRemaining()).isEqualTo(0);
+
+        // Update first progress
+        mCallbackWrapper.mValue.onEnrollmentProgress(25);
+        progress = progressLiveData.getValue();
+        assertThat(progress).isNotNull();
+        assertThat(progress.getSteps()).isEqualTo(25);
+        assertThat(progress.getRemaining()).isEqualTo(25);
+
+        // Update second progress
+        mCallbackWrapper.mValue.onEnrollmentProgress(20);
+        progress = progressLiveData.getValue();
+        assertThat(progress).isNotNull();
+        assertThat(progress.getSteps()).isEqualTo(25);
+        assertThat(progress.getRemaining()).isEqualTo(20);
+
+        // Update latest progress
+        mCallbackWrapper.mValue.onEnrollmentProgress(0);
+        progress = progressLiveData.getValue();
+        assertThat(progress).isNotNull();
+        assertThat(progress.getSteps()).isEqualTo(25);
+        assertThat(progress.getRemaining()).isEqualTo(0);
+    }
+
+    @Test
     public void testGetErrorMessageLiveData() {
         // Start enrollment
         mViewModel.setToken(new byte[] { 1, 2, 3 });
@@ -262,4 +366,17 @@
     private static class TestWrapper<T> {
         T mValue;
     }
+
+    private static class TestHandler extends Handler {
+
+        TestHandler() {
+            super(Looper.getMainLooper());
+        }
+
+        @Override
+        public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
+            msg.getCallback().run();
+            return true;
+        }
+    }
 }