[automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: 364eb998be -s ours am: aa006392a2 -s ours am: 5f53b4c10e -s ours am: 5f846bb466 -s ours

am skip reason: subject contains skip directive

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Settings/+/23788719

Change-Id: I4a34153ae7b2389c7e84652ea6eb01a97680a604
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Android.bp b/Android.bp
index 4d07913..fd97dc3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -149,14 +149,17 @@
     srcs: ["proguard.flags"],
 }
 
-// The sources for Settings need to be exposed to SettingsGoogle, etc.
-// so they can run the com.android.settingslib.search.IndexableProcessor
-// over all the sources together.
+// Deprecated. The sources for Settings need to be exposed to ArcSettings, so they can run the
+// com.android.settingslib.search.IndexableProcessor over all the sources together.
+// Use "-Acom.android.settingslib.search.processor.package=" instead to generate the search data
+// separately for different modules.
 filegroup {
     name: "Settings_srcs",
     srcs: ["src/**/*.java", "src/**/*.kt"],
 }
 
+// Deprecated. Do not depend on this, only depend on Settings-core, and its manifest is also
+// included.
 filegroup {
     name: "Settings_manifest",
     srcs: ["AndroidManifest.xml"],
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 2c3e7f3..9c5d6c6 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2397,6 +2397,8 @@
             <intent-filter android:priority="1">
                 <action android:name="android.app.action.CONFIRM_DEVICE_CREDENTIAL" />
                 <action android:name="android.app.action.CONFIRM_FRP_CREDENTIAL" />
+                <action android:name="android.app.action.PREPARE_REPAIR_MODE_DEVICE_CREDENTIAL" />
+                <action android:name="android.app.action.CONFIRM_REPAIR_MODE_DEVICE_CREDENTIAL" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
diff --git a/res/layout-land/udfps_enroll_enrolling.xml b/res/layout-land/udfps_enroll_enrolling.xml
deleted file mode 100644
index 743684f..0000000
--- a/res/layout-land/udfps_enroll_enrolling.xml
+++ /dev/null
@@ -1,101 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright (C) 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
--->
-
-<com.google.android.setupdesign.GlifLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/setup_wizard_layout"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:layout="@layout/sud_glif_blank_template"
-    style="?attr/fingerprint_layout_theme">
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:orientation="horizontal"
-        android:clipToPadding="false"
-        android:clipChildren="false">
-
-        <!-- Both texts are kept as separate text views so it doesn't jump around in portrait.
-            See layouts/fingerprint_enroll_enrolling_base.xml. -->
-        <LinearLayout
-            android:id="@+id/layout_container"
-            android:layout_width="0dp"
-            android:layout_weight="1"
-            android:layout_height="match_parent"
-            android:layout_marginStart="?attr/sudMarginStart"
-            android:layout_marginEnd="@dimen/enroll_margin_end"
-            android:layout_marginBottom="@dimen/sud_content_frame_padding_bottom"
-            android:paddingStart="@dimen/enroll_padding_start"
-            android:paddingEnd="@dimen/enroll_padding_end"
-            android:clipChildren="false"
-            android:clipToPadding="false"
-            android:orientation="vertical">
-
-            <ScrollView
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:clipChildren="false"
-                android:clipToPadding="false"
-                android:fillViewport="true">
-
-                <LinearLayout
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:orientation="vertical"
-                    android:clipChildren="false"
-                    android:clipToPadding="false">
-
-                    <ImageView
-                        android:id="@+id/sud_layout_icon"
-                        style="@style/SudGlifIcon"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:scaleType="fitStart"
-                        android:layout_marginStart="0dp"
-                        android:layout_marginEnd="0dp"
-                        android:src="@drawable/ic_lock" />
-
-                    <TextView
-                        android:id="@+id/suc_layout_title"
-                        style="@style/SudGlifHeaderTitle"
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        android:layout_marginStart="0dp"
-                        android:layout_marginEnd="0dp" />
-
-                    <TextView
-                        style="@style/SudDescription.Glif"
-                        android:id="@+id/sud_layout_subtitle"
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"/>
-
-                    <Space
-                        android:layout_width="0dp"
-                        android:layout_height="0dp"
-                        android:layout_weight="1" />
-
-                </LinearLayout>
-
-            </ScrollView>
-
-        </LinearLayout>
-
-    </LinearLayout>
-
-    <include layout="@layout/udfps_enroll_view" />
-</com.google.android.setupdesign.GlifLayout>
\ No newline at end of file
diff --git a/res/layout/locale_order_list.xml b/res/layout/locale_order_list.xml
index 5c1db15..da1eb62 100644
--- a/res/layout/locale_order_list.xml
+++ b/res/layout/locale_order_list.xml
@@ -27,11 +27,11 @@
         android:clipChildren="true"
         android:orientation="vertical">
 
-        <com.android.settings.localepicker.LocaleRecyclerView
+        <androidx.recyclerview.widget.RecyclerView
             android:id="@+id/dragList"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:scrollbars="vertical"/>
+            android:scrollbars="none"/>
 
         <Button
             android:id="@+id/add_language"
diff --git a/res/layout/modifier_key_item.xml b/res/layout/modifier_key_item.xml
index a189479..683f631 100644
--- a/res/layout/modifier_key_item.xml
+++ b/res/layout/modifier_key_item.xml
@@ -19,8 +19,7 @@
     android:layout_marginTop="8dip"
     android:layout_marginBottom="8dip"
     android:minHeight="?android:attr/listPreferredItemHeight"
-    android:paddingEnd="?android:attr/scrollbarSize"
-    android:layout_weight="1">
+    android:paddingEnd="?android:attr/scrollbarSize">
 
     <ImageView
         android:id="@+id/modifier_key_check_icon"
@@ -36,7 +35,7 @@
 
     <TextView
         android:id="@+id/modifier_key_text"
-        android:layout_width="match_parent"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_centerVertical="true"
         android:textDirection="locale"
@@ -46,4 +45,38 @@
         android:ellipsize="marquee"
         android:fadingEdge="horizontal" />
 
+    <TextView
+        android:id="@+id/modifier_key_left_bracket"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerVertical="true"
+        android:textDirection="locale"
+        android:padding="1dp"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:layout_toEndOf="@+id/modifier_key_text"
+        android:fadingEdge="horizontal" />
+
+    <ImageView
+        android:id="@+id/modifier_key_action_key_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerVertical="true"
+        android:layout_toEndOf="@+id/modifier_key_left_bracket"
+        android:fadingEdge="horizontal"
+        android:tint="?android:attr/textColorPrimary"/>
+
+    <TextView
+        android:id="@+id/modifier_key_right_bracket"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerVertical="true"
+        android:textDirection="locale"
+        android:padding="1dp"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:layout_toEndOf="@+id/modifier_key_action_key_icon"
+        android:fadingEdge="horizontal" />
+
+    <View android:layout_width="wrap_content"
+          android:layout_height="match_parent" />
+
 </RelativeLayout>
diff --git a/res/layout/modifier_keys_custom_key.xml b/res/layout/modifier_keys_custom_key.xml
new file mode 100644
index 0000000..f390c00
--- /dev/null
+++ b/res/layout/modifier_keys_custom_key.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+          http://www.apache.org/licenses/LICENSE-2.0
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:gravity="center_vertical"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:background="?android:attr/selectableItemBackground">
+
+    <FrameLayout
+        android:id="@+id/icon_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+        <androidx.preference.internal.PreferenceImageView
+            android:id="@android:id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:maxWidth="48dp"
+            app:maxHeight="48dp" />
+    </FrameLayout>
+
+    <RelativeLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp"
+        android:layout_weight="1">
+
+        <TextView android:id="@+id/title"
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:singleLine="true"
+                  android:textAppearance="?android:attr/textAppearanceLarge"
+                  android:textColor="?android:attr/textColorPrimary"
+                  android:fadingEdge="horizontal" />
+
+        <TextView android:id="@+id/summary"
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:layout_below="@+id/title"
+                  android:layout_alignStart="@+id/title"
+                  android:layout_alignLeft="@+id/title"
+                  android:textAppearance="?android:attr/textAppearanceSmall"
+                  android:textColor="?android:attr/textColorSecondary"
+                  android:maxLines="4" />
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingStart="15dp"
+            android:layout_toEndOf="@+id/title"
+            android:gravity="center_vertical"
+            android:orientation="horizontal">
+
+            <TextView
+                android:id="@+id/modifier_key_left_bracket"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textDirection="locale"
+                android:paddingStart="1dp"
+                android:paddingEnd="1dp"
+                android:textAppearance="?android:attr/textAppearanceLarge"
+                android:textColor="?android:attr/textColorPrimary"
+                android:fadingEdge="horizontal" />
+
+            <ImageView
+                android:id="@+id/modifier_key_action_key_icon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:fadingEdge="horizontal"
+                android:tint="?android:attr/textColorPrimary"/>
+
+            <TextView
+                android:id="@+id/modifier_key_right_bracket"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textDirection="locale"
+                android:paddingStart="1dp"
+                android:paddingEnd="1dp"
+                android:textAppearance="?android:attr/textAppearanceLarge"
+                android:textColor="?android:attr/textColorPrimary"
+                android:fadingEdge="horizontal" />
+        </LinearLayout>
+    </RelativeLayout>
+
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout android:id="@android:id/widget_frame"
+                  android:layout_width="wrap_content"
+                  android:layout_height="match_parent"
+                  android:gravity="center_vertical"
+                  android:orientation="vertical" />
+</LinearLayout>
diff --git a/res/layout/udfps_enroll_enrolling.xml b/res/layout/udfps_enroll_enrolling.xml
index 05556ff..366a87c 100644
--- a/res/layout/udfps_enroll_enrolling.xml
+++ b/res/layout/udfps_enroll_enrolling.xml
@@ -15,7 +15,7 @@
   ~ limitations under the License.
   -->
 
-<com.google.android.setupdesign.GlifLayout
+<com.android.settings.biometrics.fingerprint.UdfpsEnrollEnrollingView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
@@ -78,4 +78,4 @@
       </LinearLayout>
     </LinearLayout>
 
-</com.google.android.setupdesign.GlifLayout>
+</com.android.settings.biometrics.fingerprint.UdfpsEnrollEnrollingView>
diff --git a/res/layout/udfps_enroll_view.xml b/res/layout/udfps_enroll_view.xml
index 6bf339b..bd62609 100644
--- a/res/layout/udfps_enroll_view.xml
+++ b/res/layout/udfps_enroll_view.xml
@@ -18,7 +18,8 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/udfps_animation_view"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:visibility="gone">
 
     <ImageView
         android:id="@+id/udfps_enroll_animation_fp_progress_view"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index cb33e7f..3184a1f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -258,8 +258,10 @@
 
     <!-- Title for stylus device details page [CHAR LIMIT=50] -->
     <string name="stylus_device_details_title">Stylus</string>
-    <!-- Preference title for setting the default note taking app [CHAR LIMIT=none] -->
-    <string name="stylus_default_notes_app">Default notes app</string>
+    <!-- Preference title for setting the app that opens user presses stylus button [CHAR LIMIT=none] -->
+    <string name="stylus_default_notes_app">Tail button press</string>
+    <!-- Summary for the app that opens when user presses stylus tail button, if set to a work profile app [CHAR LIMIT=none] -->
+    <string name="stylus_default_notes_summary_work"><xliff:g id="app_name" example="Mail">%s</xliff:g> (Work profile)</string>
     <!-- Preference title for toggling whether handwriting in textfields is enabled [CHAR LIMIT=none] -->
     <string name="stylus_textfield_handwriting">Write in text fields</string>
     <!-- Preference title for toggling whether stylus button presses are ignored [CHAR LIMIT=none] -->
@@ -411,7 +413,7 @@
     <!-- The title of the menu entry of Numbers system preference. [CHAR LIMIT=50]  -->
     <string name="numbers_preferences_title">Numbers preferences</string>
     <!-- The summary of default string for each regional preference. [CHAR LIMIT=50] -->
-    <string name="default_string_of_regional_preference">Use app default</string>
+    <string name="default_string_of_regional_preference">Use default</string>
     <!-- The title of Celsius for preference of temperature unit. [CHAR LIMIT=50] -->
     <string name="celsius_temperature_unit">Celsius (\u00B0C)</string>
     <!-- The title of Fahrenheit for preference of temperature unit. [CHAR LIMIT=50] -->
@@ -2658,6 +2660,8 @@
     <string name="build_number">Build number</string>
     <!-- About phone screen, tapping this button will take user to a seperate UI to check Google Play system update [CHAR LIMIT=60] -->
     <string name="module_version">Google Play system update</string>
+    <!-- About phone screen, show a list of battery information  [CHAR LIMIT=60] -->
+    <string name="battery_info">Battery information</string>
 
     <!-- About phone screen, show when a value of some status item is unavailable. -->
     <string name="device_info_not_available">Not available</string>
@@ -2729,6 +2733,16 @@
     <string name="status_serial_number">Serial number</string>
     <!-- About phone, status item title.  How long the device has been running since its last reboot. -->
     <string name="status_up_time">Up time</string>
+
+    <!-- About phone, status item title. The battery manufacture date. [CHAR LIMIT=60]-->
+    <string name="battery_manufacture_date">Manufacture date</string>
+    <!-- About phone, status item title. Date of first use of the battery. [CHAR LIMIT=60]-->
+    <string name="battery_first_use_date">Date of first use</string>
+    <!-- About phone, status item title. Count of battery full charge/discharge cycles [CHAR LIMIT=60]-->
+    <string name="battery_cycle_count">Cycle count</string>
+    <!-- About phone, status item title. The status summary for cycle count that's not available. [CHAR LIMIT=40] -->
+    <string name="battery_cycle_count_not_available">Unavailable</string>
+
     <!-- SD card & phone storage settings summary. Displayed when the total memory usage is being calculated. Will be replaced with a number like "12.3 GB" when finished calucating. [CHAR LIMIT=30] -->
     <string name="memory_calculating_size">Calculating\u2026</string>
 
@@ -3012,8 +3026,6 @@
     <string name="reset_bluetooth_wifi_complete_toast">Bluetooth &amp; Wi\u2011Fi have been reset</string>
 
     <!-- Erase Euicc -->
-    <!-- Confirmation button of dialog to confirm resetting user's app preferences [CHAR LIMIT=NONE] -->
-    <string name="erase_euicc_data_button">Erase</string>
     <!-- Erase Euicc dialog and SD card & phone storage settings screen, title for the menu option and checkbox to let user decide whether erase eSIM data together [CHAR LIMIT=50] -->
     <string name="reset_esim_title">Erase eSIMs</string>
     <!-- Erase Euicc dialog and SD card & phone storage settings screen, message for the checkbox to let user decide whether erase eSIM data together [CHAR LIMIT=NONE] -->
@@ -3483,6 +3495,18 @@
     <!-- Checkbox label to set password as new screen lock if remote device credential validation succeeds. [CHAR LIMIT=43] -->
     <string name="lockpassword_remote_validation_set_password_as_screenlock">Also use password to unlock this device</string>
 
+    <!-- Header shown when pattern needs to be solved before the device exits repair mode. [CHAR LIMIT=100] [DO NOT TRANSLATE] TODO(b/275677027): update with finalized UX string -->
+    <string name="lockpassword_confirm_repair_mode_pattern_header">Verify pattern</string>
+    <!-- Header shown when the pin needs to be solved before the device exits repair mode. [CHAR LIMIT=100] [DO NOT TRANSLATE] TODO(b/275677027): update with finalized UX string -->
+    <string name="lockpassword_confirm_repair_mode_pin_header">Verify PIN</string>
+    <!-- Header shown when the password needs to be solved before the device exits repair mode. [CHAR LIMIT=100] [DO NOT TRANSLATE] TODO(b/275677027): update with finalized UX string -->
+    <string name="lockpassword_confirm_repair_mode_password_header">Verify password</string>
+    <!-- An explanation text that the pattern needs to be solved before the device exits repair mode. [CHAR LIMIT=100] [DO NOT TRANSLATE] TODO(b/275677027): update with finalized UX string -->
+    <string name="lockpassword_confirm_repair_mode_pattern_details" translatable="false">Enter your device pattern enrolled in normal mode to continue</string>
+    <!-- An explanation text that the PIN needs to be solved before the device exits repair mode. [CHAR LIMIT=100] [DO NOT TRANSLATE] TODO(b/275677027): update with finalized UX string -->
+    <string name="lockpassword_confirm_repair_mode_pin_details" translatable="false">Enter your device PIN enrolled in normal mode to continue</string>
+    <!-- An explanation text that the password needs to be solved before the device exits repair mode. [CHAR LIMIT=100] [DO NOT TRANSLATE] TODO(b/275677027): update with finalized UX string -->
+    <string name="lockpassword_confirm_repair_mode_password_details" translatable="false">Enter your device password enrolled in normal mode to continue</string>
 
     <!-- Security & location settings screen, change security method screen instruction if user
          enters incorrect PIN [CHAR LIMIT=30] -->
@@ -5511,6 +5535,8 @@
     <string name="battery_usage_less_than_percent">&lt; <xliff:g id="percentage">%1$s</xliff:g></string>
     <!-- Process Stats strings -->
     <skip />
+    <!-- Description of battery information footer text. [CHAR LIMIT=NONE] -->
+    <string name="battery_cycle_count_footer">Due to quality inspections before shipping, the cycle count may not be zero on first use</string>
 
     <!-- [CHAR LIMIT=NONE] Activity title for Process Stats summary -->
     <string name="process_stats_summary_title">Process Stats</string>
@@ -7022,6 +7048,7 @@
     <string name="keywords_sim_status_iccid_esim">network, mobile network state, service state, signal strength, mobile network type, roaming, iccid, eid</string>
     <string name="keywords_esim_eid">eid</string>
     <string name="keywords_model_and_hardware">serial number, hardware version</string>
+    <string name="keywords_battery_info">battery info, manufacture date, cycle count, first use</string>
     <string name="keywords_android_version">android security patch level, baseband version, kernel version</string>
     <!-- Search keywords for dark mode settings [CHAR LIMIT=NONE] -->
     <string name="keywords_dark_ui_mode">theme, light, dark, mode, light sensitivity, photophobia, make darker, darken, dark mode, migraine</string>
@@ -9693,12 +9720,6 @@
     <!-- [CHAR_LIMIT=60] Label for special access screen -->
     <string name="special_access">Special app access</string>
 
-    <!-- Summary for special access settings [CHAR_LIMIT=NONE] -->
-    <plurals name="special_access_summary">
-        <item quantity="one">1 app can use unrestricted data</item>
-        <item quantity="other"><xliff:g id="count" example="10">%d</xliff:g> apps can use unrestricted data</item>
-    </plurals>
-
     <!-- Title for the See more preference item in Special app access settings [CHAR LIMIT=30] -->
     <string name="special_access_more">See more</string>
 
@@ -10515,8 +10536,6 @@
     <string name="platform_compat_default_disabled_title">Default disabled changes</string>
     <!-- Title for target SDK gated app compat changes category (do not translate 'targetSdkVersion') [CHAR LIMIT=50] -->
     <string name="platform_compat_target_sdk_title">Enabled for targetSdkVersion &gt;= <xliff:g id="number" example="29">%d</xliff:g></string>
-    <!-- Title for the dialog shown when no debuggable apps are available [CHAR LIMIT=30] -->
-    <string name="platform_compat_dialog_title_no_apps">No apps available</string>
     <!-- Explanatory text shown when no debuggable apps are available [CHAR LIMIT=NONE] -->
     <string name="platform_compat_dialog_text_no_apps">App compatibility changes can only be modified for debuggable apps. Install a debuggable app and try again.</string>
 
diff --git a/res/xml/apps.xml b/res/xml/apps.xml
index ae51bae..03212c9 100644
--- a/res/xml/apps.xml
+++ b/res/xml/apps.xml
@@ -105,7 +105,6 @@
         android:key="special_access"
         android:fragment="com.android.settings.applications.specialaccess.SpecialAccessSettings"
         android:title="@string/special_access"
-        android:order="20"
-        settings:controller="com.android.settings.applications.SpecialAppAccessPreferenceController"/>
+        android:order="20"/>
 
 </PreferenceScreen>
diff --git a/res/xml/battery_info.xml b/res/xml/battery_info.xml
new file mode 100644
index 0000000..8e3c31f
--- /dev/null
+++ b/res/xml/battery_info.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:title="@string/battery_info"
+    settings:keywords="@string/keywords_battery_info">
+
+    <Preference
+        android:key="battery_info_manufacture_date"
+        android:title="@string/battery_manufacture_date"
+        android:summary="@string/summary_placeholder"
+        settings:controller="com.android.settings.deviceinfo.batteryinfo.BatteryManufactureDatePreferenceController"
+        settings:enableCopying="true"/>
+
+    <Preference
+        android:key="battery_info_first_use_date"
+        android:title="@string/battery_first_use_date"
+        android:summary="@string/summary_placeholder"
+        settings:controller="com.android.settings.deviceinfo.batteryinfo.BatteryFirstUseDatePreferenceController"
+        settings:enableCopying="true"/>
+
+    <Preference
+        android:key="battery_info_cycle_count"
+        android:title="@string/battery_cycle_count"
+        android:summary="@string/summary_placeholder"
+        settings:controller="com.android.settings.deviceinfo.batteryinfo.BatteryCycleCountPreferenceController"
+        settings:enableCopying="true"/>
+
+    <com.android.settingslib.widget.FooterPreference
+        android:key="battery_info_footer"
+        android:title="@string/battery_cycle_count_footer"
+        android:selectable="false"
+        settings:searchable="false" />
+</PreferenceScreen>
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index 68e4e78..32acac6 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -258,7 +258,7 @@
             android:key="platform_compat_dashboard"
             android:title="@string/platform_compat_dashboard_title"
             android:summary="@string/platform_compat_dashboard_summary"
-            android:fragment="com.android.settings.development.compat.PlatformCompatDashboard"
+            settings:controller="com.android.settings.spa.development.compat.PlatformCompatPreferenceController"
             />
 
         <SwitchPreference
@@ -464,6 +464,11 @@
             android:title="@string/pointer_location"
             android:summary="@string/pointer_location_summary" />
 
+        <SwitchPreference
+            android:key="show_key_presses"
+            android:title="@string/show_key_presses"
+            android:summary="@string/show_key_presses_summary" />
+
     </PreferenceCategory>
 
     <PreferenceCategory
diff --git a/res/xml/languages.xml b/res/xml/languages.xml
index 0f45540..5269d99 100644
--- a/res/xml/languages.xml
+++ b/res/xml/languages.xml
@@ -18,7 +18,7 @@
 <PreferenceScreen
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:settings="http://schemas.android.com/apk/res-auto"
-    android:title="@string/language_settings">
+    android:title="@string/language_picker_title">
 
     <com.android.settingslib.widget.TopIntroPreference
         android:title="@string/desc_introduction_of_language_picker"
diff --git a/res/xml/modifier_keys_settings.xml b/res/xml/modifier_keys_settings.xml
index 63e7ee1..25525ae 100644
--- a/res/xml/modifier_keys_settings.xml
+++ b/res/xml/modifier_keys_settings.xml
@@ -21,25 +21,22 @@
     android:title="@string/modifier_keys_settings"
     android:key="modifier_keys_all"
     settings:controller="com.android.settings.inputmethod.ModifierKeysPreferenceController">
-    <Preference
+
+    <com.android.settingslib.widget.LayoutPreference
         android:key="modifier_keys_caps_lock"
-        android:title="@string/modifier_keys_caps_lock"
-        android:summary="@string/modifier_keys_default_summary"/>
+        android:layout="@layout/modifier_keys_custom_key" />
 
-    <Preference
+    <com.android.settingslib.widget.LayoutPreference
         android:key="modifier_keys_ctrl"
-        android:title="@string/modifier_keys_ctrl"
-        android:summary="@string/modifier_keys_default_summary"/>
+        android:layout="@layout/modifier_keys_custom_key" />
 
-    <Preference
+    <com.android.settingslib.widget.LayoutPreference
         android:key="modifier_keys_meta"
-        android:title="@string/modifier_keys_meta"
-        android:summary="@string/modifier_keys_default_summary"/>
+        android:layout="@layout/modifier_keys_custom_key" />
 
-    <Preference
+    <com.android.settingslib.widget.LayoutPreference
         android:key="modifier_keys_alt"
-        android:title="@string/modifier_keys_alt"
-        android:summary="@string/modifier_keys_default_summary"/>
+        android:layout="@layout/modifier_keys_custom_key" />
 
     <Preference
         android:key="modifier_keys_restore"
diff --git a/res/xml/my_device_info.xml b/res/xml/my_device_info.xml
index 4cbe13f..6576742 100644
--- a/res/xml/my_device_info.xml
+++ b/res/xml/my_device_info.xml
@@ -144,6 +144,14 @@
             android:summary="@string/summary_placeholder"
             android:fragment="com.android.settings.deviceinfo.firmwareversion.FirmwareVersionSettings"
             settings:controller="com.android.settings.deviceinfo.firmwareversion.FirmwareVersionPreferenceController"/>
+
+        <!-- Battery information -->
+        <Preference
+            android:key="battery_info"
+            android:order="43"
+            android:title="@string/battery_info"
+            android:fragment="com.android.settings.deviceinfo.batteryinfo.BatteryInfoFragment"
+            settings:keywords="@string/keywords_battery_info"/>
     </PreferenceCategory>
 
     <PreferenceCategory
diff --git a/res/layout/wifi_api_test.xml b/res/xml/wifi_api_test.xml
similarity index 100%
rename from res/layout/wifi_api_test.xml
rename to res/xml/wifi_api_test.xml
diff --git a/src/com/android/settings/SettingsActivityUtil.kt b/src/com/android/settings/SettingsActivityUtil.kt
index cac341f..65d26de 100644
--- a/src/com/android/settings/SettingsActivityUtil.kt
+++ b/src/com/android/settings/SettingsActivityUtil.kt
@@ -35,6 +35,7 @@
 import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider
 import com.android.settings.spa.app.specialaccess.MediaManagementAppsAppListProvider
 import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider
+import com.android.settings.spa.app.specialaccess.NfcTagAppsSettingsProvider
 import com.android.settings.spa.app.specialaccess.PictureInPictureListProvider
 import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider
 import com.android.settings.wifi.ChangeWifiStateDetails
@@ -62,6 +63,8 @@
             MediaManagementAppsAppListProvider.getAppInfoRoutePrefix(),
         ChangeWifiStateDetails::class.qualifiedName to
             WifiControlAppListProvider.getAppInfoRoutePrefix(),
+        NfcTagAppsSettingsProvider::class.qualifiedName to
+            NfcTagAppsSettingsProvider.getAppInfoRoutePrefix(),
     )
 
     @JvmStatic
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 68b1a48..a2195df 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -708,9 +708,13 @@
         final int userId = bundle.getInt(Intent.EXTRA_USER_ID, UserHandle.myUserId());
         if (userId == LockPatternUtils.USER_FRP) {
             return allowAnyUser ? userId : checkUserOwnsFrpCredential(context, userId);
-        } else {
-            return allowAnyUser ? userId : enforceSameOwner(context, userId);
         }
+        if (userId == LockPatternUtils.USER_REPAIR_MODE) {
+            enforceRepairModeActive(context);
+            // any users can exit repair mode
+            return userId;
+        }
+        return allowAnyUser ? userId : enforceSameOwner(context, userId);
     }
 
     /**
@@ -730,6 +734,16 @@
     }
 
     /**
+     * Throws {@link SecurityException} if repair mode is not active on the device.
+     */
+    private static void enforceRepairModeActive(Context context) {
+        if (LockPatternUtils.isRepairModeActive(context)) {
+            return;
+        }
+        throw new SecurityException("Repair mode is not active on the device.");
+    }
+
+    /**
      * Returns the given user id if it belongs to the current user.
      *
      * @throws SecurityException if the given userId does not belong to the current user group.
diff --git a/src/com/android/settings/accessibility/AccessibilityQuickSettingsPrimarySwitchPreferenceController.java b/src/com/android/settings/accessibility/AccessibilityQuickSettingsPrimarySwitchPreferenceController.java
index 9681a42..e82cd96 100644
--- a/src/com/android/settings/accessibility/AccessibilityQuickSettingsPrimarySwitchPreferenceController.java
+++ b/src/com/android/settings/accessibility/AccessibilityQuickSettingsPrimarySwitchPreferenceController.java
@@ -66,6 +66,10 @@
     @Override
     public void onDestroy() {
         mHandler.removeCallbacksAndMessages(null);
+        final boolean isTooltipWindowShowing = mTooltipWindow != null && mTooltipWindow.isShowing();
+        if (isTooltipWindowShowing) {
+            mTooltipWindow.dismiss();
+        }
     }
 
     @Override
@@ -126,10 +130,17 @@
             return;
         }
 
-        mTooltipWindow = new AccessibilityQuickSettingsTooltipWindow(mContext);
-        mTooltipWindow.setup(getTileTooltipContent(),
-                R.drawable.accessibility_auto_added_qs_tooltip_illustration);
-        mTooltipWindow.showAtTopCenter(mPreference.getSwitch());
+        // TODO (287728819): Move tooltip showing to SystemUI
+        // Since the lifecycle of controller is independent of that of the preference, doing
+        // null check on switch is a temporary solution for the case that switch view
+        // is not ready when we would like to show the tooltip.  If the switch is not ready,
+        // we give up showing the tooltip and also do not reshow it in the future.
+        if (mPreference.getSwitch() != null) {
+            mTooltipWindow = new AccessibilityQuickSettingsTooltipWindow(mContext);
+            mTooltipWindow.setup(getTileTooltipContent(),
+                    R.drawable.accessibility_auto_added_qs_tooltip_illustration);
+            mTooltipWindow.showAtTopCenter(mPreference.getSwitch());
+        }
         AccessibilityQuickSettingUtils.optInValueToSharedPreferences(mContext, tileComponentName);
         mNeedsQSTooltipReshow = false;
     }
diff --git a/src/com/android/settings/accessibility/HearingAidHelper.java b/src/com/android/settings/accessibility/HearingAidHelper.java
index 66a37f8..1b9bdc4 100644
--- a/src/com/android/settings/accessibility/HearingAidHelper.java
+++ b/src/com/android/settings/accessibility/HearingAidHelper.java
@@ -56,7 +56,8 @@
      * @return a list of hearing aids {@link BluetoothDevice} objects
      */
     public List<BluetoothDevice> getConnectedHearingAidDeviceList() {
-        if (!isHearingAidSupported()) {
+        if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()
+                || !isHearingAidSupported()) {
             return new ArrayList<>();
         }
         final List<BluetoothDevice> deviceList = new ArrayList<>();
@@ -88,9 +89,6 @@
      * supported.
      */
     public boolean isHearingAidSupported() {
-        if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
-            return false;
-        }
         final List<Integer> supportedList = mBluetoothAdapter.getSupportedProfiles();
         return supportedList.contains(BluetoothProfile.HEARING_AID)
                 || supportedList.contains(BluetoothProfile.HAP_CLIENT);
diff --git a/src/com/android/settings/accessibility/HearingAidUtils.java b/src/com/android/settings/accessibility/HearingAidUtils.java
index 42484f9..4315093 100644
--- a/src/com/android/settings/accessibility/HearingAidUtils.java
+++ b/src/com/android/settings/accessibility/HearingAidUtils.java
@@ -23,6 +23,7 @@
 
 import com.android.settings.bluetooth.HearingAidPairingDialogFragment;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CsipSetCoordinatorProfile;
 import com.android.settingslib.bluetooth.HearingAidInfo;
 
 /** Provides utility methods related hearing aids. */
@@ -40,6 +41,11 @@
      */
     public static void launchHearingAidPairingDialog(FragmentManager fragmentManager,
             @NonNull CachedBluetoothDevice device) {
+        // No need to show the pair another ear dialog if the device supports and enables CSIP.
+        // CSIP will pair other devices in the same set automatically.
+        if (isCsipSupportedAndEnabled(device)) {
+            return;
+        }
         if (device.isConnectedAshaHearingAidDevice()
                 && device.getDeviceMode() == HearingAidInfo.DeviceMode.MODE_BINAURAL
                 && device.getSubDevice() == null) {
@@ -56,4 +62,10 @@
         HearingAidPairingDialogFragment.newInstance(device.getAddress()).show(fragmentManager,
                 HearingAidPairingDialogFragment.TAG);
     }
+
+    private static boolean isCsipSupportedAndEnabled(@NonNull CachedBluetoothDevice device) {
+        return device.getProfiles().stream().anyMatch(
+                profile -> (profile instanceof CsipSetCoordinatorProfile)
+                        && (profile.isEnabled(device.getDevice())));
+    }
 }
diff --git a/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java b/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java
index 4c860eb..6bd8747 100644
--- a/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java
+++ b/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java
@@ -28,7 +28,6 @@
 import com.android.settings.R;
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.widget.LabeledSeekBarPreference;
-import com.android.settings.widget.SeekBarPreference;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnCreate;
 import com.android.settingslib.core.lifecycle.events.OnDestroy;
@@ -111,6 +110,10 @@
     public void onDestroy() {
         // remove runnables in the queue.
         mHandler.removeCallbacksAndMessages(null);
+        final boolean isTooltipWindowShowing = mTooltipWindow != null && mTooltipWindow.isShowing();
+        if (isTooltipWindowShowing) {
+            mTooltipWindow.dismiss();
+        }
     }
 
     @Override
@@ -210,11 +213,19 @@
             return;
         }
 
-        mTooltipWindow = new AccessibilityQuickSettingsTooltipWindow(mContext);
-        mTooltipWindow.setup(getTileTooltipContent(),
-                R.drawable.accessibility_auto_added_qs_tooltip_illustration);
-        mTooltipWindow.showAtTopCenter(mSeekBarPreference.getSeekbar());
-        AccessibilityQuickSettingUtils.optInValueToSharedPreferences(mContext, tileComponentName);
+        // TODO (287728819): Move tooltip showing to SystemUI
+        // Since the lifecycle of controller is independent of that of the preference, doing
+        // null check on seekbar is a temporary solution for the case that seekbar view
+        // is not ready when we would like to show the tooltip.  If the seekbar is not ready,
+        // we give up showing the tooltip and also do not reshow it in the future.
+        if (mSeekBarPreference.getSeekbar() != null) {
+            mTooltipWindow = new AccessibilityQuickSettingsTooltipWindow(mContext);
+            mTooltipWindow.setup(getTileTooltipContent(),
+                    R.drawable.accessibility_auto_added_qs_tooltip_illustration);
+            mTooltipWindow.showAtTopCenter(mSeekBarPreference.getSeekbar());
+        }
+        AccessibilityQuickSettingUtils.optInValueToSharedPreferences(mContext,
+                tileComponentName);
         mNeedsQSTooltipReshow = false;
     }
 
diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
index edbd120..6a4344f 100644
--- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
@@ -296,6 +296,10 @@
     public void onDestroyView() {
         super.onDestroyView();
         removeActionBarToggleSwitch();
+        final boolean isTooltipWindowShowing = mTooltipWindow != null && mTooltipWindow.isShowing();
+        if (isTooltipWindowShowing) {
+            mTooltipWindow.dismiss();
+        }
     }
 
     @Override
diff --git a/src/com/android/settings/applications/AppDashboardFragment.java b/src/com/android/settings/applications/AppDashboardFragment.java
index 7e203b0..11f8405 100644
--- a/src/com/android/settings/applications/AppDashboardFragment.java
+++ b/src/com/android/settings/applications/AppDashboardFragment.java
@@ -66,7 +66,6 @@
     @Override
     public void onAttach(Context context) {
         super.onAttach(context);
-        use(SpecialAppAccessPreferenceController.class).setSession(getSettingsLifecycle());
         mAppsPreferenceController = use(AppsPreferenceController.class);
         mAppsPreferenceController.setFragment(this /* fragment */);
         getSettingsLifecycle().addObserver(mAppsPreferenceController);
diff --git a/src/com/android/settings/applications/SpecialAppAccessPreferenceController.java b/src/com/android/settings/applications/SpecialAppAccessPreferenceController.java
deleted file mode 100644
index 42f5930..0000000
--- a/src/com/android/settings/applications/SpecialAppAccessPreferenceController.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-package com.android.settings.applications;
-
-import android.app.Application;
-import android.content.Context;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
-
-import com.android.settings.R;
-import com.android.settings.core.BasePreferenceController;
-import com.android.settings.datausage.AppStateDataUsageBridge;
-import com.android.settings.datausage.AppStateDataUsageBridge.DataUsageState;
-import com.android.settings.datausage.DataSaverBackend;
-import com.android.settingslib.applications.ApplicationsState;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-import com.android.settingslib.core.lifecycle.LifecycleObserver;
-import com.android.settingslib.core.lifecycle.events.OnDestroy;
-import com.android.settingslib.core.lifecycle.events.OnStart;
-import com.android.settingslib.core.lifecycle.events.OnStop;
-
-import java.util.ArrayList;
-
-public class SpecialAppAccessPreferenceController extends BasePreferenceController implements
-        AppStateBaseBridge.Callback, ApplicationsState.Callbacks, LifecycleObserver, OnStart,
-        OnStop, OnDestroy {
-
-    @VisibleForTesting
-    ApplicationsState.Session mSession;
-
-    private final ApplicationsState mApplicationsState;
-    private final AppStateDataUsageBridge mDataUsageBridge;
-    private final DataSaverBackend mDataSaverBackend;
-
-    private Preference mPreference;
-    private boolean mExtraLoaded;
-
-
-    public SpecialAppAccessPreferenceController(Context context, String key) {
-        super(context, key);
-        mApplicationsState = ApplicationsState.getInstance(
-                (Application) context.getApplicationContext());
-        mDataSaverBackend = new DataSaverBackend(context);
-        mDataUsageBridge = new AppStateDataUsageBridge(mApplicationsState, this, mDataSaverBackend);
-    }
-
-    public void setSession(Lifecycle lifecycle) {
-        mSession = mApplicationsState.newSession(this, lifecycle);
-    }
-
-    @Override
-    public int getAvailabilityStatus() {
-        return AVAILABLE;
-    }
-
-    @Override
-    public void displayPreference(PreferenceScreen screen) {
-        super.displayPreference(screen);
-        mPreference = screen.findPreference(getPreferenceKey());
-    }
-
-    @Override
-    public void onStart() {
-        mDataUsageBridge.resume(true /* forceLoadAllApps */);
-    }
-
-    @Override
-    public void onStop() {
-        mDataUsageBridge.pause();
-    }
-
-    @Override
-    public void onDestroy() {
-        mDataUsageBridge.release();
-    }
-
-    @Override
-    public void updateState(Preference preference) {
-        updateSummary();
-    }
-
-    @Override
-    public void onExtraInfoUpdated() {
-        mExtraLoaded = true;
-        updateSummary();
-    }
-
-    private void updateSummary() {
-        if (!mExtraLoaded || mPreference == null) {
-            return;
-        }
-
-        final ArrayList<ApplicationsState.AppEntry> allApps = mSession.getAllApps();
-        int count = 0;
-        for (ApplicationsState.AppEntry entry : allApps) {
-            if (!ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(entry)) {
-                continue;
-            }
-            if (entry.extraInfo instanceof DataUsageState
-                    && ((DataUsageState) entry.extraInfo).isDataSaverAllowlisted) {
-                count++;
-            }
-        }
-        mPreference.setSummary(mContext.getResources().getQuantityString(
-                R.plurals.special_access_summary, count, count));
-    }
-
-    @Override
-    public void onRunningStateChanged(boolean running) {
-    }
-
-    @Override
-    public void onPackageListChanged() {
-    }
-
-    @Override
-    public void onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps) {
-    }
-
-    @Override
-    public void onPackageIconChanged() {
-    }
-
-    @Override
-    public void onPackageSizeChanged(String packageName) {
-    }
-
-    @Override
-    public void onAllSizesComputed() {
-    }
-
-    @Override
-    public void onLauncherInfoChanged() {
-        // when the value of the AppEntry.hasLauncherEntry was changed.
-        updateSummary();
-    }
-
-    @Override
-    public void onLoadEntriesCompleted() {
-    }
-}
diff --git a/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt b/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt
index 78a4a6b..6574f69 100644
--- a/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt
+++ b/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt
@@ -63,6 +63,7 @@
 import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider
 import com.android.settings.spa.app.specialaccess.MediaManagementAppsAppListProvider
 import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider
+import com.android.settings.spa.app.specialaccess.NfcTagAppsSettingsProvider
 import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider
 import com.android.settings.spa.notification.AppListNotificationsPageProvider
 import com.android.settings.spa.system.AppLanguagesPageProvider
@@ -112,6 +113,7 @@
             LIST_TYPE_NOTIFICATION -> AppListNotificationsPageProvider.name
             LIST_TYPE_APPS_LOCALE -> AppLanguagesPageProvider.name
             LIST_TYPE_MAIN -> AllAppListPageProvider.name
+            LIST_TYPE_NFC_TAG_APPS -> NfcTagAppsSettingsProvider.getAppListRoute()
             else -> null
         }
     }
diff --git a/src/com/android/settings/applications/specialaccess/DataSaverController.java b/src/com/android/settings/applications/specialaccess/DataSaverController.java
deleted file mode 100644
index d1fd202..0000000
--- a/src/com/android/settings/applications/specialaccess/DataSaverController.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-package com.android.settings.applications.specialaccess;
-
-import android.content.Context;
-
-import com.android.settings.R;
-import com.android.settings.core.BasePreferenceController;
-
-public class DataSaverController extends BasePreferenceController {
-
-    public DataSaverController(Context context, String key) {
-        super(context, key);
-    }
-
-    @AvailabilityStatus
-    public int getAvailabilityStatus() {
-        return mContext.getResources().getBoolean(R.bool.config_show_data_saver)
-                ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
-    }
-}
diff --git a/src/com/android/settings/applications/specialaccess/DataSaverController.kt b/src/com/android/settings/applications/specialaccess/DataSaverController.kt
new file mode 100644
index 0000000..baed0aa
--- /dev/null
+++ b/src/com/android/settings/applications/specialaccess/DataSaverController.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.applications.specialaccess
+
+import android.content.Context
+import android.net.NetworkPolicyManager
+import android.os.UserHandle
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import androidx.preference.Preference
+import androidx.preference.PreferenceScreen
+import com.android.settings.R
+import com.android.settings.core.BasePreferenceController
+import com.android.settingslib.spa.framework.util.formatString
+import com.android.settingslib.spaprivileged.model.app.AppListRepository
+import com.android.settingslib.spaprivileged.model.app.AppListRepositoryImpl
+import com.google.common.annotations.VisibleForTesting
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.async
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+class DataSaverController(context: Context, key: String) : BasePreferenceController(context, key) {
+
+    private lateinit var preference: Preference
+
+    @AvailabilityStatus
+    override fun getAvailabilityStatus(): Int = when {
+        mContext.resources.getBoolean(R.bool.config_show_data_saver) -> AVAILABLE
+        else -> UNSUPPORTED_ON_DEVICE
+    }
+
+    override fun displayPreference(screen: PreferenceScreen) {
+        super.displayPreference(screen)
+        preference = screen.findPreference(preferenceKey)!!
+    }
+
+    override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
+        viewLifecycleOwner.lifecycleScope.launch {
+            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
+                preference.summary = getUnrestrictedSummary(mContext)
+            }
+        }
+    }
+
+    companion object {
+        @VisibleForTesting
+        suspend fun getUnrestrictedSummary(
+            context: Context,
+            appListRepository: AppListRepository =
+                AppListRepositoryImpl(context.applicationContext),
+        ) = context.formatString(
+            R.string.data_saver_unrestricted_summary,
+            "count" to getAllowCount(context.applicationContext, appListRepository),
+        )
+
+        private suspend fun getAllowCount(context: Context, appListRepository: AppListRepository) =
+            withContext(Dispatchers.IO) {
+                coroutineScope {
+                    val appsDeferred = async {
+                        appListRepository.loadAndFilterApps(
+                            userId = UserHandle.myUserId(),
+                            isSystemApp = false,
+                        )
+                    }
+                    val uidsAllowed = NetworkPolicyManager.from(context)
+                        .getUidsWithPolicy(NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND)
+                    appsDeferred.await().count { app -> app.uid in uidsAllowed }
+                }
+            }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/biometrics/BiometricEnrollBase.java b/src/com/android/settings/biometrics/BiometricEnrollBase.java
index 2f852f0..6e11079 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollBase.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollBase.java
@@ -133,6 +133,7 @@
     protected long mChallenge;
     protected boolean mFromSettingsSummary;
     protected FooterBarMixin mFooterBarMixin;
+    protected boolean mShouldSetFooterBarBackground = true;
     @Nullable
     protected ScreenSizeFoldProvider mScreenSizeFoldProvider;
     @Nullable
@@ -191,12 +192,14 @@
         super.onPostCreate(savedInstanceState);
         initViews();
 
-        @SuppressLint("VisibleForTests")
-        final LinearLayout buttonContainer = mFooterBarMixin != null
-                ? mFooterBarMixin.getButtonContainer()
-                : null;
-        if (buttonContainer != null) {
-            buttonContainer.setBackgroundColor(getBackgroundColor());
+        if (mShouldSetFooterBarBackground) {
+            @SuppressLint("VisibleForTests")
+            final LinearLayout buttonContainer = mFooterBarMixin != null
+                    ? mFooterBarMixin.getButtonContainer()
+                    : null;
+            if (buttonContainer != null) {
+                buttonContainer.setBackgroundColor(getBackgroundColor());
+            }
         }
     }
 
@@ -331,7 +334,7 @@
     }
 
     @ColorInt
-    private int getBackgroundColor() {
+    public int getBackgroundColor() {
         final ColorStateList stateList = Utils.getColorAttr(this, android.R.attr.windowBackground);
         return stateList != null ? stateList.getDefaultColor() : Color.TRANSPARENT;
     }
diff --git a/src/com/android/settings/biometrics/BiometricEnrollSidecar.java b/src/com/android/settings/biometrics/BiometricEnrollSidecar.java
index 97d46a4..369fa4b 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollSidecar.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollSidecar.java
@@ -48,11 +48,16 @@
         /**
          * Called when a pointer down event has occurred.
          */
-        default void onPointerDown(int sensorId) { }
+        default void onUdfpsPointerDown(int sensorId) { }
         /**
          * Called when a pointer up event has occurred.
          */
-        default void onPointerUp(int sensorId) { }
+        default void onUdfpsPointerUp(int sensorId) { }
+
+        /**
+         * Called when udfps overlay is shown.
+         */
+        default void onUdfpsOverlayShown() { }
     }
 
     private int mEnrollmentSteps = -1;
@@ -126,29 +131,36 @@
         }
     }
 
-    private class QueuedPointerDown extends QueuedEvent {
+    private class QueuedUdfpsPointerDown extends QueuedEvent {
         private final int sensorId;
 
-        public QueuedPointerDown(int sensorId) {
+        QueuedUdfpsPointerDown(int sensorId) {
             this.sensorId = sensorId;
         }
 
         @Override
         public void send(Listener listener) {
-            listener.onPointerDown(sensorId);
+            listener.onUdfpsPointerDown(sensorId);
         }
     }
 
-    private class QueuedPointerUp extends QueuedEvent {
+    private class QueuedUdfpsPointerUp extends QueuedEvent {
         private final int sensorId;
 
-        public QueuedPointerUp(int sensorId) {
+        QueuedUdfpsPointerUp(int sensorId) {
             this.sensorId = sensorId;
         }
 
         @Override
         public void send(Listener listener) {
-            listener.onPointerUp(sensorId);
+            listener.onUdfpsPointerUp(sensorId);
+        }
+    }
+
+    private class QueuedUdfpsOverlayShown extends QueuedEvent {
+        @Override
+        public void send(Listener listener) {
+            listener.onUdfpsOverlayShown();
         }
     }
 
@@ -249,19 +261,27 @@
         }
     }
 
-    protected void onPointerDown(int sensorId) {
+    protected void onUdfpsPointerDown(int sensorId) {
         if (mListener != null) {
-            mListener.onPointerDown(sensorId);
+            mListener.onUdfpsPointerDown(sensorId);
         } else {
-            mQueuedEvents.add(new QueuedPointerDown(sensorId));
+            mQueuedEvents.add(new QueuedUdfpsPointerDown(sensorId));
         }
     }
 
-    protected void onPointerUp(int sensorId) {
+    protected void onUdfpsPointerUp(int sensorId) {
         if (mListener != null) {
-            mListener.onPointerUp(sensorId);
+            mListener.onUdfpsPointerUp(sensorId);
         } else {
-            mQueuedEvents.add(new QueuedPointerUp(sensorId));
+            mQueuedEvents.add(new QueuedUdfpsPointerUp(sensorId));
+        }
+    }
+
+    protected void onUdfpsOverlayShown() {
+        if (mListener != null) {
+            mListener.onUdfpsOverlayShown();
+        } else {
+            mQueuedEvents.add(new QueuedUdfpsOverlayShown());
         }
     }
 
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
index bff998a..bea0c33 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
@@ -120,6 +120,8 @@
     protected void onCreate(Bundle savedInstanceState) {
         mFaceManager = getFaceManager();
 
+        super.onCreate(savedInstanceState);
+
         if (savedInstanceState == null
                 && !WizardManagerHelper.isAnySetupWizard(getIntent())
                 && !getIntent().getBooleanExtra(EXTRA_FROM_SETTINGS_SUMMARY, false)
@@ -130,8 +132,6 @@
             finish();
         }
 
-        super.onCreate(savedInstanceState);
-
         // Wait super::onCreated() then return because SuperNotCalledExceptio will be thrown
         // if we don't wait for it.
         if (isFinishing()) {
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintAuthenticateSidecar.java b/src/com/android/settings/biometrics/fingerprint/FingerprintAuthenticateSidecar.java
index 4264056..f3c8aba 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintAuthenticateSidecar.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintAuthenticateSidecar.java
@@ -21,6 +21,7 @@
 import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
 import android.os.CancellationSignal;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.core.InstrumentedFragment;
 
 /**
@@ -80,7 +81,6 @@
 
                 @Override
                 public void onAuthenticationError(int errMsgId, CharSequence errString) {
-                    mCancellationSignal = null;
                     if (mListener != null) {
                         mListener.onAuthenticationError(errMsgId, errString);
                     } else {
@@ -108,10 +108,12 @@
     }
 
     public void stopAuthentication() {
-        if (mCancellationSignal != null && !mCancellationSignal.isCanceled()) {
+        if (mCancellationSignal != null) {
+            // This will automatically check if the cancel has been sent and if so
+            // it won't send it again.
             mCancellationSignal.cancel();
+            mCancellationSignal = null;
         }
-        mCancellationSignal = null;
     }
 
     public void setListener(Listener listener) {
@@ -129,4 +131,9 @@
         }
         mListener = listener;
     }
+
+    @VisibleForTesting
+    boolean isCancelled() {
+        return mCancellationSignal == null || mCancellationSignal.isCanceled();
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
index f653942..dbdb024 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
@@ -32,10 +32,8 @@
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.graphics.Point;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
-import android.graphics.Rect;
 import android.graphics.drawable.Animatable2;
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.Drawable;
@@ -48,22 +46,16 @@
 import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.text.TextUtils;
-import android.util.FeatureFlagUtils;
 import android.util.Log;
-import android.view.DisplayInfo;
 import android.view.MotionEvent;
 import android.view.OrientationEventListener;
 import android.view.Surface;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
 import android.widget.ProgressBar;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
@@ -79,25 +71,20 @@
 import com.android.settings.biometrics.BiometricsEnrollEnrolling;
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
 import com.android.settingslib.display.DisplayDensityUtils;
-import com.android.settingslib.udfps.UdfpsOverlayParams;
-import com.android.settingslib.udfps.UdfpsUtils;
 
 import com.airbnb.lottie.LottieAnimationView;
 import com.airbnb.lottie.LottieCompositionFactory;
 import com.airbnb.lottie.LottieProperty;
 import com.airbnb.lottie.model.KeyPath;
-import com.google.android.setupcompat.template.FooterActionButton;
 import com.google.android.setupcompat.template.FooterBarMixin;
 import com.google.android.setupcompat.template.FooterButton;
 import com.google.android.setupcompat.util.WizardManagerHelper;
-import com.google.android.setupdesign.GlifLayout;
 import com.google.android.setupdesign.template.DescriptionMixin;
 import com.google.android.setupdesign.template.HeaderMixin;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
-import java.util.Locale;
 
 /**
  * Activity which handles the actual enrolling for fingerprint.
@@ -176,8 +163,6 @@
     @VisibleForTesting
     @Nullable
     UdfpsEnrollHelper mUdfpsEnrollHelper;
-    // TODO(b/260617060): Do not hard-code mScaleFactor, referring to AuthController.
-    private float mScaleFactor = 1.0f;
     private ObjectAnimator mProgressAnim;
     private TextView mErrorText;
     private Interpolator mFastOutSlowInInterpolator;
@@ -206,7 +191,7 @@
     private boolean mHaveShownSfpsLeftEdgeLottie;
     private boolean mHaveShownSfpsRightEdgeLottie;
     private boolean mShouldShowLottie;
-    private UdfpsUtils mUdfpsUtils;
+
     private ObjectAnimator mHelpAnimation;
 
     private OrientationEventListener mOrientationEventListener;
@@ -251,88 +236,17 @@
 
         mAccessibilityManager = getSystemService(AccessibilityManager.class);
         mIsAccessibilityEnabled = mAccessibilityManager.isEnabled();
-        mUdfpsUtils = new UdfpsUtils();
 
-        final boolean isLayoutRtl = (TextUtils.getLayoutDirectionFromLocale(
-                Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL);
         listenOrientationEvent();
 
         if (mCanAssumeUdfps) {
-            int rotation = getApplicationContext().getDisplay().getRotation();
-            final GlifLayout layout = (GlifLayout) getLayoutInflater().inflate(
-                    R.layout.udfps_enroll_enrolling, null, false);
-            final UdfpsEnrollView udfpsEnrollView = layout.findViewById(R.id.udfps_animation_view);
-            updateUdfpsEnrollView(udfpsEnrollView, props.get(0));
-            switch (rotation) {
-                case Surface.ROTATION_90:
-                    final View sudContent = layout.findViewById(R.id.sud_layout_content);
-                    if (sudContent != null) {
-                        sudContent.setPadding(sudContent.getPaddingLeft(), 0,
-                                sudContent.getPaddingRight(), sudContent.getPaddingBottom());
-                    }
+            final UdfpsEnrollEnrollingView layout =
+                    (UdfpsEnrollEnrollingView) getLayoutInflater().inflate(
+                            R.layout.udfps_enroll_enrolling, null, false);
+            setUdfpsEnrollHelper();
+            layout.initView(props.get(0), mUdfpsEnrollHelper, mAccessibilityManager);
 
-                    final LinearLayout layoutContainer = layout.findViewById(
-                            R.id.layout_container);
-                    final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
-                            LinearLayout.LayoutParams.MATCH_PARENT,
-                            LinearLayout.LayoutParams.MATCH_PARENT);
-
-                    lp.setMarginEnd((int) getResources().getDimension(
-                            R.dimen.rotation_90_enroll_margin_end));
-                    layoutContainer.setPaddingRelative((int) getResources().getDimension(
-                            R.dimen.rotation_90_enroll_padding_start), 0, isLayoutRtl
-                            ? 0 : (int) getResources().getDimension(
-                            R.dimen.rotation_90_enroll_padding_end), 0);
-                    layoutContainer.setLayoutParams(lp);
-
-                    setOnHoverListener(true, layout, udfpsEnrollView);
-                    setContentView(layout, lp);
-                    break;
-
-                case Surface.ROTATION_0:
-                case Surface.ROTATION_180:
-                    // In the portrait mode, layout_container's height is 0, so it's
-                    // always shown at the bottom of the screen.
-                    final FrameLayout portraitLayoutContainer = layout.findViewById(
-                            R.id.layout_container);
-
-                    // In the portrait mode, the title and lottie animation view may
-                    // overlap when title needs three lines, so adding some paddings
-                    // between them, and adjusting the fp progress view here accordingly.
-                    final int layoutLottieAnimationPadding = (int) getResources()
-                            .getDimension(R.dimen.udfps_lottie_padding_top);
-                    portraitLayoutContainer.setPadding(0,
-                            layoutLottieAnimationPadding, 0, 0);
-                    final ImageView progressView = udfpsEnrollView.findViewById(
-                            R.id.udfps_enroll_animation_fp_progress_view);
-                    progressView.setPadding(0, -(layoutLottieAnimationPadding),
-                            0, layoutLottieAnimationPadding);
-                    final ImageView fingerprintView = udfpsEnrollView.findViewById(
-                            R.id.udfps_enroll_animation_fp_view);
-                    fingerprintView.setPadding(0, -layoutLottieAnimationPadding,
-                            0, layoutLottieAnimationPadding);
-
-                    // TODO(b/260970216) Instead of hiding the description text view, we should
-                    //  make the header view scrollable if the text is too long.
-                    // If description text view has overlap with udfps progress view, hide it.
-                    View view = layout.getDescriptionTextView();
-                    layout.getViewTreeObserver().addOnDrawListener(() -> {
-                        if (view.getVisibility() == View.VISIBLE
-                                && hasOverlap(view, udfpsEnrollView)) {
-                            view.setVisibility(View.GONE);
-                        }
-                    });
-
-                    setOnHoverListener(false, layout, udfpsEnrollView);
-                    setContentView(layout);
-                    break;
-
-                case Surface.ROTATION_270:
-                default:
-                    setOnHoverListener(true, layout, udfpsEnrollView);
-                    setContentView(layout);
-                    break;
-            }
+            setContentView(layout);
             setDescriptionText(R.string.security_settings_udfps_enroll_start_message);
         } else if (mCanAssumeSfps) {
             setContentView(R.layout.sfps_enroll_enrolling);
@@ -372,22 +286,11 @@
                         .build()
         );
 
-        if (FeatureFlagUtils.isEnabled(getApplicationContext(),
-                FeatureFlagUtils.SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS)) {
-            // Remove the space view and make the width of footer button container WRAP_CONTENT
-            // to avoid hiding the udfps view progress bar bottom.
-            final LinearLayout buttonContainer = mFooterBarMixin.getButtonContainer();
-            View spaceView = null;
-            for (int i = 0; i < buttonContainer.getChildCount(); i++) {
-                if (!(buttonContainer.getChildAt(i) instanceof FooterActionButton)) {
-                    spaceView = buttonContainer.getChildAt(i);
-                    break;
-                }
-            }
-            if (spaceView != null) {
-                spaceView.setVisibility(View.GONE);
-                buttonContainer.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
-            }
+        // If it's udfps, set the background color only for secondary button if necessary.
+        if (mCanAssumeUdfps) {
+            mShouldSetFooterBarBackground = false;
+            ((UdfpsEnrollEnrollingView) getLayout()).setSecondaryButtonBackground(
+                    getBackgroundColor());
         }
 
         final LayerDrawable fingerprintDrawable = mProgressBar != null
@@ -925,19 +828,26 @@
     }
 
     @Override
-    public void onPointerDown(int sensorId) {
+    public void onUdfpsPointerDown(int sensorId) {
         if (mUdfpsEnrollHelper != null) {
             mUdfpsEnrollHelper.onPointerDown(sensorId);
         }
     }
 
     @Override
-    public void onPointerUp(int sensorId) {
+    public void onUdfpsPointerUp(int sensorId) {
         if (mUdfpsEnrollHelper != null) {
             mUdfpsEnrollHelper.onPointerUp(sensorId);
         }
     }
 
+    @Override
+    public void onUdfpsOverlayShown() {
+        if (mCanAssumeUdfps) {
+            findViewById(R.id.udfps_animation_view).setVisibility(View.VISIBLE);
+        }
+    }
+
     private void updateProgress(boolean animate) {
         if (mSidecar == null || !mSidecar.isEnrolling()) {
             Log.d(TAG, "Enrollment not started yet");
@@ -1230,30 +1140,7 @@
         }
     }
 
-    private UdfpsEnrollView updateUdfpsEnrollView(UdfpsEnrollView udfpsEnrollView,
-                                                  FingerprintSensorPropertiesInternal udfpsProps) {
-        DisplayInfo displayInfo = new DisplayInfo();
-        getDisplay().getDisplayInfo(displayInfo);
-        mScaleFactor = mUdfpsUtils.getScaleFactor(displayInfo);
-        Rect udfpsBounds = udfpsProps.getLocation().getRect();
-        udfpsBounds.scale(mScaleFactor);
-
-        final Rect overlayBounds = new Rect(
-                0, /* left */
-                displayInfo.getNaturalHeight() / 2, /* top */
-                displayInfo.getNaturalWidth(), /* right */
-                displayInfo.getNaturalHeight() /* botom */);
-
-        UdfpsOverlayParams params = new UdfpsOverlayParams(
-                udfpsBounds,
-                overlayBounds,
-                displayInfo.getNaturalWidth(),
-                displayInfo.getNaturalHeight(),
-                mScaleFactor,
-                displayInfo.rotation);
-
-        udfpsEnrollView.setOverlayParams(params);
-
+    private void setUdfpsEnrollHelper() {
         mUdfpsEnrollHelper = (UdfpsEnrollHelper) getSupportFragmentManager().findFragmentByTag(
                 FingerprintEnrollEnrolling.TAG_UDFPS_HELPER);
         if (mUdfpsEnrollHelper == null) {
@@ -1263,57 +1150,6 @@
                     .add(mUdfpsEnrollHelper, FingerprintEnrollEnrolling.TAG_UDFPS_HELPER)
                     .commitAllowingStateLoss();
         }
-        udfpsEnrollView.setEnrollHelper(mUdfpsEnrollHelper);
-
-        return udfpsEnrollView;
-    }
-
-    private void setOnHoverListener(boolean isLandscape, GlifLayout enrollLayout,
-            UdfpsEnrollView udfpsEnrollView) {
-        if (!mIsAccessibilityEnabled) return;
-
-        final Context context = getApplicationContext();
-        final View.OnHoverListener onHoverListener = (v, event) -> {
-            // Map the touch to portrait mode if the device is in
-            // landscape mode.
-            final Point scaledTouch =
-                    mUdfpsUtils.getTouchInNativeCoordinates(event.getPointerId(0),
-                            event, udfpsEnrollView.getOverlayParams());
-
-            if (mUdfpsUtils.isWithinSensorArea(event.getPointerId(0), event,
-                    udfpsEnrollView.getOverlayParams())) {
-                return false;
-            }
-
-            final String theStr = mUdfpsUtils.onTouchOutsideOfSensorArea(
-                    mAccessibilityManager.isTouchExplorationEnabled(), context,
-                    scaledTouch.x, scaledTouch.y, udfpsEnrollView.getOverlayParams());
-            if (theStr != null) {
-                v.announceForAccessibility(theStr);
-            }
-            return false;
-        };
-
-        enrollLayout.findManagedViewById(isLandscape ? R.id.sud_landscape_content_area
-                : R.id.sud_layout_content).setOnHoverListener(onHoverListener);
-    }
-
-
-    @VisibleForTesting boolean hasOverlap(View view1, View view2) {
-        int[] firstPosition = new int[2];
-        int[] secondPosition = new int[2];
-
-        view1.getLocationOnScreen(firstPosition);
-        view2.getLocationOnScreen(secondPosition);
-
-        // Rect constructor parameters: left, top, right, bottom
-        Rect rectView1 = new Rect(firstPosition[0], firstPosition[1],
-                firstPosition[0] + view1.getMeasuredWidth(),
-                firstPosition[1] + view1.getMeasuredHeight());
-        Rect rectView2 = new Rect(secondPosition[0], secondPosition[1],
-                secondPosition[0] + view2.getMeasuredWidth(),
-                secondPosition[1] + view2.getMeasuredHeight());
-        return rectView1.intersect(rectView2);
     }
 
     public static class IconTouchDialog extends InstrumentedDialogFragment {
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java
index 5d04cd6..493302b 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java
@@ -124,13 +124,18 @@
         }
 
         @Override
-        public void onPointerDown(int sensorId) {
-            FingerprintEnrollSidecar.super.onPointerDown(sensorId);
+        public void onUdfpsPointerDown(int sensorId) {
+            FingerprintEnrollSidecar.super.onUdfpsPointerDown(sensorId);
         }
 
         @Override
-        public void onPointerUp(int sensorId) {
-            FingerprintEnrollSidecar.super.onPointerUp(sensorId);
+        public void onUdfpsPointerUp(int sensorId) {
+            FingerprintEnrollSidecar.super.onUdfpsPointerUp(sensorId);
+        }
+
+        @Override
+        public void onUdfpsOverlayShown() {
+            FingerprintEnrollSidecar.super.onUdfpsOverlayShown();
         }
     };
 
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
index fb3319c..9a4d632 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
@@ -169,7 +169,8 @@
         private static final String KEY_LAUNCHED_CONFIRM = "launched_confirm";
         private static final String KEY_HAS_FIRST_ENROLLED = "has_first_enrolled";
         private static final String KEY_IS_ENROLLING = "is_enrolled";
-        private static final String KEY_REQUIRE_SCREEN_ON_TO_AUTH =
+        @VisibleForTesting
+        static final String KEY_REQUIRE_SCREEN_ON_TO_AUTH =
                 "security_settings_require_screen_on_to_auth";
         private static final String KEY_FINGERPRINTS_ENROLLED_CATEGORY =
                 "security_settings_fingerprints_enrolled";
@@ -536,10 +537,6 @@
 
         private void addFingerprintPreferences(PreferenceGroup root) {
             final String fpPrefKey = addFingerprintItemPreferences(root);
-            if (isSfps()) {
-                scrollToPreference(fpPrefKey);
-                addFingerprintUnlockCategory();
-            }
             for (AbstractPreferenceController controller : mControllers) {
                 if (controller instanceof FingerprintSettingsPreferenceController) {
                     ((FingerprintSettingsPreferenceController) controller).setUserId(mUserId);
@@ -547,6 +544,14 @@
                     ((FingerprintUnlockCategoryController) controller).setUserId(mUserId);
                 }
             }
+
+            // This needs to be after setting ids, otherwise
+            // |mRequireScreenOnToAuthPreferenceController.isChecked| is always checking the primary
+            // user instead of the user with |mUserId|.
+            if (isSfps()) {
+                scrollToPreference(fpPrefKey);
+                addFingerprintUnlockCategory();
+            }
             createFooterPreference(root);
         }
 
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java b/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java
index 36325a7..306b1a3 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java
@@ -98,13 +98,18 @@
         }
 
         @Override
-        public void onPointerDown(int sensorId) {
-            mCallback.onPointerDown(sensorId);
+        public void onUdfpsPointerDown(int sensorId) {
+            mCallback.onUdfpsPointerDown(sensorId);
         }
 
         @Override
-        public void onPointerUp(int sensorId) {
-            mCallback.onPointerUp(sensorId);
+        public void onUdfpsPointerUp(int sensorId) {
+            mCallback.onUdfpsPointerUp(sensorId);
+        }
+
+        @Override
+        public void onUdfpsOverlayShown() {
+            mCallback.onUdfpsOverlayShown();
         }
     }
 
diff --git a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollEnrollingView.java b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollEnrollingView.java
new file mode 100644
index 0000000..df2f2f7
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollEnrollingView.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics.fingerprint;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.DisplayInfo;
+import android.view.Gravity;
+import android.view.Surface;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+import androidx.annotation.ColorInt;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.R;
+import com.android.settingslib.udfps.UdfpsOverlayParams;
+import com.android.settingslib.udfps.UdfpsUtils;
+
+import com.google.android.setupcompat.template.FooterBarMixin;
+import com.google.android.setupdesign.GlifLayout;
+import com.google.android.setupdesign.view.BottomScrollView;
+
+import java.util.Locale;
+
+/**
+ * View for udfps enrolling.
+ */
+public class UdfpsEnrollEnrollingView extends GlifLayout {
+    private final UdfpsUtils mUdfpsUtils;
+    private final Context mContext;
+    // We don't need to listen to onConfigurationChanged() for mRotation here because
+    // FingerprintEnrollEnrolling is always recreated once the configuration is changed.
+    private final int mRotation;
+    private final boolean mIsLandscape;
+    private final boolean mShouldUseReverseLandscape;
+    private UdfpsEnrollView mUdfpsEnrollView;
+    private View mHeaderView;
+    private AccessibilityManager mAccessibilityManager;
+
+
+    public UdfpsEnrollEnrollingView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mContext = context;
+        mRotation = mContext.getDisplay().getRotation();
+        mIsLandscape = mRotation == Surface.ROTATION_90 || mRotation == Surface.ROTATION_270;
+        final boolean isLayoutRtl = (TextUtils.getLayoutDirectionFromLocale(Locale.getDefault())
+                == View.LAYOUT_DIRECTION_RTL);
+        mShouldUseReverseLandscape = (mRotation == Surface.ROTATION_90 && isLayoutRtl)
+                || (mRotation == Surface.ROTATION_270 && !isLayoutRtl);
+
+        mUdfpsUtils = new UdfpsUtils();
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mHeaderView = findViewById(R.id.sud_landscape_header_area);
+        mUdfpsEnrollView = findViewById(R.id.udfps_animation_view);
+    }
+
+    void initView(FingerprintSensorPropertiesInternal udfpsProps,
+            UdfpsEnrollHelper udfpsEnrollHelper,
+            AccessibilityManager accessibilityManager) {
+        mAccessibilityManager = accessibilityManager;
+        initUdfpsEnrollView(udfpsProps, udfpsEnrollHelper);
+
+        if (!mIsLandscape) {
+            adjustPortraitPaddings();
+        } else if (mShouldUseReverseLandscape) {
+            swapHeaderAndContent();
+        }
+        setOnHoverListener();
+    }
+
+    void setSecondaryButtonBackground(@ColorInt int color) {
+        // Set the button background only when the button is not under udfps overlay to avoid UI
+        // overlap.
+        if (!mIsLandscape || mShouldUseReverseLandscape) {
+            return;
+        }
+        final Button secondaryButtonView =
+                getMixin(FooterBarMixin.class).getSecondaryButtonView();
+        secondaryButtonView.setBackgroundColor(color);
+        if (mRotation == Surface.ROTATION_90) {
+            secondaryButtonView.setGravity(Gravity.START);
+        } else {
+            secondaryButtonView.setGravity(Gravity.END);
+        }
+        mHeaderView.post(() -> {
+            secondaryButtonView.setLayoutParams(
+                    new LinearLayout.LayoutParams(mHeaderView.getMeasuredWidth(),
+                            ViewGroup.LayoutParams.WRAP_CONTENT));
+        });
+    }
+
+    private void initUdfpsEnrollView(FingerprintSensorPropertiesInternal udfpsProps,
+                                     UdfpsEnrollHelper udfpsEnrollHelper) {
+        DisplayInfo displayInfo = new DisplayInfo();
+        mContext.getDisplay().getDisplayInfo(displayInfo);
+
+        final float scaleFactor = mUdfpsUtils.getScaleFactor(displayInfo);
+        Rect udfpsBounds = udfpsProps.getLocation().getRect();
+        udfpsBounds.scale(scaleFactor);
+
+        final Rect overlayBounds = new Rect(
+                0, /* left */
+                displayInfo.getNaturalHeight() / 2, /* top */
+                displayInfo.getNaturalWidth(), /* right */
+                displayInfo.getNaturalHeight() /* botom */);
+
+        UdfpsOverlayParams params = new UdfpsOverlayParams(
+                udfpsBounds,
+                overlayBounds,
+                displayInfo.getNaturalWidth(),
+                displayInfo.getNaturalHeight(),
+                scaleFactor,
+                displayInfo.rotation);
+
+        mUdfpsEnrollView.setOverlayParams(params);
+        mUdfpsEnrollView.setEnrollHelper(udfpsEnrollHelper);
+    }
+
+    private void adjustPortraitPaddings() {
+        // In the portrait mode, layout_container's height is 0, so it's
+        // always shown at the bottom of the screen.
+        final FrameLayout portraitLayoutContainer = findViewById(R.id.layout_container);
+
+        // In the portrait mode, the title and lottie animation view may
+        // overlap when title needs three lines, so adding some paddings
+        // between them, and adjusting the fp progress view here accordingly.
+        final int layoutLottieAnimationPadding = (int) getResources()
+                .getDimension(R.dimen.udfps_lottie_padding_top);
+        portraitLayoutContainer.setPadding(0,
+                layoutLottieAnimationPadding, 0, 0);
+        final ImageView progressView = mUdfpsEnrollView.findViewById(
+                R.id.udfps_enroll_animation_fp_progress_view);
+        progressView.setPadding(0, -(layoutLottieAnimationPadding),
+                0, layoutLottieAnimationPadding);
+        final ImageView fingerprintView = mUdfpsEnrollView.findViewById(
+                R.id.udfps_enroll_animation_fp_view);
+        fingerprintView.setPadding(0, -layoutLottieAnimationPadding,
+                0, layoutLottieAnimationPadding);
+
+        // TODO(b/260970216) Instead of hiding the description text view, we should
+        //  make the header view scrollable if the text is too long.
+        // If description text view has overlap with udfps progress view, hide it.
+        final View descView = getDescriptionTextView();
+        getViewTreeObserver().addOnDrawListener(() -> {
+            if (descView.getVisibility() == View.VISIBLE
+                    && hasOverlap(descView, mUdfpsEnrollView)) {
+                descView.setVisibility(View.GONE);
+            }
+        });
+    }
+
+    private void setOnHoverListener() {
+        if (!mAccessibilityManager.isEnabled()) return;
+
+        final View.OnHoverListener onHoverListener = (v, event) -> {
+            // Map the touch to portrait mode if the device is in
+            // landscape mode.
+            final Point scaledTouch =
+                    mUdfpsUtils.getTouchInNativeCoordinates(event.getPointerId(0),
+                            event, mUdfpsEnrollView.getOverlayParams());
+
+            if (mUdfpsUtils.isWithinSensorArea(event.getPointerId(0), event,
+                    mUdfpsEnrollView.getOverlayParams())) {
+                return false;
+            }
+
+            final String theStr = mUdfpsUtils.onTouchOutsideOfSensorArea(
+                    mAccessibilityManager.isTouchExplorationEnabled(), mContext,
+                    scaledTouch.x, scaledTouch.y, mUdfpsEnrollView.getOverlayParams());
+            if (theStr != null) {
+                v.announceForAccessibility(theStr);
+            }
+            return false;
+        };
+
+        findManagedViewById(mIsLandscape ? R.id.sud_landscape_content_area
+                : R.id.sud_layout_content).setOnHoverListener(onHoverListener);
+    }
+
+    private void swapHeaderAndContent() {
+        // Reverse header and body
+        ViewGroup parentView = (ViewGroup) mHeaderView.getParent();
+        parentView.removeView(mHeaderView);
+        parentView.addView(mHeaderView);
+
+        // Hide scroll indicators
+        BottomScrollView headerScrollView = mHeaderView.findViewById(R.id.sud_header_scroll_view);
+        headerScrollView.setScrollIndicators(0);
+    }
+
+    @VisibleForTesting
+    boolean hasOverlap(View view1, View view2) {
+        int[] firstPosition = new int[2];
+        int[] secondPosition = new int[2];
+
+        view1.getLocationOnScreen(firstPosition);
+        view2.getLocationOnScreen(secondPosition);
+
+        // Rect constructor parameters: left, top, right, bottom
+        Rect rectView1 = new Rect(firstPosition[0], firstPosition[1],
+                firstPosition[0] + view1.getMeasuredWidth(),
+                firstPosition[1] + view1.getMeasuredHeight());
+        Rect rectView2 = new Rect(secondPosition[0], secondPosition[1],
+                secondPosition[0] + view2.getMeasuredWidth(),
+                secondPosition[1] + view2.getMeasuredHeight());
+        return rectView1.intersect(rectView2);
+    }
+}
diff --git a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java
index d77d9d3..7074288 100644
--- a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java
+++ b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java
@@ -103,12 +103,12 @@
         }
 
         @Override
-        public void onPointerDown(int sensorId) {
+        public void onUdfpsPointerDown(int sensorId) {
             mPointerDownLiveData.postValue(sensorId);
         }
 
         @Override
-        public void onPointerUp(int sensorId) {
+        public void onUdfpsPointerUp(int sensorId) {
             mPointerUpLiveData.postValue(sensorId);
         }
     };
diff --git a/src/com/android/settings/bluetooth/BlockingPrefWithSliceController.java b/src/com/android/settings/bluetooth/BlockingPrefWithSliceController.java
index 93a2747..0690186 100644
--- a/src/com/android/settings/bluetooth/BlockingPrefWithSliceController.java
+++ b/src/com/android/settings/bluetooth/BlockingPrefWithSliceController.java
@@ -59,7 +59,7 @@
  * until {@link Slice} is fully loaded.
  */
 public class BlockingPrefWithSliceController extends BasePreferenceController implements
-        LifecycleObserver, OnStart, OnStop, Observer<Slice>, BasePreferenceController.UiBlocker{
+        LifecycleObserver, OnStart, OnStop, Observer<Slice>, BasePreferenceController.UiBlocker {
     private static final String TAG = "BlockingPrefWithSliceController";
 
     private static final String PREFIX_KEY = "slice_preference_item_";
@@ -225,7 +225,8 @@
             } else {
                 expectedActivityIntent = intentFromSliceAction;
             }
-            if (expectedActivityIntent != null) {
+            if (expectedActivityIntent != null && expectedActivityIntent.resolveActivity(
+                    mContext.getPackageManager()) != null) {
                 Log.d(TAG, "setIntent: ActivityIntent" + expectedActivityIntent);
                 // Since UI needs to support the Settings' 2 panel feature, the intent can't use the
                 // FLAG_ACTIVITY_NEW_TASK. The above intent may have the FLAG_ACTIVITY_NEW_TASK
@@ -234,6 +235,7 @@
                 preference.setIntent(expectedActivityIntent);
             } else {
                 Log.d(TAG, "setIntent: Intent is null");
+                preference.setSelectable(false);
             }
         }
 
diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java b/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java
index 7ee61ee..f2bc6fc 100644
--- a/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java
+++ b/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java
@@ -128,7 +128,7 @@
             if (device != null && mSelectedList.contains(device)) {
                 setResult(RESULT_OK);
                 finish();
-            } else if (mDevicePreferenceMap.containsKey(cachedDevice)) {
+            } else {
                 onDeviceDeleted(cachedDevice);
             }
         }
@@ -175,8 +175,6 @@
     public void updateContent(int bluetoothState) {
         switch (bluetoothState) {
             case BluetoothAdapter.STATE_ON:
-                mDevicePreferenceMap.clear();
-                clearPreferenceGroupCache();
                 mBluetoothAdapter.enable();
                 enableScanning();
                 break;
@@ -187,14 +185,6 @@
         }
     }
 
-    /**
-     * Clears all cached preferences in {@code preferenceGroup}.
-     */
-    private void clearPreferenceGroupCache() {
-        cacheRemoveAllPrefs(mAvailableDevicesCategory);
-        removeCachedPrefs(mAvailableDevicesCategory);
-    }
-
     @VisibleForTesting
     void showBluetoothTurnedOnToast() {
         Toast.makeText(getContext(), R.string.connected_device_bluetooth_turned_on_toast,
diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
index 5256f3d..039080b 100644
--- a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
+++ b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -35,6 +35,8 @@
 import android.widget.ImageView;
 
 import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.app.AlertDialog;
 import androidx.preference.Preference;
@@ -52,6 +54,7 @@
 import java.util.HashSet;
 import java.util.Set;
 import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * BluetoothDevicePreference is the preference type used to display each remote
@@ -79,7 +82,9 @@
     @VisibleForTesting
     BluetoothAdapter mBluetoothAdapter;
     private final boolean mShowDevicesWithoutNames;
-    private final long mCurrentTime;
+    @NonNull
+    private static final AtomicInteger sNextId = new AtomicInteger();
+    private final int mId;
     private final int mType;
 
     private AlertDialog mDisconnectDialog;
@@ -127,8 +132,9 @@
 
         mCachedDevice = cachedDevice;
         mCallback = new BluetoothDevicePreferenceCallback();
-        mCurrentTime = System.currentTimeMillis();
+        mId = sNextId.getAndIncrement();
         mType = type;
+        setVisible(false);
 
         onPreferenceAttributesChanged();
     }
@@ -229,35 +235,41 @@
 
     @SuppressWarnings("FutureReturnValueIgnored")
     void onPreferenceAttributesChanged() {
-        Pair<Drawable, String> pair = mCachedDevice.getDrawableWithDescription();
-        setIcon(pair.first);
-        contentDescription = pair.second;
-
-        /*
-         * The preference framework takes care of making sure the value has
-         * changed before proceeding. It will also call notifyChanged() if
-         * any preference info has changed from the previous value.
-         */
-        setTitle(mCachedDevice.getName());
         try {
             ThreadUtils.postOnBackgroundThread(() -> {
+                @Nullable String name = mCachedDevice.getName();
                 // Null check is done at the framework
-                ThreadUtils.postOnMainThread(() -> setSummary(getConnectionSummary()));
+                @Nullable String connectionSummary = getConnectionSummary();
+                @NonNull Pair<Drawable, String> pair = mCachedDevice.getDrawableWithDescription();
+                boolean isBusy = mCachedDevice.isBusy();
+                // Device is only visible in the UI if it has a valid name besides MAC address or
+                // when user allows showing devices without user-friendly name in developer settings
+                boolean isVisible =
+                        mShowDevicesWithoutNames || mCachedDevice.hasHumanReadableName();
+
+                ThreadUtils.postOnMainThread(() -> {
+                    /*
+                     * The preference framework takes care of making sure the value has
+                     * changed before proceeding. It will also call notifyChanged() if
+                     * any preference info has changed from the previous value.
+                     */
+                    setTitle(name);
+                    setSummary(connectionSummary);
+                    setIcon(pair.first);
+                    contentDescription = pair.second;
+                    // Used to gray out the item
+                    setEnabled(!isBusy);
+                    setVisible(isVisible);
+
+                    // This could affect ordering, so notify that
+                    if (mNeedNotifyHierarchyChanged) {
+                        notifyHierarchyChanged();
+                    }
+                });
             });
         } catch (RejectedExecutionException e) {
             Log.w(TAG, "Handler thread unavailable, skipping getConnectionSummary!");
         }
-        // Used to gray out the item
-        setEnabled(!mCachedDevice.isBusy());
-
-        // Device is only visible in the UI if it has a valid name besides MAC address or when user
-        // allows showing devices without user-friendly name in developer settings
-        setVisible(mShowDevicesWithoutNames || mCachedDevice.hasHumanReadableName());
-
-        // This could affect ordering, so notify that
-        if (mNeedNotifyHierarchyChanged) {
-            notifyHierarchyChanged();
-        }
     }
 
     @Override
@@ -311,7 +323,7 @@
                 return mCachedDevice
                         .compareTo(((BluetoothDevicePreference) another).mCachedDevice);
             case SortType.TYPE_FIFO:
-                return mCurrentTime > ((BluetoothDevicePreference) another).mCurrentTime ? 1 : -1;
+                return mId > ((BluetoothDevicePreference) another).mId ? 1 : -1;
             default:
                 return super.compareTo(another);
         }
diff --git a/src/com/android/settings/bluetooth/BluetoothFindBroadcastsFragment.java b/src/com/android/settings/bluetooth/BluetoothFindBroadcastsFragment.java
index 05bc179..f9d083d 100644
--- a/src/com/android/settings/bluetooth/BluetoothFindBroadcastsFragment.java
+++ b/src/com/android/settings/bluetooth/BluetoothFindBroadcastsFragment.java
@@ -125,6 +125,10 @@
                         Log.w(TAG, "onSourceAdded: mSelectedPreference == null!");
                         return;
                     }
+                    if (mLeBroadcastAssistant != null
+                            && mLeBroadcastAssistant.isSearchInProgress()) {
+                        mLeBroadcastAssistant.stopSearchingForSources();
+                    }
                     getActivity().runOnUiThread(() -> updateListCategoryFromBroadcastMetadata(
                             mSelectedPreference.getBluetoothLeBroadcastMetadata(), true));
                 }
@@ -232,6 +236,9 @@
     public void onStop() {
         super.onStop();
         if (mLeBroadcastAssistant != null) {
+            if (mLeBroadcastAssistant.isSearchInProgress()) {
+                mLeBroadcastAssistant.stopSearchingForSources();
+            }
             mLeBroadcastAssistant.unregisterServiceCallBack(mBroadcastAssistantCallback);
         }
     }
diff --git a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java
deleted file mode 100644
index a4a9891..0000000
--- a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * Copyright (C) 2011 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.bluetooth;
-
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.le.BluetoothLeScanner;
-import android.bluetooth.le.ScanCallback;
-import android.bluetooth.le.ScanFilter;
-import android.bluetooth.le.ScanResult;
-import android.bluetooth.le.ScanSettings;
-import android.os.Bundle;
-import android.os.SystemProperties;
-import android.text.BidiFormatter;
-import android.util.Log;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceCategory;
-import androidx.preference.PreferenceGroup;
-
-import com.android.settings.R;
-import com.android.settings.dashboard.RestrictedDashboardFragment;
-import com.android.settingslib.bluetooth.BluetoothCallback;
-import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
-import com.android.settingslib.bluetooth.LocalBluetoothManager;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * Parent class for settings fragments that contain a list of Bluetooth
- * devices.
- *
- * @see DevicePickerFragment
- */
-// TODO: Refactor this fragment
-public abstract class DeviceListPreferenceFragment extends
-        RestrictedDashboardFragment implements BluetoothCallback {
-
-    private static final String TAG = "DeviceListPreferenceFragment";
-
-    private static final String KEY_BT_SCAN = "bt_scan";
-
-    // Copied from BluetoothDeviceNoNamePreferenceController.java
-    private static final String BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY =
-            "persist.bluetooth.showdeviceswithoutnames";
-
-    private BluetoothDeviceFilter.Filter mFilter;
-    private List<ScanFilter> mLeScanFilters;
-    private ScanCallback mScanCallback;
-
-    @VisibleForTesting
-    protected boolean mScanEnabled;
-
-    protected BluetoothDevice mSelectedDevice;
-
-    protected BluetoothAdapter mBluetoothAdapter;
-    protected LocalBluetoothManager mLocalManager;
-    protected CachedBluetoothDeviceManager mCachedDeviceManager;
-
-    @VisibleForTesting
-    protected PreferenceGroup mDeviceListGroup;
-
-    protected final HashMap<CachedBluetoothDevice, BluetoothDevicePreference> mDevicePreferenceMap =
-            new HashMap<>();
-    protected final List<BluetoothDevice> mSelectedList = new ArrayList<>();
-
-    protected boolean mShowDevicesWithoutNames;
-
-    public DeviceListPreferenceFragment(String restrictedKey) {
-        super(restrictedKey);
-        mFilter = BluetoothDeviceFilter.ALL_FILTER;
-    }
-
-    protected final void setFilter(BluetoothDeviceFilter.Filter filter) {
-        mFilter = filter;
-    }
-
-    protected final void setFilter(int filterType) {
-        mFilter = BluetoothDeviceFilter.getFilter(filterType);
-    }
-
-    /**
-     * Sets the bluetooth device scanning filter with {@link ScanFilter}s. It will change to start
-     * {@link BluetoothLeScanner} which will scan BLE device only.
-     *
-     * @param leScanFilters list of settings to filter scan result
-     */
-    protected void setFilter(List<ScanFilter> leScanFilters) {
-        mFilter = null;
-        mLeScanFilters = leScanFilters;
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        mLocalManager = Utils.getLocalBtManager(getActivity());
-        if (mLocalManager == null) {
-            Log.e(TAG, "Bluetooth is not supported on this device");
-            return;
-        }
-        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
-        mCachedDeviceManager = mLocalManager.getCachedDeviceManager();
-        mShowDevicesWithoutNames = SystemProperties.getBoolean(
-                BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY, false);
-
-        initPreferencesFromPreferenceScreen();
-
-        mDeviceListGroup = (PreferenceCategory) findPreference(getDeviceListKey());
-    }
-
-    /** find and update preference that already existed in preference screen */
-    protected abstract void initPreferencesFromPreferenceScreen();
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        if (mLocalManager == null || isUiRestricted()) return;
-
-        mLocalManager.setForegroundActivity(getActivity());
-        mLocalManager.getEventManager().registerCallback(this);
-    }
-
-    @Override
-    public void onStop() {
-        super.onStop();
-        if (mLocalManager == null || isUiRestricted()) {
-            return;
-        }
-
-        removeAllDevices();
-        mLocalManager.setForegroundActivity(null);
-        mLocalManager.getEventManager().unregisterCallback(this);
-    }
-
-    void removeAllDevices() {
-        mDevicePreferenceMap.clear();
-        mDeviceListGroup.removeAll();
-    }
-
-    void addCachedDevices() {
-        Collection<CachedBluetoothDevice> cachedDevices =
-                mCachedDeviceManager.getCachedDevicesCopy();
-        for (CachedBluetoothDevice cachedDevice : cachedDevices) {
-            onDeviceAdded(cachedDevice);
-        }
-    }
-
-    @Override
-    public boolean onPreferenceTreeClick(Preference preference) {
-        if (KEY_BT_SCAN.equals(preference.getKey())) {
-            startScanning();
-            return true;
-        }
-
-        if (preference instanceof BluetoothDevicePreference) {
-            BluetoothDevicePreference btPreference = (BluetoothDevicePreference) preference;
-            CachedBluetoothDevice device = btPreference.getCachedDevice();
-            mSelectedDevice = device.getDevice();
-            mSelectedList.add(mSelectedDevice);
-            onDevicePreferenceClick(btPreference);
-            return true;
-        }
-
-        return super.onPreferenceTreeClick(preference);
-    }
-
-    protected void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
-        btPreference.onClicked();
-    }
-
-    @Override
-    public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
-        if (mDevicePreferenceMap.get(cachedDevice) != null) {
-            return;
-        }
-
-        // Prevent updates while the list shows one of the state messages
-        if (mBluetoothAdapter.getState() != BluetoothAdapter.STATE_ON) {
-            return;
-        }
-
-        if (mFilter != null && mFilter.matches(cachedDevice.getDevice())) {
-            createDevicePreference(cachedDevice);
-        }
-    }
-
-    void createDevicePreference(CachedBluetoothDevice cachedDevice) {
-        if (mDeviceListGroup == null) {
-            Log.w(TAG, "Trying to create a device preference before the list group/category "
-                    + "exists!");
-            return;
-        }
-
-        String key = cachedDevice.getDevice().getAddress();
-        BluetoothDevicePreference preference = (BluetoothDevicePreference) getCachedPreference(key);
-
-        if (preference == null) {
-            preference = new BluetoothDevicePreference(getPrefContext(), cachedDevice,
-                    mShowDevicesWithoutNames, BluetoothDevicePreference.SortType.TYPE_FIFO);
-            preference.setKey(key);
-            //Set hideSecondTarget is true if it's bonded device.
-            preference.hideSecondTarget(true);
-            mDeviceListGroup.addPreference(preference);
-        }
-
-        initDevicePreference(preference);
-        mDevicePreferenceMap.put(cachedDevice, preference);
-    }
-
-    protected void initDevicePreference(BluetoothDevicePreference preference) {
-        // Does nothing by default
-    }
-
-    @VisibleForTesting
-    void updateFooterPreference(Preference myDevicePreference) {
-        final BidiFormatter bidiFormatter = BidiFormatter.getInstance();
-
-        myDevicePreference.setTitle(getString(
-                R.string.bluetooth_footer_mac_message,
-                bidiFormatter.unicodeWrap(mBluetoothAdapter.getAddress())));
-    }
-
-    @Override
-    public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {
-        BluetoothDevicePreference preference = mDevicePreferenceMap.remove(cachedDevice);
-        if (preference != null) {
-            mDeviceListGroup.removePreference(preference);
-        }
-    }
-
-    @VisibleForTesting
-    protected void enableScanning() {
-        // BluetoothAdapter already handles repeated scan requests
-        if (!mScanEnabled) {
-            startScanning();
-            mScanEnabled = true;
-        }
-    }
-
-    @VisibleForTesting
-    protected void disableScanning() {
-        if (mScanEnabled) {
-            stopScanning();
-            mScanEnabled = false;
-        }
-    }
-
-    @Override
-    public void onScanningStateChanged(boolean started) {
-        if (!started && mScanEnabled) {
-            startScanning();
-        }
-    }
-
-    /**
-     * Return the key of the {@link PreferenceGroup} that contains the bluetooth devices
-     */
-    public abstract String getDeviceListKey();
-
-    public boolean shouldShowDevicesWithoutNames() {
-        return mShowDevicesWithoutNames;
-    }
-
-    @VisibleForTesting
-    void startScanning() {
-        if (mFilter != null) {
-            startClassicScanning();
-        } else if (mLeScanFilters != null) {
-            startLeScanning();
-        }
-
-    }
-
-    @VisibleForTesting
-    void stopScanning() {
-        if (mFilter != null) {
-            stopClassicScanning();
-        } else if (mLeScanFilters != null) {
-            stopLeScanning();
-        }
-    }
-
-    private void startClassicScanning() {
-        if (!mBluetoothAdapter.isDiscovering()) {
-            mBluetoothAdapter.startDiscovery();
-        }
-    }
-
-    private void stopClassicScanning() {
-        if (mBluetoothAdapter.isDiscovering()) {
-            mBluetoothAdapter.cancelDiscovery();
-        }
-    }
-
-    private void startLeScanning() {
-        final BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
-        final ScanSettings settings = new ScanSettings.Builder()
-                .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
-                .build();
-        mScanCallback = new ScanCallback() {
-            @Override
-            public void onScanResult(int callbackType, ScanResult result) {
-                final BluetoothDevice device = result.getDevice();
-                CachedBluetoothDevice cachedDevice = mCachedDeviceManager.findDevice(device);
-                if (cachedDevice == null) {
-                    cachedDevice = mCachedDeviceManager.addDevice(device);
-                }
-                // Only add device preference when it's not found in the map and there's no other
-                // state message showing in the list
-                if (mDevicePreferenceMap.get(cachedDevice) == null
-                        && mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) {
-                    createDevicePreference(cachedDevice);
-                }
-            }
-
-            @Override
-            public void onScanFailed(int errorCode) {
-                Log.w(TAG, "BLE Scan failed with error code " + errorCode);
-            }
-        };
-        scanner.startScan(mLeScanFilters, settings, mScanCallback);
-    }
-
-    private void stopLeScanning() {
-        final BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
-        if (scanner != null) {
-            scanner.stopScan(mScanCallback);
-        }
-    }
-}
diff --git a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.kt b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.kt
new file mode 100644
index 0000000..9c86e43
--- /dev/null
+++ b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.kt
@@ -0,0 +1,348 @@
+/*
+ * 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.bluetooth
+
+import android.bluetooth.BluetoothAdapter
+import android.bluetooth.BluetoothDevice
+import android.bluetooth.le.BluetoothLeScanner
+import android.bluetooth.le.ScanCallback
+import android.bluetooth.le.ScanFilter
+import android.bluetooth.le.ScanResult
+import android.bluetooth.le.ScanSettings
+import android.os.Bundle
+import android.os.SystemProperties
+import android.text.BidiFormatter
+import android.util.Log
+import android.view.View
+import androidx.annotation.VisibleForTesting
+import androidx.lifecycle.LifecycleCoroutineScope
+import androidx.lifecycle.lifecycleScope
+import androidx.preference.Preference
+import androidx.preference.PreferenceCategory
+import androidx.preference.PreferenceGroup
+import com.android.settings.R
+import com.android.settings.dashboard.RestrictedDashboardFragment
+import com.android.settingslib.bluetooth.BluetoothCallback
+import com.android.settingslib.bluetooth.BluetoothDeviceFilter
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import java.util.concurrent.ConcurrentHashMap
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * Parent class for settings fragments that contain a list of Bluetooth devices.
+ *
+ * @see DevicePickerFragment
+ *
+ * TODO: Refactor this fragment
+ */
+abstract class DeviceListPreferenceFragment(restrictedKey: String?) :
+    RestrictedDashboardFragment(restrictedKey), BluetoothCallback {
+
+    private var filter: BluetoothDeviceFilter.Filter? = BluetoothDeviceFilter.ALL_FILTER
+    private var leScanFilters: List<ScanFilter>? = null
+
+    @JvmField
+    @VisibleForTesting
+    var mScanEnabled = false
+
+    @JvmField
+    var mSelectedDevice: BluetoothDevice? = null
+
+    @JvmField
+    var mBluetoothAdapter: BluetoothAdapter? = null
+
+    @JvmField
+    var mLocalManager: LocalBluetoothManager? = null
+
+    @JvmField
+    var mCachedDeviceManager: CachedBluetoothDeviceManager? = null
+
+    @JvmField
+    @VisibleForTesting
+    var mDeviceListGroup: PreferenceGroup? = null
+
+    @VisibleForTesting
+    val devicePreferenceMap =
+        ConcurrentHashMap<CachedBluetoothDevice, BluetoothDevicePreference>()
+
+    @JvmField
+    val mSelectedList: MutableList<BluetoothDevice> = ArrayList()
+
+    private var showDevicesWithoutNames = false
+
+    protected fun setFilter(filter: BluetoothDeviceFilter.Filter?) {
+        this.filter = filter
+    }
+
+    protected fun setFilter(filterType: Int) {
+        filter = BluetoothDeviceFilter.getFilter(filterType)
+    }
+
+    /**
+     * Sets the bluetooth device scanning filter with [ScanFilter]s. It will change to start
+     * [BluetoothLeScanner] which will scan BLE device only.
+     *
+     * @param leScanFilters list of settings to filter scan result
+     */
+    fun setFilter(leScanFilters: List<ScanFilter>?) {
+        filter = null
+        this.leScanFilters = leScanFilters
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        mLocalManager = Utils.getLocalBtManager(activity)
+        if (mLocalManager == null) {
+            Log.e(TAG, "Bluetooth is not supported on this device")
+            return
+        }
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
+        mCachedDeviceManager = mLocalManager!!.cachedDeviceManager
+        showDevicesWithoutNames = SystemProperties.getBoolean(
+            BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY, false
+        )
+        initPreferencesFromPreferenceScreen()
+        mDeviceListGroup = findPreference<Preference>(deviceListKey) as PreferenceCategory
+    }
+
+    /** find and update preference that already existed in preference screen  */
+    protected abstract fun initPreferencesFromPreferenceScreen()
+
+    private var lifecycleScope: LifecycleCoroutineScope? = null
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        lifecycleScope = viewLifecycleOwner.lifecycleScope
+    }
+
+    override fun onStart() {
+        super.onStart()
+        if (mLocalManager == null || isUiRestricted) return
+        mLocalManager!!.foregroundActivity = activity
+        mLocalManager!!.eventManager.registerCallback(this)
+    }
+
+    override fun onStop() {
+        super.onStop()
+        if (mLocalManager == null || isUiRestricted) {
+            return
+        }
+        removeAllDevices()
+        mLocalManager!!.foregroundActivity = null
+        mLocalManager!!.eventManager.unregisterCallback(this)
+    }
+
+    fun removeAllDevices() {
+        devicePreferenceMap.clear()
+        mDeviceListGroup!!.removeAll()
+    }
+
+    fun addCachedDevices() {
+        lifecycleScope?.launch {
+            withContext(Dispatchers.Default) {
+                val cachedDevices = mCachedDeviceManager!!.cachedDevicesCopy
+                for (cachedDevice in cachedDevices) {
+                    onDeviceAdded(cachedDevice)
+                }
+            }
+        }
+    }
+
+    override fun onPreferenceTreeClick(preference: Preference): Boolean {
+        if (KEY_BT_SCAN == preference.key) {
+            startScanning()
+            return true
+        }
+        if (preference is BluetoothDevicePreference) {
+            val device = preference.cachedDevice.device
+            mSelectedDevice = device
+            mSelectedList.add(device)
+            onDevicePreferenceClick(preference)
+            return true
+        }
+        return super.onPreferenceTreeClick(preference)
+    }
+
+    protected open fun onDevicePreferenceClick(btPreference: BluetoothDevicePreference) {
+        btPreference.onClicked()
+    }
+
+    override fun onDeviceAdded(cachedDevice: CachedBluetoothDevice) {
+        lifecycleScope?.launch {
+            addDevice(cachedDevice)
+        }
+    }
+
+    private suspend fun addDevice(cachedDevice: CachedBluetoothDevice) =
+        withContext(Dispatchers.Default) {
+            // Prevent updates while the list shows one of the state messages
+            if (mBluetoothAdapter!!.state == BluetoothAdapter.STATE_ON &&
+                filter?.matches(cachedDevice.device) == true
+            ) {
+                createDevicePreference(cachedDevice)
+            }
+        }
+
+    private suspend fun createDevicePreference(cachedDevice: CachedBluetoothDevice) {
+        if (mDeviceListGroup == null) {
+            Log.w(
+                TAG,
+                "Trying to create a device preference before the list group/category exists!",
+            )
+            return
+        }
+        // Only add device preference when it's not found in the map and there's no other state
+        // message showing in the list
+        val preference = devicePreferenceMap.computeIfAbsent(cachedDevice) {
+            BluetoothDevicePreference(
+                prefContext,
+                cachedDevice,
+                showDevicesWithoutNames,
+                BluetoothDevicePreference.SortType.TYPE_FIFO,
+            ).apply {
+                key = cachedDevice.device.address
+                //Set hideSecondTarget is true if it's bonded device.
+                hideSecondTarget(true)
+            }
+        }
+        withContext(Dispatchers.Main) {
+            mDeviceListGroup!!.addPreference(preference)
+            initDevicePreference(preference)
+        }
+    }
+
+    protected open fun initDevicePreference(preference: BluetoothDevicePreference?) {
+        // Does nothing by default
+    }
+
+    @VisibleForTesting
+    fun updateFooterPreference(myDevicePreference: Preference) {
+        val bidiFormatter = BidiFormatter.getInstance()
+        myDevicePreference.title = getString(
+            R.string.bluetooth_footer_mac_message,
+            bidiFormatter.unicodeWrap(mBluetoothAdapter!!.address)
+        )
+    }
+
+    override fun onDeviceDeleted(cachedDevice: CachedBluetoothDevice) {
+        devicePreferenceMap.remove(cachedDevice)?.let {
+            mDeviceListGroup!!.removePreference(it)
+        }
+    }
+
+    @VisibleForTesting
+    open fun enableScanning() {
+        // BluetoothAdapter already handles repeated scan requests
+        if (!mScanEnabled) {
+            startScanning()
+            mScanEnabled = true
+        }
+    }
+
+    @VisibleForTesting
+    fun disableScanning() {
+        if (mScanEnabled) {
+            stopScanning()
+            mScanEnabled = false
+        }
+    }
+
+    override fun onScanningStateChanged(started: Boolean) {
+        if (!started && mScanEnabled) {
+            startScanning()
+        }
+    }
+
+    /**
+     * Return the key of the [PreferenceGroup] that contains the bluetooth devices
+     */
+    abstract val deviceListKey: String
+
+    @VisibleForTesting
+    open fun startScanning() {
+        if (filter != null) {
+            startClassicScanning()
+        } else if (leScanFilters != null) {
+            startLeScanning()
+        }
+    }
+
+    @VisibleForTesting
+    open fun stopScanning() {
+        if (filter != null) {
+            stopClassicScanning()
+        } else if (leScanFilters != null) {
+            stopLeScanning()
+        }
+    }
+
+    private fun startClassicScanning() {
+        if (!mBluetoothAdapter!!.isDiscovering) {
+            mBluetoothAdapter!!.startDiscovery()
+        }
+    }
+
+    private fun stopClassicScanning() {
+        if (mBluetoothAdapter!!.isDiscovering) {
+            mBluetoothAdapter!!.cancelDiscovery()
+        }
+    }
+
+    private val scanCallback = object : ScanCallback() {
+        override fun onScanResult(callbackType: Int, result: ScanResult) {
+            lifecycleScope?.launch {
+                withContext(Dispatchers.Default) {
+                    if (mBluetoothAdapter!!.state == BluetoothAdapter.STATE_ON) {
+                        val device = result.device
+                        val cachedDevice = mCachedDeviceManager!!.findDevice(device)
+                            ?: mCachedDeviceManager!!.addDevice(device)
+                        createDevicePreference(cachedDevice)
+                    }
+                }
+            }
+        }
+
+        override fun onScanFailed(errorCode: Int) {
+            Log.w(TAG, "BLE Scan failed with error code $errorCode")
+        }
+    }
+
+    private fun startLeScanning() {
+        val scanner = mBluetoothAdapter!!.bluetoothLeScanner
+        val settings = ScanSettings.Builder()
+            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
+            .build()
+        scanner.startScan(leScanFilters, settings, scanCallback)
+    }
+
+    private fun stopLeScanning() {
+        val scanner = mBluetoothAdapter!!.bluetoothLeScanner
+        scanner?.stopScan(scanCallback)
+    }
+
+    companion object {
+        private const val TAG = "DeviceListPreferenceFragment"
+        private const val KEY_BT_SCAN = "bt_scan"
+
+        // Copied from BluetoothDeviceNoNamePreferenceController.java
+        private const val BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY =
+            "persist.bluetooth.showdeviceswithoutnames"
+    }
+}
diff --git a/src/com/android/settings/connecteddevice/stylus/StylusDevicesController.java b/src/com/android/settings/connecteddevice/stylus/StylusDevicesController.java
index c93a1c6..ec9f4c6 100644
--- a/src/com/android/settings/connecteddevice/stylus/StylusDevicesController.java
+++ b/src/com/android/settings/connecteddevice/stylus/StylusDevicesController.java
@@ -16,12 +16,17 @@
 
 package com.android.settings.connecteddevice.stylus;
 
+import android.app.Dialog;
 import android.app.role.RoleManager;
 import android.bluetooth.BluetoothDevice;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.Settings;
 import android.provider.Settings.Secure;
 import android.text.TextUtils;
@@ -38,6 +43,8 @@
 import androidx.preference.SwitchPreference;
 
 import com.android.settings.R;
+import com.android.settings.dashboard.profileselector.ProfileSelectDialog;
+import com.android.settings.dashboard.profileselector.UserAdapter;
 import com.android.settingslib.bluetooth.BluetoothUtils;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.core.AbstractPreferenceController;
@@ -45,6 +52,7 @@
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnResume;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -73,6 +81,9 @@
     @VisibleForTesting
     PreferenceCategory mPreferencesContainer;
 
+    @VisibleForTesting
+    Dialog mDialog;
+
     public StylusDevicesController(Context context, InputDevice inputDevice,
             CachedBluetoothDevice cachedBluetoothDevice, Lifecycle lifecycle) {
         super(context);
@@ -100,8 +111,8 @@
         pref.setOnPreferenceClickListener(this);
         pref.setEnabled(true);
 
-        List<String> roleHolders = rm.getRoleHoldersAsUser(RoleManager.ROLE_NOTES,
-                mContext.getUser());
+        UserHandle user = getDefaultNoteTaskProfile();
+        List<String> roleHolders = rm.getRoleHoldersAsUser(RoleManager.ROLE_NOTES, user);
         if (roleHolders.isEmpty()) {
             pref.setSummary(R.string.default_app_none);
             return pref;
@@ -117,7 +128,13 @@
         } catch (PackageManager.NameNotFoundException e) {
             Log.e(TAG, "Notes role package not found.");
         }
-        pref.setSummary(appName);
+
+        if (mContext.getSystemService(UserManager.class).isManagedProfile(user.getIdentifier())) {
+            pref.setSummary(
+                    mContext.getString(R.string.stylus_default_notes_summary_work, appName));
+        } else {
+            pref.setSummary(appName);
+        }
         return pref;
     }
 
@@ -155,7 +172,13 @@
                 String packageName = pm.getPermissionControllerPackageName();
                 Intent intent = new Intent(Intent.ACTION_MANAGE_DEFAULT_APP).setPackage(
                         packageName).putExtra(Intent.EXTRA_ROLE_NAME, RoleManager.ROLE_NOTES);
-                mContext.startActivity(intent);
+
+                List<UserHandle> users = getUserAndManagedProfiles();
+                if (users.size() <= 1) {
+                    mContext.startActivity(intent);
+                } else {
+                    createAndShowProfileSelectDialog(intent, users);
+                }
                 break;
             case KEY_HANDWRITING:
                 Settings.Secure.putInt(mContext.getContentResolver(),
@@ -229,6 +252,56 @@
         return inputMethod != null && inputMethod.supportsStylusHandwriting();
     }
 
+    private List<UserHandle> getUserAndManagedProfiles() {
+        UserManager um = mContext.getSystemService(UserManager.class);
+        final List<UserHandle> userManagedProfiles = new ArrayList<>();
+        // Add the current user, then add all the associated managed profiles.
+        final UserHandle currentUser = Process.myUserHandle();
+        userManagedProfiles.add(currentUser);
+
+        final List<UserInfo> userInfos = um.getUsers();
+        for (UserInfo info : userInfos) {
+            int userId = info.id;
+            if (um.isManagedProfile(userId)
+                    && um.getProfileParent(userId).id == currentUser.getIdentifier()) {
+                userManagedProfiles.add(UserHandle.of(userId));
+            }
+        }
+        return userManagedProfiles;
+    }
+
+    private UserHandle getDefaultNoteTaskProfile() {
+        final int userId = Secure.getInt(
+                mContext.getContentResolver(),
+                Secure.DEFAULT_NOTE_TASK_PROFILE,
+                UserHandle.myUserId());
+        return UserHandle.of(userId);
+    }
+
+    @VisibleForTesting
+    UserAdapter.OnClickListener createProfileDialogClickCallback(
+            Intent intent, List<UserHandle> users) {
+        // TODO(b/281659827): improve UX flow for when activity is cancelled
+        return (int position) -> {
+            intent.putExtra(Intent.EXTRA_USER, users.get(position));
+
+            Secure.putInt(mContext.getContentResolver(),
+                    Secure.DEFAULT_NOTE_TASK_PROFILE,
+                    users.get(position).getIdentifier());
+            mContext.startActivity(intent);
+
+            mDialog.dismiss();
+        };
+    }
+
+    private void createAndShowProfileSelectDialog(Intent intent, List<UserHandle> users) {
+        mDialog = ProfileSelectDialog.createDialog(
+                mContext,
+                users,
+                createProfileDialogClickCallback(intent, users));
+        mDialog.show();
+    }
+
     /**
      * Identifies whether a device is a stylus using the associated {@link InputDevice} or
      * {@link CachedBluetoothDevice}.
diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index 149d1f4..b5d12a5 100644
--- a/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -94,6 +94,7 @@
 import com.android.settings.deviceinfo.PublicVolumeSettings;
 import com.android.settings.deviceinfo.StorageDashboardFragment;
 import com.android.settings.deviceinfo.aboutphone.MyDeviceInfoFragment;
+import com.android.settings.deviceinfo.batteryinfo.BatteryInfoFragment;
 import com.android.settings.deviceinfo.firmwareversion.FirmwareVersionSettings;
 import com.android.settings.deviceinfo.legal.ModuleLicensesDashboard;
 import com.android.settings.display.AutoBrightnessSettings;
@@ -371,7 +372,8 @@
             NfcAndPaymentFragment.class.getName(),
             ColorAndMotionFragment.class.getName(),
             LongBackgroundTasksDetails.class.getName(),
-            RegionalPreferencesEntriesFragment.class.getName()
+            RegionalPreferencesEntriesFragment.class.getName(),
+            BatteryInfoFragment.class.getName()
     };
 
     public static final String[] SETTINGS_FOR_RESTRICTED = {
diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java
index f8a5d76..d4acfa1 100644
--- a/src/com/android/settings/dashboard/DashboardFragment.java
+++ b/src/com/android/settings/dashboard/DashboardFragment.java
@@ -25,11 +25,14 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.view.View;
 
 import androidx.annotation.CallSuper;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceCategory;
 import androidx.preference.PreferenceGroup;
@@ -170,6 +173,15 @@
     }
 
     @Override
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        LifecycleOwner viewLifecycleOwner = getViewLifecycleOwner();
+        for (AbstractPreferenceController controller : mControllers) {
+            controller.onViewCreated(viewLifecycleOwner);
+        }
+    }
+
+    @Override
     public void onCategoriesChanged(Set<String> categories) {
         final String categoryKey = getCategoryKey();
         final DashboardCategory dashboardCategory =
diff --git a/src/com/android/settings/datausage/BillingCycleSettings.java b/src/com/android/settings/datausage/BillingCycleSettings.java
index 3047d73..c3ddb2e 100644
--- a/src/com/android/settings/datausage/BillingCycleSettings.java
+++ b/src/com/android/settings/datausage/BillingCycleSettings.java
@@ -22,8 +22,6 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.res.Resources;
-import android.icu.text.MeasureFormat;
-import android.icu.util.MeasureUnit;
 import android.net.NetworkPolicy;
 import android.net.NetworkTemplate;
 import android.os.Bundle;
@@ -322,14 +320,10 @@
             final boolean isLimit = getArguments().getBoolean(EXTRA_LIMIT);
             final long bytes = isLimit ? editor.getPolicyLimitBytes(template)
                     : editor.getPolicyWarningBytes(template);
-            final long limitDisabled = isLimit ? LIMIT_DISABLED : WARNING_DISABLED;
 
-            final MeasureFormat formatter = MeasureFormat.getInstance(
-                    getContext().getResources().getConfiguration().locale,
-                    MeasureFormat.FormatWidth.SHORT);
             final String[] unitNames = new String[] {
-                formatter.getUnitDisplayName(MeasureUnit.MEGABYTE),
-                formatter.getUnitDisplayName(MeasureUnit.GIGABYTE)
+                    DataUsageFormatter.INSTANCE.getBytesDisplayUnit(getResources(), MIB_IN_BYTES),
+                    DataUsageFormatter.INSTANCE.getBytesDisplayUnit(getResources(), GIB_IN_BYTES),
             };
             final ArrayAdapter<String> adapter = new ArrayAdapter<String>(
                     getContext(), android.R.layout.simple_spinner_item, unitNames);
diff --git a/src/com/android/settings/datausage/DataSaverBackend.java b/src/com/android/settings/datausage/DataSaverBackend.java
index e47ecbd..6a39234 100644
--- a/src/com/android/settings/datausage/DataSaverBackend.java
+++ b/src/com/android/settings/datausage/DataSaverBackend.java
@@ -196,8 +196,10 @@
     public interface Listener {
         void onDataSaverChanged(boolean isDataSaving);
 
-        void onAllowlistStatusChanged(int uid, boolean isAllowlisted);
+        /** This is called when allow list status is changed. */
+        default void onAllowlistStatusChanged(int uid, boolean isAllowlisted) {}
 
-        void onDenylistStatusChanged(int uid, boolean isDenylisted);
+        /** This is called when deny list status is changed. */
+        default void onDenylistStatusChanged(int uid, boolean isDenylisted) {}
     }
 }
diff --git a/src/com/android/settings/datausage/DataSaverSummary.kt b/src/com/android/settings/datausage/DataSaverSummary.kt
index 1d9cbb7..0828d36 100644
--- a/src/com/android/settings/datausage/DataSaverSummary.kt
+++ b/src/com/android/settings/datausage/DataSaverSummary.kt
@@ -15,33 +15,22 @@
  */
 package com.android.settings.datausage
 
-import android.app.Application
 import android.app.settings.SettingsEnums
 import android.content.Context
 import android.os.Bundle
 import android.telephony.SubscriptionManager
 import android.widget.Switch
-import androidx.lifecycle.lifecycleScope
-import androidx.preference.Preference
 import com.android.settings.R
 import com.android.settings.SettingsActivity
-import com.android.settings.SettingsPreferenceFragment
-import com.android.settings.applications.AppStateBaseBridge
-import com.android.settings.datausage.AppStateDataUsageBridge.DataUsageState
+import com.android.settings.dashboard.DashboardFragment
 import com.android.settings.search.BaseSearchIndexProvider
 import com.android.settings.widget.SettingsMainSwitchBar
-import com.android.settingslib.applications.ApplicationsState
 import com.android.settingslib.search.SearchIndexable
-import com.android.settingslib.spa.framework.util.formatString
-import kotlinx.coroutines.launch
 
 @SearchIndexable
-class DataSaverSummary : SettingsPreferenceFragment() {
+class DataSaverSummary : DashboardFragment() {
     private lateinit var switchBar: SettingsMainSwitchBar
     private lateinit var dataSaverBackend: DataSaverBackend
-    private lateinit var unrestrictedAccess: Preference
-    private var dataUsageBridge: AppStateDataUsageBridge? = null
-    private var session: ApplicationsState.Session? = null
 
     // Flag used to avoid infinite loop due if user switch it on/off too quick.
     private var switching = false
@@ -54,8 +43,6 @@
             return
         }
 
-        addPreferencesFromResource(R.xml.data_saver)
-        unrestrictedAccess = findPreference(KEY_UNRESTRICTED_ACCESS)!!
         dataSaverBackend = DataSaverBackend(requireContext())
     }
 
@@ -72,27 +59,12 @@
 
     override fun onResume() {
         super.onResume()
-        dataSaverBackend.refreshAllowlist()
-        dataSaverBackend.refreshDenylist()
         dataSaverBackend.addListener(dataSaverBackendListener)
-        dataUsageBridge?.resume(/* forceLoadAllApps= */ true)
-            ?: viewLifecycleOwner.lifecycleScope.launch {
-                val applicationsState = ApplicationsState.getInstance(
-                    requireContext().applicationContext as Application
-                )
-                dataUsageBridge = AppStateDataUsageBridge(
-                    applicationsState, dataUsageBridgeCallbacks, dataSaverBackend
-                )
-                session =
-                    applicationsState.newSession(applicationsStateCallbacks, settingsLifecycle)
-                dataUsageBridge?.resume(/* forceLoadAllApps= */ true)
-            }
     }
 
     override fun onPause() {
         super.onPause()
         dataSaverBackend.remListener(dataSaverBackendListener)
-        dataUsageBridge?.pause()
     }
 
     private fun onSwitchChanged(isChecked: Boolean) {
@@ -104,9 +76,10 @@
         }
     }
 
+    override fun getPreferenceScreenResId() = R.xml.data_saver
     override fun getMetricsCategory() = SettingsEnums.DATA_SAVER_SUMMARY
-
     override fun getHelpResource() = R.string.help_url_data_saver
+    override fun getLogTag() = TAG
 
     private val dataSaverBackendListener = object : DataSaverBackend.Listener {
         override fun onDataSaverChanged(isDataSaving: Boolean) {
@@ -115,51 +88,10 @@
                 switching = false
             }
         }
-
-        override fun onAllowlistStatusChanged(uid: Int, isAllowlisted: Boolean) {}
-
-        override fun onDenylistStatusChanged(uid: Int, isDenylisted: Boolean) {}
-    }
-
-    private val dataUsageBridgeCallbacks = AppStateBaseBridge.Callback {
-        updateUnrestrictedAccessSummary()
-    }
-
-    private val applicationsStateCallbacks = object : ApplicationsState.Callbacks {
-        override fun onRunningStateChanged(running: Boolean) {}
-
-        override fun onPackageListChanged() {}
-
-        override fun onRebuildComplete(apps: ArrayList<ApplicationsState.AppEntry>?) {}
-
-        override fun onPackageIconChanged() {}
-
-        override fun onPackageSizeChanged(packageName: String?) {}
-
-        override fun onAllSizesComputed() {
-            updateUnrestrictedAccessSummary()
-        }
-
-        override fun onLauncherInfoChanged() {
-            updateUnrestrictedAccessSummary()
-        }
-
-        override fun onLoadEntriesCompleted() {}
-    }
-
-    private fun updateUnrestrictedAccessSummary() {
-        if (!isAdded || isFinishingOrDestroyed) return
-        val allApps = session?.allApps ?: return
-        val count = allApps.count {
-            ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(it) &&
-                (it.extraInfo as? DataUsageState)?.isDataSaverAllowlisted == true
-        }
-        unrestrictedAccess.summary =
-            resources.formatString(R.string.data_saver_unrestricted_summary, "count" to count)
     }
 
     companion object {
-        private const val KEY_UNRESTRICTED_ACCESS = "unrestricted_access"
+        private const val TAG = "DataSaverSummary"
 
         private fun Context.isDataSaverVisible(): Boolean =
             resources.getBoolean(R.bool.config_show_data_saver)
diff --git a/src/com/android/settings/datausage/DataUsageFormatter.kt b/src/com/android/settings/datausage/DataUsageFormatter.kt
new file mode 100644
index 0000000..16a9ae8
--- /dev/null
+++ b/src/com/android/settings/datausage/DataUsageFormatter.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.datausage
+
+import android.content.res.Resources
+import android.text.format.Formatter
+
+object DataUsageFormatter {
+
+    /**
+     * Gets the display unit of the given bytes.
+     *
+     * Similar to MeasureFormat.getUnitDisplayName(), but with the expected result for the bytes in
+     * Settings, and align with other places in Settings.
+     */
+    fun Resources.getBytesDisplayUnit(bytes: Long): String =
+        Formatter.formatBytes(this, bytes, Formatter.FLAG_IEC_UNITS).units
+}
\ No newline at end of file
diff --git a/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java b/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java
index 0d91fdd..b7b2759 100644
--- a/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java
+++ b/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java
@@ -25,12 +25,4 @@
     int REQUEST_CODE_DEBUG_APP = 1;
 
     int REQUEST_MOCK_LOCATION_APP = 2;
-
-    int REQUEST_CODE_ANGLE_ALL_USE_ANGLE = 3;
-
-    int REQUEST_CODE_ANGLE_DRIVER_PKGS = 4;
-
-    int REQUEST_CODE_ANGLE_DRIVER_VALUES = 5;
-
-    int REQUEST_COMPAT_CHANGE_APP = 6;
 }
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index f7be1aa..047b219 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -675,6 +675,7 @@
         controllers.add(new NfcVerboseVendorLogPreferenceController(context, fragment));
         controllers.add(new ShowTapsPreferenceController(context));
         controllers.add(new PointerLocationPreferenceController(context));
+        controllers.add(new ShowKeyPressesPreferenceController(context));
         controllers.add(new ShowSurfaceUpdatesPreferenceController(context));
         controllers.add(new ShowLayoutBoundsPreferenceController(context));
         controllers.add(new ShowRefreshRatePreferenceController(context));
diff --git a/src/com/android/settings/development/EnableVerboseVendorLoggingPreferenceController.java b/src/com/android/settings/development/EnableVerboseVendorLoggingPreferenceController.java
index 051cede..f13143d 100644
--- a/src/com/android/settings/development/EnableVerboseVendorLoggingPreferenceController.java
+++ b/src/com/android/settings/development/EnableVerboseVendorLoggingPreferenceController.java
@@ -29,6 +29,7 @@
 
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settingslib.development.DeveloperOptionsPreferenceController;
+import com.android.settingslib.utils.ThreadUtils;
 
 import java.util.NoSuchElementException;
 
@@ -66,23 +67,34 @@
         return isIDumpstateDeviceAidlServiceAvailable() || isIDumpstateDeviceV1_1ServiceAvailable();
     }
 
+    @SuppressWarnings("FutureReturnValueIgnored")
     @Override
     public boolean onPreferenceChange(Preference preference, Object newValue) {
         final boolean isEnabled = (Boolean) newValue;
-        setVerboseLoggingEnabled(isEnabled);
+        // IDumpstateDevice IPC may be blocking when system is extremely heavily-loaded.
+        // Post to background thread to avoid ANR. Ignore the returned Future.
+        ThreadUtils.postOnBackgroundThread(() ->
+                setVerboseLoggingEnabled(isEnabled));
         return true;
     }
 
+    @SuppressWarnings("FutureReturnValueIgnored")
     @Override
     public void updateState(Preference preference) {
-        final boolean enabled = getVerboseLoggingEnabled();
-        ((SwitchPreference) mPreference).setChecked(enabled);
+        ThreadUtils.postOnBackgroundThread(() -> {
+                    final boolean enabled = getVerboseLoggingEnabled();
+                    ThreadUtils.getUiThreadHandler().post(() ->
+                            ((SwitchPreference) mPreference).setChecked(enabled));
+                }
+        );
     }
 
+    @SuppressWarnings("FutureReturnValueIgnored")
     @Override
     protected void onDeveloperOptionsSwitchDisabled() {
         super.onDeveloperOptionsSwitchDisabled();
-        setVerboseLoggingEnabled(false);
+        ThreadUtils.postOnBackgroundThread(() ->
+                setVerboseLoggingEnabled(false));
         ((SwitchPreference) mPreference).setChecked(false);
     }
 
diff --git a/src/com/android/settings/development/ShowKeyPressesPreferenceController.java b/src/com/android/settings/development/ShowKeyPressesPreferenceController.java
new file mode 100644
index 0000000..247f59a
--- /dev/null
+++ b/src/com/android/settings/development/ShowKeyPressesPreferenceController.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 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.development;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.SwitchPreference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.development.DeveloperOptionsPreferenceController;
+
+/** PreferenceController that controls the "Show key presses" developer option. */
+public class ShowKeyPressesPreferenceController extends
+        DeveloperOptionsPreferenceController implements
+        Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
+
+    private static final String SHOW_KEY_PRESSES_KEY = "show_key_presses";
+
+    @VisibleForTesting
+    static final int SETTING_VALUE_ON = 1;
+    @VisibleForTesting
+    static final int SETTING_VALUE_OFF = 0;
+
+    public ShowKeyPressesPreferenceController(Context context) {
+        super(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return SHOW_KEY_PRESSES_KEY;
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        final boolean isEnabled = (Boolean) newValue;
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SHOW_KEY_PRESSES, isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF);
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        int showKeyPresses = Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.SHOW_KEY_PRESSES, SETTING_VALUE_OFF);
+        ((SwitchPreference) mPreference).setChecked(showKeyPresses != SETTING_VALUE_OFF);
+    }
+
+    @Override
+    protected void onDeveloperOptionsSwitchDisabled() {
+        super.onDeveloperOptionsSwitchDisabled();
+        Settings.System.putInt(mContext.getContentResolver(), Settings.System.SHOW_KEY_PRESSES,
+                SETTING_VALUE_OFF);
+        ((SwitchPreference) mPreference).setChecked(false);
+    }
+}
diff --git a/src/com/android/settings/development/compat/PlatformCompatDashboard.java b/src/com/android/settings/development/compat/PlatformCompatDashboard.java
index f8cbf21..3f0ffc7 100644
--- a/src/com/android/settings/development/compat/PlatformCompatDashboard.java
+++ b/src/com/android/settings/development/compat/PlatformCompatDashboard.java
@@ -17,21 +17,16 @@
 package com.android.settings.development.compat;
 
 import static com.android.internal.compat.OverrideAllowedState.ALLOWED;
-import static com.android.settings.development.DevelopmentOptionsActivityRequestCodes.REQUEST_COMPAT_CHANGE_APP;
 
-import android.app.Activity;
-import android.app.AlertDialog;
 import android.app.settings.SettingsEnums;
 import android.compat.Compatibility.ChangeConfig;
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.text.TextUtils;
 import android.util.ArraySet;
 
 import androidx.annotation.VisibleForTesting;
@@ -40,35 +35,28 @@
 import androidx.preference.PreferenceCategory;
 import androidx.preference.SwitchPreference;
 
-import com.android.internal.compat.AndroidBuildClassifier;
 import com.android.internal.compat.CompatibilityChangeConfig;
 import com.android.internal.compat.CompatibilityChangeInfo;
 import com.android.internal.compat.IPlatformCompat;
 import com.android.settings.R;
 import com.android.settings.dashboard.DashboardFragment;
-import com.android.settings.development.AppPicker;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
 
-
 /**
  * Dashboard for Platform Compat preferences.
  */
 public class PlatformCompatDashboard extends DashboardFragment {
     private static final String TAG = "PlatformCompatDashboard";
-    private static final String COMPAT_APP = "compat_app";
+    public static final String COMPAT_APP = "compat_app";
 
     private IPlatformCompat mPlatformCompat;
 
     private CompatibilityChangeInfo[] mChanges;
 
-    private AndroidBuildClassifier mAndroidBuildClassifier = new AndroidBuildClassifier();
-
-    private boolean mShouldStartAppPickerOnResume = true;
-
     @VisibleForTesting
     String mSelectedApp;
 
@@ -108,32 +96,6 @@
         } catch (RemoteException e) {
             throw new RuntimeException("Could not list changes!", e);
         }
-        if (icicle != null) {
-            mShouldStartAppPickerOnResume = false;
-            mSelectedApp = icicle.getString(COMPAT_APP);
-        }
-    }
-
-    @Override
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        if (requestCode == REQUEST_COMPAT_CHANGE_APP) {
-            mShouldStartAppPickerOnResume = false;
-            switch (resultCode) {
-                case Activity.RESULT_OK:
-                    mSelectedApp = data.getAction();
-                    break;
-                case Activity.RESULT_CANCELED:
-                    if (TextUtils.isEmpty(mSelectedApp)) {
-                        finish();
-                    }
-                    break;
-                case AppPicker.RESULT_NO_MATCHING_APPS:
-                    mSelectedApp = null;
-                    break;
-            }
-            return;
-        }
-        super.onActivityResult(requestCode, resultCode, data);
     }
 
     @Override
@@ -142,33 +104,18 @@
         if (isFinishingOrDestroyed()) {
             return;
         }
-        if (!mShouldStartAppPickerOnResume) {
-            if (TextUtils.isEmpty(mSelectedApp)) {
-                new AlertDialog.Builder(getContext())
-                        .setTitle(R.string.platform_compat_dialog_title_no_apps)
-                        .setMessage(R.string.platform_compat_dialog_text_no_apps)
-                        .setPositiveButton(R.string.okay, (dialog, which) -> finish())
-                        .setOnDismissListener(dialog -> finish())
-                        .setCancelable(false)
-                        .show();
-                return;
-            }
-            try {
-                final ApplicationInfo applicationInfo = getApplicationInfo();
-                addPreferences(applicationInfo);
-                return;
-            } catch (PackageManager.NameNotFoundException e) {
-                mShouldStartAppPickerOnResume = true;
-                mSelectedApp = null;
-            }
+        Bundle arguments = getArguments();
+        if (arguments == null) {
+            finish();
+            return;
         }
-        startAppPicker();
-    }
-
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        outState.putString(COMPAT_APP, mSelectedApp);
+        mSelectedApp = arguments.getString(COMPAT_APP);
+        try {
+            final ApplicationInfo applicationInfo = getApplicationInfo();
+            addPreferences(applicationInfo);
+        } catch (PackageManager.NameNotFoundException ignored) {
+            finish();
+        }
     }
 
     private void addPreferences(ApplicationInfo applicationInfo) {
@@ -266,12 +213,6 @@
         appPreference.setIcon(icon);
         appPreference.setSummary(getString(R.string.platform_compat_selected_app_summary,
                                          mSelectedApp, applicationInfo.targetSdkVersion));
-        appPreference.setKey(mSelectedApp);
-        appPreference.setOnPreferenceClickListener(
-                preference -> {
-                    startAppPicker();
-                    return true;
-                });
         return appPreference;
     }
 
@@ -294,17 +235,6 @@
         }
     }
 
-    private void startAppPicker() {
-        final Intent intent = new Intent(getContext(), AppPicker.class)
-                .putExtra(AppPicker.EXTRA_INCLUDE_NOTHING, false);
-        // If build is neither userdebug nor eng, only include debuggable apps
-        final boolean debuggableBuild = mAndroidBuildClassifier.isDebuggableBuild();
-        if (!debuggableBuild) {
-            intent.putExtra(AppPicker.EXTRA_DEBUGGABLE, true /* value */);
-        }
-        startActivityForResult(intent, REQUEST_COMPAT_CHANGE_APP);
-    }
-
     private class CompatChangePreferenceChangeListener implements OnPreferenceChangeListener {
         private final long changeId;
 
diff --git a/src/com/android/settings/deviceinfo/batteryinfo/BatteryCycleCountPreferenceController.java b/src/com/android/settings/deviceinfo/batteryinfo/BatteryCycleCountPreferenceController.java
new file mode 100644
index 0000000..b022fcf
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/batteryinfo/BatteryCycleCountPreferenceController.java
@@ -0,0 +1,51 @@
+/*
+ * 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.deviceinfo.batteryinfo;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.BatteryManager;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.fuelgauge.BatteryUtils;
+
+/**
+ * A controller that manages the information about battery cycle count.
+ */
+public class BatteryCycleCountPreferenceController extends BasePreferenceController {
+
+    public BatteryCycleCountPreferenceController(Context context,
+            String preferenceKey) {
+        super(context, preferenceKey);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AVAILABLE;
+    }
+
+    @Override
+    public CharSequence getSummary() {
+        final Intent batteryIntent = BatteryUtils.getBatteryIntent(mContext);
+        final int cycleCount = batteryIntent.getIntExtra(BatteryManager.EXTRA_CYCLE_COUNT, -1);
+
+        return cycleCount == -1
+                ? mContext.getText(R.string.battery_cycle_count_not_available)
+                : Integer.toString(cycleCount);
+    }
+}
diff --git a/src/com/android/settings/deviceinfo/batteryinfo/BatteryFirstUseDatePreferenceController.java b/src/com/android/settings/deviceinfo/batteryinfo/BatteryFirstUseDatePreferenceController.java
new file mode 100644
index 0000000..6c7a743
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/batteryinfo/BatteryFirstUseDatePreferenceController.java
@@ -0,0 +1,67 @@
+/*
+ * 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.deviceinfo.batteryinfo;
+
+import android.content.Context;
+import android.os.BatteryManager;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.fuelgauge.BatterySettingsFeatureProvider;
+import com.android.settings.fuelgauge.BatteryUtils;
+import com.android.settings.overlay.FeatureFactory;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A controller that manages the information about battery first use date.
+ */
+public class BatteryFirstUseDatePreferenceController extends BasePreferenceController {
+
+    private final BatterySettingsFeatureProvider mBatterySettingsFeatureProvider;
+    private final BatteryManager mBatteryManager;
+
+    private long mFirstUseDateInMs;
+
+    public BatteryFirstUseDatePreferenceController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
+        mBatterySettingsFeatureProvider = FeatureFactory.getFactory(
+                context).getBatterySettingsFeatureProvider();
+        mBatteryManager = mContext.getSystemService(BatteryManager.class);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return mBatterySettingsFeatureProvider.isFirstUseDateAvailable(mContext, getFirstUseDate())
+                ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+    }
+
+    @Override
+    public CharSequence getSummary() {
+        return isAvailable()
+                ? BatteryUtils.getBatteryInfoFormattedDate(mFirstUseDateInMs)
+                : null;
+    }
+
+    private long getFirstUseDate() {
+        if (mFirstUseDateInMs == 0L) {
+            final long firstUseDateInSec = mBatteryManager.getLongProperty(
+                    BatteryManager.BATTERY_PROPERTY_FIRST_USAGE_DATE);
+            mFirstUseDateInMs = TimeUnit.MILLISECONDS.convert(firstUseDateInSec, TimeUnit.SECONDS);
+        }
+        return mFirstUseDateInMs;
+    }
+}
diff --git a/src/com/android/settings/deviceinfo/batteryinfo/BatteryInfoFragment.java b/src/com/android/settings/deviceinfo/batteryinfo/BatteryInfoFragment.java
new file mode 100644
index 0000000..1731212
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/batteryinfo/BatteryInfoFragment.java
@@ -0,0 +1,51 @@
+/*
+ * 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.deviceinfo.batteryinfo;
+
+import android.app.settings.SettingsEnums;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.search.SearchIndexable;
+
+/**
+ * A fragment that shows battery hardware information.
+ */
+@SearchIndexable
+public class BatteryInfoFragment extends DashboardFragment {
+
+    public static final String TAG = "BatteryInfo";
+
+    @Override
+    public int getMetricsCategory() {
+        return SettingsEnums.SETTINGS_BATTERY_INFORMATION;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.battery_info;
+    }
+
+    @Override
+    protected String getLogTag() {
+        return TAG;
+    }
+
+    public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+            new BaseSearchIndexProvider(R.xml.battery_info);
+}
diff --git a/src/com/android/settings/deviceinfo/batteryinfo/BatteryManufactureDatePreferenceController.java b/src/com/android/settings/deviceinfo/batteryinfo/BatteryManufactureDatePreferenceController.java
new file mode 100644
index 0000000..ff54c77
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/batteryinfo/BatteryManufactureDatePreferenceController.java
@@ -0,0 +1,69 @@
+/*
+ * 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.deviceinfo.batteryinfo;
+
+import android.content.Context;
+import android.os.BatteryManager;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.fuelgauge.BatterySettingsFeatureProvider;
+import com.android.settings.fuelgauge.BatteryUtils;
+import com.android.settings.overlay.FeatureFactory;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A controller that manages the information about battery manufacture date.
+ */
+public class BatteryManufactureDatePreferenceController extends BasePreferenceController {
+
+    private final BatterySettingsFeatureProvider mBatterySettingsFeatureProvider;
+    private final BatteryManager mBatteryManager;
+
+    private long mManufactureDateInMs;
+
+    public BatteryManufactureDatePreferenceController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
+        mBatterySettingsFeatureProvider = FeatureFactory.getFactory(
+                context).getBatterySettingsFeatureProvider();
+        mBatteryManager = mContext.getSystemService(BatteryManager.class);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return mBatterySettingsFeatureProvider.isManufactureDateAvailable(mContext,
+                getManufactureDate())
+                ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+    }
+
+    @Override
+    public CharSequence getSummary() {
+        return isAvailable()
+                ? BatteryUtils.getBatteryInfoFormattedDate(mManufactureDateInMs)
+                : null;
+    }
+
+    private long getManufactureDate() {
+        if (mManufactureDateInMs == 0L) {
+            final long manufactureDateInSec = mBatteryManager.getLongProperty(
+                    BatteryManager.BATTERY_PROPERTY_MANUFACTURING_DATE);
+            mManufactureDateInMs = TimeUnit.MILLISECONDS.convert(manufactureDateInSec,
+                    TimeUnit.SECONDS);
+        }
+        return mManufactureDateInMs;
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java
index f6efb24..260fde0 100644
--- a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java
+++ b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java
@@ -16,9 +16,14 @@
 
 package com.android.settings.fuelgauge;
 
-import android.content.ComponentName;
+import android.content.Context;
 
 /** Feature provider for battery settings usage. */
 public interface BatterySettingsFeatureProvider {
 
+    /** Returns true if manufacture date should be shown */
+    boolean isManufactureDateAvailable(Context context, long manufactureDateMs);
+
+    /** Returns true if first use date should be shown */
+    boolean isFirstUseDateAvailable(Context context, long firstUseDateMs);
 }
diff --git a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java
index 39fe118..6b456b7 100644
--- a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java
+++ b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java
@@ -21,9 +21,13 @@
 /** Feature provider implementation for battery settings usage. */
 public class BatterySettingsFeatureProviderImpl implements BatterySettingsFeatureProvider {
 
-    protected Context mContext;
+    @Override
+    public boolean isManufactureDateAvailable(Context context, long manufactureDateMs) {
+        return false;
+    }
 
-    public BatterySettingsFeatureProviderImpl(Context context) {
-        mContext = context.getApplicationContext();
+    @Override
+    public boolean isFirstUseDateAvailable(Context context, long firstUseDateMs) {
+        return false;
     }
 }
diff --git a/src/com/android/settings/fuelgauge/BatteryUtils.java b/src/com/android/settings/fuelgauge/BatteryUtils.java
index 12760b1..29c7591 100644
--- a/src/com/android/settings/fuelgauge/BatteryUtils.java
+++ b/src/com/android/settings/fuelgauge/BatteryUtils.java
@@ -64,8 +64,10 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.time.Duration;
 import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.format.FormatStyle;
 import java.util.List;
 
 /**
@@ -451,12 +453,10 @@
 
     @VisibleForTesting
     Estimate getEnhancedEstimate() {
-        Estimate estimate = null;
-        // Get enhanced prediction if available
-        if (Duration.between(Estimate.getLastCacheUpdateTime(mContext), Instant.now())
-                .compareTo(Duration.ofSeconds(10)) < 0) {
-            estimate = Estimate.getCachedEstimateIfAvailable(mContext);
-        } else if (mPowerUsageFeatureProvider != null &&
+        // Align the same logic in the BatteryControllerImpl.updateEstimate()
+        Estimate estimate = Estimate.getCachedEstimateIfAvailable(mContext);
+        if (estimate == null &&
+                mPowerUsageFeatureProvider != null &&
                 mPowerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(mContext)) {
             estimate = mPowerUsageFeatureProvider.getEnhancedBatteryPrediction(mContext);
             if (estimate != null) {
@@ -673,6 +673,14 @@
         }
         return summary.toString();
     }
+    /** Format the date of battery related info */
+    public static CharSequence getBatteryInfoFormattedDate(long dateInMs) {
+        final Instant instant = Instant.ofEpochMilli(dateInMs);
+        final String localDate = instant.atZone(ZoneId.systemDefault()).toLocalDate().format(
+                DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG));
+
+        return localDate;
+    }
 
     /** Builds the battery usage time information for one timestamp. */
     private static String buildBatteryUsageTimeInfo(final Context context, long timeInMs,
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
index 8aabc37..fdafca6 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
@@ -20,9 +20,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.SparseIntArray;
-import android.view.View;
 
-import androidx.annotation.IdRes;
+import androidx.annotation.DrawableRes;
 import androidx.annotation.IntDef;
 import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
@@ -134,7 +133,8 @@
 
     public abstract CharSequence getSummary(Context context);
 
-    @IdRes
+    /** Gets the drawable resource id for the icon. */
+    @DrawableRes
     public abstract int getIconId();
 
     /**
@@ -162,21 +162,12 @@
         preference.setTitle(getTitle(context));
         preference.setSummary(getSummary(context));
         preference.setIcon(getIconId());
-        @IdRes int iconTintColorId = getIconTintColorId();
-        if (iconTintColorId != View.NO_ID) {
-            preference.getIcon().setTint(context.getColor(iconTintColorId));
-        }
         final CardPreference cardPreference = castToCardPreferenceSafely(preference);
         if (cardPreference != null) {
             cardPreference.resetLayoutState();
         }
     }
 
-    /** Returns the color resid for tinting {@link #getIconId()} or {@link View#NO_ID} if none. */
-    public @IdRes int getIconTintColorId() {
-        return View.NO_ID;
-    }
-
     public boolean shouldShowDialog() {
         return mShowDialog;
     }
diff --git a/src/com/android/settings/inputmethod/KeyboardSettingsFeatureProvider.java b/src/com/android/settings/inputmethod/KeyboardSettingsFeatureProvider.java
new file mode 100644
index 0000000..7255107
--- /dev/null
+++ b/src/com/android/settings/inputmethod/KeyboardSettingsFeatureProvider.java
@@ -0,0 +1,56 @@
+/*
+ * 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.inputmethod;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+
+import androidx.annotation.Nullable;
+import androidx.preference.PreferenceScreen;
+
+/**
+ * Provider for Keyboard settings related features.
+ */
+public interface KeyboardSettingsFeatureProvider {
+
+    /**
+     * Checks whether the connected device supports firmware update.
+     *
+     * @return true if the connected device supports firmware update.
+     */
+    boolean supportsFirmwareUpdate();
+
+    /**
+     * Add firmware update preference category .
+     *
+     * @param context The context to initialize the application with.
+     * @param screen  The {@link PreferenceScreen} to add the firmware update preference category.
+     *
+     * @return true if the category is added successfully.
+     */
+    boolean addFirmwareUpdateCategory(Context context, PreferenceScreen screen);
+
+    /**
+     * Get custom action key icon.
+     *
+     * @param context Context for accessing resources.
+     *
+     * @return Returns the image of the icon, or null if there is no any custom icon.
+     */
+    @Nullable
+    Drawable getActionKeyIcon(Context context);
+}
diff --git a/src/com/android/settings/inputmethod/KeyboardSettingsFeatureProviderImpl.java b/src/com/android/settings/inputmethod/KeyboardSettingsFeatureProviderImpl.java
new file mode 100644
index 0000000..26b10e5
--- /dev/null
+++ b/src/com/android/settings/inputmethod/KeyboardSettingsFeatureProviderImpl.java
@@ -0,0 +1,43 @@
+/*
+ * 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.inputmethod;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+
+import androidx.preference.PreferenceScreen;
+
+/**
+ * Provider implementation for keyboard settings related features.
+ */
+public class KeyboardSettingsFeatureProviderImpl implements KeyboardSettingsFeatureProvider {
+
+    @Override
+    public boolean supportsFirmwareUpdate() {
+        return false;
+    }
+
+    @Override
+    public boolean addFirmwareUpdateCategory(Context context, PreferenceScreen screen) {
+        return false;
+    }
+
+    @Override
+    public Drawable getActionKeyIcon(Context context) {
+        return null;
+    };
+}
diff --git a/src/com/android/settings/inputmethod/ModifierKeysPickerDialogFragment.java b/src/com/android/settings/inputmethod/ModifierKeysPickerDialogFragment.java
index 949e656..076173a 100644
--- a/src/com/android/settings/inputmethod/ModifierKeysPickerDialogFragment.java
+++ b/src/com/android/settings/inputmethod/ModifierKeysPickerDialogFragment.java
@@ -22,6 +22,7 @@
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.content.Context;
+import android.graphics.drawable.Drawable;
 import android.hardware.input.InputManager;
 import android.os.Bundle;
 import android.text.Spannable;
@@ -39,10 +40,12 @@
 import android.widget.ListView;
 import android.widget.TextView;
 
+import androidx.core.graphics.drawable.DrawableCompat;
 import androidx.fragment.app.DialogFragment;
 import androidx.preference.Preference;
 
 import com.android.settings.R;
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.Utils;
 
 import java.util.ArrayList;
@@ -60,6 +63,11 @@
     private String mKeyDefaultName;
     private String mKeyFocus;
     private Activity mActivity;
+    private KeyboardSettingsFeatureProvider mFeatureProvider;
+    private Drawable mActionKeyDrawable;
+    private TextView mLeftBracket;
+    private TextView mRightBracket;
+    private ImageView mActionKeyIcon;
 
     private List<int[]> mRemappableKeyList =
             new ArrayList<>(Arrays.asList(
@@ -83,6 +91,8 @@
         super.onCreateDialog(savedInstanceState);
 
         mActivity = getActivity();
+        FeatureFactory featureFactory = FeatureFactory.getFactory(mActivity);
+        mFeatureProvider = featureFactory.getKeyboardSettingsFeatureProvider();
         InputManager inputManager = mActivity.getSystemService(InputManager.class);
         mKeyDefaultName = getArguments().getString(DEFAULT_KEY);
         mKeyFocus = getArguments().getString(SELECTION_KEY);
@@ -97,6 +107,10 @@
         for (int i = 0; i < modifierKeys.size(); i++) {
             mRemappableKeyMap.put(modifierKeys.get(i), mRemappableKeyList.get(i));
         }
+        Drawable drawable = mFeatureProvider.getActionKeyIcon(mActivity);
+        if (drawable != null) {
+            mActionKeyDrawable = DrawableCompat.wrap(drawable);
+        }
 
         View dialoglayout  =
                 LayoutInflater.from(mActivity).inflate(R.layout.modifier_key_picker_dialog, null);
@@ -226,10 +240,18 @@
                 checkIcon.setImageAlpha(255);
                 view.setBackground(
                         mActivity.getDrawable(R.drawable.modifier_key_lisetview_background));
+                if (mActionKeyDrawable != null && i == 2) {
+                    setActionKeyIcon(view);
+                    setActionKeyColor(getColorOfMaterialColorPrimary());
+                }
             } else {
                 textView.setTextColor(getColorOfTextColorPrimary());
                 checkIcon.setImageAlpha(0);
                 view.setBackground(null);
+                if (mActionKeyDrawable != null && i == 2) {
+                    setActionKeyIcon(view);
+                    setActionKeyColor(getColorOfTextColorPrimary());
+                }
             }
             return view;
         }
@@ -243,6 +265,21 @@
         }
     }
 
+    private void setActionKeyIcon(View view) {
+        mLeftBracket = view.findViewById(R.id.modifier_key_left_bracket);
+        mRightBracket = view.findViewById(R.id.modifier_key_right_bracket);
+        mActionKeyIcon = view.findViewById(R.id.modifier_key_action_key_icon);
+        mLeftBracket.setText("(");
+        mRightBracket.setText(")");
+        mActionKeyIcon.setImageDrawable(mActionKeyDrawable);
+    }
+
+    private void setActionKeyColor(int color) {
+        mLeftBracket.setTextColor(color);
+        mRightBracket.setTextColor(color);
+        DrawableCompat.setTint(mActionKeyDrawable, color);
+    }
+
     private int getColorOfTextColorPrimary() {
         return Utils.getColorAttrDefaultColor(mActivity, android.R.attr.textColorPrimary);
     }
diff --git a/src/com/android/settings/inputmethod/ModifierKeysPreferenceController.java b/src/com/android/settings/inputmethod/ModifierKeysPreferenceController.java
index 5d8149a..77def48 100644
--- a/src/com/android/settings/inputmethod/ModifierKeysPreferenceController.java
+++ b/src/com/android/settings/inputmethod/ModifierKeysPreferenceController.java
@@ -17,12 +17,16 @@
 package com.android.settings.inputmethod;
 
 import android.content.Context;
+import android.graphics.drawable.Drawable;
 import android.hardware.input.InputManager;
 import android.os.Bundle;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.style.ForegroundColorSpan;
+import android.util.Pair;
 import android.view.KeyEvent;
+import android.widget.ImageView;
+import android.widget.TextView;
 
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentManager;
@@ -31,7 +35,9 @@
 
 import com.android.settings.R;
 import com.android.settings.core.BasePreferenceController;
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.Utils;
+import com.android.settingslib.widget.LayoutPreference;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -53,6 +59,7 @@
     private FragmentManager mFragmentManager;
     private final InputManager mIm;
     private PreferenceScreen mScreen;
+    private Drawable mDrawable;
 
     private final List<Integer> mRemappableKeys = new ArrayList<>(
             Arrays.asList(
@@ -61,6 +68,14 @@
                     KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_ALT_RIGHT,
                     KeyEvent.KEYCODE_CAPS_LOCK));
 
+    private final List<Pair<String, Integer>> mKeys = new ArrayList<>(
+            Arrays.asList(
+                    Pair.create(KEY_PREFERENCE_CTRL, R.string.modifier_keys_ctrl),
+                    Pair.create(KEY_PREFERENCE_META, R.string.modifier_keys_meta),
+                    Pair.create(KEY_PREFERENCE_ALT, R.string.modifier_keys_alt),
+                    Pair.create(KEY_PREFERENCE_CAPS_LOCK, R.string.modifier_keys_caps_lock)
+            ));
+
     private String[] mKeyNames = new String[] {
             mContext.getString(R.string.modifier_keys_ctrl),
             mContext.getString(R.string.modifier_keys_ctrl),
@@ -74,6 +89,9 @@
         super(context, key);
         mIm = context.getSystemService(InputManager.class);
         Objects.requireNonNull(mIm, "InputManager service cannot be null");
+        KeyboardSettingsFeatureProvider featureProvider =
+                FeatureFactory.getFactory(context).getKeyboardSettingsFeatureProvider();
+        mDrawable = featureProvider.getActionKeyIcon(context);
     }
 
     public void setFragment(Fragment parent) {
@@ -91,33 +109,59 @@
     }
 
     private void refreshUi() {
+        initDefaultKeysName();
         for (Map.Entry<Integer, Integer> entry : mIm.getModifierKeyRemapping().entrySet()) {
             int fromKey = entry.getKey();
             int toKey = entry.getValue();
             int index = mRemappableKeys.indexOf(toKey);
 
             if (isCtrl(fromKey) && mRemappableKeys.contains(toKey)) {
-                Preference preference = mScreen.findPreference(KEY_PREFERENCE_CTRL);
-                preference.setSummary(changeSummaryColor(mKeyNames[index]));
+                setSummaryColor(KEY_PREFERENCE_CTRL, index);
             }
 
             if (isMeta(fromKey) && mRemappableKeys.contains(toKey)) {
-                Preference preference = mScreen.findPreference(KEY_PREFERENCE_META);
-                preference.setSummary(changeSummaryColor(mKeyNames[index]));
+                setSummaryColor(KEY_PREFERENCE_META, index);
             }
 
             if (isAlt(fromKey) && mRemappableKeys.contains(toKey)) {
-                Preference preference = mScreen.findPreference(KEY_PREFERENCE_ALT);
-                preference.setSummary(changeSummaryColor(mKeyNames[index]));
+                setSummaryColor(KEY_PREFERENCE_ALT, index);
             }
 
             if (isCapLock(fromKey) && mRemappableKeys.contains(toKey)) {
-                Preference preference = mScreen.findPreference(KEY_PREFERENCE_CAPS_LOCK);
-                preference.setSummary(changeSummaryColor(mKeyNames[index]));
+                setSummaryColor(KEY_PREFERENCE_CAPS_LOCK, index);
             }
         }
     }
 
+    private void initDefaultKeysName() {
+        for (Pair<String, Integer> key : mKeys) {
+            LayoutPreference layoutPreference = mScreen.findPreference(key.first);
+            TextView title = layoutPreference.findViewById(R.id.title);
+            TextView summary = layoutPreference.findViewById(R.id.summary);
+            title.setText(key.second);
+            summary.setText(R.string.modifier_keys_default_summary);
+
+            if (key.first.equals(KEY_PREFERENCE_META) && mDrawable != null) {
+                setActionKeyIcon(layoutPreference, mDrawable);
+            }
+        }
+    }
+
+    private static void setActionKeyIcon(LayoutPreference preference, Drawable drawable) {
+        TextView leftBracket = preference.findViewById(R.id.modifier_key_left_bracket);
+        TextView rightBracket = preference.findViewById(R.id.modifier_key_right_bracket);
+        ImageView actionKeyIcon = preference.findViewById(R.id.modifier_key_action_key_icon);
+        leftBracket.setText("(");
+        rightBracket.setText(")");
+        actionKeyIcon.setImageDrawable(drawable);
+    }
+
+    private void setSummaryColor(String key, int targetIndex) {
+        LayoutPreference layoutPreference = mScreen.findPreference(key);
+        TextView summary = layoutPreference.findViewById(R.id.summary);
+        summary.setText(changeSummaryColor(mKeyNames[targetIndex]));
+    }
+
     @Override
     public boolean handlePreferenceTreeClick(Preference preference) {
         if (preference.getKey().equals(KEY_RESTORE_PREFERENCE)) {
@@ -137,12 +181,14 @@
         ModifierKeysPickerDialogFragment fragment = new ModifierKeysPickerDialogFragment();
         fragment.setTargetFragment(mParent, 0);
         Bundle bundle = new Bundle();
+        TextView title = ((LayoutPreference) preference).findViewById(R.id.title);
+        TextView summary = ((LayoutPreference) preference).findViewById(R.id.summary);
         bundle.putString(
                 ModifierKeysPickerDialogFragment.DEFAULT_KEY,
-                preference.getTitle().toString());
+                title.getText().toString());
         bundle.putString(
                 ModifierKeysPickerDialogFragment.SELECTION_KEY,
-                preference.getSummary().toString());
+                summary.getText().toString());
         fragment.setArguments(bundle);
         fragment.show(mFragmentManager, KEY_TAG);
     }
diff --git a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
index 936de38..f504fba 100644
--- a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
+++ b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
@@ -48,6 +48,7 @@
 import com.android.settings.Settings;
 import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settingslib.search.SearchIndexable;
 import com.android.settingslib.utils.ThreadUtils;
@@ -75,6 +76,7 @@
     private InputManager mIm;
     private InputMethodManager mImm;
     private InputDeviceIdentifier mAutoInputDeviceIdentifier;
+    private KeyboardSettingsFeatureProvider mFeatureProvider;
     @NonNull
     private PreferenceCategory mKeyboardAssistanceCategory;
     @NonNull
@@ -82,6 +84,7 @@
 
     private Intent mIntentWaitingForResult;
     private boolean mIsNewKeyboardSettings;
+    private boolean mSupportsFirmwareUpdate;
 
     static final String EXTRA_BT_ADDRESS = "extra_bt_address";
     private String mBluetoothAddress;
@@ -104,6 +107,12 @@
                 (SwitchPreference) mKeyboardAssistanceCategory.findPreference(
                         SHOW_VIRTUAL_KEYBOARD_SWITCH));
 
+        FeatureFactory featureFactory = FeatureFactory.getFactory(getContext());
+        mFeatureProvider = featureFactory.getKeyboardSettingsFeatureProvider();
+        mSupportsFirmwareUpdate = mFeatureProvider.supportsFirmwareUpdate();
+        if (mSupportsFirmwareUpdate) {
+            mFeatureProvider.addFirmwareUpdateCategory(getContext(), getPreferenceScreen());
+        }
         mIsNewKeyboardSettings = FeatureFlagUtils.isEnabled(
                 getContext(), FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI);
         boolean isModifierKeySettingsEnabled = FeatureFlagUtils
@@ -247,6 +256,9 @@
         }
         mKeyboardAssistanceCategory.setOrder(1);
         preferenceScreen.addPreference(mKeyboardAssistanceCategory);
+        if (mSupportsFirmwareUpdate) {
+            mFeatureProvider.addFirmwareUpdateCategory(getPrefContext(), preferenceScreen);
+        }
         updateShowVirtualKeyboardSwitch();
     }
 
diff --git a/src/com/android/settings/inputmethod/TouchGesturesButtonPreferenceController.java b/src/com/android/settings/inputmethod/TouchGesturesButtonPreferenceController.java
index 7efa637..bbe65c1 100644
--- a/src/com/android/settings/inputmethod/TouchGesturesButtonPreferenceController.java
+++ b/src/com/android/settings/inputmethod/TouchGesturesButtonPreferenceController.java
@@ -63,9 +63,7 @@
 
     @Override
     public int getAvailabilityStatus() {
-        boolean touchGestureDeveloperMode = FeatureFlagUtils
-                .isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_TRACKPAD_GESTURE);
-        return touchGestureDeveloperMode ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+        return AVAILABLE;
     }
 
     private void showTouchpadGestureEducation() {
diff --git a/src/com/android/settings/localepicker/LocaleListEditor.java b/src/com/android/settings/localepicker/LocaleListEditor.java
index 7ec08f7..55cff3b 100644
--- a/src/com/android/settings/localepicker/LocaleListEditor.java
+++ b/src/com/android/settings/localepicker/LocaleListEditor.java
@@ -104,7 +104,6 @@
 
         addPreferencesFromResource(R.xml.languages);
         final Activity activity = getActivity();
-        activity.setTitle(R.string.language_picker_title);
         mLocaleHelperPreferenceController = new LocaleHelperPreferenceController(activity);
         final PreferenceScreen screen = getPreferenceScreen();
         mLocalePickerPreference = screen.findPreference(KEY_LANGUAGES_PICKER);
@@ -358,12 +357,12 @@
         final LocaleLinearLayoutManager llm = new LocaleLinearLayoutManager(getContext(), mAdapter);
         llm.setAutoMeasureEnabled(true);
         list.setLayoutManager(llm);
-
         list.setHasFixedSize(true);
         list.setNestedScrollingEnabled(false);
         mAdapter.setRecyclerView(list);
         list.setAdapter(mAdapter);
         list.setOnTouchListener(this);
+        list.requestFocus();
 
         mAddLanguage = layout.findViewById(R.id.add_language);
         mAddLanguage.setOnClickListener(new View.OnClickListener() {
diff --git a/src/com/android/settings/localepicker/LocaleRecyclerView.java b/src/com/android/settings/localepicker/LocaleRecyclerView.java
deleted file mode 100644
index 4a5f28b..0000000
--- a/src/com/android/settings/localepicker/LocaleRecyclerView.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.localepicker;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-
-import androidx.recyclerview.widget.RecyclerView;
-
-class LocaleRecyclerView extends RecyclerView {
-    public LocaleRecyclerView(Context context) {
-        super(context);
-    }
-
-    public LocaleRecyclerView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public LocaleRecyclerView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-}
diff --git a/src/com/android/settings/network/EraseEuiccDataDialogFragment.java b/src/com/android/settings/network/EraseEuiccDataDialogFragment.java
index 32903bd..0200e52 100644
--- a/src/com/android/settings/network/EraseEuiccDataDialogFragment.java
+++ b/src/com/android/settings/network/EraseEuiccDataDialogFragment.java
@@ -23,7 +23,6 @@
 import android.content.DialogInterface;
 import android.os.AsyncTask;
 import android.os.Bundle;
-import android.os.RecoverySystem;
 import android.util.Log;
 
 import androidx.annotation.NonNull;
@@ -62,7 +61,7 @@
         return new AlertDialog.Builder(getActivity())
                 .setTitle(R.string.reset_esim_title)
                 .setMessage(R.string.reset_esim_desc)
-                .setPositiveButton(R.string.erase_euicc_data_button, this)
+                .setPositiveButton(R.string.erase_sim_confirm_button, this)
                 .setNegativeButton(R.string.cancel, null)
                 .setOnDismissListener(this)
                 .create();
diff --git a/src/com/android/settings/network/telephony/AbstractMobileNetworkSettings.java b/src/com/android/settings/network/telephony/AbstractMobileNetworkSettings.java
index 245ac83..7addb59 100644
--- a/src/com/android/settings/network/telephony/AbstractMobileNetworkSettings.java
+++ b/src/com/android/settings/network/telephony/AbstractMobileNetworkSettings.java
@@ -18,7 +18,6 @@
 
 import android.os.SystemClock;
 import android.text.TextUtils;
-import android.util.Log;
 
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
@@ -66,8 +65,7 @@
 
     TelephonyStatusControlSession setTelephonyAvailabilityStatus(
             Collection<AbstractPreferenceController> listOfPrefControllers) {
-        return (new TelephonyStatusControlSession.Builder(listOfPrefControllers))
-                .build();
+        return new TelephonyStatusControlSession(listOfPrefControllers, getLifecycle());
     }
 
     @Override
diff --git a/src/com/android/settings/network/telephony/TelephonyStatusControlSession.java b/src/com/android/settings/network/telephony/TelephonyStatusControlSession.java
deleted file mode 100644
index 3716f1f..0000000
--- a/src/com/android/settings/network/telephony/TelephonyStatusControlSession.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.network.telephony;
-
-import android.util.Log;
-
-import com.android.settings.core.BasePreferenceController;
-import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.utils.ThreadUtils;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-
-/**
- * Session for controlling the status of TelephonyPreferenceController(s).
- *
- * Within this session, result of {@link BasePreferenceController#availabilityStatus()}
- * would be under control.
- */
-public class TelephonyStatusControlSession implements AutoCloseable {
-
-    private static final String LOG_TAG = "TelephonyStatusControlSS";
-
-    private Collection<AbstractPreferenceController> mControllers;
-    private Collection<Future<Boolean>> mResult = new ArrayList<>();
-
-    /**
-     * Buider of session
-     */
-    public static class Builder {
-        private Collection<AbstractPreferenceController> mControllers;
-
-        /**
-         * Constructor
-         *
-         * @param controllers is a collection of {@link AbstractPreferenceController}
-         *        which would have {@link BasePreferenceController#availabilityStatus()}
-         *        under control within this session.
-         */
-        public Builder(Collection<AbstractPreferenceController> controllers) {
-            mControllers = controllers;
-        }
-
-        /**
-         * Method to build this session.
-         * @return {@link TelephonyStatusControlSession} session been setup.
-         */
-        public TelephonyStatusControlSession build() {
-            return new TelephonyStatusControlSession(mControllers);
-        }
-    }
-
-    private TelephonyStatusControlSession(Collection<AbstractPreferenceController> controllers) {
-        mControllers = controllers;
-        controllers.forEach(prefCtrl -> mResult
-                .add(ThreadUtils.postOnBackgroundThread(() -> setupAvailabilityStatus(prefCtrl))));
-
-    }
-
-    /**
-     * Close the session.
-     *
-     * No longer control the status.
-     */
-    public void close() {
-        //check the background thread is finished then unset the status of availability.
-
-        for (Future<Boolean> result : mResult) {
-            try {
-                result.get();
-            } catch (ExecutionException | InterruptedException exception) {
-                Log.e(LOG_TAG, "setup availability status failed!", exception);
-            }
-        }
-        unsetAvailabilityStatus(mControllers);
-    }
-
-    private Boolean setupAvailabilityStatus(AbstractPreferenceController controller) {
-        try {
-            if (controller instanceof TelephonyAvailabilityHandler) {
-                int status = ((BasePreferenceController) controller)
-                        .getAvailabilityStatus();
-                ((TelephonyAvailabilityHandler) controller).setAvailabilityStatus(status);
-            }
-            return true;
-        } catch (Exception exception) {
-            Log.e(LOG_TAG, "Setup availability status failed!", exception);
-            return false;
-        }
-    }
-
-    private void unsetAvailabilityStatus(
-            Collection<AbstractPreferenceController> controllerLists) {
-        controllerLists.stream()
-                .filter(controller -> controller instanceof TelephonyAvailabilityHandler)
-                .map(TelephonyAvailabilityHandler.class::cast)
-                .forEach(controller -> {
-                    controller.unsetAvailabilityStatus();
-                });
-    }
-}
diff --git a/src/com/android/settings/network/telephony/TelephonyStatusControlSession.kt b/src/com/android/settings/network/telephony/TelephonyStatusControlSession.kt
new file mode 100644
index 0000000..0e63c8c
--- /dev/null
+++ b/src/com/android/settings/network/telephony/TelephonyStatusControlSession.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.network.telephony
+
+import android.util.Log
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.coroutineScope
+import com.android.settings.core.BasePreferenceController
+import com.android.settingslib.core.AbstractPreferenceController
+import com.google.common.collect.Sets
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.yield
+
+/**
+ * Session for controlling the status of TelephonyPreferenceController(s).
+ *
+ * Within this session, result of [BasePreferenceController.getAvailabilityStatus]
+ * would be under control.
+ */
+class TelephonyStatusControlSession(
+    private val controllers: Collection<AbstractPreferenceController>,
+    lifecycle: Lifecycle,
+) : AutoCloseable {
+    private var job: Job? = null
+    private val controllerSet = Sets.newConcurrentHashSet<TelephonyAvailabilityHandler>()
+
+    init {
+        job = lifecycle.coroutineScope.launch(Dispatchers.Default) {
+            for (controller in controllers) {
+                launch {
+                    setupAvailabilityStatus(controller)
+                }
+            }
+        }
+    }
+
+    /**
+     * Close the session.
+     *
+     * No longer control the status.
+     */
+    override fun close() {
+        job?.cancel()
+        unsetAvailabilityStatus()
+    }
+
+    private suspend fun setupAvailabilityStatus(controller: AbstractPreferenceController): Boolean =
+        try {
+            if (controller is TelephonyAvailabilityHandler) {
+                val status = (controller as BasePreferenceController).availabilityStatus
+                yield() // prompt cancellation guarantee
+                if (controllerSet.add(controller)) {
+                    controller.setAvailabilityStatus(status)
+                }
+            }
+            true
+        } catch (exception: Exception) {
+            Log.e(LOG_TAG, "Setup availability status failed!", exception)
+            false
+        }
+
+    private fun unsetAvailabilityStatus() {
+        for (controller in controllerSet) {
+            controller.unsetAvailabilityStatus()
+        }
+    }
+
+    companion object {
+        private const val LOG_TAG = "TelephonyStatusControlSS"
+    }
+}
diff --git a/src/com/android/settings/notification/app/ConversationListPreferenceController.java b/src/com/android/settings/notification/app/ConversationListPreferenceController.java
index f893df3..6703e4e 100644
--- a/src/com/android/settings/notification/app/ConversationListPreferenceController.java
+++ b/src/com/android/settings/notification/app/ConversationListPreferenceController.java
@@ -23,6 +23,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.notification.ConversationChannelWrapper;
+import android.text.BidiFormatter;
 import android.text.TextUtils;
 
 import androidx.annotation.VisibleForTesting;
@@ -132,7 +133,7 @@
     CharSequence getTitle(ConversationChannelWrapper conversation) {
         ShortcutInfo si = conversation.getShortcutInfo();
         return si != null
-                ? si.getLabel()
+                ? BidiFormatter.getInstance().unicodeWrap(si.getLabel())
                 : conversation.getNotificationChannel().getName();
     }
 
diff --git a/src/com/android/settings/overlay/FeatureFactory.java b/src/com/android/settings/overlay/FeatureFactory.java
index c536a38..249caa1 100644
--- a/src/com/android/settings/overlay/FeatureFactory.java
+++ b/src/com/android/settings/overlay/FeatureFactory.java
@@ -40,6 +40,7 @@
 import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
 import com.android.settings.gestures.AssistGestureFeatureProvider;
 import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
+import com.android.settings.inputmethod.KeyboardSettingsFeatureProvider;
 import com.android.settings.localepicker.LocaleFeatureProvider;
 import com.android.settings.panel.PanelFeatureProvider;
 import com.android.settings.search.SearchFeatureProvider;
@@ -130,8 +131,7 @@
     /**
      * Gets implementation for Battery Settings provider.
      */
-    public abstract BatterySettingsFeatureProvider getBatterySettingsFeatureProvider(
-            Context context);
+    public abstract BatterySettingsFeatureProvider getBatterySettingsFeatureProvider();
 
     public abstract DashboardFeatureProvider getDashboardFeatureProvider(Context context);
 
@@ -204,6 +204,11 @@
      */
     public abstract WifiFeatureProvider getWifiFeatureProvider();
 
+    /**
+     * Retrieves implementation for keyboard settings feature.
+     */
+    public abstract KeyboardSettingsFeatureProvider getKeyboardSettingsFeatureProvider();
+
     public static final class FactoryNotFoundException extends RuntimeException {
         public FactoryNotFoundException(Throwable throwable) {
             super("Unable to create factory. Did you misconfigure Proguard?", throwable);
diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.java b/src/com/android/settings/overlay/FeatureFactoryImpl.java
index 3ddda47..60adf95 100644
--- a/src/com/android/settings/overlay/FeatureFactoryImpl.java
+++ b/src/com/android/settings/overlay/FeatureFactoryImpl.java
@@ -61,6 +61,8 @@
 import com.android.settings.gestures.AssistGestureFeatureProviderImpl;
 import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
 import com.android.settings.homepage.contextualcards.ContextualCardFeatureProviderImpl;
+import com.android.settings.inputmethod.KeyboardSettingsFeatureProvider;
+import com.android.settings.inputmethod.KeyboardSettingsFeatureProviderImpl;
 import com.android.settings.localepicker.LocaleFeatureProvider;
 import com.android.settings.localepicker.LocaleFeatureProviderImpl;
 import com.android.settings.panel.PanelFeatureProvider;
@@ -116,6 +118,7 @@
     private AccessibilityMetricsFeatureProvider mAccessibilityMetricsFeatureProvider;
     private AdvancedVpnFeatureProvider mAdvancedVpnFeatureProvider;
     private WifiFeatureProvider mWifiFeatureProvider;
+    private KeyboardSettingsFeatureProvider mKeyboardSettingsFeatureProvider;
 
     @Override
     public HardwareInfoFeatureProvider getHardwareInfoFeatureProvider() {
@@ -154,9 +157,9 @@
     }
 
     @Override
-    public BatterySettingsFeatureProvider getBatterySettingsFeatureProvider(Context context) {
+    public BatterySettingsFeatureProvider getBatterySettingsFeatureProvider() {
         if (mBatterySettingsFeatureProvider == null) {
-            mBatterySettingsFeatureProvider = new BatterySettingsFeatureProviderImpl(context);
+            mBatterySettingsFeatureProvider = new BatterySettingsFeatureProviderImpl();
         }
         return mBatterySettingsFeatureProvider;
     }
@@ -372,4 +375,12 @@
         }
         return mWifiFeatureProvider;
     }
+
+    @Override
+    public KeyboardSettingsFeatureProvider getKeyboardSettingsFeatureProvider() {
+        if (mKeyboardSettingsFeatureProvider == null) {
+            mKeyboardSettingsFeatureProvider = new KeyboardSettingsFeatureProviderImpl();
+        }
+        return mKeyboardSettingsFeatureProvider;
+    }
 }
diff --git a/src/com/android/settings/password/ChooseLockPattern.java b/src/com/android/settings/password/ChooseLockPattern.java
index dc36220..a2fd986 100644
--- a/src/com/android/settings/password/ChooseLockPattern.java
+++ b/src/com/android/settings/password/ChooseLockPattern.java
@@ -442,7 +442,8 @@
         protected boolean mForFace;
         protected boolean mForBiometrics;
 
-        private static final String KEY_UI_STAGE = "uiStage";
+        @VisibleForTesting
+        static final String KEY_UI_STAGE = "uiStage";
         private static final String KEY_PATTERN_CHOICE = "chosenPattern";
         private static final String KEY_CURRENT_PATTERN = "currentPattern";
 
@@ -718,10 +719,6 @@
             final GlifLayout layout = getActivity().findViewById(R.id.setup_wizard_layout);
             mUiStage = stage;
 
-            if (stage == Stage.Introduction) {
-                layout.setDescriptionText(stage.headerMessage);
-            }
-
             // header text, footer text, visibility and
             // enabled state all known from the stage
             if (stage == Stage.ChoiceTooShort) {
@@ -744,16 +741,13 @@
                 Theme theme = getActivity().getTheme();
                 theme.resolveAttribute(R.attr.colorError, typedValue, true);
                 mHeaderText.setTextColor(typedValue.data);
+            } else if (mDefaultHeaderColorList != null) {
+                mHeaderText.setTextColor(mDefaultHeaderColorList);
+            }
 
-            } else {
-                if (mDefaultHeaderColorList != null) {
-                    mHeaderText.setTextColor(mDefaultHeaderColorList);
-                }
 
-                if (stage == Stage.NeedToConfirm) {
-                    mHeaderText.setText(stage.headerMessage);
-                    layout.setHeaderText(R.string.lockpassword_draw_your_pattern_again_header);
-                }
+            if (stage == Stage.ConfirmWrong || stage == Stage.NeedToConfirm) {
+                layout.setHeaderText(R.string.lockpassword_draw_your_pattern_again_header);
             }
 
             updateFooterLeftButton(stage);
diff --git a/src/com/android/settings/password/ChooseLockSettingsHelper.java b/src/com/android/settings/password/ChooseLockSettingsHelper.java
index 216f7db..9533314 100644
--- a/src/com/android/settings/password/ChooseLockSettingsHelper.java
+++ b/src/com/android/settings/password/ChooseLockSettingsHelper.java
@@ -71,6 +71,8 @@
     // Gatekeeper password handle, which can subsequently be used to generate Gatekeeper
     // HardwareAuthToken(s) via LockSettingsService#verifyGatekeeperPasswordHandle
     public static final String EXTRA_KEY_GK_PW_HANDLE = "gk_pw_handle";
+    public static final String EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW =
+            "request_write_repair_mode_pw";
 
     /**
      * When EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL and EXTRA_KEY_UNIFICATION_PROFILE_ID are
@@ -152,6 +154,7 @@
         @Nullable private RemoteLockscreenValidationSession mRemoteLockscreenValidationSession;
         @Nullable private ComponentName mRemoteLockscreenValidationServiceComponent;
         private boolean mRequestGatekeeperPasswordHandle;
+        private boolean mRequestWriteRepairModePassword;
         private boolean mTaskOverlay;
 
         public Builder(@NonNull Activity activity) {
@@ -336,6 +339,17 @@
         }
 
         /**
+         * @param requestWriteRepairModePassword Set {@code true} to request that
+         * LockSettingsService writes the password data to the repair mode file after the user
+         * credential is verified successfully.
+         */
+        @NonNull public Builder setRequestWriteRepairModePassword(
+                boolean requestWriteRepairModePassword) {
+            mRequestWriteRepairModePassword = requestWriteRepairModePassword;
+            return this;
+        }
+
+        /**
          * Support of ActivityResultLauncher.
          *
          * Which allowing the launch operation be controlled externally.
@@ -348,7 +362,8 @@
         }
 
         @NonNull public ChooseLockSettingsHelper build() {
-            if (!mAllowAnyUserId && mUserId != LockPatternUtils.USER_FRP) {
+            if (!mAllowAnyUserId && mUserId != LockPatternUtils.USER_FRP
+                    && mUserId != LockPatternUtils.USER_REPAIR_MODE) {
                 Utils.enforceSameOwner(mActivity, mUserId);
             }
 
@@ -385,7 +400,7 @@
                 mBuilder.mRemoteLockscreenValidationSession,
                 mBuilder.mRemoteLockscreenValidationServiceComponent, mBuilder.mAllowAnyUserId,
                 mBuilder.mForegroundOnly, mBuilder.mRequestGatekeeperPasswordHandle,
-                mBuilder.mTaskOverlay);
+                mBuilder.mRequestWriteRepairModePassword, mBuilder.mTaskOverlay);
     }
 
     private boolean launchConfirmationActivity(int request, @Nullable CharSequence title,
@@ -396,7 +411,7 @@
             @Nullable RemoteLockscreenValidationSession remoteLockscreenValidationSession,
             @Nullable ComponentName remoteLockscreenValidationServiceComponent,
             boolean allowAnyUser, boolean foregroundOnly, boolean requestGatekeeperPasswordHandle,
-            boolean taskOverlay) {
+            boolean requestWriteRepairModePassword, boolean taskOverlay) {
         Optional<Class<?>> activityClass = determineAppropriateActivityClass(
                 returnCredentials, forceVerifyPath, userId, remoteLockscreenValidationSession);
         if (activityClass.isEmpty()) {
@@ -407,7 +422,7 @@
                 returnCredentials, external, forceVerifyPath, userId, alternateButton,
                 checkboxLabel, remoteLockscreenValidation, remoteLockscreenValidationSession,
                 remoteLockscreenValidationServiceComponent, allowAnyUser, foregroundOnly,
-                requestGatekeeperPasswordHandle, taskOverlay);
+                requestGatekeeperPasswordHandle, requestWriteRepairModePassword, taskOverlay);
     }
 
     private boolean launchConfirmationActivity(int request, CharSequence title, CharSequence header,
@@ -418,7 +433,7 @@
             @Nullable RemoteLockscreenValidationSession remoteLockscreenValidationSession,
             @Nullable ComponentName remoteLockscreenValidationServiceComponent,
             boolean allowAnyUser, boolean foregroundOnly, boolean requestGatekeeperPasswordHandle,
-            boolean taskOverlay) {
+            boolean requestWriteRepairModePassword, boolean taskOverlay) {
         final Intent intent = new Intent();
         intent.putExtra(ConfirmDeviceCredentialBaseFragment.TITLE_TEXT, title);
         intent.putExtra(ConfirmDeviceCredentialBaseFragment.HEADER_TEXT, header);
@@ -442,6 +457,8 @@
         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_ALLOW_ANY_USER, allowAnyUser);
         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE,
                 requestGatekeeperPasswordHandle);
+        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW,
+                requestWriteRepairModePassword);
 
         intent.setClassName(SETTINGS_PACKAGE_NAME, activityClass.getName());
         intent.putExtra(SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE,
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
index fabca6b..f2f6520 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
@@ -23,6 +23,7 @@
 import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_PASSWORD;
 import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_PATTERN;
 import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_PIN;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
 
 import android.app.Activity;
 import android.app.KeyguardManager;
@@ -32,6 +33,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Configuration;
 import android.graphics.Color;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricPrompt;
@@ -166,11 +168,18 @@
         mDetails = intent.getCharSequenceExtra(KeyguardManager.EXTRA_DESCRIPTION);
         String alternateButton = intent.getStringExtra(
                 KeyguardManager.EXTRA_ALTERNATE_BUTTON_LABEL);
-        boolean frp = KeyguardManager.ACTION_CONFIRM_FRP_CREDENTIAL.equals(intent.getAction());
-        boolean remoteValidation =
+        final boolean frp =
+                KeyguardManager.ACTION_CONFIRM_FRP_CREDENTIAL.equals(intent.getAction());
+        final boolean repairMode =
+                KeyguardManager.ACTION_CONFIRM_REPAIR_MODE_DEVICE_CREDENTIAL
+                        .equals(intent.getAction());
+        final boolean remoteValidation =
                 KeyguardManager.ACTION_CONFIRM_REMOTE_DEVICE_CREDENTIAL.equals(intent.getAction());
         mTaskOverlay = isInternalActivity()
                 && intent.getBooleanExtra(KeyguardManager.EXTRA_FORCE_TASK_OVERLAY, false);
+        final boolean prepareRepairMode =
+                KeyguardManager.ACTION_PREPARE_REPAIR_MODE_DEVICE_CREDENTIAL.equals(
+                        intent.getAction());
 
         mUserId = UserHandle.myUserId();
         if (isInternalActivity()) {
@@ -219,6 +228,14 @@
                     .setExternal(true)
                     .setUserId(LockPatternUtils.USER_FRP)
                     .show();
+        } else if (repairMode) {
+            final ChooseLockSettingsHelper.Builder builder =
+                    new ChooseLockSettingsHelper.Builder(this);
+            launchedCDC = builder.setHeader(mTitle)
+                    .setDescription(mDetails)
+                    .setExternal(true)
+                    .setUserId(LockPatternUtils.USER_REPAIR_MODE)
+                    .show();
         } else if (remoteValidation) {
             RemoteLockscreenValidationSession remoteLockscreenValidationSession =
                     intent.getParcelableExtra(
@@ -244,6 +261,17 @@
                     .setExternal(true)
                     .show();
             return;
+        } else if (prepareRepairMode) {
+            final ChooseLockSettingsHelper.Builder builder =
+                    new ChooseLockSettingsHelper.Builder(this);
+            launchedCDC = builder.setHeader(mTitle)
+                    .setDescription(mDetails)
+                    .setExternal(true)
+                    .setUserId(mUserId)
+                    .setTaskOverlay(mTaskOverlay)
+                    .setRequestWriteRepairModePassword(true)
+                    .setForceVerifyPath(true)
+                    .show();
         } else if (isEffectiveUserManagedProfile && isInternalActivity()) {
             mCredentialMode = CREDENTIAL_MANAGED;
             if (isBiometricAllowed(effectiveUserId, mUserId)) {
@@ -353,6 +381,12 @@
         // Translucent activity that is "visible", so it doesn't complain about finish()
         // not being called before onResume().
         setVisible(true);
+
+        if ((getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
+                != Configuration.UI_MODE_NIGHT_YES) {
+            getWindow().getInsetsController().setSystemBarsAppearance(
+                    APPEARANCE_LIGHT_STATUS_BARS, APPEARANCE_LIGHT_STATUS_BARS);
+        }
     }
 
     @Override
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java b/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java
index f4cfabc..43d8440 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java
@@ -105,6 +105,8 @@
     protected final Handler mHandler = new Handler();
     protected boolean mFrp;
     protected boolean mRemoteValidation;
+    protected boolean mRequestWriteRepairModePassword;
+    protected boolean mRepairMode;
     protected CharSequence mAlternateButtonText;
     protected BiometricManager mBiometricManager;
     @Nullable protected RemoteLockscreenValidationSession mRemoteLockscreenValidationSession;
@@ -130,6 +132,8 @@
                 ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, false);
         mForceVerifyPath = intent.getBooleanExtra(
                 ChooseLockSettingsHelper.EXTRA_KEY_FORCE_VERIFY, false);
+        mRequestWriteRepairModePassword = intent.getBooleanExtra(
+                ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW, false);
 
         if (intent.getBooleanExtra(IS_REMOTE_LOCKSCREEN_VALIDATION, false)) {
             if (FeatureFlagUtils.isEnabled(getContext(),
@@ -178,6 +182,7 @@
         mUserId = Utils.getUserIdFromBundle(getActivity(), intent.getExtras(),
                 isInternalActivity());
         mFrp = (mUserId == LockPatternUtils.USER_FRP);
+        mRepairMode = (mUserId == LockPatternUtils.USER_REPAIR_MODE);
         mUserManager = UserManager.get(getActivity());
         mEffectiveUserId = mUserManager.getCredentialOwnerProfile(mUserId);
         mLockPatternUtils = new LockPatternUtils(getActivity());
@@ -266,7 +271,7 @@
     // verifyTiedProfileChallenge. In such case, we also wanna show the user message that
     // fingerprint is disabled due to device restart.
     protected boolean isStrongAuthRequired() {
-        return mFrp
+        return mFrp || mRepairMode
                 || !mLockPatternUtils.isBiometricAllowedForUser(mEffectiveUserId)
                 || !mUserManager.isUserUnlocked(mUserId);
     }
diff --git a/src/com/android/settings/password/ConfirmLockPassword.java b/src/com/android/settings/password/ConfirmLockPassword.java
index 03b89f2..c6022b5 100644
--- a/src/com/android/settings/password/ConfirmLockPassword.java
+++ b/src/com/android/settings/password/ConfirmLockPassword.java
@@ -284,6 +284,11 @@
                 return mIsAlpha ? getString(R.string.lockpassword_confirm_your_password_header_frp)
                         : getString(R.string.lockpassword_confirm_your_pin_header_frp);
             }
+            if (mRepairMode) {
+                return mIsAlpha
+                        ? getString(R.string.lockpassword_confirm_repair_mode_password_header)
+                        : getString(R.string.lockpassword_confirm_repair_mode_pin_header);
+            }
             if (mRemoteValidation) {
                 return getString(R.string.lockpassword_remote_validation_header);
             }
@@ -307,6 +312,11 @@
                 return mIsAlpha ? getString(R.string.lockpassword_confirm_your_password_details_frp)
                         : getString(R.string.lockpassword_confirm_your_pin_details_frp);
             }
+            if (mRepairMode) {
+                return mIsAlpha
+                        ? getString(R.string.lockpassword_confirm_repair_mode_password_details)
+                        : getString(R.string.lockpassword_confirm_repair_mode_pin_details);
+            }
             if (mRemoteValidation) {
                 return getContext().getString(mIsAlpha
                         ? R.string.lockpassword_remote_validation_password_details
@@ -496,7 +506,9 @@
                 }
             } else if (mForceVerifyPath)  {
                 if (isInternalActivity()) {
-                    startVerifyPassword(credential, intent, 0 /* flags */);
+                    final int flags = mRequestWriteRepairModePassword
+                            ? LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW : 0;
+                    startVerifyPassword(credential, intent, flags);
                     return;
                 }
             } else {
diff --git a/src/com/android/settings/password/ConfirmLockPattern.java b/src/com/android/settings/password/ConfirmLockPattern.java
index e99a986..a2bcb5a 100644
--- a/src/com/android/settings/password/ConfirmLockPattern.java
+++ b/src/com/android/settings/password/ConfirmLockPattern.java
@@ -179,7 +179,7 @@
                 //              ability to disable the pattern in L. Remove this block after
                 //              ensuring it's safe to do so. (Note that ConfirmLockPassword
                 //              doesn't have this).
-                if (!mFrp && !mRemoteValidation
+                if (!mFrp && !mRemoteValidation && !mRepairMode
                         && !mLockPatternUtils.isLockPatternEnabled(mEffectiveUserId)) {
                     getActivity().setResult(Activity.RESULT_OK);
                     getActivity().finish();
@@ -308,6 +308,9 @@
             if (mFrp) {
                 return getString(R.string.lockpassword_confirm_your_pattern_details_frp);
             }
+            if (mRepairMode) {
+                return getString(R.string.lockpassword_confirm_repair_mode_pattern_details);
+            }
             if (mRemoteValidation) {
                 return getString(
                         R.string.lockpassword_remote_validation_pattern_details);
@@ -402,7 +405,12 @@
         }
 
         private String getDefaultHeader() {
-            if (mFrp) return getString(R.string.lockpassword_confirm_your_pattern_header_frp);
+            if (mFrp) {
+                return getString(R.string.lockpassword_confirm_your_pattern_header_frp);
+            }
+            if (mRepairMode) {
+                return getString(R.string.lockpassword_confirm_repair_mode_pattern_header);
+            }
             if (mRemoteValidation) {
                 return getString(R.string.lockpassword_remote_validation_header);
             }
@@ -512,7 +520,9 @@
                     }
                 } else if (mForceVerifyPath) {
                     if (isInternalActivity()) {
-                        startVerifyPattern(credential, intent, 0 /* flags */);
+                        final int flags = mRequestWriteRepairModePassword
+                                ? LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW : 0;
+                        startVerifyPattern(credential, intent, flags);
                         return;
                     }
                 } else {
diff --git a/src/com/android/settings/security/ScreenPinningSettings.java b/src/com/android/settings/security/ScreenPinningSettings.java
index e219b44..8fae6e1 100644
--- a/src/com/android/settings/security/ScreenPinningSettings.java
+++ b/src/com/android/settings/security/ScreenPinningSettings.java
@@ -23,7 +23,6 @@
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.provider.SearchIndexableResource;
 import android.provider.Settings;
 import android.widget.Switch;
 
@@ -38,14 +37,12 @@
 import com.android.settings.SettingsActivity;
 import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.password.ChooseLockGeneric;
+import com.android.settings.password.ChooseLockSettingsHelper;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.widget.SettingsMainSwitchBar;
 import com.android.settingslib.search.SearchIndexable;
 import com.android.settingslib.widget.FooterPreference;
 import com.android.settingslib.widget.OnMainSwitchChangeListener;
-
-import java.util.Arrays;
-import java.util.List;
 /**
  * Screen pinning settings.
  */
@@ -56,6 +53,7 @@
     private static final String KEY_USE_SCREEN_LOCK = "use_screen_lock";
     private static final String KEY_FOOTER = "screen_pinning_settings_screen_footer";
     private static final int CHANGE_LOCK_METHOD_REQUEST = 43;
+    private static final int CONFIRM_REQUEST = 1000;
 
     private SettingsMainSwitchBar mSwitchBar;
     private SwitchPreference mUseScreenLock;
@@ -129,10 +127,10 @@
     }
 
     private boolean setScreenLockUsed(boolean isEnabled) {
+        LockPatternUtils lockPatternUtils = new LockPatternUtils(getActivity());
+        final int passwordQuality = lockPatternUtils
+                .getKeyguardStoredPasswordQuality(UserHandle.myUserId());
         if (isEnabled) {
-            LockPatternUtils lockPatternUtils = new LockPatternUtils(getActivity());
-            int passwordQuality = lockPatternUtils
-                    .getKeyguardStoredPasswordQuality(UserHandle.myUserId());
             if (passwordQuality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
                 Intent chooseLockIntent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD);
                 chooseLockIntent.putExtra(
@@ -141,6 +139,12 @@
                 startActivityForResult(chooseLockIntent, CHANGE_LOCK_METHOD_REQUEST);
                 return false;
             }
+        }  else {
+            if (passwordQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
+                final ChooseLockSettingsHelper.Builder builder =
+                        new ChooseLockSettingsHelper.Builder(getActivity(), this);
+                return builder.setRequestCode(CONFIRM_REQUEST).show();
+            }
         }
         setScreenLockUsedSetting(isEnabled);
         return true;
@@ -162,6 +166,8 @@
             setScreenLockUsed(validPassQuality);
             // Make sure the screen updates.
             mUseScreenLock.setChecked(validPassQuality);
+        } else if (requestCode == CONFIRM_REQUEST) {
+            setScreenLockUsedSetting(false);
         }
     }
 
@@ -245,14 +251,5 @@
      * For search
      */
     public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
-            new BaseSearchIndexProvider() {
-
-                @Override
-                public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
-                        boolean enabled) {
-                    final SearchIndexableResource sir = new SearchIndexableResource(context);
-                    sir.xmlResId = R.xml.screen_pinning_settings;
-                    return Arrays.asList(sir);
-                }
-            };
+            new BaseSearchIndexProvider(R.xml.screen_pinning_settings);
 }
diff --git a/src/com/android/settings/slices/RestrictedSliceUtils.java b/src/com/android/settings/slices/RestrictedSliceUtils.java
new file mode 100644
index 0000000..a5b5a14
--- /dev/null
+++ b/src/com/android/settings/slices/RestrictedSliceUtils.java
@@ -0,0 +1,81 @@
+/*
+ * 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.slices;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.provider.SettingsSlicesContract;
+
+/**
+ * A utility class to check slice Uris for restriction.
+ */
+public class RestrictedSliceUtils {
+
+    /**
+     * Uri for the notifying open networks Slice.
+     */
+    private static final Uri NOTIFY_OPEN_NETWORKS_SLICE_URI = new Uri.Builder()
+        .scheme(ContentResolver.SCHEME_CONTENT)
+        .authority(SettingsSliceProvider.SLICE_AUTHORITY)
+        .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
+        .appendPath("notify_open_networks")
+        .build();
+
+    /**
+     * Uri for the auto turning on Wi-Fi Slice.
+     */
+    private static final Uri AUTO_TURN_ON_WIFI_SLICE_URI = new Uri.Builder()
+        .scheme(ContentResolver.SCHEME_CONTENT)
+        .authority(SettingsSliceProvider.SLICE_AUTHORITY)
+        .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
+        .appendPath("enable_wifi_wakeup")
+        .build();
+
+    /**
+     * Uri for the usb tethering Slice.
+     */
+    private static final Uri USB_TETHERING_SLICE_URI = new Uri.Builder()
+        .scheme(ContentResolver.SCHEME_CONTENT)
+        .authority(SettingsSliceProvider.SLICE_AUTHORITY)
+        .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
+        .appendPath("enable_usb_tethering")
+        .build();
+
+    /**
+     * Uri for the bluetooth tethering Slice.
+     */
+    private static final Uri BLUETOOTH_TETHERING_SLICE_URI = new Uri.Builder()
+        .scheme(ContentResolver.SCHEME_CONTENT)
+        .authority(SettingsSliceProvider.SLICE_AUTHORITY)
+        .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
+        .appendPath("enable_bluetooth_tethering_2")
+        .build();
+
+    /**
+     * Returns true if the slice Uri restricts access to guest user.
+     */
+    public static boolean isGuestRestricted(Uri sliceUri) {
+        if (AUTO_TURN_ON_WIFI_SLICE_URI.equals(sliceUri)
+            || NOTIFY_OPEN_NETWORKS_SLICE_URI.equals(sliceUri)
+            || BLUETOOTH_TETHERING_SLICE_URI.equals(sliceUri)
+            || USB_TETHERING_SLICE_URI.equals(sliceUri)
+            || CustomSliceRegistry.MOBILE_DATA_SLICE_URI.equals(sliceUri)) {
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java
index 12272a7..5d2bde3 100644
--- a/src/com/android/settings/slices/SettingsSliceProvider.java
+++ b/src/com/android/settings/slices/SettingsSliceProvider.java
@@ -30,6 +30,7 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.StrictMode;
+import android.os.UserManager;
 import android.provider.Settings;
 import android.provider.SettingsSlicesContract;
 import android.text.TextUtils;
@@ -233,6 +234,14 @@
                 getContext().getTheme().rebase();
             }
 
+            // Checking if some semi-sensitive slices are requested by a guest user. If so, will
+            // return an empty slice.
+            final UserManager userManager = getContext().getSystemService(UserManager.class);
+            if (userManager.isGuestUser() && RestrictedSliceUtils.isGuestRestricted(sliceUri)) {
+                Log.i(TAG, "Guest user access denied.");
+                return null;
+            }
+
             // Before adding a slice to {@link CustomSliceManager}, please get approval
             // from the Settings team.
             if (CustomSliceRegistry.isValidUri(sliceUri)) {
diff --git a/src/com/android/settings/spa/SettingsSpaEnvironment.kt b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
index 455fe9f..b506005 100644
--- a/src/com/android/settings/spa/SettingsSpaEnvironment.kt
+++ b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
@@ -29,12 +29,14 @@
 import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider
 import com.android.settings.spa.app.specialaccess.MediaManagementAppsAppListProvider
 import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider
+import com.android.settings.spa.app.specialaccess.NfcTagAppsSettingsProvider
 import com.android.settings.spa.app.specialaccess.PictureInPictureListProvider
 import com.android.settings.spa.app.specialaccess.SpecialAppAccessPageProvider
 import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider
 import com.android.settings.spa.app.specialaccess.UseFullScreenIntentAppListProvider
 import com.android.settings.spa.core.instrumentation.SpaLogProvider
 import com.android.settings.spa.development.UsageStatsPageProvider
+import com.android.settings.spa.development.compat.PlatformCompatAppListPageProvider
 import com.android.settings.spa.home.HomePageProvider
 import com.android.settings.spa.network.NetworkAndInternetPageProvider
 import com.android.settings.spa.notification.AppListNotificationsPageProvider
@@ -61,6 +63,7 @@
             InstallUnknownAppsListProvider,
             AlarmsAndRemindersAppListProvider,
             WifiControlAppListProvider,
+            NfcTagAppsSettingsProvider,
         )
     }
 
@@ -81,6 +84,7 @@
                 LanguageAndInputPageProvider,
                 AppLanguagesPageProvider,
                 UsageStatsPageProvider,
+                PlatformCompatAppListPageProvider,
                 BackgroundInstalledAppsPageProvider,
                 CloneAppInfoSettingsProvider,
                 NetworkAndInternetPageProvider,
@@ -93,5 +97,5 @@
     override val logger =
         if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_ENABLE_SPA_METRICS))
             SpaLogProvider
-        else object: SpaLogger {}
+        else object : SpaLogger {}
 }
diff --git a/src/com/android/settings/spa/app/specialaccess/NfcTagAppsSettings.kt b/src/com/android/settings/spa/app/specialaccess/NfcTagAppsSettings.kt
new file mode 100644
index 0000000..3dede42
--- /dev/null
+++ b/src/com/android/settings/spa/app/specialaccess/NfcTagAppsSettings.kt
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.specialaccess
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager.GET_ACTIVITIES
+import android.content.pm.PackageManager.PackageInfoFlags
+import android.nfc.NfcAdapter
+import android.util.Log
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.livedata.observeAsState
+import com.android.settings.R
+import com.android.settingslib.spaprivileged.model.app.AppRecord
+import com.android.settingslib.spaprivileged.model.app.userId
+import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListModel
+import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+
+object NfcTagAppsSettingsProvider : TogglePermissionAppListProvider {
+    override val permissionType = "NfcTagAppsSettings"
+    override fun createModel(context: Context) = NfcTagAppsSettingsListModel(context)
+}
+
+data class NfcTagAppsSettingsRecord(
+    override val app: ApplicationInfo,
+    val controller: NfcTagAppsSettingsController,
+    val isSupported: Boolean,
+) : AppRecord
+
+class NfcTagAppsSettingsListModel(private val context: Context) :
+    TogglePermissionAppListModel<NfcTagAppsSettingsRecord> {
+    override val pageTitleResId = R.string.change_nfc_tag_apps_title
+    override val switchTitleResId = R.string.change_nfc_tag_apps_detail_switch
+    override val footerResId = R.string.change_nfc_tag_apps_detail_summary
+
+    private val packageManager = context.packageManager
+
+    override fun transform(
+        userIdFlow: Flow<Int>,
+        appListFlow: Flow<List<ApplicationInfo>>
+    ): Flow<List<NfcTagAppsSettingsRecord>> =
+        userIdFlow.combine(appListFlow) { userId, appList ->
+            // The appListFlow always refreshed on resume, need to update nfcTagAppsSettingsPackages
+            // here to handle status change.
+            val nfcTagAppsSettingsPackages = getNfcTagAppsSettingsPackages(userId)
+            appList.map { app ->
+                createNfcTagAppsSettingsRecord(
+                    app = app,
+                    isAllowed = nfcTagAppsSettingsPackages[app.packageName],
+                )
+            }
+        }
+
+    private fun getNfcTagAppsSettingsPackages(userId: Int): Map<String, Boolean> {
+        NfcAdapter.getDefaultAdapter(context)?.let { nfcAdapter ->
+            if (nfcAdapter.isTagIntentAppPreferenceSupported) {
+                return nfcAdapter.getTagIntentAppPreferenceForUser(userId)
+            }
+        }
+        return emptyMap()
+    }
+
+    override fun transformItem(app: ApplicationInfo) =
+        createNfcTagAppsSettingsRecord(
+            app = app,
+            isAllowed = getNfcTagAppsSettingsPackages(app.userId)[app.packageName],
+        )
+
+    private fun createNfcTagAppsSettingsRecord(
+        app: ApplicationInfo,
+        isAllowed: Boolean?,
+    ) =
+        NfcTagAppsSettingsRecord(
+            app = app,
+            isSupported = isAllowed != null,
+            controller = NfcTagAppsSettingsController(isAllowed == true),
+        )
+
+    override fun filter(
+        userIdFlow: Flow<Int>,
+        recordListFlow: Flow<List<NfcTagAppsSettingsRecord>>
+    ) = recordListFlow.map { recordList -> recordList.filter { it.isSupported } }
+
+    @Composable
+    override fun isAllowed(record: NfcTagAppsSettingsRecord) =
+        record.controller.isAllowed.observeAsState()
+
+    override fun isChangeable(record: NfcTagAppsSettingsRecord) = true
+
+    override fun setAllowed(record: NfcTagAppsSettingsRecord, newAllowed: Boolean) {
+        NfcAdapter.getDefaultAdapter(context)?.let {
+            if (
+                it.setTagIntentAppPreferenceForUser(
+                    record.app.userId,
+                    record.app.packageName,
+                    newAllowed
+                ) == NfcAdapter.TAG_INTENT_APP_PREF_RESULT_SUCCESS
+            ) {
+                record.controller.setAllowed(newAllowed)
+            } else {
+                Log.e(TAG, "Error updating TagIntentAppPreference")
+            }
+        }
+    }
+
+    private companion object {
+        const val TAG = "NfcTagAppsSettingsListModel"
+        val GET_ACTIVITIES_FLAGS = PackageInfoFlags.of(GET_ACTIVITIES.toLong())
+    }
+}
diff --git a/src/com/android/settings/spa/app/specialaccess/NfcTagAppsSettingsController.kt b/src/com/android/settings/spa/app/specialaccess/NfcTagAppsSettingsController.kt
new file mode 100644
index 0000000..6e1b7b3
--- /dev/null
+++ b/src/com/android/settings/spa/app/specialaccess/NfcTagAppsSettingsController.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.specialaccess
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+
+class NfcTagAppsSettingsController(initialStatus: Boolean) {
+    val isAllowed: LiveData<Boolean>
+        get() = _allowed
+
+    fun setAllowed(newAllowed: Boolean) {
+        _allowed.postValue(newAllowed)
+    }
+    private val _allowed = MutableLiveData<Boolean>(initialStatus)
+}
diff --git a/src/com/android/settings/spa/development/UsageStats.kt b/src/com/android/settings/spa/development/UsageStats.kt
index b681d75..4d9c455 100644
--- a/src/com/android/settings/spa/development/UsageStats.kt
+++ b/src/com/android/settings/spa/development/UsageStats.kt
@@ -32,7 +32,6 @@
         AppListPage(
             title = stringResource(R.string.testing_usage_stats),
             listModel = rememberContext(::UsageStatsListModel),
-            primaryUserOnly = true,
         )
     }
 }
diff --git a/src/com/android/settings/spa/development/compat/PlatformCompatAppList.kt b/src/com/android/settings/spa/development/compat/PlatformCompatAppList.kt
new file mode 100644
index 0000000..5f3b4e7
--- /dev/null
+++ b/src/com/android/settings/spa/development/compat/PlatformCompatAppList.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.development.compat
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+import com.android.settings.R
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.compose.rememberContext
+import com.android.settingslib.spaprivileged.template.app.AppListPage
+
+object PlatformCompatAppListPageProvider : SettingsPageProvider {
+    override val name = "PlatformCompatAppList"
+
+    @Composable
+    override fun Page(arguments: Bundle?) {
+        AppListPage(
+            title = stringResource(R.string.platform_compat_dashboard_title),
+            listModel = rememberContext(::PlatformCompatAppListModel),
+            noItemMessage = stringResource(R.string.platform_compat_dialog_text_no_apps),
+        )
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/spa/development/compat/PlatformCompatAppListModel.kt b/src/com/android/settings/spa/development/compat/PlatformCompatAppListModel.kt
new file mode 100644
index 0000000..c6752b9
--- /dev/null
+++ b/src/com/android/settings/spa/development/compat/PlatformCompatAppListModel.kt
@@ -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.spa.development.compat
+
+import android.app.settings.SettingsEnums
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.os.Build
+import androidx.compose.runtime.Composable
+import androidx.core.os.bundleOf
+import com.android.settings.core.SubSettingLauncher
+import com.android.settings.development.compat.PlatformCompatDashboard
+import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.framework.util.filterItem
+import com.android.settingslib.spa.framework.util.mapItem
+import com.android.settingslib.spaprivileged.model.app.AppListModel
+import com.android.settingslib.spaprivileged.model.app.AppRecord
+import com.android.settingslib.spaprivileged.model.app.hasFlag
+import com.android.settingslib.spaprivileged.model.app.userHandle
+import com.android.settingslib.spaprivileged.template.app.AppListItem
+import com.android.settingslib.spaprivileged.template.app.AppListItemModel
+import kotlinx.coroutines.flow.Flow
+
+data class PlatformCompatAppRecord(
+    override val app: ApplicationInfo,
+) : AppRecord
+
+class PlatformCompatAppListModel(
+    private val context: Context,
+) : AppListModel<PlatformCompatAppRecord> {
+
+    override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) =
+        appListFlow.mapItem(::PlatformCompatAppRecord)
+
+    override fun filter(
+        userIdFlow: Flow<Int>, option: Int, recordListFlow: Flow<List<PlatformCompatAppRecord>>,
+    ) = recordListFlow.filterItem { record ->
+        Build.IS_DEBUGGABLE || record.app.hasFlag(ApplicationInfo.FLAG_DEBUGGABLE)
+    }
+
+    @Composable
+    override fun getSummary(option: Int, record: PlatformCompatAppRecord) =
+        stateOf(record.app.packageName)
+
+    @Composable
+    override fun AppListItemModel<PlatformCompatAppRecord>.AppItem() {
+        AppListItem { navigateToAppCompat(app = record.app) }
+    }
+
+    private fun navigateToAppCompat(app: ApplicationInfo) {
+        SubSettingLauncher(context)
+            .setDestination(PlatformCompatDashboard::class.qualifiedName)
+            .setSourceMetricsCategory(SettingsEnums.DEVELOPMENT)
+            .setArguments(bundleOf(PlatformCompatDashboard.COMPAT_APP to app.packageName))
+            .setUserHandle(app.userHandle)
+            .launch()
+    }
+}
diff --git a/src/com/android/settings/spa/development/compat/PlatformCompatPreferenceController.kt b/src/com/android/settings/spa/development/compat/PlatformCompatPreferenceController.kt
new file mode 100644
index 0000000..c0a421c
--- /dev/null
+++ b/src/com/android/settings/spa/development/compat/PlatformCompatPreferenceController.kt
@@ -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.spa.development.compat
+
+import android.content.Context
+import androidx.preference.Preference
+import com.android.settings.core.BasePreferenceController
+import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
+
+class PlatformCompatPreferenceController(context: Context, preferenceKey: String) :
+    BasePreferenceController(context, preferenceKey) {
+    override fun getAvailabilityStatus() = AVAILABLE
+
+    override fun handlePreferenceTreeClick(preference: Preference): Boolean {
+        if (preference.key == mPreferenceKey) {
+            mContext.startSpaActivity(PlatformCompatAppListPageProvider.name)
+            return true
+        }
+        return false
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/users/UserDetailsSettings.java b/src/com/android/settings/users/UserDetailsSettings.java
index 2f9031e..402d4b1 100644
--- a/src/com/android/settings/users/UserDetailsSettings.java
+++ b/src/com/android/settings/users/UserDetailsSettings.java
@@ -79,6 +79,7 @@
 
     /** Whether to enable the app_copying fragment. */
     private static final boolean SHOW_APP_COPYING_PREF = false;
+    private static final int MESSAGE_PADDING = 20;
 
     private UserManager mUserManager;
     private UserCapabilities mUserCaps;
@@ -274,6 +275,7 @@
                 context.getDrawable(com.android.settingslib.R.drawable.ic_admin_panel_settings));
         dialogHelper.setTitle(R.string.user_revoke_admin_confirm_title);
         dialogHelper.setMessage(R.string.user_revoke_admin_confirm_message);
+        dialogHelper.setMessagePadding(MESSAGE_PADDING);
         dialogHelper.setPositiveButton(R.string.remove, view -> {
             updateUserAdminStatus(false);
             dialogHelper.getDialog().dismiss();
@@ -294,6 +296,7 @@
                 context.getDrawable(com.android.settingslib.R.drawable.ic_admin_panel_settings));
         dialogHelper.setTitle(com.android.settingslib.R.string.user_grant_admin_title);
         dialogHelper.setMessage(com.android.settingslib.R.string.user_grant_admin_message);
+        dialogHelper.setMessagePadding(MESSAGE_PADDING);
         dialogHelper.setPositiveButton(com.android.settingslib.R.string.user_grant_admin_button,
                 view -> {
                     updateUserAdminStatus(true);
diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java
index 28e02ec..d9fbc42 100644
--- a/src/com/android/settings/users/UserSettings.java
+++ b/src/com/android/settings/users/UserSettings.java
@@ -885,7 +885,6 @@
                 this::startActivityForResult,
                 userIcon,
                 user.name,
-                getString(com.android.settingslib.R.string.profile_info_settings_title),
                 (newUserName, newUserIcon) -> {
                     if (newUserIcon != userIcon) {
                         ThreadUtils.postOnBackgroundThread(() ->
diff --git a/src/com/android/settings/wifi/WifiAPITest.java b/src/com/android/settings/wifi/WifiAPITest.java
index 15465ed..c8bcf7f 100644
--- a/src/com/android/settings/wifi/WifiAPITest.java
+++ b/src/com/android/settings/wifi/WifiAPITest.java
@@ -69,7 +69,7 @@
 
     @Override
     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
-        addPreferencesFromResource(R.layout.wifi_api_test);
+        addPreferencesFromResource(R.xml.wifi_api_test);
 
         final PreferenceScreen preferenceScreen = getPreferenceScreen();
 
diff --git a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
index 3890ddf..098787c 100644
--- a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
+++ b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
@@ -201,8 +201,10 @@
     void showAlert(Intent intent) {
         final Context context = getActivity();
 
-        final CharSequence title = intent.getCharSequenceExtra(Phone.EXTRA_KEY_ALERT_TITLE);
-        final CharSequence message = intent.getCharSequenceExtra(Phone.EXTRA_KEY_ALERT_MESSAGE);
+        final CharSequence title =
+                intent.getCharSequenceExtra(ImsManager.EXTRA_WFC_REGISTRATION_FAILURE_TITLE);
+        final CharSequence message =
+                intent.getCharSequenceExtra(ImsManager.EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE);
 
         final AlertDialog.Builder builder = new AlertDialog.Builder(context);
         builder.setMessage(message)
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardTest.java
index e14e271..ea2852f 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardTest.java
@@ -38,6 +38,7 @@
 import android.view.accessibility.AccessibilityManager;
 
 import androidx.fragment.app.FragmentActivity;
+import androidx.lifecycle.LifecycleOwner;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceManager;
 import androidx.preference.PreferenceScreen;
@@ -93,6 +94,7 @@
         when(mAccessibilityManager.getInstalledAccessibilityServiceList()).thenReturn(
                 mAccessibilityServices);
         doReturn(mActivity).when(mFragment).getActivity();
+        doReturn(mock(LifecycleOwner.class)).when(mFragment).getViewLifecycleOwner();
         doReturn(mFooterBarMixin).when(mGlifLayoutView).getMixin(FooterBarMixin.class);
     }
 
diff --git a/tests/robotests/src/com/android/settings/accessibility/HearingAidHelperTest.java b/tests/robotests/src/com/android/settings/accessibility/HearingAidHelperTest.java
index 194b766..3889cf0 100644
--- a/tests/robotests/src/com/android/settings/accessibility/HearingAidHelperTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/HearingAidHelperTest.java
@@ -95,8 +95,7 @@
     }
 
     @Test
-    public void isHearingAidSupported_supported_returnTrue() {
-        mBluetoothAdapter.enable();
+    public void isHearingAidSupported_ashaSupported_returnTrue() {
         mShadowBluetoothAdapter.clearSupportedProfiles();
         mShadowBluetoothAdapter.addSupportedProfiles(BluetoothProfile.HEARING_AID);
 
@@ -104,15 +103,20 @@
     }
 
     @Test
-    public void isHearingAidSupported_bluetoothOff_returnFalse() {
+    public void isHearingAidSupported_hapSupported_returnTrue() {
         mShadowBluetoothAdapter.clearSupportedProfiles();
-        mShadowBluetoothAdapter.addSupportedProfiles(BluetoothProfile.HEARING_AID);
-        mBluetoothAdapter.disable();
+        mShadowBluetoothAdapter.addSupportedProfiles(BluetoothProfile.HAP_CLIENT);
+
+        assertThat(mHelper.isHearingAidSupported()).isTrue();
+    }
+
+    @Test
+    public void isHearingAidSupported_unsupported_returnFalse() {
+        mShadowBluetoothAdapter.clearSupportedProfiles();
 
         assertThat(mHelper.isHearingAidSupported()).isFalse();
     }
 
-
     @Test
     public void isAllHearingAidRelatedProfilesReady_allReady_returnTrue() {
         when(mHearingAidProfile.isProfileReady()).thenReturn(true);
diff --git a/tests/robotests/src/com/android/settings/accessibility/HearingAidUtilsTest.java b/tests/robotests/src/com/android/settings/accessibility/HearingAidUtilsTest.java
index 09db6e9..56ab082 100644
--- a/tests/robotests/src/com/android/settings/accessibility/HearingAidUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/HearingAidUtilsTest.java
@@ -37,8 +37,10 @@
 import com.android.settings.utils.ActivityControllerWrapper;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.CsipSetCoordinatorProfile;
 import com.android.settingslib.bluetooth.HearingAidInfo;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfile;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -52,6 +54,9 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.shadow.api.Shadow;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /** Tests for {@link HearingAidUtils}. */
 @RunWith(RobolectricTestRunner.class)
 @Config(shadows = {ShadowAlertDialogCompat.class, ShadowBluetoothAdapter.class,
@@ -72,6 +77,8 @@
     private LocalBluetoothManager mLocalBluetoothManager;
     @Mock
     private CachedBluetoothDeviceManager mCachedDeviceManager;
+    @Mock
+    private CsipSetCoordinatorProfile mCsipSetCoordinatorProfile;
     private BluetoothDevice mBluetoothDevice;
     private BluetoothAdapter mBluetoothAdapter;
     private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@@ -137,6 +144,38 @@
     }
 
     @Test
+    public void launchHearingAidPairingDialog_deviceSupportsCsip_csipEnabled_noDialog() {
+        when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
+        when(mCachedBluetoothDevice.getDeviceMode()).thenReturn(
+                HearingAidInfo.DeviceMode.MODE_BINAURAL);
+        when(mCachedBluetoothDevice.getDeviceSide()).thenReturn(
+                HearingAidInfo.DeviceSide.SIDE_LEFT);
+        makeDeviceSupportCsip();
+        makeDeviceEnableCsip(true);
+
+        HearingAidUtils.launchHearingAidPairingDialog(mFragmentManager, mCachedBluetoothDevice);
+
+        final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+        assertThat(dialog).isNull();
+    }
+
+    @Test
+    public void launchHearingAidPairingDialog_deviceSupportsCsip_csipDisabled_dialogShown() {
+        when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
+        when(mCachedBluetoothDevice.getDeviceMode()).thenReturn(
+                HearingAidInfo.DeviceMode.MODE_BINAURAL);
+        when(mCachedBluetoothDevice.getDeviceSide()).thenReturn(
+                HearingAidInfo.DeviceSide.SIDE_LEFT);
+        makeDeviceSupportCsip();
+        makeDeviceEnableCsip(false);
+
+        HearingAidUtils.launchHearingAidPairingDialog(mFragmentManager, mCachedBluetoothDevice);
+
+        final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+        assertThat(dialog.isShowing()).isTrue();
+    }
+
+    @Test
     public void launchHearingAidPairingDialog_dialogShown() {
         when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
         when(mCachedBluetoothDevice.getDeviceMode()).thenReturn(
@@ -150,6 +189,17 @@
         assertThat(dialog.isShowing()).isTrue();
     }
 
+    private void makeDeviceSupportCsip() {
+        List<LocalBluetoothProfile> uuids = new ArrayList<>();
+        uuids.add(mCsipSetCoordinatorProfile);
+        when(mCachedBluetoothDevice.getProfiles()).thenReturn(uuids);
+    }
+
+    private void makeDeviceEnableCsip(boolean enabled) {
+        when(mCsipSetCoordinatorProfile.isEnabled(mCachedBluetoothDevice.getDevice()))
+                .thenReturn(enabled);
+    }
+
     private void setupEnvironment() {
         ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
diff --git a/tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizardTest.java b/tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizardTest.java
index 1cd301f..4ee2a2d 100644
--- a/tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizardTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizardTest.java
@@ -22,6 +22,7 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
@@ -29,6 +30,7 @@
 import android.content.Context;
 
 import androidx.fragment.app.FragmentActivity;
+import androidx.lifecycle.LifecycleOwner;
 import androidx.test.core.app.ApplicationProvider;
 
 import com.android.settings.R;
@@ -73,6 +75,7 @@
         final LayoutPreference resetPreference =
                 new LayoutPreference(mContext, R.layout.accessibility_text_reading_reset_button);
         doReturn(mContext).when(mFragment).getContext();
+        doReturn(mock(LifecycleOwner.class)).when(mFragment).getViewLifecycleOwner();
         doReturn(resetPreference).when(mFragment).findPreference(RESET_KEY);
         doReturn(mFooterBarMixin).when(mGlifLayoutView).getMixin(FooterBarMixin.class);
     }
diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizardTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizardTest.java
index 84783b21..aa622f5 100644
--- a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizardTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizardTest.java
@@ -20,6 +20,7 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -27,6 +28,7 @@
 import android.app.settings.SettingsEnums;
 import android.content.Context;
 
+import androidx.lifecycle.LifecycleOwner;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceManager;
 import androidx.preference.PreferenceScreen;
@@ -75,6 +77,7 @@
         mFragment =
                 spy(new TestToggleScreenMagnificationPreferenceFragmentForSetupWizard(mContext));
         doReturn(mActivity).when(mFragment).getActivity();
+        doReturn(mock(LifecycleOwner.class)).when(mFragment).getViewLifecycleOwner();
         when(mActivity.getSwitchBar()).thenReturn(mSwitchBar);
         doReturn(mFooterBarMixin).when(mGlifLayoutView).getMixin(FooterBarMixin.class);
     }
diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizardTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizardTest.java
index c604652..77e5b1f 100644
--- a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizardTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizardTest.java
@@ -20,6 +20,7 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -28,6 +29,7 @@
 import android.content.Context;
 import android.os.Bundle;
 
+import androidx.lifecycle.LifecycleOwner;
 import androidx.preference.PreferenceManager;
 import androidx.preference.PreferenceScreen;
 import androidx.test.core.app.ApplicationProvider;
@@ -72,6 +74,7 @@
     public void setUp() {
         mFragment = spy(new TestToggleScreenReaderPreferenceFragmentForSetupWizard(mContext));
         doReturn(mActivity).when(mFragment).getActivity();
+        doReturn(mock(LifecycleOwner.class)).when(mFragment).getViewLifecycleOwner();
         when(mActivity.getSwitchBar()).thenReturn(mSwitchBar);
         doReturn(mFooterBarMixin).when(mGlifLayoutView).getMixin(FooterBarMixin.class);
     }
diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizardTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizardTest.java
index 7893831..8878064 100644
--- a/tests/robotests/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizardTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizardTest.java
@@ -20,6 +20,7 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -28,6 +29,7 @@
 import android.content.Context;
 import android.os.Bundle;
 
+import androidx.lifecycle.LifecycleOwner;
 import androidx.preference.PreferenceManager;
 import androidx.preference.PreferenceScreen;
 import androidx.test.core.app.ApplicationProvider;
@@ -72,6 +74,7 @@
     public void setUp() {
         mFragment = spy(new TestToggleSelectToSpeakPreferenceFragmentForSetupWizard(mContext));
         doReturn(mActivity).when(mFragment).getActivity();
+        doReturn(mock(LifecycleOwner.class)).when(mFragment).getViewLifecycleOwner();
         when(mActivity.getSwitchBar()).thenReturn(mSwitchBar);
         doReturn(mFooterBarMixin).when(mGlifLayoutView).getMixin(FooterBarMixin.class);
     }
diff --git a/tests/robotests/src/com/android/settings/applications/SpecialAppAccessPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/SpecialAppAccessPreferenceControllerTest.java
deleted file mode 100644
index da5ada7..0000000
--- a/tests/robotests/src/com/android/settings/applications/SpecialAppAccessPreferenceControllerTest.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.applications;
-
-import static com.android.settings.core.BasePreferenceController.AVAILABLE;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.ModuleInfo;
-import android.content.pm.PackageManager;
-
-import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
-
-import com.android.settings.R;
-import com.android.settings.datausage.AppStateDataUsageBridge;
-import com.android.settings.testutils.shadow.ShadowApplicationsState;
-import com.android.settings.testutils.shadow.ShadowUserManager;
-import com.android.settingslib.applications.ApplicationsState;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-
-import java.util.ArrayList;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowUserManager.class, ShadowApplicationsState.class})
-public class SpecialAppAccessPreferenceControllerTest {
-
-    private Context mContext;
-    @Mock
-    private ApplicationsState.Session mSession;
-    @Mock
-    private PreferenceScreen mScreen;
-    @Mock
-    private PackageManager mPackageManager;
-
-    private SpecialAppAccessPreferenceController mController;
-    private Preference mPreference;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mContext = spy(RuntimeEnvironment.application);
-        when(mContext.getApplicationContext()).thenReturn(mContext);
-        ShadowUserManager.getShadow().setProfileIdsWithDisabled(new int[]{0});
-        doReturn(mPackageManager).when(mContext).getPackageManager();
-        doReturn(new ArrayList<ModuleInfo>()).when(mPackageManager).getInstalledModules(anyInt());
-        mController = new SpecialAppAccessPreferenceController(mContext, "test_key");
-        mPreference = new Preference(mContext);
-        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
-
-        mController.mSession = mSession;
-    }
-
-    @Test
-    public void getAvailabilityState_unsearchable() {
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
-    }
-
-    @Test
-    public void updateState_shouldSetSummary() {
-        final ArrayList<ApplicationsState.AppEntry> apps = new ArrayList<>();
-        final ApplicationsState.AppEntry entry = mock(ApplicationsState.AppEntry.class);
-        entry.hasLauncherEntry = true;
-        entry.info = new ApplicationInfo();
-        entry.extraInfo = new AppStateDataUsageBridge.DataUsageState(
-                true /* allowlisted */, false /* denylisted */);
-        apps.add(entry);
-        when(mSession.getAllApps()).thenReturn(apps);
-
-        mController.displayPreference(mScreen);
-        mController.onExtraInfoUpdated();
-
-        assertThat(mPreference.getSummary())
-                .isEqualTo(mContext.getResources().getQuantityString(
-                        R.plurals.special_access_summary, 1, 1));
-    }
-
-    @Test
-    public void updateState_wrongExtraInfo_shouldNotIncludeInSummary() {
-        final ArrayList<ApplicationsState.AppEntry> apps = new ArrayList<>();
-        final ApplicationsState.AppEntry entry = mock(ApplicationsState.AppEntry.class);
-        entry.hasLauncherEntry = true;
-        entry.info = new ApplicationInfo();
-        entry.extraInfo = new AppStateNotificationBridge.NotificationsSentState();
-        apps.add(entry);
-        when(mSession.getAllApps()).thenReturn(apps);
-
-        mController.displayPreference(mScreen);
-        mController.onExtraInfoUpdated();
-
-        assertThat(mPreference.getSummary())
-                .isEqualTo(mContext.getResources().getQuantityString(
-                        R.plurals.special_access_summary, 0, 0));
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/DataSaverControllerTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/DataSaverControllerTest.java
deleted file mode 100644
index f039c97..0000000
--- a/tests/robotests/src/com/android/settings/applications/specialaccess/DataSaverControllerTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.applications.specialaccess;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.res.Resources;
-
-import com.android.settings.R;
-
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-
-@RunWith(RobolectricTestRunner.class)
-public class DataSaverControllerTest {
-
-    private Context mContext;
-    private Resources mResources;
-    private DataSaverController mController;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mContext = spy(RuntimeEnvironment.application.getApplicationContext());
-
-        mResources = spy(mContext.getResources());
-        when(mContext.getResources()).thenReturn(mResources);
-
-        mController = new DataSaverController(mContext, "key");
-    }
-
-    @Test
-    public void testDataSaver_byDefault_shouldBeShown() {
-        when(mResources.getBoolean(R.bool.config_show_data_saver)).thenReturn(true);
-        assertThat(mController.isAvailable()).isTrue();
-    }
-
-    @Ignore
-    @Test
-    @Config(qualifiers = "mcc999")
-    public void testDataSaver_ifDisabledByCarrier_shouldNotBeShown() {
-        assertThat(mController.isAvailable()).isFalse();
-    }
-
-    @Test
-    public void testDataSaver_ifDisabled_shouldNotBeShown() {
-        when(mResources.getBoolean(R.bool.config_show_data_saver)).thenReturn(false);
-        assertThat(mController.isAvailable()).isFalse();
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java b/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java
index c4da133..df15e5c 100644
--- a/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java
@@ -40,6 +40,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.hardware.face.Face;
 import android.hardware.face.FaceManager;
 import android.hardware.face.FaceSensorProperties;
@@ -116,6 +117,7 @@
     private FaceEnrollIntroduction mSpyActivity;
     private FakeFeatureFactory mFakeFeatureFactory;
     private ShadowUserManager mUserManager;
+    private Resources mResources;
 
     enum GateKeeperAction {CALL_SUPER, RETURN_BYTE_ARRAY, THROW_CREDENTIAL_NOT_MATCH}
 
@@ -245,6 +247,14 @@
         when(mFaceManager.getEnrolledFaces(anyInt())).thenReturn(faces);
     }
 
+    private void setFaceManagerToHaveWithUserId(int numEnrollments, int userId) {
+        List<Face> faces = new ArrayList<>();
+        for (int i = 0; i < numEnrollments; i++) {
+            faces.add(new Face("Face " + i /* name */, 1 /*faceId */, 1 /* deviceId */));
+        }
+        when(mFaceManager.getEnrolledFaces(userId)).thenReturn(faces);
+    }
+
     @Test
     public void intro_CheckCanEnroll() {
         setFaceManagerToHave(0 /* numEnrollments */);
@@ -546,4 +556,40 @@
         assertThat(mActivity.getPostureCallback()).isNull();
     }
 
+    @Test
+    public void testFaceEnrollIntroduction_maxFacesNotEnrolled_addUserProfile() {
+        // Enroll a face for one user
+        setFaceManagerToHaveWithUserId(1, 0);
+
+        mContext = spy(ApplicationProvider.getApplicationContext());
+        mResources = spy(mContext.getResources());
+        when(mResources.getInteger(R.integer.suw_max_faces_enrollable)).thenReturn(1);
+
+        mController = Robolectric.buildActivity(TestFaceEnrollIntroduction.class, new Intent());
+        mActivity = (TestFaceEnrollIntroduction) mController.get();
+
+        mController.create();
+
+        // The maximum number of faces is already enrolled
+        int result = mActivity.checkMaxEnrolled();
+        assertThat(result).isEqualTo(R.string.face_intro_error_max);
+
+        // Add another user profile
+        mUserManager.addUser(10, "", 0);
+        final Intent intent = new Intent();
+        intent.putExtra(Intent.EXTRA_USER_ID, 10);
+
+        when(mResources.getInteger(R.integer.suw_max_faces_enrollable)).thenReturn(2);
+
+        mController = Robolectric.buildActivity(TestFaceEnrollIntroduction.class, intent);
+        mActivity = (TestFaceEnrollIntroduction) mController.get();
+
+        mController.create();
+
+        // The maximum number of faces hasn't been enrolled, so a new face
+        // can be enrolled for the added user profile
+        result = mActivity.checkMaxEnrolled();
+        assertThat(result).isEqualTo(0);
+    }
+
 }
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java
index 959c642..0f12d1e 100644
--- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java
@@ -56,6 +56,7 @@
 import android.view.Display;
 import android.view.Surface;
 import android.view.View;
+import android.view.accessibility.AccessibilityManager;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.TextView;
@@ -314,11 +315,17 @@
     @Test
     public void fingerprintUdfpsOverlayEnrollment_descriptionViewGoneWithOverlap() {
         initializeActivityWithoutCreate(TYPE_UDFPS_OPTICAL);
-        doReturn(true).when(mActivity).hasOverlap(any(), any());
         when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0);
         createActivity();
 
-        final GlifLayout defaultLayout = spy(mActivity.findViewById(R.id.setup_wizard_layout));
+        final UdfpsEnrollEnrollingView defaultLayout = spy(
+                mActivity.findViewById(R.id.setup_wizard_layout));
+        doReturn(true).when(defaultLayout).hasOverlap(any(), any());
+
+        // Somehow spy doesn't work, and we need to call initView manually.
+        defaultLayout.initView(mFingerprintManager.getSensorPropertiesInternal().get(0),
+                mActivity.mUdfpsEnrollHelper,
+                mActivity.getSystemService(AccessibilityManager.class));
         final TextView descriptionTextView = defaultLayout.getDescriptionTextView();
 
         defaultLayout.getViewTreeObserver().dispatchOnDraw();
@@ -328,11 +335,17 @@
     @Test
     public void fingerprintUdfpsOverlayEnrollment_descriptionViewVisibleWithoutOverlap() {
         initializeActivityWithoutCreate(TYPE_UDFPS_OPTICAL);
-        doReturn(false).when(mActivity).hasOverlap(any(), any());
         when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0);
         createActivity();
 
-        final GlifLayout defaultLayout = spy(mActivity.findViewById(R.id.setup_wizard_layout));
+        final UdfpsEnrollEnrollingView defaultLayout = spy(
+                mActivity.findViewById(R.id.setup_wizard_layout));
+        doReturn(false).when(defaultLayout).hasOverlap(any(), any());
+
+        // Somehow spy doesn't work, and we need to call initView manually.
+        defaultLayout.initView(mFingerprintManager.getSensorPropertiesInternal().get(0),
+                mActivity.mUdfpsEnrollHelper,
+                mActivity.getSystemService(AccessibilityManager.class));
         final TextView descriptionTextView = defaultLayout.getDescriptionTextView();
 
         defaultLayout.getViewTreeObserver().dispatchOnDraw();
@@ -340,6 +353,19 @@
     }
 
     @Test
+    public void fingerprintUdfpsOverlayEnrollment_udfpsAnimationViewVisibility() {
+        initializeActivityWithoutCreate(TYPE_UDFPS_OPTICAL);
+        when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0);
+        createActivity();
+
+        final UdfpsEnrollView enrollView = mActivity.findViewById(R.id.udfps_animation_view);
+        assertThat(enrollView.getVisibility()).isEqualTo(View.GONE);
+
+        mActivity.onUdfpsOverlayShown();
+        assertThat(enrollView.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
     public void forwardEnrollProgressEvents() {
         initializeActivityFor(TYPE_UDFPS_OPTICAL);
 
@@ -380,11 +406,11 @@
     }
 
     @Test
-    public void forwardEnrollPointerDownEvents() {
+    public void forwardUdfpsEnrollPointerDownEvents() {
         initializeActivityFor(TYPE_UDFPS_OPTICAL);
 
         EnrollListener listener = new EnrollListener(mActivity);
-        mActivity.onPointerDown(0);
+        mActivity.onUdfpsPointerDown(0);
         assertThat(listener.mProgress).isFalse();
         assertThat(listener.mHelp).isFalse();
         assertThat(listener.mAcquired).isFalse();
@@ -393,11 +419,11 @@
     }
 
     @Test
-    public void forwardEnrollPointerUpEvents() {
+    public void forwardUdfpsEnrollPointerUpEvents() {
         initializeActivityFor(TYPE_UDFPS_OPTICAL);
 
         EnrollListener listener = new EnrollListener(mActivity);
-        mActivity.onPointerUp(0);
+        mActivity.onUdfpsPointerUp(0);
         assertThat(listener.mProgress).isFalse();
         assertThat(listener.mHelp).isFalse();
         assertThat(listener.mAcquired).isFalse();
@@ -578,7 +604,6 @@
         mContext = spy(RuntimeEnvironment.application);
         mActivity = spy(FingerprintEnrollEnrolling.class);
 
-        when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props);
         when(mContext.getDisplay()).thenReturn(mMockDisplay);
         when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0);
 
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java
index 18b05ad..8b70550 100644
--- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java
@@ -16,12 +16,14 @@
 
 package com.android.settings.biometrics.fingerprint;
 
+import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON;
 import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
 
 import static com.android.settings.biometrics.fingerprint.FingerprintSettings.FingerprintSettingsFragment;
 import static com.android.settings.biometrics.fingerprint.FingerprintSettings.FingerprintSettingsFragment.ADD_FINGERPRINT_REQUEST;
 import static com.android.settings.biometrics.fingerprint.FingerprintSettings.FingerprintSettingsFragment.CHOOSE_LOCK_GENERIC_REQUEST;
 import static com.android.settings.biometrics.fingerprint.FingerprintSettings.FingerprintSettingsFragment.KEY_FINGERPRINT_ADD;
+import static com.android.settings.biometrics.fingerprint.FingerprintSettings.FingerprintSettingsFragment.KEY_REQUIRE_SCREEN_ON_TO_AUTH;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -39,11 +41,16 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.UserInfo;
 import android.hardware.biometrics.ComponentInfoInternal;
 import android.hardware.biometrics.SensorProperties;
 import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorProperties;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.UserHandle;
+import android.provider.Settings;
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
 
@@ -61,6 +68,7 @@
 import com.android.settings.testutils.shadow.ShadowSettingsPreferenceFragment;
 import com.android.settings.testutils.shadow.ShadowUserManager;
 import com.android.settings.testutils.shadow.ShadowUtils;
+import com.android.settingslib.RestrictedSwitchPreference;
 
 import org.junit.After;
 import org.junit.Before;
@@ -68,6 +76,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
@@ -81,6 +90,9 @@
 @Config(shadows = {ShadowSettingsPreferenceFragment.class, ShadowUtils.class, ShadowFragment.class,
         ShadowUserManager.class, ShadowLockPatternUtils.class})
 public class FingerprintSettingsFragmentTest {
+    private static final int PRIMARY_USER_ID = 0;
+    private static final int GUEST_USER_ID = 10;
+
     private FingerprintSettingsFragment mFragment;
     private Context mContext;
     private FragmentActivity mActivity;
@@ -92,11 +104,26 @@
     @Mock
     private FragmentTransaction mFragmentTransaction;
 
+    @Captor
+    private ArgumentCaptor<CancellationSignal> mCancellationSignalArgumentCaptor =
+            ArgumentCaptor.forClass(CancellationSignal.class);
+    @Captor
+    private ArgumentCaptor<FingerprintManager.AuthenticationCallback>
+            mAuthenticationCallbackArgumentCaptor = ArgumentCaptor.forClass(
+            FingerprintManager.AuthenticationCallback.class);
+
+    private FingerprintAuthenticateSidecar mFingerprintAuthenticateSidecar;
+
     @Before
     public void setUp() {
-        doReturn(true).when(mFingerprintManager).isHardwareDetected();
         ShadowUtils.setFingerprintManager(mFingerprintManager);
         FakeFeatureFactory.setupForTest();
+
+        mContext = spy(ApplicationProvider.getApplicationContext());
+        mFragment = spy(new FingerprintSettingsFragment());
+        doReturn(mContext).when(mFragment).getContext();
+
+        doReturn(true).when(mFingerprintManager).isHardwareDetected();
     }
 
     @After
@@ -146,19 +173,71 @@
                 false)).isTrue();
     }
 
+    // Test the case when FingerprintAuthenticateSidecar receives an error callback from the
+    // framework or from another authentication client. The cancellation signal should not be set
+    // to null because there may exist a running authentication client.
+    // The signal can only be cancelled from the caller in FingerprintSettings.
+    @Test
+    public void testCancellationSignalLifeCycle() {
+        setUpFragment(false);
+
+        mFingerprintAuthenticateSidecar.setFingerprintManager(mFingerprintManager);
+
+        doNothing().when(mFingerprintManager).authenticate(any(),
+                mCancellationSignalArgumentCaptor.capture(),
+                mAuthenticationCallbackArgumentCaptor.capture(), any(), anyInt());
+
+        mFingerprintAuthenticateSidecar.startAuthentication(1);
+
+        assertThat(mAuthenticationCallbackArgumentCaptor.getValue()).isNotNull();
+        assertThat(mCancellationSignalArgumentCaptor.getValue()).isNotNull();
+
+        // Authentication error callback should not cancel the signal.
+        mAuthenticationCallbackArgumentCaptor.getValue().onAuthenticationError(0, "");
+        assertThat(mFingerprintAuthenticateSidecar.isCancelled()).isFalse();
+
+        // The signal should be cancelled when caller stops the authentication.
+        mFingerprintAuthenticateSidecar.stopAuthentication();
+        assertThat(mFingerprintAuthenticateSidecar.isCancelled()).isTrue();
+    }
+
+    @Test
+    public void testGuestUserRequireScreenOnToAuth() {
+        Settings.Secure.putIntForUser(
+                mContext.getContentResolver(),
+                Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED,
+                0,
+                UserHandle.of(PRIMARY_USER_ID).getIdentifier());
+
+        Settings.Secure.putIntForUser(
+                mContext.getContentResolver(),
+                Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED,
+                1,
+                UserHandle.of(GUEST_USER_ID).getIdentifier());
+
+        setUpFragment(false, GUEST_USER_ID, TYPE_POWER_BUTTON);
+
+        final RestrictedSwitchPreference requireScreenOnToAuthPreference = mFragment.findPreference(
+                KEY_REQUIRE_SCREEN_ON_TO_AUTH);
+        assertThat(requireScreenOnToAuthPreference.isChecked()).isTrue();
+    }
+
     private void setUpFragment(boolean showChooseLock) {
+        setUpFragment(showChooseLock, PRIMARY_USER_ID, TYPE_UDFPS_OPTICAL);
+    }
+
+    private void setUpFragment(boolean showChooseLock, int userId,
+            @FingerprintSensorProperties.SensorType int sensorType) {
+        ShadowUserManager.getShadow().addProfile(new UserInfo(userId, "", 0));
+
         Intent intent = new Intent();
         if (!showChooseLock) {
             intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, new byte[0]);
             intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 1L);
         }
-
+        intent.putExtra(Intent.EXTRA_USER_ID, userId);
         mActivity = spy(Robolectric.buildActivity(FragmentActivity.class, intent).get());
-        mContext = spy(ApplicationProvider.getApplicationContext());
-
-        mFragment = spy(new FingerprintSettingsFragment());
         doReturn(mActivity).when(mFragment).getActivity();
-        doReturn(mContext).when(mFragment).getContext();
 
         FragmentManager fragmentManager = mock(FragmentManager.class);
         doReturn(mFragmentTransaction).when(fragmentManager).beginTransaction();
@@ -166,9 +245,13 @@
         doReturn(fragmentManager).when(mFragment).getFragmentManager();
         doReturn(fragmentManager).when(mActivity).getSupportFragmentManager();
 
+        mFingerprintAuthenticateSidecar = new FingerprintAuthenticateSidecar();
+        doReturn(mFingerprintAuthenticateSidecar).when(fragmentManager).findFragmentByTag(
+                "authenticate_sidecar");
+
         doNothing().when(mFragment).startActivityForResult(any(Intent.class), anyInt());
 
-        setSensor();
+        setSensor(sensorType);
 
         // Start fragment
         mFragment.onAttach(mContext);
@@ -177,14 +260,14 @@
         mFragment.onResume();
     }
 
-    private void setSensor() {
+    private void setSensor(@FingerprintSensorProperties.SensorType int sensorType) {
         final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
         props.add(new FingerprintSensorPropertiesInternal(
                 0 /* sensorId */,
                 SensorProperties.STRENGTH_STRONG,
                 1 /* maxEnrollmentsPerUser */,
                 new ArrayList<ComponentInfoInternal>(),
-                TYPE_UDFPS_OPTICAL,
+                sensorType,
                 true /* resetLockoutRequiresHardwareAuthToken */));
         doReturn(props).when(mFingerprintManager).getSensorPropertiesInternal();
     }
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBaseTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBaseTest.java
index 184f521..7c598e0 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBaseTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBaseTest.java
@@ -202,7 +202,7 @@
                 new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
                         true, BluetoothDevicePreference.SortType.TYPE_FIFO);
         final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS);
-        mFragment.mDevicePreferenceMap.put(mCachedBluetoothDevice, preference);
+        mFragment.getDevicePreferenceMap().put(mCachedBluetoothDevice, preference);
 
         when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
         when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
@@ -210,7 +210,7 @@
         mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
                 BluetoothProfile.A2DP, BluetoothAdapter.STATE_CONNECTED);
 
-        assertThat(mFragment.mDevicePreferenceMap.size()).isEqualTo(0);
+        assertThat(mFragment.getDevicePreferenceMap().size()).isEqualTo(0);
     }
 
     @Test
@@ -221,7 +221,7 @@
                         true, BluetoothDevicePreference.SortType.TYPE_FIFO);
         final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS);
         final BluetoothDevice device2 = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
-        mFragment.mDevicePreferenceMap.put(mCachedBluetoothDevice, preference);
+        mFragment.getDevicePreferenceMap().put(mCachedBluetoothDevice, preference);
 
         when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
         when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDetailTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDetailTest.java
index 5fbfee8..ce67051 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDetailTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDetailTest.java
@@ -27,7 +27,12 @@
 import android.bluetooth.BluetoothAdapter;
 import android.content.Context;
 import android.os.Bundle;
+import android.view.View;
 
+import androidx.annotation.NonNull;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
 import androidx.test.core.app.ApplicationProvider;
 
 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
@@ -53,6 +58,20 @@
 
     private final Context mContext = ApplicationProvider.getApplicationContext();
 
+    private final Lifecycle mFakeLifecycle = new Lifecycle() {
+        @Override
+        public void addObserver(@NonNull LifecycleObserver observer) {}
+
+        @Override
+        public void removeObserver(@NonNull LifecycleObserver observer) {}
+
+        @NonNull
+        @Override
+        public State getCurrentState() {
+            return State.CREATED;
+        }
+    };
+
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private LocalBluetoothManager mLocalManager;
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
@@ -74,6 +93,8 @@
                 .findPreference(BluetoothPairingDetail.KEY_AVAIL_DEVICES);
         doReturn(mFooterPreference).when(mFragment)
                 .findPreference(BluetoothPairingDetail.KEY_FOOTER_PREF);
+        doReturn(new View(mContext)).when(mFragment).getView();
+        doReturn((LifecycleOwner) () -> mFakeLifecycle).when(mFragment).getViewLifecycleOwner();
         doReturn(Collections.emptyList()).when(mDeviceManager).getCachedDevicesCopy();
 
         mFragment.mBluetoothAdapter = mBluetoothAdapter;
@@ -82,7 +103,7 @@
         mFragment.mDeviceListGroup = mAvailableDevicesCategory;
         mFragment.onViewCreated(mFragment.getView(), Bundle.EMPTY);
     }
-//
+
     @Test
     public void initPreferencesFromPreferenceScreen_findPreferences() {
         mFragment.initPreferencesFromPreferenceScreen();
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 f4fa397..1fcf396 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDevicesControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDevicesControllerTest.java
@@ -18,6 +18,10 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doNothing;
@@ -27,13 +31,17 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.Dialog;
 import android.app.role.RoleManager;
 import android.bluetooth.BluetoothDevice;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.Process;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.Settings;
 import android.provider.Settings.Secure;
 import android.view.InputDevice;
@@ -48,6 +56,7 @@
 import androidx.test.core.app.ApplicationProvider;
 
 import com.android.settings.R;
+import com.android.settings.dashboard.profileselector.UserAdapter;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
@@ -59,7 +68,9 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 
+import java.util.Arrays;
 import java.util.Collections;
+import java.util.List;
 
 @RunWith(RobolectricTestRunner.class)
 public class StylusDevicesControllerTest {
@@ -79,6 +90,8 @@
     @Mock
     private PackageManager mPm;
     @Mock
+    private UserManager mUserManager;
+    @Mock
     private RoleManager mRm;
     @Mock
     private Lifecycle mLifecycle;
@@ -87,7 +100,6 @@
     @Mock
     private BluetoothDevice mBluetoothDevice;
 
-
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -101,6 +113,7 @@
 
         when(mContext.getSystemService(InputMethodManager.class)).thenReturn(mImm);
         when(mContext.getSystemService(RoleManager.class)).thenReturn(mRm);
+        when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
         doNothing().when(mContext).startActivity(any());
 
         when(mImm.getCurrentInputMethodInfo()).thenReturn(mInputMethodInfo);
@@ -115,6 +128,8 @@
         when(mPm.getApplicationInfo(eq(NOTES_PACKAGE_NAME),
                 any(PackageManager.ApplicationInfoFlags.class))).thenReturn(new ApplicationInfo());
         when(mPm.getApplicationLabel(any(ApplicationInfo.class))).thenReturn(NOTES_APP_LABEL);
+        when(mUserManager.getUsers()).thenReturn(Arrays.asList(new UserInfo(0, "default", 0)));
+        when(mUserManager.isManagedProfile(anyInt())).thenReturn(false);
 
         when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
 
@@ -228,22 +243,36 @@
         when(mInputMethodInfo.supportsStylusHandwriting()).thenReturn(false);
 
         showScreen(mController);
-        Preference handwritingPref = mPreferenceContainer.getPreference(1);
 
+        Preference handwritingPref = mPreferenceContainer.getPreference(1);
         assertThat(handwritingPref.isVisible()).isFalse();
     }
 
     @Test
-    public void defaultNotesPreference_showsNotesRoleApp() {
+    public void defaultNotesPreference_singleUser_showsNotesRoleApp() {
         showScreen(mController);
-        Preference defaultNotesPref = mPreferenceContainer.getPreference(0);
 
+        Preference defaultNotesPref = mPreferenceContainer.getPreference(0);
         assertThat(defaultNotesPref.getTitle().toString()).isEqualTo(
                 mContext.getString(R.string.stylus_default_notes_app));
         assertThat(defaultNotesPref.getSummary().toString()).isEqualTo(NOTES_APP_LABEL.toString());
     }
 
     @Test
+    public void defaultNotesPreference_workProfileUser_showsWorkNotesRoleApp() {
+        when(mUserManager.isManagedProfile(0)).thenReturn(true);
+
+        showScreen(mController);
+
+        Preference defaultNotesPref = mPreferenceContainer.getPreference(0);
+        assertThat(defaultNotesPref.getTitle().toString()).isEqualTo(
+                mContext.getString(R.string.stylus_default_notes_app));
+        assertThat(defaultNotesPref.getSummary().toString()).isEqualTo(
+                mContext.getString(R.string.stylus_default_notes_summary_work,
+                        NOTES_APP_LABEL.toString()));
+    }
+
+    @Test
     public void defaultNotesPreference_roleHolderChanges_updatesPreference() {
         showScreen(mController);
         Preference defaultNotesPref = mPreferenceContainer.getPreference(0);
@@ -267,7 +296,7 @@
     }
 
     @Test
-    public void defaultNotesPreferenceClick_sendsManageDefaultRoleIntent() {
+    public void defaultNotesPreferenceClick_singleUser_sendsManageDefaultRoleIntent() {
         final String permissionPackageName = "permissions.package";
         when(mPm.getPermissionControllerPackageName()).thenReturn(permissionPackageName);
         final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
@@ -282,6 +311,76 @@
         assertThat(intent.getPackage()).isEqualTo(permissionPackageName);
         assertThat(intent.getStringExtra(Intent.EXTRA_ROLE_NAME)).isEqualTo(
                 RoleManager.ROLE_NOTES);
+        assertNull(mController.mDialog);
+    }
+
+    @Test
+    public void defaultNotesPreferenceClick_multiUserManagedProfile_showsProfileSelectorDialog() {
+        mContext.setTheme(R.style.Theme_AppCompat);
+        final String permissionPackageName = "permissions.package";
+        final UserHandle currentUser = Process.myUserHandle();
+        List<UserInfo> userInfos = Arrays.asList(
+                new UserInfo(currentUser.getIdentifier(), "current", 0),
+                new UserInfo(1, "profile", UserInfo.FLAG_PROFILE)
+        );
+        when(mUserManager.getUsers()).thenReturn(userInfos);
+        when(mUserManager.isManagedProfile(1)).thenReturn(true);
+        when(mUserManager.getUserInfo(currentUser.getIdentifier())).thenReturn(userInfos.get(0));
+        when(mUserManager.getUserInfo(1)).thenReturn(userInfos.get(1));
+        when(mUserManager.getProfileParent(1)).thenReturn(userInfos.get(0));
+        when(mPm.getPermissionControllerPackageName()).thenReturn(permissionPackageName);
+
+        showScreen(mController);
+        Preference defaultNotesPref = mPreferenceContainer.getPreference(0);
+        mController.onPreferenceClick(defaultNotesPref);
+
+        assertTrue(mController.mDialog.isShowing());
+    }
+
+    @Test
+    public void defaultNotesPreferenceClick_noManagedProfile_sendsManageDefaultRoleIntent() {
+        final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+        mContext.setTheme(R.style.Theme_AppCompat);
+        final String permissionPackageName = "permissions.package";
+        final UserHandle currentUser = Process.myUserHandle();
+        List<UserInfo> userInfos = Arrays.asList(
+                new UserInfo(currentUser.getIdentifier(), "current", 0),
+                new UserInfo(1, "other", UserInfo.FLAG_FULL)
+        );
+        when(mUserManager.getUsers()).thenReturn(userInfos);
+        when(mUserManager.isManagedProfile(1)).thenReturn(false);
+        when(mUserManager.getUserInfo(currentUser.getIdentifier())).thenReturn(userInfos.get(0));
+        when(mUserManager.getUserInfo(1)).thenReturn(userInfos.get(1));
+        when(mUserManager.getProfileParent(any())).thenReturn(null);
+        when(mPm.getPermissionControllerPackageName()).thenReturn(permissionPackageName);
+
+        showScreen(mController);
+        Preference defaultNotesPref = mPreferenceContainer.getPreference(0);
+        mController.onPreferenceClick(defaultNotesPref);
+
+        verify(mContext).startActivity(captor.capture());
+        Intent intent = captor.getValue();
+        assertThat(intent.getAction()).isEqualTo(Intent.ACTION_MANAGE_DEFAULT_APP);
+        assertThat(intent.getPackage()).isEqualTo(permissionPackageName);
+        assertThat(intent.getStringExtra(Intent.EXTRA_ROLE_NAME)).isEqualTo(
+                RoleManager.ROLE_NOTES);
+        assertNull(mController.mDialog);
+    }
+
+    @Test
+    public void profileSelectDialogClickCallback_onClick_sendsIntent() {
+        Intent intent = new Intent();
+        UserHandle user1 = mock(UserHandle.class);
+        UserHandle user2 = mock(UserHandle.class);
+        List<UserHandle> users = Arrays.asList(user1, user2);
+        mController.mDialog = new Dialog(mContext);
+        UserAdapter.OnClickListener callback = mController
+                .createProfileDialogClickCallback(intent, users);
+
+        callback.onClick(1);
+
+        assertEquals(intent.getExtra(Intent.EXTRA_USER), user2);
+        verify(mContext).startActivity(intent);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/development/ShowKeyPressesPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/ShowKeyPressesPreferenceControllerTest.java
new file mode 100644
index 0000000..b7fb902
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/ShowKeyPressesPreferenceControllerTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 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.development;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class ShowKeyPressesPreferenceControllerTest {
+
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private SwitchPreference mPreference;
+
+    private Context mContext;
+
+    private ShowKeyPressesPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mController = new ShowKeyPressesPreferenceController(mContext);
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
+        mController.displayPreference(mScreen);
+    }
+
+    @Test
+    public void updateState_showKeyPressesEnabled_shouldCheckedPreference() {
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SHOW_KEY_PRESSES, ShowTapsPreferenceController.SETTING_VALUE_ON);
+
+        mController.updateState(mPreference);
+
+        verify(mPreference).setChecked(true);
+    }
+
+    @Test
+    public void updateState_showKeyPressesDisabled_shouldUncheckedPreference() {
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SHOW_KEY_PRESSES, ShowTapsPreferenceController.SETTING_VALUE_OFF);
+
+        mController.updateState(mPreference);
+
+        verify(mPreference).setChecked(false);
+    }
+
+    @Test
+    public void onPreferenceChange_preferenceChecked_shouldEnableShowKeyPresses() {
+        mController.onPreferenceChange(mPreference, true /* new value */);
+
+        final int showKeyPresses = Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.SHOW_KEY_PRESSES, -1 /* default */);
+
+        assertThat(showKeyPresses).isEqualTo(ShowTapsPreferenceController.SETTING_VALUE_ON);
+    }
+
+    @Test
+    public void onPreferenceChange_preferenceUnchecked_shouldDisableShowKeyPresses() {
+        mController.onPreferenceChange(mPreference, false /* new value */);
+
+        final int showTapsMode = Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.SHOW_KEY_PRESSES, -1 /* default */);
+
+        assertThat(showTapsMode).isEqualTo(ShowTapsPreferenceController.SETTING_VALUE_OFF);
+    }
+
+    @Test
+    public void onDeveloperOptionsSwitchDisabled_preferenceShouldBeEnabled() {
+        mController.onDeveloperOptionsSwitchDisabled();
+
+        final int showTapsMode = Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.SHOW_KEY_PRESSES, -1 /* default */);
+
+        assertThat(showTapsMode).isEqualTo(ShowTapsPreferenceController.SETTING_VALUE_OFF);
+        verify(mPreference).setEnabled(false);
+        verify(mPreference).setChecked(false);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryCycleCountPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryCycleCountPreferenceControllerTest.java
new file mode 100644
index 0000000..4d1b4d0
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryCycleCountPreferenceControllerTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.deviceinfo.batteryinfo;
+
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.BatteryManager;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class BatteryCycleCountPreferenceControllerTest {
+    private BatteryCycleCountPreferenceController mController;
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = spy(ApplicationProvider.getApplicationContext());
+        mController = new BatteryCycleCountPreferenceController(mContext,
+                "battery_info_cycle_count");
+    }
+
+    @Test
+    public void getAvailabilityStatus_returnAvailable() {
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+    }
+
+    @Test
+    public void getSummary_returnExpectedResult() {
+        final Intent batteryIntent = new Intent();
+        batteryIntent.putExtra(BatteryManager.EXTRA_CYCLE_COUNT, 10);
+        doReturn(batteryIntent).when(mContext).registerReceiver(any(), any());
+
+        assertThat(mController.getSummary()).isEqualTo("10");
+    }
+
+    @Test
+    public void getSummary_noValue_returnUnavailable() {
+        final Intent batteryIntent = new Intent();
+        doReturn(batteryIntent).when(mContext).registerReceiver(any(), any());
+
+        assertThat(mController.getSummary()).isEqualTo(
+                mContext.getText(R.string.battery_cycle_count_not_available));
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryFirstUseDatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryFirstUseDatePreferenceControllerTest.java
new file mode 100644
index 0000000..ff8ea62
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryFirstUseDatePreferenceControllerTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.deviceinfo.batteryinfo;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
+
+import android.content.Context;
+import android.os.BatteryManager;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.testutils.FakeFeatureFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowBatteryManager;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBatteryManager.class})
+public class BatteryFirstUseDatePreferenceControllerTest {
+    private BatteryFirstUseDatePreferenceController mController;
+    private Context mContext;
+    private BatteryManager mBatteryManager;
+    private ShadowBatteryManager mShadowBatteryManager;
+    private FakeFeatureFactory mFactory;
+
+    @Before
+    public void setUp() {
+        mContext = ApplicationProvider.getApplicationContext();
+        mBatteryManager = mContext.getSystemService(BatteryManager.class);
+        mShadowBatteryManager = shadowOf(mBatteryManager);
+        mFactory = FakeFeatureFactory.setupForTest();
+        mController = new BatteryFirstUseDatePreferenceController(mContext,
+                "battery_info_first_use_date");
+    }
+
+    @Test
+    public void getAvailabilityStatus_dateAvailable_returnAvailable() {
+        when(mFactory.batterySettingsFeatureProvider.isFirstUseDateAvailable(eq(mContext),
+                anyLong())).thenReturn(true);
+
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+    }
+
+    @Test
+    public void getAvailabilityStatus_dateUnavailable_returnNotAvailable() {
+        when(mFactory.batterySettingsFeatureProvider.isFirstUseDateAvailable(eq(mContext),
+                anyLong())).thenReturn(false);
+
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
+    }
+
+    @Test
+    public void getSummary_available_returnExpectedDate() {
+        when(mFactory.batterySettingsFeatureProvider.isFirstUseDateAvailable(eq(mContext),
+                anyLong())).thenReturn(true);
+        mShadowBatteryManager.setLongProperty(BatteryManager.BATTERY_PROPERTY_FIRST_USAGE_DATE,
+                1669680000L);
+
+        final CharSequence result = mController.getSummary();
+
+        assertThat(result.toString()).isEqualTo("November 29, 2022");
+    }
+
+    @Test
+    public void getSummary_unavailable_returnNull() {
+        when(mFactory.batterySettingsFeatureProvider.isFirstUseDateAvailable(eq(mContext),
+                anyLong())).thenReturn(false);
+
+        assertThat(mController.getSummary()).isNull();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryManufactureDatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryManufactureDatePreferenceControllerTest.java
new file mode 100644
index 0000000..608ce00
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryManufactureDatePreferenceControllerTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.deviceinfo.batteryinfo;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
+
+import android.content.Context;
+import android.os.BatteryManager;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.testutils.FakeFeatureFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowBatteryManager;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBatteryManager.class})
+public class BatteryManufactureDatePreferenceControllerTest {
+
+    private BatteryManufactureDatePreferenceController mController;
+    private Context mContext;
+    private BatteryManager mBatteryManager;
+    private ShadowBatteryManager mShadowBatteryManager;
+    private FakeFeatureFactory mFactory;
+
+    @Before
+    public void setUp() {
+        mContext = ApplicationProvider.getApplicationContext();
+        mBatteryManager = mContext.getSystemService(BatteryManager.class);
+        mShadowBatteryManager = shadowOf(mBatteryManager);
+        mFactory = FakeFeatureFactory.setupForTest();
+        mController = new BatteryManufactureDatePreferenceController(mContext,
+                "battery_info_manufacture_date");
+    }
+
+    @Test
+    public void getAvailabilityStatus_dateAvailable_returnAvailable() {
+        when(mFactory.batterySettingsFeatureProvider.isManufactureDateAvailable(eq(mContext),
+                anyLong())).thenReturn(true);
+
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+    }
+
+    @Test
+    public void getAvailabilityStatus_dateUnavailable_returnNotAvailable() {
+        when(mFactory.batterySettingsFeatureProvider.isManufactureDateAvailable(eq(mContext),
+                anyLong())).thenReturn(false);
+
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
+    }
+
+    @Test
+    public void getSummary_available_returnExpectedDate() {
+        when(mFactory.batterySettingsFeatureProvider.isManufactureDateAvailable(eq(mContext),
+                anyLong())).thenReturn(true);
+        mShadowBatteryManager.setLongProperty(BatteryManager.BATTERY_PROPERTY_MANUFACTURING_DATE,
+                1669680000L);
+
+        final CharSequence result = mController.getSummary();
+
+        assertThat(result.toString()).isEqualTo("November 29, 2022");
+    }
+
+    @Test
+    public void getSummary_unavailable_returnNull() {
+        when(mFactory.batterySettingsFeatureProvider.isManufactureDateAvailable(eq(mContext),
+                anyLong())).thenReturn(false);
+
+        assertThat(mController.getSummary()).isNull();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImplTest.java
new file mode 100644
index 0000000..29f09ea
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImplTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.fuelgauge;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.eq;
+
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class BatterySettingsFeatureProviderImplTest {
+    private BatterySettingsFeatureProviderImpl mImpl;
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mImpl = new BatterySettingsFeatureProviderImpl();
+        mContext = ApplicationProvider.getApplicationContext();
+    }
+
+    @Test
+    public void isManufactureDateAvailable_returnFalse() {
+        assertThat(mImpl.isManufactureDateAvailable(eq(mContext), anyLong())).isFalse();
+    }
+
+    @Test
+    public void isFirstUseDateAvailable_returnFalse() {
+        assertThat(mImpl.isFirstUseDateAvailable(eq(mContext), anyLong())).isFalse();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTipTest.java
index 3513168..ecac4f9 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTipTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTipTest.java
@@ -18,11 +18,11 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.content.Context;
-import android.view.View;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.view.View;
 
-import androidx.annotation.IdRes;
+import androidx.annotation.DrawableRes;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceViewHolder;
 
@@ -45,7 +45,7 @@
 
     private static final String TITLE = "title";
     private static final String SUMMARY = "summary";
-    @IdRes
+    @DrawableRes
     private static final int ICON_ID = R.drawable.ic_fingerprint;
 
     private Context mContext;
diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockPatternTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockPatternTest.java
index 442d021..301a6db 100644
--- a/tests/robotests/src/com/android/settings/password/ChooseLockPatternTest.java
+++ b/tests/robotests/src/com/android/settings/password/ChooseLockPatternTest.java
@@ -18,12 +18,15 @@
 
 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
 
+import static com.android.settings.password.ChooseLockPattern.ChooseLockPatternFragment.KEY_UI_STAGE;
+
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.robolectric.RuntimeEnvironment.application;
 
 import android.content.Intent;
+import android.os.Bundle;
 import android.os.UserHandle;
 import android.view.View;
 
@@ -34,6 +37,8 @@
 import com.android.settings.password.ChooseLockPattern.IntentBuilder;
 import com.android.settings.testutils.shadow.ShadowUtils;
 
+import com.google.android.setupdesign.GlifLayout;
+
 import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -119,6 +124,21 @@
         assertThat(flags & FLAG_SECURE).isEqualTo(FLAG_SECURE);
     }
 
+    @Test
+    public void headerText_stageConfirmWrong() {
+        ChooseLockPattern activity = createActivity(true);
+        ChooseLockPatternFragment fragment = (ChooseLockPatternFragment)
+                activity.getSupportFragmentManager().findFragmentById(R.id.main_content);
+        final GlifLayout layout = fragment.getView().findViewById(R.id.setup_wizard_layout);
+        Bundle savedInstanceState = new Bundle();
+        savedInstanceState.putInt(KEY_UI_STAGE,
+                ChooseLockPatternFragment.Stage.ConfirmWrong.ordinal());
+
+        fragment.onViewCreated(layout, savedInstanceState);
+        assertThat(layout.getHeaderText().toString()).isEqualTo(activity.getResources().getString(
+                R.string.lockpassword_draw_your_pattern_again_header));
+    }
+
     private ChooseLockPattern createActivity(boolean addFingerprintExtra) {
         return Robolectric.buildActivity(
                 ChooseLockPattern.class,
diff --git a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
index b7d249d..4903a28 100644
--- a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
@@ -119,6 +119,7 @@
     private Context mContext;
     private SettingsSliceProvider mProvider;
     private ShadowPackageManager mPackageManager;
+    private ShadowUserManager mShadowUserManager;
 
     @Mock
     private SliceManager mManager;
@@ -157,6 +158,7 @@
         when(mManager.getPinnedSlices()).thenReturn(Collections.emptyList());
 
         mPackageManager = Shadows.shadowOf(mContext.getPackageManager());
+        mShadowUserManager = ShadowUserManager.getShadow();
 
         SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
     }
@@ -293,6 +295,37 @@
     }
 
     @Test
+    public void onBindSlice_guestRestricted_returnsNull() {
+        final String key = "enable_usb_tethering";
+        mShadowUserManager.setGuestUser(true);
+        final Uri testUri = new Uri.Builder()
+            .scheme(ContentResolver.SCHEME_CONTENT)
+            .authority(SettingsSliceProvider.SLICE_AUTHORITY)
+            .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
+            .appendPath(key)
+            .build();
+
+        final Slice slice = mProvider.onBindSlice(testUri);
+
+        assertThat(slice).isNull();
+    }
+
+    @Test
+    public void onBindSlice_notGuestRestricted_returnsNotNull() {
+        final String key = "enable_usb_tethering";
+        final Uri testUri = new Uri.Builder()
+            .scheme(ContentResolver.SCHEME_CONTENT)
+            .authority(SettingsSliceProvider.SLICE_AUTHORITY)
+            .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
+            .appendPath(key)
+            .build();
+
+        final Slice slice = mProvider.onBindSlice(testUri);
+
+        assertThat(slice).isNotNull();
+    }
+
+    @Test
     public void getDescendantUris_fullActionUri_returnsSelf() {
         final Collection<Uri> descendants = mProvider.onGetSliceDescendants(ACTION_SLICE_URI);
 
diff --git a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
index 29a6da3..fcb01b4 100644
--- a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -39,6 +39,7 @@
 import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
 import com.android.settings.gestures.AssistGestureFeatureProvider;
 import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
+import com.android.settings.inputmethod.KeyboardSettingsFeatureProvider;
 import com.android.settings.localepicker.LocaleFeatureProvider;
 import com.android.settings.overlay.DockUpdaterFeatureProvider;
 import com.android.settings.overlay.FeatureFactory;
@@ -95,6 +96,7 @@
     public AccessibilityMetricsFeatureProvider mAccessibilityMetricsFeatureProvider;
     public AdvancedVpnFeatureProvider mAdvancedVpnFeatureProvider;
     public WifiFeatureProvider mWifiFeatureProvider;
+    public KeyboardSettingsFeatureProvider mKeyboardSettingsFeatureProvider;
 
     /**
      * Call this in {@code @Before} method of the test class to use fake factory.
@@ -147,6 +149,7 @@
         mAccessibilityMetricsFeatureProvider = mock(AccessibilityMetricsFeatureProvider.class);
         mAdvancedVpnFeatureProvider = mock(AdvancedVpnFeatureProvider.class);
         mWifiFeatureProvider = mock(WifiFeatureProvider.class);
+        mKeyboardSettingsFeatureProvider = mock(KeyboardSettingsFeatureProvider.class);
     }
 
     @Override
@@ -170,7 +173,7 @@
     }
 
     @Override
-    public BatterySettingsFeatureProvider getBatterySettingsFeatureProvider(Context context) {
+    public BatterySettingsFeatureProvider getBatterySettingsFeatureProvider() {
         return batterySettingsFeatureProvider;
     }
 
@@ -303,4 +306,9 @@
     public WifiFeatureProvider getWifiFeatureProvider() {
         return mWifiFeatureProvider;
     }
+
+    @Override
+    public KeyboardSettingsFeatureProvider getKeyboardSettingsFeatureProvider() {
+        return mKeyboardSettingsFeatureProvider;
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java
index df38420..324a829 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java
@@ -55,6 +55,7 @@
     private int[] profileIdsForUser = new int[0];
     private boolean mUserSwitchEnabled;
     private Bundle mDefaultGuestUserRestriction = new Bundle();
+    private boolean mIsGuestUser = false;
 
     private @UserManager.UserSwitchabilityResult int mSwitchabilityStatus =
             UserManager.SWITCHABILITY_STATUS_OK;
@@ -270,4 +271,13 @@
             mUserProfileInfos.get(i).flags |= UserInfo.FLAG_ADMIN;
         }
     }
+
+    @Implementation
+    protected boolean isGuestUser() {
+        return mIsGuestUser;
+    }
+
+    public void setGuestUser(boolean isGuestUser) {
+        mIsGuestUser = isGuestUser;
+    }
 }
diff --git a/tests/spa_unit/src/com/android/settings/applications/specialaccess/DataSaverControllerTest.kt b/tests/spa_unit/src/com/android/settings/applications/specialaccess/DataSaverControllerTest.kt
new file mode 100644
index 0000000..c2413af
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/applications/specialaccess/DataSaverControllerTest.kt
@@ -0,0 +1,134 @@
+/*
+ * 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.applications.specialaccess
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.res.Resources
+import android.net.NetworkPolicyManager
+import android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.android.settings.applications.specialaccess.DataSaverController.Companion.getUnrestrictedSummary
+import com.android.settings.core.BasePreferenceController.AVAILABLE
+import com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE
+import com.android.settingslib.spaprivileged.model.app.AppListRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Spy
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.Mockito.`when` as whenever
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+class DataSaverControllerTest {
+    @get:Rule
+    val mockito: MockitoRule = MockitoJUnit.rule()
+
+    @Spy
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    @Spy
+    private val resources: Resources = context.resources
+
+    @Mock
+    private lateinit var networkPolicyManager: NetworkPolicyManager
+
+    @Mock
+    private lateinit var dataSaverController: DataSaverController
+
+    @Before
+    fun setUp() {
+        whenever(context.applicationContext).thenReturn(context)
+        whenever(context.resources).thenReturn(resources)
+        whenever(NetworkPolicyManager.from(context)).thenReturn(networkPolicyManager)
+
+        dataSaverController = DataSaverController(context, "key")
+    }
+
+    @Test
+    fun getAvailabilityStatus_whenConfigOn_available() {
+        whenever(resources.getBoolean(R.bool.config_show_data_saver)).thenReturn(true)
+        assertThat(dataSaverController.availabilityStatus).isEqualTo(AVAILABLE)
+    }
+
+    @Test
+    fun getAvailabilityStatus_whenConfigOff_unsupportedOnDevice() {
+        whenever(resources.getBoolean(R.bool.config_show_data_saver)).thenReturn(false)
+        assertThat(dataSaverController.availabilityStatus).isEqualTo(UNSUPPORTED_ON_DEVICE)
+    }
+
+    @Test
+    fun getUnrestrictedSummary_whenTwoAppsAllowed() = runTest {
+        whenever(
+            networkPolicyManager.getUidsWithPolicy(POLICY_ALLOW_METERED_BACKGROUND)
+        ).thenReturn(intArrayOf(APP1.uid, APP2.uid))
+
+        val summary =
+            getUnrestrictedSummary(context = context, appListRepository = FakeAppListRepository)
+
+        assertThat(summary)
+            .isEqualTo("2 apps allowed to use unrestricted data when Data Saver is on")
+    }
+
+    @Test
+    fun getUnrestrictedSummary_whenNoAppsAllowed() = runTest {
+        whenever(
+            networkPolicyManager.getUidsWithPolicy(POLICY_ALLOW_METERED_BACKGROUND)
+        ).thenReturn(intArrayOf())
+
+        val summary =
+            getUnrestrictedSummary(context = context, appListRepository = FakeAppListRepository)
+
+        assertThat(summary)
+            .isEqualTo("0 apps allowed to use unrestricted data when Data Saver is on")
+    }
+
+    private companion object {
+        val APP1 = ApplicationInfo().apply { uid = 10001 }
+        val APP2 = ApplicationInfo().apply { uid = 10002 }
+        val APP3 = ApplicationInfo().apply { uid = 10003 }
+
+        object FakeAppListRepository : AppListRepository {
+            override suspend fun loadApps(
+                userId: Int,
+                loadInstantApps: Boolean,
+                matchAnyUserForAdmin: Boolean,
+            ) = emptyList<ApplicationInfo>()
+
+            override fun showSystemPredicate(
+                userIdFlow: Flow<Int>,
+                showSystemFlow: Flow<Boolean>,
+            ): Flow<(app: ApplicationInfo) -> Boolean> = flowOf { false }
+
+            override fun getSystemPackageNamesBlocking(userId: Int): Set<String> = emptySet()
+
+            override suspend fun loadAndFilterApps(userId: Int, isSystemApp: Boolean) =
+                listOf(APP1, APP2, APP3)
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/datausage/DataUsageFormatterTest.kt b/tests/spa_unit/src/com/android/settings/datausage/DataUsageFormatterTest.kt
new file mode 100644
index 0000000..dc6a421
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/datausage/DataUsageFormatterTest.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.datausage
+
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.datausage.DataUsageFormatter.getBytesDisplayUnit
+import com.google.common.truth.Truth.assertThat
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class DataUsageFormatterTest {
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    @Test
+    fun getUnitDisplayName_megaByte() {
+        val displayName = context.resources.getBytesDisplayUnit(ONE_MEGA_BYTE_IN_BYTES)
+
+        assertThat(displayName).isEqualTo("MB")
+    }
+
+    @Test
+    fun getUnitDisplayName_gigaByte() {
+        val displayName = context.resources.getBytesDisplayUnit(ONE_GIGA_BYTE_IN_BYTES)
+
+        assertThat(displayName).isEqualTo("GB")
+    }
+
+    private companion object {
+        const val ONE_MEGA_BYTE_IN_BYTES = 1024L * 1024
+        const val ONE_GIGA_BYTE_IN_BYTES = 1024L * 1024 * 1024
+    }
+}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyStatusControlSessionTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyStatusControlSessionTest.kt
new file mode 100644
index 0000000..7e6a91b
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyStatusControlSessionTest.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.telephony
+
+import android.content.Context
+import androidx.lifecycle.testing.TestLifecycleOwner
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.core.BasePreferenceController
+import com.android.settingslib.spa.testutils.waitUntil
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+class TelephonyStatusControlSessionTest {
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    @Test
+    fun init() = runTest {
+        val controller = TestController(context)
+
+        val session = TelephonyStatusControlSession(
+            controllers = listOf(controller),
+            lifecycle = TestLifecycleOwner().lifecycle,
+        )
+
+        waitUntil { controller.availabilityStatus == STATUS }
+        session.close()
+    }
+
+    @Test
+    fun close() = runTest {
+        val controller = TestController(context)
+
+        val session = TelephonyStatusControlSession(
+            controllers = listOf(controller),
+            lifecycle = TestLifecycleOwner().lifecycle,
+        )
+        session.close()
+
+        assertThat(controller.availabilityStatus).isNull()
+    }
+
+    private companion object {
+        const val KEY = "key"
+        const val STATUS = BasePreferenceController.AVAILABLE
+    }
+
+    private class TestController(context: Context) : BasePreferenceController(context, KEY),
+        TelephonyAvailabilityHandler {
+
+        var availabilityStatus: Int? = null
+        override fun getAvailabilityStatus(): Int = STATUS
+
+        override fun setAvailabilityStatus(status: Int) {
+            availabilityStatus = status
+        }
+
+        override fun unsetAvailabilityStatus() {
+            availabilityStatus = null
+        }
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/spa/development/compat/PlatformCompatAppListModelTest.kt b/tests/spa_unit/src/com/android/settings/spa/development/compat/PlatformCompatAppListModelTest.kt
new file mode 100644
index 0000000..78aca85
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/development/compat/PlatformCompatAppListModelTest.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.development.compat
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.PackageInfoFlags
+import androidx.compose.runtime.State
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Spy
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.Mockito.`when` as whenever
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+class PlatformCompatAppListModelTest {
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    @get:Rule
+    val mockito: MockitoRule = MockitoJUnit.rule()
+
+    @Spy
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    @Mock
+    private lateinit var packageManager: PackageManager
+
+    private lateinit var listModel: PlatformCompatAppListModel
+
+    @Before
+    fun setUp() {
+        whenever(context.packageManager).thenReturn(packageManager)
+        whenever(packageManager.getInstalledPackagesAsUser(any<PackageInfoFlags>(), anyInt()))
+            .thenReturn(emptyList())
+        listModel = PlatformCompatAppListModel(context)
+    }
+
+    @Test
+    fun transform() = runTest {
+        val recordListFlow = listModel.transform(
+            userIdFlow = flowOf(USER_ID),
+            appListFlow = flowOf(listOf(APP)),
+        )
+
+        val recordList = recordListFlow.first()
+        assertThat(recordList).hasSize(1)
+        val record = recordList[0]
+        assertThat(record.app).isSameInstanceAs(APP)
+    }
+
+    @Test
+    fun getSummary() = runTest {
+        val summaryState = getSummaryState(APP)
+
+        assertThat(summaryState.value).isEqualTo(PACKAGE_NAME)
+    }
+
+    private fun getSummaryState(app: ApplicationInfo): State<String> {
+        lateinit var summary: State<String>
+        composeTestRule.setContent {
+            summary = listModel.getSummary(
+                option = 0,
+                record = PlatformCompatAppRecord(app),
+            )
+        }
+        return summary
+    }
+
+    private companion object {
+        const val USER_ID = 0
+        const val PACKAGE_NAME = "package.name"
+        val APP = ApplicationInfo().apply {
+            packageName = PACKAGE_NAME
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
index 99d4f32..68078f8 100644
--- a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
+++ b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
@@ -34,6 +34,7 @@
 import com.android.settings.fuelgauge.PowerUsageFeatureProvider
 import com.android.settings.gestures.AssistGestureFeatureProvider
 import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider
+import com.android.settings.inputmethod.KeyboardSettingsFeatureProvider
 import com.android.settings.localepicker.LocaleFeatureProvider
 import com.android.settings.overlay.DockUpdaterFeatureProvider
 import com.android.settings.overlay.FeatureFactory
@@ -84,9 +85,7 @@
         TODO("Not yet implemented")
     }
 
-    override fun getBatterySettingsFeatureProvider(
-        context: Context?,
-    ): BatterySettingsFeatureProvider {
+    override fun getBatterySettingsFeatureProvider(): BatterySettingsFeatureProvider {
         TODO("Not yet implemented")
     }
 
@@ -187,4 +186,8 @@
     override fun getWifiFeatureProvider(): WifiFeatureProvider {
         TODO("Not yet implemented")
     }
+
+    override fun getKeyboardSettingsFeatureProvider(): KeyboardSettingsFeatureProvider {
+        TODO("Not yet implemented")
+    }
 }
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 bdb45b0..2c830ad 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
@@ -379,7 +379,7 @@
 
         // Notify acquire message
         final int value = 33;
-        mCallbackWrapper.mValue.onPointerDown(value);
+        mCallbackWrapper.mValue.onUdfpsPointerDown(value);
         assertThat(liveData.getValue()).isEqualTo(value);
     }
 
@@ -397,7 +397,7 @@
 
         // Notify acquire message
         final int value = 44;
-        mCallbackWrapper.mValue.onPointerUp(value);
+        mCallbackWrapper.mValue.onUdfpsPointerUp(value);
         assertThat(liveData.getValue()).isEqualTo(value);
     }
 
diff --git a/tests/unit/src/com/android/settings/bluetooth/BlockingPrefWithSliceControllerTest.java b/tests/unit/src/com/android/settings/bluetooth/BlockingPrefWithSliceControllerTest.java
index 65b6977..d5a2585 100644
--- a/tests/unit/src/com/android/settings/bluetooth/BlockingPrefWithSliceControllerTest.java
+++ b/tests/unit/src/com/android/settings/bluetooth/BlockingPrefWithSliceControllerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.bluetooth;
 
+import static androidx.slice.builders.ListBuilder.ICON_IMAGE;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -24,8 +26,8 @@
 import static org.mockito.Mockito.verify;
 
 import android.app.PendingIntent;
-import android.content.Context;
 import android.content.ContentResolver;
+import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
 
@@ -42,20 +44,20 @@
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
-import com.android.settings.bluetooth.BlockingPrefWithSliceController;
-
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
+@RunWith(AndroidJUnit4.class)
 public class BlockingPrefWithSliceControllerTest {
     private static final String KEY = "bt_device_slice_category";
-    private static final String TEST_URI_AUTHORITY = "com.android.authority.test";
+    private static final String TEST_URI_AUTHORITY = "com.android.settings";
     private static final String TEST_EXTRA_INTENT = "EXTRA_INTENT";
     private static final String TEST_EXTRA_PENDING_INTENT = "EXTRA_PENDING_INTENT";
     private static final String TEST_INTENT_ACTION = "test";
@@ -71,6 +73,8 @@
     private LiveData<Slice> mLiveData;
     @Mock
     private PreferenceCategory mPreferenceCategory;
+    @Captor
+    ArgumentCaptor<Preference> mPreferenceArgumentCaptor;
 
     private Context mContext;
     private BlockingPrefWithSliceController mController;
@@ -130,6 +134,14 @@
         verify(mController.mPreferenceCategory).addPreference(any());
     }
 
+    @Test
+    public void onChanged_sliceWithoutValidIntent_makePreferenceUnselectable() {
+        mController.onChanged(buildTestSlice());
+
+        verify(mController.mPreferenceCategory).addPreference(mPreferenceArgumentCaptor.capture());
+        assertThat(mPreferenceArgumentCaptor.getValue().isSelectable()).isFalse();
+    }
+
     private Slice buildTestSlice() {
         Uri uri =
                 new Uri.Builder()
@@ -141,7 +153,7 @@
         IconCompat icon = mock(IconCompat.class);
         listBuilder.addRow(
                 new RowBuilder()
-                        .setTitleItem(icon, ListBuilder.ICON_IMAGE)
+                        .setTitleItem(icon, ICON_IMAGE)
                         .setTitle(TEST_SLICE_TITLE)
                         .setSubtitle(TEST_SLICE_SUBTITLE)
                         .setPrimaryAction(
@@ -153,7 +165,7 @@
                                                 PendingIntent.FLAG_UPDATE_CURRENT
                                                         | PendingIntent.FLAG_IMMUTABLE),
                                         icon,
-                                        ListBuilder.ICON_IMAGE,
+                                        ICON_IMAGE,
                                         "")));
         return listBuilder.build();
     }
diff --git a/tests/unit/src/com/android/settings/inputmethod/KeyboardSettingsFeatureProviderImplTest.java b/tests/unit/src/com/android/settings/inputmethod/KeyboardSettingsFeatureProviderImplTest.java
new file mode 100644
index 0000000..6675d5a
--- /dev/null
+++ b/tests/unit/src/com/android/settings/inputmethod/KeyboardSettingsFeatureProviderImplTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.inputmethod;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.Looper;
+
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class KeyboardSettingsFeatureProviderImplTest {
+
+    private Context mContext;
+    private KeyboardSettingsFeatureProviderImpl mFeatureProvider;
+
+    @Before
+    public void setUp() {
+        mContext = ApplicationProvider.getApplicationContext();
+        mFeatureProvider = new KeyboardSettingsFeatureProviderImpl();
+    }
+
+    @Test
+    public void supportsFirmwareUpdate_defaultValue_returnsFalse() {
+        assertThat(mFeatureProvider.supportsFirmwareUpdate()).isFalse();
+    }
+
+    @Test
+    public void addFirmwareUpdateCategory_defaultValue_returnsFalse() {
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+        PreferenceManager preferenceManager = new PreferenceManager(mContext);
+        PreferenceScreen screen = preferenceManager.createPreferenceScreen(mContext);
+
+        assertThat(mFeatureProvider.addFirmwareUpdateCategory(mContext, screen)).isFalse();
+    }
+
+    @Test
+    public void getActionKeyIcon_defaultValue_returnsNull() {
+        assertThat(mFeatureProvider.getActionKeyIcon(mContext)).isNull();
+    }
+}
diff --git a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
index 697217b..7a49865 100644
--- a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -37,6 +37,7 @@
 import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
 import com.android.settings.gestures.AssistGestureFeatureProvider;
 import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
+import com.android.settings.inputmethod.KeyboardSettingsFeatureProvider;
 import com.android.settings.localepicker.LocaleFeatureProvider;
 import com.android.settings.overlay.DockUpdaterFeatureProvider;
 import com.android.settings.overlay.FeatureFactory;
@@ -90,6 +91,7 @@
     public AccessibilityMetricsFeatureProvider mAccessibilityMetricsFeatureProvider;
     public AdvancedVpnFeatureProvider mAdvancedVpnFeatureProvider;
     public WifiFeatureProvider mWifiFeatureProvider;
+    public KeyboardSettingsFeatureProvider mKeyboardSettingsFeatureProvider;
 
     /**
      * Call this in {@code @Before} method of the test class to use fake factory.
@@ -133,6 +135,7 @@
         mAccessibilityMetricsFeatureProvider = mock(AccessibilityMetricsFeatureProvider.class);
         mAdvancedVpnFeatureProvider = mock(AdvancedVpnFeatureProvider.class);
         mWifiFeatureProvider = mock(WifiFeatureProvider.class);
+        mKeyboardSettingsFeatureProvider = mock(KeyboardSettingsFeatureProvider.class);
     }
 
     @Override
@@ -156,7 +159,7 @@
     }
 
     @Override
-    public BatterySettingsFeatureProvider getBatterySettingsFeatureProvider(Context context) {
+    public BatterySettingsFeatureProvider getBatterySettingsFeatureProvider() {
         return batterySettingsFeatureProvider;
     }
 
@@ -289,4 +292,9 @@
     public WifiFeatureProvider getWifiFeatureProvider() {
         return mWifiFeatureProvider;
     }
+
+    @Override
+    public KeyboardSettingsFeatureProvider getKeyboardSettingsFeatureProvider() {
+        return mKeyboardSettingsFeatureProvider;
+    }
 }