Merge "Adds the strings for ODAD content protection setting page." into main
diff --git a/Android.bp b/Android.bp
index f5b593d..a56cc19 100644
--- a/Android.bp
+++ b/Android.bp
@@ -117,11 +117,10 @@
         "factory_reset_flags_lib",
         "android.content.pm.flags-aconfig-java",
         "FingerprintManagerInteractor",
+        "notification_flags_lib",
     ],
 
-    plugins: [
-        "androidx.room_room-compiler-plugin",
-    ],
+    plugins: ["androidx.room_room-compiler-plugin"],
 
     errorprone: {
         extra_check_modules: ["//external/nullaway:nullaway_plugin"],
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 31f4768..9e33467 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -3198,6 +3198,7 @@
                 <action android:name="android.intent.action.BOOT_COMPLETED"/>
                 <action android:name="com.google.android.setupwizard.SETUP_WIZARD_FINISHED"/>
                 <action android:name="com.android.settings.battery.action.PERIODIC_JOB_RECHECK"/>
+                <action android:name="android.intent.action.TIME_SET"/>
             </intent-filter>
         </receiver>
 
diff --git a/res/drawable/ic_battery_uninstalled.xml b/res/drawable/ic_battery_uninstalled.xml
new file mode 100644
index 0000000..2d8ea86
--- /dev/null
+++ b/res/drawable/ic_battery_uninstalled.xml
@@ -0,0 +1,33 @@
+<!--
+  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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24.0dp"
+    android:height="24.0dp"
+    android:tint="@color/battery_usage_system_icon_color"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M15,4V3H9v1H4v2h1v13c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V6h1V4H15zM17,19H7V6h10V19z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M9,8h2v9h-2z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M13,8h2v9h-2z" />
+</vector>
diff --git a/res/layout/fingerprint_v2_enroll_introduction.xml b/res/layout/fingerprint_v2_enroll_introduction.xml
index e9dd08a..2fd1f9c 100644
--- a/res/layout/fingerprint_v2_enroll_introduction.xml
+++ b/res/layout/fingerprint_v2_enroll_introduction.xml
@@ -16,199 +16,210 @@
   -->
 
 <com.google.android.setupdesign.GlifLayout
-xmlns:android="http://schemas.android.com/apk/res/android"
-xmlns:app="http://schemas.android.com/apk/res-auto"
-style="?attr/fingerprint_layout_theme"
-android:id="@+id/setup_wizard_layout"
-android:layout_width="match_parent"
-android:layout_height="match_parent">
-
-<LinearLayout
-    style="@style/SudContentFrame"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/setup_wizard_layout"
+    style="?attr/fingerprint_layout_theme"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:clipChildren="false"
-    android:clipToPadding="false"
-    android:orientation="vertical">
+    android:layout_height="match_parent">
 
-    <com.google.android.setupdesign.view.RichTextView
-        android:id="@+id/error_text"
-        style="@style/SudDescription.Glif"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content" />
-
-    <FrameLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content">
-
-        <ImageView
-            style="@style/SudContentIllustration"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:contentDescription="@null"
-            android:src="@drawable/fingerprint_enroll_introduction" />
-
-    </FrameLayout>
-
-    <!-- Contains the extra information text at the bottom -->
     <LinearLayout
+        style="@style/SudContentFrame"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
+        android:clipChildren="false"
+        android:clipToPadding="false"
         android:orientation="vertical">
 
-        <!-- How it works -->
-        <TextView
+        <com.google.android.setupdesign.view.RichTextView
+            android:id="@+id/error_text"
+            style="@style/SudDescription.Glif"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            style="@style/BiometricEnrollIntroTitle"
-            android:text="@string/security_settings_fingerprint_v2_enroll_introduction_footer_title_2" />
+            android:layout_height="wrap_content" />
 
+        <FrameLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <ImageView
+                style="@style/SudContentIllustration"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:contentDescription="@null"
+                android:src="@drawable/fingerprint_enroll_introduction" />
+
+        </FrameLayout>
+
+        <!-- Contains the extra information text at the bottom -->
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:orientation="horizontal">
+            android:orientation="vertical">
 
-            <ImageView
-                android:id="@+id/icon_fingerprint"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:contentDescription="@null"
-                android:src="@drawable/ic_fingerprint_24dp"/>
-            <Space
-                android:layout_width="16dp"
-                android:layout_height="wrap_content"/>
+            <!-- How it works -->
             <TextView
-                android:id="@+id/footer_message_2"
+                style="@style/BiometricEnrollIntroTitle"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                style="@style/BiometricEnrollIntroMessage" />
-        </LinearLayout>
+                android:text="@string/security_settings_fingerprint_v2_enroll_introduction_footer_title_2" />
 
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal">
-
-            <ImageView
-                android:id="@+id/icon_device_locked"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:contentDescription="@null"
-                android:src="@drawable/ic_lock_24dp"/>
-            <Space
-                android:layout_width="16dp"
-                android:layout_height="wrap_content"/>
-            <TextView
-                android:id="@+id/footer_message_3"
+            <LinearLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                style="@style/BiometricEnrollIntroMessage" />
-        </LinearLayout>
+                android:orientation="horizontal">
 
-        <!-- You're in control -->
-        <TextView
-            android:id="@+id/footer_title_1"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            style="@style/BiometricEnrollIntroTitle" />
+                <ImageView
+                    android:id="@+id/icon_fingerprint"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:contentDescription="@null"
+                    android:src="@drawable/ic_fingerprint_24dp" />
 
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal">
+                <Space
+                    android:layout_width="16dp"
+                    android:layout_height="wrap_content" />
 
-            <ImageView
-                android:id="@+id/icon_trash_can"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:contentDescription="@null"
-                android:src="@drawable/ic_trash_can"/>
-            <Space
-                android:layout_width="16dp"
-                android:layout_height="wrap_content"/>
-            <TextView
-                android:id="@+id/footer_message_4"
+                <TextView
+                    android:id="@+id/footer_message_2"
+                    style="@style/BiometricEnrollIntroMessage"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content" />
+            </LinearLayout>
+
+            <LinearLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                style="@style/BiometricEnrollIntroMessage" />
-        </LinearLayout>
+                android:orientation="horizontal">
 
-        <!-- Keep in mind -->
-        <TextView
-            android:id="@+id/footer_title_2"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            style="@style/BiometricEnrollIntroTitle"
-            android:text="@string/security_settings_face_enroll_introduction_info_title"/>
+                <ImageView
+                    android:id="@+id/icon_device_locked"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:contentDescription="@null"
+                    android:src="@drawable/ic_lock_24dp" />
 
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal">
+                <Space
+                    android:layout_width="16dp"
+                    android:layout_height="wrap_content" />
 
-            <ImageView
-                android:id="@+id/icon_info"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:contentDescription="@null"
-                android:src="@drawable/ic_info_outline_24dp"/>
-            <Space
-                android:layout_width="16dp"
-                android:layout_height="wrap_content"/>
+                <TextView
+                    android:id="@+id/footer_message_3"
+                    style="@style/BiometricEnrollIntroMessage"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content" />
+            </LinearLayout>
+
+            <!-- You're in control -->
             <TextView
-                android:id="@+id/footer_message_5"
+                android:id="@+id/footer_title_1"
+                style="@style/BiometricEnrollIntroTitle"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+
+            <LinearLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                style="@style/BiometricEnrollIntroMessage" />
-        </LinearLayout>
+                android:orientation="horizontal">
 
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal">
+                <ImageView
+                    android:id="@+id/icon_trash_can"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:contentDescription="@null"
+                    android:src="@drawable/ic_trash_can" />
 
-            <ImageView
-                android:id="@+id/icon_shield"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:contentDescription="@null"
-                android:src="@drawable/ic_guarantee"/>
-            <Space
-                android:layout_width="16dp"
-                android:layout_height="wrap_content"/>
+                <Space
+                    android:layout_width="16dp"
+                    android:layout_height="wrap_content" />
+
+                <TextView
+                    android:id="@+id/footer_message_4"
+                    style="@style/BiometricEnrollIntroMessage"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content" />
+            </LinearLayout>
+
+            <!-- Keep in mind -->
             <TextView
-                android:id="@+id/footer_message_6"
+                android:id="@+id/footer_title_2"
+                style="@style/BiometricEnrollIntroTitle"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                style="@style/BiometricEnrollIntroMessage" />
-        </LinearLayout>
+                android:text="@string/security_settings_face_enroll_introduction_info_title" />
 
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal">
-
-            <ImageView
-                android:id="@+id/icon_link"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:contentDescription="@null"
-                android:src="@drawable/ic_link_24dp"/>
-            <Space
-                android:layout_width="16dp"
-                android:layout_height="wrap_content"/>
-            <TextView
-                android:id="@+id/footer_learn_more"
-                android:linksClickable="true"
+            <LinearLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                style="@style/BiometricEnrollIntroMessage"
-                android:paddingBottom="0dp"
-                android:text="@string/security_settings_fingerprint_v2_enroll_introduction_message_learn_more" />
+                android:orientation="horizontal">
+
+                <ImageView
+                    android:id="@+id/icon_info"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:contentDescription="@null"
+                    android:src="@drawable/ic_info_outline_24dp" />
+
+                <Space
+                    android:layout_width="16dp"
+                    android:layout_height="wrap_content" />
+
+                <TextView
+                    android:id="@+id/footer_message_5"
+                    style="@style/BiometricEnrollIntroMessage"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content" />
+            </LinearLayout>
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal">
+
+                <ImageView
+                    android:id="@+id/icon_shield"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:contentDescription="@null"
+                    android:src="@drawable/ic_guarantee" />
+
+                <Space
+                    android:layout_width="16dp"
+                    android:layout_height="wrap_content" />
+
+                <TextView
+                    android:id="@+id/footer_message_6"
+                    style="@style/BiometricEnrollIntroMessage"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content" />
+            </LinearLayout>
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal">
+
+                <ImageView
+                    android:id="@+id/icon_link"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:contentDescription="@null"
+                    android:src="@drawable/ic_link_24dp" />
+
+                <Space
+                    android:layout_width="16dp"
+                    android:layout_height="wrap_content" />
+
+                <TextView
+                    android:id="@+id/footer_learn_more"
+                    style="@style/BiometricEnrollIntroMessage"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:linksClickable="true"
+                    android:paddingBottom="0dp"
+                    android:text="@string/security_settings_fingerprint_v2_enroll_introduction_message_learn_more" />
+            </LinearLayout>
+
         </LinearLayout>
 
     </LinearLayout>
 
-</LinearLayout>
-
 </com.google.android.setupdesign.GlifLayout>
\ No newline at end of file
diff --git a/res/layout/preference_icon_credman.xml b/res/layout/preference_icon_credman.xml
new file mode 100644
index 0000000..3db0c3b
--- /dev/null
+++ b/res/layout/preference_icon_credman.xml
@@ -0,0 +1,98 @@
+<?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.
+  -->
+
+<!-- Based off preference_material_settings.xml except that ripple on only on the left side. -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:gravity="center_vertical"
+    android:background="@android:color/transparent"
+    android:clipToPadding="false">
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:background="?android:attr/selectableItemBackground"
+        android:gravity="start|center_vertical"
+        android:clipToPadding="false"
+        android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+        <LinearLayout
+            android:id="@+id/icon_frame"
+            android:layout_width="48dp"
+            android:layout_height="40dp"
+            android:gravity="start|center_vertical"
+            android:minWidth="56dp"
+            android:orientation="horizontal"
+            android:clipToPadding="false"
+            android:paddingTop="4dp"
+            android:paddingBottom="4dp">
+            <androidx.preference.internal.PreferenceImageView
+                android:id="@android:id/icon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                settings:maxWidth="32dp"
+                settings:maxHeight="32dp" />
+        </LinearLayout>
+
+        <RelativeLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:paddingTop="16dp"
+            android:paddingBottom="16dp">
+
+            <TextView
+                android:id="@android:id/title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:singleLine="true"
+                android:textAppearance="?android:attr/textAppearanceListItem"
+                android:ellipsize="marquee" />
+
+            <TextView
+                android:id="@android:id/summary"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_below="@android:id/title"
+                android:layout_alignStart="@android:id/title"
+                android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+                android:textColor="?android:attr/textColorSecondary"
+                android:maxLines="10" />
+
+        </RelativeLayout>
+
+    </LinearLayout>
+
+    <include layout="@layout/preference_two_target_divider" />
+
+    <!-- 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:paddingEnd="16dp"
+        android:minWidth="61dp"
+        android:gravity="center"
+        android:orientation="vertical" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 01a8126..484af0a 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -1474,4 +1474,19 @@
     <string-array name="terms_of_address_supported_languages">
         <item>fr</item> <!-- French -->
     </string-array>
+
+    <!-- Values for notification cooldown -->
+    <string-array name="notification_polite_options">
+        <item>@string/notification_polite_all_apps</item>
+        <item>@string/notification_polite_conversations</item>
+        <item>@string/notification_polite_disabled</item>
+    </string-array>
+
+    <!-- Values for notification cooldown -->
+    <string-array name="notification_polite_options_values" translatable="false">
+        <item>0</item>
+        <item>1</item>
+        <item>2</item>
+    </string-array>
+
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 84ddb5a..7a467d5 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -5567,8 +5567,10 @@
     <string name="battery_usage_without_time"></string>
     <!-- Description for the specific time slot in the battery usage page [CHAR LIMIT=120] -->
     <string name="battery_usage_since_last_full_charge">since last full charge</string>
-   <!-- Description for system apps aggregated battery usage data [CHAR LIMIT=120] -->
+    <!-- Description for system apps aggregated battery usage data [CHAR LIMIT=120] -->
     <string name="battery_usage_system_apps">System apps</string>
+    <!-- Description for uninstalled apps aggregated battery usage data [CHAR LIMIT=120] -->
+    <string name="battery_usage_uninstalled_apps">Uninstalled apps</string>
     <!-- Description for others battery usage data [CHAR LIMIT=120] -->
     <string name="battery_usage_others">Others</string>
 
@@ -6184,6 +6186,8 @@
     <string name="account_settings_menu_auto_sync_personal">Auto-sync personal data</string>
     <!--  Title for menu option to enable global auto-sync of work account data [CHAR LIMIT=30] -->
     <string name="account_settings_menu_auto_sync_work">Auto-sync work data</string>
+    <!--  Title for menu option to enable global auto-sync of private account data [CHAR LIMIT=30] -->
+    <string name="account_settings_menu_auto_sync_private">Auto-sync private data</string>
 
     <!-- Title for option to change data usage cycle day. [CHAR LIMIT=32] -->
     <string name="data_usage_change_cycle">Change cycle\u2026</string>
@@ -8210,6 +8214,20 @@
     <string name="notif_listener_more_settings">More settings</string>
     <string name="notif_listener_more_settings_desc">More settings are available inside this app</string>
 
+    <!-- Title for Polite Notifications setting  [CHAR LIMIT=30]-->
+    <string name="notification_polite_title">Notification cooldown</string>
+    <string name="notification_polite_all_apps">Apply cooldown to all notifications</string>
+    <string name="notification_polite_all_apps_summary">Gradually lower the notification volume when you get many successive notifications from the same app</string>
+    <string name="notification_polite_conversations">Apply cooldown to conversations</string>
+    <string name="notification_polite_conversations_summary">Gradually lower the notification volume when you get many messages from the same chat within a short period of time</string>
+    <string name="notification_polite_disabled">Don\'t use notification cooldown</string>
+    <string name="notification_polite_disabled_summary">Never lower notification volume, regardless of the amount of successive notifications from the same app</string>
+    <string name="notification_polite_vibrate_unlocked">Vibrate when unlocked</string>
+    <string name="notification_polite_vibrate_unlocked_summary">Only vibrate when screen is unlocked</string>
+    <string name="notification_polite_work">Apply to work profiles</string>
+    <string name="notification_polite_work_summary">Apply the notification cooldown settings from your personal profile to your work profile</string>
+
+
     <!-- Title for managing VR (virtual reality) helper services. [CHAR LIMIT=50] -->
     <string name="vr_listeners_title">VR helper services</string>
 
diff --git a/res/xml/accounts_private_dashboard_settings.xml b/res/xml/accounts_private_dashboard_settings.xml
new file mode 100644
index 0000000..79bcce9
--- /dev/null
+++ b/res/xml/accounts_private_dashboard_settings.xml
@@ -0,0 +1,66 @@
+<?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:key="user_and_account_settings_screen"
+    android:title="@string/account_dashboard_title"
+    settings:keywords="@string/keywords_accounts">
+
+    <PreferenceCategory
+        android:key="passwords_category"
+        android:order="10"
+        android:persistent="false"
+        android:title="@string/autofill_passwords"
+        settings:controller="com.android.settings.applications.autofill.PasswordsPreferenceController"
+        settings:keywords="@string/autofill_keywords" />
+
+    <PreferenceCategory
+        android:key="default_service_category"
+        android:order="20"
+        android:title="@string/autofill_app">
+
+        <com.android.settings.widget.GearPreference
+            android:fragment="com.android.settings.applications.defaultapps.DefaultAutofillPicker"
+            android:key="default_autofill_private"
+            android:title="@string/autofill_app"
+            settings:keywords="@string/autofill_keywords">
+            <extra
+                android:name="for_work"
+                android:value="false" />
+        </com.android.settings.widget.GearPreference>
+    </PreferenceCategory>
+
+    <PreferenceCategory
+        android:key="dashboard_tile_placeholder"
+        android:order="130"/>
+
+    <SwitchPreference
+        android:key="auto_sync_account_data"
+        android:title="@string/auto_sync_account_title"
+        android:summary="@string/auto_sync_account_summary"
+        android:order="200"
+        settings:allowDividerAbove="true"/>
+
+    <SwitchPreference
+        android:key="auto_sync_private_account_data"
+        android:title="@string/account_settings_menu_auto_sync_private"
+        android:summary="@string/auto_sync_account_summary"
+        android:order="210"/>
+
+</PreferenceScreen>
diff --git a/res/xml/accounts_private_dashboard_settings_credman.xml b/res/xml/accounts_private_dashboard_settings_credman.xml
new file mode 100644
index 0000000..88b2736
--- /dev/null
+++ b/res/xml/accounts_private_dashboard_settings_credman.xml
@@ -0,0 +1,67 @@
+<?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:key="user_and_account_settings_screen"
+    android:title="@string/account_dashboard_title"
+    settings:keywords="@string/keywords_accounts">
+
+    <PreferenceCategory
+        android:key="default_service_category"
+        android:order="10"
+        android:title="@string/credman_chosen_app_title">
+
+        <com.android.settings.widget.GearPreference
+            android:fragment="com.android.settings.applications.credentials.DefaultCombinedPickerPrivate"
+            android:key="default_credman_autofill_private"
+            android:title="@string/credman_chosen_app_title"
+            settings:searchable="false">
+            settings:keywords="@string/credman_autofill_keywords">
+            <extra
+                android:name="for_work"
+                android:value="false" />
+        </com.android.settings.widget.GearPreference>
+    </PreferenceCategory>
+
+    <PreferenceCategory
+        android:key="credman_category"
+        android:order="20"
+        android:persistent="false"
+        android:title="@string/credman_credentials"
+        settings:controller="com.android.settings.applications.credentials.CredentialManagerPreferenceController"
+        settings:keywords="@string/credman_keywords" />
+
+    <PreferenceCategory
+        android:key="dashboard_tile_placeholder"
+        android:order="130"/>
+
+    <SwitchPreference
+        android:key="auto_sync_account_data"
+        android:title="@string/auto_sync_account_title"
+        android:summary="@string/auto_sync_account_summary"
+        android:order="200"
+        settings:allowDividerAbove="true"/>
+
+    <SwitchPreference
+        android:key="auto_sync_private_account_data"
+        android:title="@string/account_settings_menu_auto_sync_private"
+        android:summary="@string/auto_sync_account_summary"
+        android:order="210"/>
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/res/xml/apps.xml b/res/xml/apps.xml
index 85d187c..c4313f6 100644
--- a/res/xml/apps.xml
+++ b/res/xml/apps.xml
@@ -111,9 +111,7 @@
         android:key="advanced_category"
         android:title="@string/advanced_apps"
         android:order="21"
-        android:visibility="gone"
-        settings:searchable="false"
-        settings:controller="com.android.settings.widget.PreferenceCategoryController">
+        settings:searchable="false">
 
         <Preference
             android:key="aspect_ratio_apps"
diff --git a/res/xml/configure_notification_settings.xml b/res/xml/configure_notification_settings.xml
index 32081cf..ee88215 100644
--- a/res/xml/configure_notification_settings.xml
+++ b/res/xml/configure_notification_settings.xml
@@ -180,5 +180,13 @@
             android:title="@string/notification_assistant_title"
             android:summary="@string/notification_assistant_summary"
             settings:controller="com.android.settings.notification.NotificationAssistantPreferenceController"/>
+
+        <Preference
+            android:fragment="com.android.settings.notification.PoliteNotificationsPreferenceFragment"
+            android:key="polite_notifications_preference"
+            android:order="26"
+            android:persistent="false"
+            android:title="@string/notification_polite_title"
+            settings:controller="com.android.settings.notification.PoliteNotificationsPreferenceController" />
     </PreferenceCategory>
 </PreferenceScreen>
diff --git a/res/xml/polite_notifications_settings.xml b/res/xml/polite_notifications_settings.xml
new file mode 100644
index 0000000..5e09c3b
--- /dev/null
+++ b/res/xml/polite_notifications_settings.xml
@@ -0,0 +1,44 @@
+<?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/notification_polite_title">
+
+    <com.android.settings.RestrictedListPreference
+        android:key="polite_notifications_pref_dlg"
+        android:persistent="false"
+        android:title="@string/notification_polite_title"
+        android:entries="@array/notification_polite_options"
+        android:entryValues="@array/notification_polite_options_values"
+        settings:controller="com.android.settings.notification.PoliteNotificationFilterController"/>
+
+    <SwitchPreference
+        android:key="notification_polite_vibrate_unlocked"
+        android:persistent="false"
+        android:title="@string/notification_polite_vibrate_unlocked"
+        android:summary="@string/notification_polite_vibrate_unlocked_summary"
+        settings:controller="com.android.settings.notification.PoliteNotifVibrateUnlockedToggleController" />
+
+    <SwitchPreference
+        android:key="notification_polite_work_profile"
+        android:persistent="false"
+        android:title="@string/notification_polite_work"
+        android:summary="@string/notification_polite_work_summary"
+        settings:controller="com.android.settings.notification.PoliteNotifWorkProfileToggleController" />
+
+</PreferenceScreen>
diff --git a/res/xml/wifi_settings.xml b/res/xml/wifi_settings.xml
deleted file mode 100644
index eadea68..0000000
--- a/res/xml/wifi_settings.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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/wifi_settings"
-        settings:keywords="@string/keywords_wifi">
-
-    <com.android.settings.wifi.LinkablePreference
-        android:key="wifi_status_message"/>
-
-    <PreferenceCategory
-        android:key="connected_access_point"
-        android:layout="@layout/preference_category_no_label"/>
-
-    <PreferenceCategory
-        android:key="access_points"
-        android:layout="@layout/preference_category_no_label"/>
-
-    <Preference
-        android:key="configure_wifi_settings"
-        android:title="@string/wifi_configure_settings_preference_title"
-        settings:allowDividerAbove="true"
-        android:fragment="com.android.settings.wifi.ConfigureWifiSettings"/>
-
-    <Preference
-        android:key="saved_networks"
-        android:title="@string/wifi_saved_access_points_label"
-        android:fragment="com.android.settings.wifi.savedaccesspoints2.SavedAccessPointsWifiSettings2"/>
-
-    <com.android.settings.datausage.DataUsagePreference
-        android:key="wifi_data_usage"
-        android:title="@string/wifi_data_usage"/>
-</PreferenceScreen>
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index b41650c..29eabdb 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -18,6 +18,9 @@
 
 import static android.content.Intent.EXTRA_USER;
 import static android.content.Intent.EXTRA_USER_ID;
+import static android.os.UserManager.USER_TYPE_FULL_SYSTEM;
+import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
+import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE;
 import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
 import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
 
@@ -63,6 +66,7 @@
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Flags;
 import android.os.IBinder;
 import android.os.INetworkManagementService;
 import android.os.RemoteException;
@@ -111,6 +115,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.settings.dashboard.profileselector.ProfileFragmentBridge;
 import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
+import com.android.settings.dashboard.profileselector.ProfileSelectFragment.ProfileType;
 import com.android.settings.password.ChooseLockSettingsHelper;
 import com.android.settingslib.widget.ActionBarShadowController;
 import com.android.settingslib.widget.AdaptiveIcon;
@@ -118,6 +123,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
+import java.util.Objects;
 import java.util.Set;
 
 public final class Utils extends com.android.settingslib.Utils {
@@ -441,6 +447,38 @@
     }
 
     /**
+     * Returns the profile of userType of the current user or {@code null} if none is found or a
+     * profile exists, but it is disabled.
+     */
+    @Nullable
+    public static UserHandle getProfileOfType(
+            @NonNull UserManager userManager, @ProfileType int userType) {
+        final List<UserHandle> userProfiles = userManager.getUserProfiles();
+        String umUserType = getUmUserType(userType);
+        for (UserHandle profile : userProfiles) {
+            if (profile.getIdentifier() == UserHandle.myUserId()) {
+                continue;
+            }
+            final UserInfo userInfo = userManager.getUserInfo(profile.getIdentifier());
+            if (Objects.equals(umUserType, userInfo.userType)) {
+                return profile;
+            }
+        }
+        return null;
+    }
+
+    private static String getUmUserType(@ProfileType int userType) throws IllegalArgumentException {
+        if (userType == ProfileType.WORK) {
+            return USER_TYPE_PROFILE_MANAGED;
+        } else if (userType == ProfileType.PRIVATE) {
+            return USER_TYPE_PROFILE_PRIVATE;
+        } else if (userType == ProfileType.PERSONAL) {
+            return USER_TYPE_FULL_SYSTEM;
+        }
+        throw new IllegalArgumentException("Cannot get user type for ALL types");
+    }
+
+    /**
      * Returns the managed profile of the current user or {@code null} if none is found. Unlike
      * {@link #getManagedProfile} this method returns enabled and disabled managed profiles.
      */
@@ -479,15 +517,20 @@
         return UserHandle.USER_NULL;
     }
 
-    /** Returns user ID of current user, throws IllegalStateException if it's not available. */
-    public static int getCurrentUserId(UserManager userManager, boolean isWorkProfile)
-            throws IllegalStateException {
-        if (isWorkProfile) {
-            final UserHandle managedUserHandle = getManagedProfile(userManager);
-            if (managedUserHandle == null) {
-                throw new IllegalStateException("Work profile user ID is not available.");
+    /**
+     * Returns user ID of the user of specified type under the current context, throws
+     * IllegalStateException if it's not available.
+     */
+    public static int getCurrentUserIdOfType(
+            @NonNull UserManager userManager,
+            @ProfileType int userType) throws IllegalStateException {
+        if (userType != ProfileType.PERSONAL) {
+            final UserHandle userHandle = getProfileOfType(userManager, userType);
+            if (userHandle == null) {
+                throw new IllegalStateException("User ID of requested profile type is not "
+                        + "available.");
             }
-            return managedUserHandle.getIdentifier();
+            return userHandle.getIdentifier();
         }
         return UserHandle.myUserId();
     }
@@ -1223,8 +1266,10 @@
         List<UserHandle> profiles = userManager.getUserProfiles();
         for (UserHandle userHandle : profiles) {
             UserProperties userProperties = userManager.getUserProperties(userHandle);
-            if (userProperties.getShowInSettings()
-                    == UserProperties.SHOW_IN_SETTINGS_SEPARATE) {
+            if (userProperties.getShowInSettings() == UserProperties.SHOW_IN_SETTINGS_SEPARATE) {
+                if (Flags.allowPrivateProfile() && userProperties.getHideInSettingsInQuietMode()) {
+                    return !userManager.isQuietModeEnabled(userHandle);
+                }
                 return true;
             }
         }
diff --git a/src/com/android/settings/accounts/AccountDashboardFragment.java b/src/com/android/settings/accounts/AccountDashboardFragment.java
index 7816fd7..81aefd9 100644
--- a/src/com/android/settings/accounts/AccountDashboardFragment.java
+++ b/src/com/android/settings/accounts/AccountDashboardFragment.java
@@ -31,14 +31,17 @@
 import com.android.settings.applications.autofill.PasswordsPreferenceController;
 import com.android.settings.applications.credentials.CredentialManagerPreferenceController;
 import com.android.settings.applications.credentials.DefaultCombinedPreferenceController;
+import com.android.settings.applications.credentials.DefaultPrivateCombinedPreferenceController;
 import com.android.settings.applications.credentials.DefaultWorkCombinedPreferenceController;
 import com.android.settings.applications.defaultapps.DefaultAutofillPreferenceController;
+import com.android.settings.applications.defaultapps.DefaultPrivateAutofillPreferenceController;
 import com.android.settings.applications.defaultapps.DefaultWorkAutofillPreferenceController;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.users.AutoSyncDataPreferenceController;
 import com.android.settings.users.AutoSyncPersonalDataPreferenceController;
+import com.android.settings.users.AutoSyncPrivateDataPreferenceController;
 import com.android.settings.users.AutoSyncWorkDataPreferenceController;
 import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.search.SearchIndexable;
@@ -111,9 +114,11 @@
         if (CredentialManager.isServiceEnabled(context)) {
             controllers.add(new DefaultCombinedPreferenceController(context));
             controllers.add(new DefaultWorkCombinedPreferenceController(context));
+            controllers.add(new DefaultPrivateCombinedPreferenceController(context));
         } else {
             controllers.add(new DefaultAutofillPreferenceController(context));
             controllers.add(new DefaultWorkAutofillPreferenceController(context));
+            controllers.add(new DefaultPrivateAutofillPreferenceController(context));
         }
     }
 
@@ -132,6 +137,7 @@
         controllers.add(new AutoSyncDataPreferenceController(context, parent));
         controllers.add(new AutoSyncPersonalDataPreferenceController(context, parent));
         controllers.add(new AutoSyncWorkDataPreferenceController(context, parent));
+        controllers.add(new AutoSyncPrivateDataPreferenceController(context, parent));
     }
 
     private static int getPreferenceLayoutResId(Context context) {
diff --git a/src/com/android/settings/accounts/AccountPreferenceController.java b/src/com/android/settings/accounts/AccountPreferenceController.java
index db6a4ae..33b3888 100644
--- a/src/com/android/settings/accounts/AccountPreferenceController.java
+++ b/src/com/android/settings/accounts/AccountPreferenceController.java
@@ -296,14 +296,15 @@
             updateProfileUi(userInfo);
         } else {
             List<UserInfo> profiles = mUm.getProfiles(UserHandle.myUserId());
-            final int profilesCount = profiles.size();
-            for (int i = 0; i < profilesCount; i++) {
-                if (profiles.get(i).isManagedProfile()
-                        && (mType & ProfileSelectFragment.ProfileType.WORK) != 0) {
-                    updateProfileUi(profiles.get(i));
-                } else if (!profiles.get(i).isManagedProfile()
-                        && (mType & ProfileSelectFragment.ProfileType.PERSONAL) != 0) {
-                    updateProfileUi(profiles.get(i));
+            for (UserInfo profile : profiles) {
+                if ((profile.isManagedProfile()
+                            && (mType & ProfileSelectFragment.ProfileType.WORK) != 0)
+                        || (profile.isPrivateProfile()
+                            && (mType & ProfileSelectFragment.ProfileType.PRIVATE) != 0)
+                        || (!profile.isManagedProfile()
+                            && !profile.isPrivateProfile()
+                            && (mType & ProfileSelectFragment.ProfileType.PERSONAL) != 0)) {
+                    updateProfileUi(profile);
                 }
             }
         }
diff --git a/src/com/android/settings/accounts/AccountPrivateDashboardFragment.java b/src/com/android/settings/accounts/AccountPrivateDashboardFragment.java
new file mode 100644
index 0000000..9794b4a
--- /dev/null
+++ b/src/com/android/settings/accounts/AccountPrivateDashboardFragment.java
@@ -0,0 +1,109 @@
+/*
+ * 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.accounts;
+
+import static android.provider.Settings.EXTRA_AUTHORITIES;
+
+import static com.android.settings.accounts.AccountDashboardFragment.buildAutofillPreferenceControllers;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.credentials.CredentialManager;
+
+import com.android.settings.R;
+import com.android.settings.applications.autofill.PasswordsPreferenceController;
+import com.android.settings.applications.credentials.CredentialManagerPreferenceController;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
+import com.android.settings.users.AutoSyncDataPreferenceController;
+import com.android.settings.users.AutoSyncPrivateDataPreferenceController;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AccountPrivateDashboardFragment extends DashboardFragment {
+    private static final String TAG = "AccountPrivateFrag";
+
+    @Override
+    public int getMetricsCategory() {
+        return SettingsEnums.ACCOUNT_PRIVATE;
+    }
+
+    @Override
+    protected String getLogTag() {
+        return TAG;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        if (this.getContext() != null && CredentialManager.isServiceEnabled(this.getContext())) {
+            return R.xml.accounts_private_dashboard_settings_credman;
+        }
+        return R.xml.accounts_private_dashboard_settings;
+    }
+
+    @Override
+    public int getHelpResource() {
+        return R.string.help_url_user_and_account_dashboard;
+    }
+
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        if (CredentialManager.isServiceEnabled(context)) {
+            CredentialManagerPreferenceController cmpp =
+                    use(CredentialManagerPreferenceController.class);
+            CredentialManagerPreferenceController.Delegate delegate =
+                    new CredentialManagerPreferenceController.Delegate() {
+                        public void setActivityResult(int resultCode) {
+                            getActivity().setResult(resultCode);
+                        }
+                        public void forceDelegateRefresh() {
+                            forceUpdatePreferences();
+                        }
+                    };
+            cmpp.init(this, getFragmentManager(), getIntent(), delegate, /*isWorkProfile=*/false);
+        } else {
+            getSettingsLifecycle().addObserver(use(PasswordsPreferenceController.class));
+        }
+    }
+
+    @Override
+    protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+        final List<AbstractPreferenceController> controllers = new ArrayList<>();
+        buildAutofillPreferenceControllers(context, controllers);
+        final String[] authorities = getIntent().getStringArrayExtra(EXTRA_AUTHORITIES);
+        buildAccountPreferenceControllers(context, authorities, controllers);
+        return controllers;
+    }
+
+    private void buildAccountPreferenceControllers(
+            Context context,
+            String[] authorities,
+            List<AbstractPreferenceController> controllers) {
+        final AccountPreferenceController accountPrefController =
+                new AccountPreferenceController(
+                        context,
+                        this,
+                        authorities,
+                        ProfileSelectFragment.ProfileType.PRIVATE);
+        controllers.add(accountPrefController);
+        controllers.add(new AutoSyncDataPreferenceController(context, this));
+        controllers.add(new AutoSyncPrivateDataPreferenceController(context, this));
+    }
+}
diff --git a/src/com/android/settings/applications/AdvancedAppsPreferenceCategoryController.java b/src/com/android/settings/applications/AdvancedAppsPreferenceCategoryController.java
new file mode 100644
index 0000000..6a518fd
--- /dev/null
+++ b/src/com/android/settings/applications/AdvancedAppsPreferenceCategoryController.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.applications;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+
+import com.android.settings.widget.PreferenceCategoryController;
+
+/**
+ * Preference category controller for Advanced category under Apps page
+ */
+public class AdvancedAppsPreferenceCategoryController extends PreferenceCategoryController {
+
+    public AdvancedAppsPreferenceCategoryController(@NonNull Context context,
+            @NonNull String preferenceKey) {
+        super(context, preferenceKey);
+    }
+}
diff --git a/src/com/android/settings/applications/AppDashboardFragment.java b/src/com/android/settings/applications/AppDashboardFragment.java
index 11f8405..41160d8 100644
--- a/src/com/android/settings/applications/AppDashboardFragment.java
+++ b/src/com/android/settings/applications/AppDashboardFragment.java
@@ -20,9 +20,12 @@
 import android.content.Context;
 import android.provider.SearchIndexableResource;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.R;
+import com.android.settings.applications.appcompat.UserAspectRatioAppsPreferenceController;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.widget.PreferenceCategoryController;
 import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.search.SearchIndexable;
 
@@ -35,11 +38,21 @@
 public class AppDashboardFragment extends DashboardFragment {
 
     private static final String TAG = "AppDashboardFragment";
+    private static final String ADVANCED_CATEGORY_KEY = "advanced_category";
+    private static final String ASPECT_RATIO_PREF_KEY = "aspect_ratio_apps";
     private AppsPreferenceController mAppsPreferenceController;
 
     private static List<AbstractPreferenceController> buildPreferenceControllers(Context context) {
         final List<AbstractPreferenceController> controllers = new ArrayList<>();
         controllers.add(new AppsPreferenceController(context));
+
+        final UserAspectRatioAppsPreferenceController aspectRatioAppsPreferenceController =
+                new UserAspectRatioAppsPreferenceController(context, ASPECT_RATIO_PREF_KEY);
+        final AdvancedAppsPreferenceCategoryController advancedCategoryController =
+                new AdvancedAppsPreferenceCategoryController(context, ADVANCED_CATEGORY_KEY);
+        advancedCategoryController.setChildren(List.of(aspectRatioAppsPreferenceController));
+        controllers.add(advancedCategoryController);
+
         return controllers;
     }
 
@@ -75,6 +88,11 @@
         getSettingsLifecycle().addObserver(hibernatedAppsPreferenceController);
     }
 
+    @VisibleForTesting
+    PreferenceCategoryController getAdvancedAppsPreferenceCategoryController() {
+        return use(AdvancedAppsPreferenceCategoryController.class);
+    }
+
     @Override
     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
         return buildPreferenceControllers(context);
diff --git a/src/com/android/settings/applications/autofill/PasswordsPreferenceController.java b/src/com/android/settings/applications/autofill/PasswordsPreferenceController.java
index 03a551f..73fef1b 100644
--- a/src/com/android/settings/applications/autofill/PasswordsPreferenceController.java
+++ b/src/com/android/settings/applications/autofill/PasswordsPreferenceController.java
@@ -17,6 +17,7 @@
 package com.android.settings.applications.autofill;
 
 import static android.app.admin.DevicePolicyResources.Strings.Settings.AUTO_SYNC_PERSONAL_DATA;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.AUTO_SYNC_PRIVATE_DATA;
 import static android.app.admin.DevicePolicyResources.Strings.Settings.AUTO_SYNC_WORK_DATA;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.service.autofill.AutofillService.EXTRA_RESULT;
@@ -122,6 +123,8 @@
                 AUTO_SYNC_PERSONAL_DATA, R.string.account_settings_menu_auto_sync_personal);
         replaceEnterpriseStringTitle(screen, "auto_sync_work_account_data",
                 AUTO_SYNC_WORK_DATA, R.string.account_settings_menu_auto_sync_work);
+        replaceEnterpriseStringTitle(screen, "auto_sync_private_account_data",
+                AUTO_SYNC_PRIVATE_DATA, R.string.account_settings_menu_auto_sync_private);
     }
 
     private void addPasswordPreferences(
diff --git a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
index 98d56cc..de06806 100644
--- a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
+++ b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
@@ -45,7 +45,6 @@
 import android.provider.Settings;
 import android.service.autofill.AutofillServiceInfo;
 import android.text.TextUtils;
-import android.util.IconDrawableFactory;
 import android.util.Log;
 import android.view.View;
 import android.widget.Switch;
@@ -96,9 +95,11 @@
     private static final String ALTERNATE_INTENT = "android.settings.SYNC_SETTINGS";
     private static final String PRIMARY_INTENT = "android.settings.CREDENTIAL_PROVIDER";
     private static final int MAX_SELECTABLE_PROVIDERS = 5;
+    private static final String SETTINGS_ACTIVITY_INTENT_ACTION = "android.intent.action.MAIN";
+    private static final String SETTINGS_ACTIVITY_INTENT_CATEGORY =
+            "android.intent.category.LAUNCHER";
 
     private final PackageManager mPm;
-    private final IconDrawableFactory mIconFactory;
     private final List<CredentialProviderInfo> mServices;
     private final Set<String> mEnabledPackageNames;
     private final @Nullable CredentialManager mCredentialManager;
@@ -119,7 +120,6 @@
     public CredentialManagerPreferenceController(Context context, String preferenceKey) {
         super(context, preferenceKey);
         mPm = context.getPackageManager();
-        mIconFactory = IconDrawableFactory.newInstance(mContext);
         mServices = new ArrayList<>();
         mEnabledPackageNames = new HashSet<>();
         mExecutor = ContextCompat.getMainExecutor(mContext);
@@ -484,13 +484,22 @@
                 continue;
             }
 
+            // Get the settings activity.
+            CharSequence settingsActivity =
+                    combinedInfo.getCredentialProviderInfos().get(0).getSettingsActivity();
+
             Drawable icon = combinedInfo.getAppIcon(context, getUser());
             CharSequence title = combinedInfo.getAppName(context);
 
             // Build the pref and add it to the output & group.
             CombiPreference pref =
                     addProviderPreference(
-                            context, title, icon, packageName, combinedInfo.getSettingsSubtitle());
+                            context,
+                            title == null ? "" : title,
+                            icon,
+                            packageName,
+                            combinedInfo.getSettingsSubtitle(),
+                            settingsActivity);
             output.put(packageName, pref);
             group.addPreference(pref);
         }
@@ -510,7 +519,8 @@
                 label == null ? "" : label,
                 service.getServiceIcon(mContext),
                 service.getServiceInfo().packageName,
-                service.getSettingsSubtitle());
+                service.getSettingsSubtitle(),
+                service.getSettingsActivity());
     }
 
     /**
@@ -569,43 +579,90 @@
             @NonNull CharSequence title,
             @Nullable Drawable icon,
             @NonNull String packageName,
-            @Nullable CharSequence subtitle) {
+            @Nullable CharSequence subtitle,
+            @Nullable CharSequence settingsActivity) {
         final CombiPreference pref =
                 new CombiPreference(prefContext, mEnabledPackageNames.contains(packageName));
         pref.setTitle(title);
 
         if (icon != null) {
-            pref.setIcon(Utils.getSafeIcon(icon));
+            pref.setIcon(icon);
         }
 
+        pref.setLayoutResource(R.layout.preference_icon_credman);
+
         if (subtitle != null) {
             pref.setSummary(subtitle);
         }
 
         pref.setPreferenceListener(
-                (p, isChecked) -> {
-                    if (isChecked) {
-                        if (mEnabledPackageNames.size() >= MAX_SELECTABLE_PROVIDERS) {
-                            // Show the error if too many enabled.
-                            pref.setChecked(false);
-                            final DialogFragment fragment = newErrorDialogFragment();
+                new CombiPreference.OnCombiPreferenceClickListener() {
+                    @Override
+                    public void onCheckChanged(CombiPreference p, boolean isChecked) {
+                        if (isChecked) {
+                            if (mEnabledPackageNames.size() >= MAX_SELECTABLE_PROVIDERS) {
+                                // Show the error if too many enabled.
+                                pref.setChecked(false);
+                                final DialogFragment fragment = newErrorDialogFragment();
 
-                            if (fragment == null || mFragmentManager == null) {
+                                if (fragment == null || mFragmentManager == null) {
+                                    return;
+                                }
+
+                                fragment.show(mFragmentManager, ErrorDialogFragment.TAG);
                                 return;
                             }
 
-                            fragment.show(mFragmentManager, ErrorDialogFragment.TAG);
+                            togglePackageNameEnabled(packageName);
+
+                            // Enable all prefs.
+                            if (mPrefs.containsKey(packageName)) {
+                                mPrefs.get(packageName).setChecked(true);
+                            }
+                        } else {
+                            togglePackageNameDisabled(packageName);
+                        }
+                    }
+
+                    @Override
+                    public void onLeftSideClicked() {
+                        if (settingsActivity == null) {
+                            Log.w(TAG, "settingsActivity was null");
                             return;
                         }
 
-                        togglePackageNameEnabled(packageName);
-
-                        // Enable all prefs.
-                        if (mPrefs.containsKey(packageName)) {
-                            mPrefs.get(packageName).setChecked(true);
+                        String settingsActivityStr = String.valueOf(settingsActivity);
+                        ComponentName cn = ComponentName.unflattenFromString(settingsActivityStr);
+                        if (cn == null) {
+                            Log.w(
+                                    TAG,
+                                    "Failed to deserialize settingsActivity attribute, we got: "
+                                            + settingsActivityStr);
+                            return;
                         }
-                    } else {
-                        togglePackageNameDisabled(packageName);
+
+                        Intent intent = new Intent(SETTINGS_ACTIVITY_INTENT_ACTION);
+                        intent.addCategory(SETTINGS_ACTIVITY_INTENT_CATEGORY);
+                        intent.setComponent(cn);
+
+                        Context context = mContext;
+                        int currentUserId = getUser();
+                        int contextUserId = context.getUser().getIdentifier();
+
+                        if (currentUserId != contextUserId) {
+                            Log.d(
+                                    TAG,
+                                    "onLeftSideClicked(): using context for current user ("
+                                            + currentUserId
+                                            + ") instead of user "
+                                            + contextUserId
+                                            + " on headless system user mode");
+                            context =
+                                    context.createContextAsUser(
+                                            UserHandle.of(currentUserId), /* flags= */ 0);
+                        }
+
+                        context.startActivity(intent);
                     }
                 });
 
@@ -921,6 +978,9 @@
         public interface OnCombiPreferenceClickListener {
             /** Called when the check is updated */
             void onCheckChanged(CombiPreference p, boolean isChecked);
+
+            /** Called when the left side is clicked. */
+            void onLeftSideClicked();
         }
 
         public CombiPreference(Context context, boolean initialValue) {
@@ -969,6 +1029,18 @@
                 // Store this for later.
                 mSwitch = switchView;
             }
+
+            super.setOnPreferenceClickListener(
+                    new Preference.OnPreferenceClickListener() {
+                        @Override
+                        public boolean onPreferenceClick(Preference preference) {
+                            if (mOnClickListener != null) {
+                                mOnClickListener.onLeftSideClicked();
+                            }
+
+                            return true;
+                        }
+                    });
         }
     }
 }
diff --git a/src/com/android/settings/applications/credentials/DefaultCombinedPickerPrivate.java b/src/com/android/settings/applications/credentials/DefaultCombinedPickerPrivate.java
new file mode 100644
index 0000000..722cb1a
--- /dev/null
+++ b/src/com/android/settings/applications/credentials/DefaultCombinedPickerPrivate.java
@@ -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.applications.credentials;
+
+import android.os.UserManager;
+
+import com.android.settings.Utils;
+import com.android.settings.dashboard.profileselector.ProfileSelectFragment.ProfileType;
+
+public class DefaultCombinedPickerPrivate extends DefaultCombinedPicker {
+    @Override
+    protected int getUser() {
+        UserManager userManager = getContext().getSystemService(UserManager.class);
+        return Utils.getCurrentUserIdOfType(userManager, ProfileType.PRIVATE);
+    }
+}
diff --git a/src/com/android/settings/applications/credentials/DefaultPrivateCombinedPreferenceController.kt b/src/com/android/settings/applications/credentials/DefaultPrivateCombinedPreferenceController.kt
new file mode 100644
index 0000000..d606f3c
--- /dev/null
+++ b/src/com/android/settings/applications/credentials/DefaultPrivateCombinedPreferenceController.kt
@@ -0,0 +1,57 @@
+/*
+ * 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.credentials
+
+import android.content.Context
+import android.content.Intent
+import android.os.UserHandle
+import com.android.settings.Utils
+import com.android.settings.dashboard.profileselector.ProfileSelectFragment
+import com.android.settingslib.applications.DefaultAppInfo
+
+class DefaultPrivateCombinedPreferenceController(context: Context?) : DefaultCombinedPreferenceController(context) {
+    private val userHandle: UserHandle? =
+            Utils.getProfileOfType(mUserManager, ProfileSelectFragment.ProfileType.PRIVATE)
+
+    override fun isAvailable(): Boolean {
+        return if (userHandle == null) {
+            false
+        } else super.isAvailable()
+    }
+
+    override fun getPreferenceKey(): String {
+        return "default_credman_autofill_private"
+    }
+
+    override fun getSettingIntent(info: DefaultAppInfo ?): Intent ? {
+        if (info == null) {
+            return null
+        }
+        return userHandle?.let { handle ->
+            AutofillSettingIntentProvider(mContext, handle.identifier, info.key).intent
+        } ?: null
+    }
+
+    override fun startActivity(intent: Intent) {
+        userHandle?.let { handle ->
+            mContext.startActivityAsUser(intent, handle)
+        }
+    }
+
+    override fun getUser(): Int {
+        return userHandle?.identifier ?: UserHandle.myUserId()
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/applications/defaultapps/DefaultPrivateAutofillPreferenceController.kt b/src/com/android/settings/applications/defaultapps/DefaultPrivateAutofillPreferenceController.kt
new file mode 100644
index 0000000..67211b4
--- /dev/null
+++ b/src/com/android/settings/applications/defaultapps/DefaultPrivateAutofillPreferenceController.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.applications.defaultapps
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.os.UserHandle
+import android.provider.Settings
+import android.text.TextUtils
+import com.android.settings.Utils
+import com.android.settings.dashboard.profileselector.ProfileSelectFragment
+import com.android.settingslib.applications.DefaultAppInfo
+
+class DefaultPrivateAutofillPreferenceController(context: Context?) : DefaultAutofillPreferenceController(context) {
+    private val userHandle: UserHandle? = Utils
+            .getProfileOfType(mUserManager, ProfileSelectFragment.ProfileType.PRIVATE)
+
+    override fun isAvailable(): Boolean {
+        return if (userHandle == null) {
+            false
+        } else super.isAvailable()
+    }
+
+    override fun getPreferenceKey(): String {
+        return "default_autofill_private"
+    }
+
+    override fun getDefaultAppInfo(): DefaultAppInfo ? {
+        val flattenComponent = userHandle?.let { handle ->
+            Settings.Secure.getStringForUser(
+                    mContext.contentResolver,
+                    DefaultAutofillPicker.SETTING,
+                    handle.identifier
+            )
+        }
+        return if (!flattenComponent.isNullOrEmpty()) {
+            userHandle?.let {
+                DefaultAppInfo(
+                        mContext,
+                        mPackageManager,
+                        it.identifier,
+                        ComponentName.unflattenFromString(flattenComponent))
+            }
+        } else null
+    }
+
+    override fun startActivity(intent: Intent) {
+        if (userHandle == null) {
+            mContext.startActivityAsUser(intent, UserHandle.CURRENT)
+        } else mContext.startActivityAsUser(intent, userHandle)
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/backup/SettingsBackupHelper.java b/src/com/android/settings/backup/SettingsBackupHelper.java
index a682df8..ad7e314 100644
--- a/src/com/android/settings/backup/SettingsBackupHelper.java
+++ b/src/com/android/settings/backup/SettingsBackupHelper.java
@@ -16,10 +16,13 @@
 
 package com.android.settings.backup;
 
+import static com.android.settings.localepicker.LocaleNotificationDataManager.LOCALE_NOTIFICATION;
+
 import android.app.backup.BackupAgentHelper;
 import android.app.backup.BackupDataInputStream;
 import android.app.backup.BackupDataOutput;
 import android.app.backup.BackupHelper;
+import android.app.backup.SharedPreferencesBackupHelper;
 import android.os.ParcelFileDescriptor;
 
 import com.android.settings.fuelgauge.BatteryBackupHelper;
@@ -33,12 +36,15 @@
  * Backup agent for Settings APK
  */
 public class SettingsBackupHelper extends BackupAgentHelper {
+    private static final String PREF_LOCALE_NOTIFICATION = "localeNotificationSharedPref";
 
     @Override
     public void onCreate() {
         super.onCreate();
         addHelper("no-op", new NoOpHelper());
         addHelper(BatteryBackupHelper.TAG, new BatteryBackupHelper(this));
+        addHelper(PREF_LOCALE_NOTIFICATION,
+                new SharedPreferencesBackupHelper(this, LOCALE_NOTIFICATION));
     }
 
     @Override
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt
index bbd67b5..58fcea6 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt
@@ -42,6 +42,7 @@
 import com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED
 import com.android.settings.biometrics.GatekeeperPasswordProvider
 import com.android.settings.biometrics.fingerprint2.domain.interactor.FingerprintManagerInteractorImpl
+import com.android.settings.biometrics.fingerprint2.shared.model.FingerprintViewModel
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollConfirmationV2Fragment
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollEnrollingV2Fragment
 import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollFindSensorV2Fragment
@@ -82,6 +83,7 @@
   private lateinit var accessibilityViewModel: AccessibilityViewModel
   private lateinit var foldStateViewModel: FoldStateViewModel
   private lateinit var orientationStateViewModel: OrientationStateViewModel
+  private lateinit var fingerprintScrollViewModel: FingerprintScrollViewModel
   private val coroutineDispatcher = Dispatchers.Default
 
   /** Result listener for ChooseLock activity flow. */
@@ -210,8 +212,9 @@
       )[FingerprintEnrollViewModel::class.java]
 
     // Initialize scroll view model
-    ViewModelProvider(this, FingerprintScrollViewModel.FingerprintScrollViewModelFactory())[
-      FingerprintScrollViewModel::class.java]
+    fingerprintScrollViewModel =
+      ViewModelProvider(this, FingerprintScrollViewModel.FingerprintScrollViewModelFactory())[
+        FingerprintScrollViewModel::class.java]
 
     // Initialize AccessibilityViewModel
     accessibilityViewModel =
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollIntroV2Fragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollIntroV2Fragment.kt
index dbf6d12..898b158 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollIntroV2Fragment.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/fragment/FingerprintEnrollIntroV2Fragment.kt
@@ -25,10 +25,13 @@
 import android.text.Html
 import android.text.method.LinkMovementMethod
 import android.util.Log
+import android.view.LayoutInflater
 import android.view.View
+import android.view.ViewGroup
 import android.widget.ImageView
 import android.widget.ScrollView
 import android.widget.TextView
+import androidx.annotation.VisibleForTesting
 import androidx.fragment.app.Fragment
 import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.lifecycleScope
@@ -72,48 +75,69 @@
  * 2. How the data will be stored
  * 3. How the user can access and remove their data
  */
-class FingerprintEnrollIntroV2Fragment : Fragment(R.layout.fingerprint_v2_enroll_introduction) {
-  private lateinit var footerBarMixin: FooterBarMixin
-  private lateinit var textModel: TextModel
-  private lateinit var navigationViewModel: FingerprintEnrollNavigationViewModel
-  private lateinit var fingerprintEnrollViewModel: FingerprintEnrollViewModel
-  private lateinit var fingerprintScrollViewModel: FingerprintScrollViewModel
-  private lateinit var gateKeeperViewModel: FingerprintGatekeeperViewModel
+class FingerprintEnrollIntroV2Fragment() : Fragment(R.layout.fingerprint_v2_enroll_introduction) {
 
-  override fun onCreate(savedInstanceState: Bundle?) {
-    super.onCreate(savedInstanceState)
-    navigationViewModel =
-      ViewModelProvider(requireActivity())[FingerprintEnrollNavigationViewModel::class.java]
-    fingerprintEnrollViewModel =
-      ViewModelProvider(requireActivity())[FingerprintEnrollViewModel::class.java]
-    fingerprintScrollViewModel =
-      ViewModelProvider(requireActivity())[FingerprintScrollViewModel::class.java]
-    gateKeeperViewModel =
-      ViewModelProvider(requireActivity())[FingerprintGatekeeperViewModel::class.java]
+  /** Used for testing purposes */
+  private var factory: ViewModelProvider.Factory? = null
+
+  @VisibleForTesting
+  constructor(theFactory: ViewModelProvider.Factory) : this() {
+    factory = theFactory
   }
 
-  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
-    super.onViewCreated(view, savedInstanceState)
+  private val viewModelProvider: ViewModelProvider by lazy {
+    if (factory != null) {
+      ViewModelProvider(requireActivity(), factory!!)
+    } else {
+      ViewModelProvider(requireActivity())
+    }
+  }
 
-    lifecycleScope.launch {
-      combine(
-          navigationViewModel.enrollType,
-          fingerprintEnrollViewModel.sensorType,
-        ) { enrollType, sensorType ->
-          Pair(enrollType, sensorType)
-        }
-        .collect { (enrollType, sensorType) ->
-          textModel =
-            when (enrollType) {
-              Unicorn -> getUnicornTextModel()
-              else -> getNormalTextModel()
-            }
+  private lateinit var footerBarMixin: FooterBarMixin
+  private lateinit var textModel: TextModel
 
-          setupFooterBarAndScrollView(view)
+  // Note that the ViewModels cannot be requested before the onCreate call
+  private val navigationViewModel: FingerprintEnrollNavigationViewModel by lazy {
+    viewModelProvider[FingerprintEnrollNavigationViewModel::class.java]
+  }
+  private val fingerprintViewModel: FingerprintEnrollViewModel by lazy {
+    viewModelProvider[FingerprintEnrollViewModel::class.java]
+  }
+  private val fingerprintScrollViewModel: FingerprintScrollViewModel by lazy {
+    viewModelProvider[FingerprintScrollViewModel::class.java]
+  }
+  private val gateKeeperViewModel: FingerprintGatekeeperViewModel by lazy {
+    viewModelProvider[FingerprintGatekeeperViewModel::class.java]
+  }
 
-          if (savedInstanceState == null) {
-            getLayout()?.setHeaderText(textModel.headerText)
-            getLayout()?.setDescriptionText(textModel.descriptionText)
+  override fun onCreateView(
+    inflater: LayoutInflater,
+    container: ViewGroup?,
+    savedInstanceState: Bundle?
+  ): View? =
+    super.onCreateView(inflater, container, savedInstanceState).also { theView ->
+      val view = theView!!
+
+      viewLifecycleOwner.lifecycleScope.launch {
+        combine(
+            navigationViewModel.enrollType,
+            fingerprintViewModel.sensorType,
+          ) { enrollType, sensorType ->
+            Pair(enrollType, sensorType)
+          }
+          .collect { (enrollType, sensorType) ->
+            textModel =
+              when (enrollType) {
+                Unicorn -> getUnicornTextModel()
+                else -> getNormalTextModel()
+              }
+
+            setupFooterBarAndScrollView(view)
+
+            val layout = view as GlifLayout
+
+            layout.setHeaderText(textModel.headerText)
+            layout.setDescriptionText(textModel.descriptionText)
 
             // Set color filter for the following icons.
             val colorFilter = getIconColorFilter()
@@ -158,9 +182,9 @@
             view.requireViewById<TextView?>(R.id.footer_title_1).setText(textModel.footerTitleOne)
             view.requireViewById<TextView?>(R.id.footer_title_2).setText(textModel.footerTitleOne)
           }
-        }
+      }
+      return view
     }
-  }
 
   private fun setFooterLink(view: View) {
     val footerLink: TextView = view.requireViewById(R.id.footer_learn_more)
@@ -185,17 +209,18 @@
         navigationViewModel.nextStep()
       }
 
-    val layout: GlifLayout = requireActivity().requireViewById(R.id.setup_wizard_layout)
+    val layout: GlifLayout = view.findViewById(R.id.setup_wizard_layout)!!
     footerBarMixin = layout.getMixin(FooterBarMixin::class.java)
     footerBarMixin.primaryButton =
-      FooterButton.Builder(requireActivity())
+      FooterButton.Builder(requireContext())
         .setText(R.string.security_settings_face_enroll_introduction_more)
         .setListener(onNextButtonClick)
         .setButtonType(FooterButton.ButtonType.OPT_IN)
         .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary)
         .build()
+
     footerBarMixin.setSecondaryButton(
-      FooterButton.Builder(requireActivity())
+      FooterButton.Builder(requireContext())
         .setText(textModel.negativeButton)
         .setListener({ Log.d(TAG, "prevClicked") })
         .setButtonType(FooterButton.ButtonType.NEXT)
@@ -211,8 +236,8 @@
 
     val requireScrollMixin = layout.getMixin(RequireScrollMixin::class.java)
     requireScrollMixin.requireScrollWithButton(
-      requireActivity(),
-      footerBarMixin.primaryButton,
+      requireContext(),
+      primaryButton,
       R.string.security_settings_face_enroll_introduction_more,
       onNextButtonClick
     )
@@ -224,7 +249,7 @@
       }
     }
 
-    lifecycleScope.launch {
+    viewLifecycleOwner.lifecycleScope.launch {
       fingerprintScrollViewModel.hasReadConsentScreen.collect { consented ->
         if (consented) {
           primaryButton.setText(
@@ -244,7 +269,7 @@
     // the flow. For instance if someone launches the activity with an invalid challenge, it
     // either 1) Fails or 2) Launched confirmDeviceCredential
     primaryButton.isEnabled = false
-    lifecycleScope.launch {
+    viewLifecycleOwner.lifecycleScope.launch {
       gateKeeperViewModel.hasValidGatekeeperInfo.collect { primaryButton.isEnabled = it }
     }
   }
@@ -284,8 +309,4 @@
       PorterDuff.Mode.SRC_IN
     )
   }
-
-  private fun getLayout(): GlifLayout? {
-    return requireView().findViewById(R.id.setup_wizard_layout) as GlifLayout?
-  }
 }
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrolllNavigationViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrolllNavigationViewModel.kt
index d2bb321..97c8271 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrolllNavigationViewModel.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/viewmodel/FingerprintEnrolllNavigationViewModel.kt
@@ -31,11 +31,6 @@
 
 private const val TAG = "FingerprintEnrollNavigationViewModel"
 
-/** Interface to validate a gatekeeper hat */
-interface Validator {
-  fun validateGateKeeper(challenge: Long?): Boolean
-}
-
 /**
  * The [EnrollType] for fingerprint enrollment indicates information on how the flow should behave.
  */
@@ -56,7 +51,6 @@
  */
 class FingerprintEnrollNavigationViewModel(
   private val dispatcher: CoroutineDispatcher,
-  private val validator: Validator,
   private val fingerprintManagerInteractor: FingerprintManagerInteractor,
   private val gatekeeperViewModel: FingerprintGatekeeperViewModel,
   private val canSkipConfirm: Boolean
@@ -145,11 +139,6 @@
 
       return FingerprintEnrollNavigationViewModel(
         backgroundDispatcher,
-        object : Validator {
-          override fun validateGateKeeper(challenge: Long?): Boolean {
-            return challenge != null
-          }
-        },
         fingerprintManagerInteractor,
         fingerprintGatekeeperViewModel,
         canSkipConfirm,
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherController.java b/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherController.java
index 562a469..d1d00d8 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherController.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.bluetooth;
 
+import android.bluetooth.BluetoothDevice;
 import android.content.Context;
 
 import androidx.preference.PreferenceFragmentCompat;
@@ -75,7 +76,6 @@
     protected void refresh() {
         updateButtonPreferenceTitle(mPreference);
         setPreferencesVisibility(getButtonPreferenceVisibility(mCachedDevice));
-
     }
 
     private void updateButtonPreferenceTitle(ButtonPreference preference) {
@@ -97,7 +97,7 @@
         if (!cachedDevice.isConnectedHearingAidDevice()) {
             return false;
         }
-        return isBinauralMode(cachedDevice) && !isOtherSideConnected(cachedDevice);
+        return isBinauralMode(cachedDevice) && !isOtherSideBonded(cachedDevice);
     }
 
     private void launchPairingDetail() {
@@ -112,25 +112,16 @@
         return cachedDevice.getDeviceMode() == HearingAidInfo.DeviceMode.MODE_BINAURAL;
     }
 
-    private boolean isOtherSideConnected(CachedBluetoothDevice cachedDevice) {
-        // Check sub device for ASHA hearing aid
-        if (cachedDevice.isConnectedAshaHearingAidDevice()) {
-            final CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
-            if (subDevice != null && subDevice.isConnectedAshaHearingAidDevice()) {
-                return true;
-            }
-        }
+    private boolean isOtherSideBonded(CachedBluetoothDevice cachedDevice) {
+        final CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
+        final boolean subDeviceBonded =
+                subDevice != null && subDevice.getBondState() == BluetoothDevice.BOND_BONDED;
 
-        // Check member device for LE audio hearing aid
-        if (cachedDevice.isConnectedLeAudioHearingAidDevice()) {
-            final Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice();
-            for (CachedBluetoothDevice memberDevice : memberDevices) {
-                if (memberDevice.isConnectedLeAudioHearingAidDevice()) {
-                    return true;
-                }
-            }
-        }
+        final Set<CachedBluetoothDevice> memberDevice = cachedDevice.getMemberDevice();
+        final boolean allMemberDevicesBonded =
+                !memberDevice.isEmpty() && memberDevice.stream().allMatch(
+                        device -> device.getBondState() == BluetoothDevice.BOND_BONDED);
 
-        return false;
+        return subDeviceBonded || allMemberDevicesBonded;
     }
 }
diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectAccountFragment.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectAccountFragment.java
index cf91031..77dc3a7 100644
--- a/src/com/android/settings/dashboard/profileselector/ProfileSelectAccountFragment.java
+++ b/src/com/android/settings/dashboard/profileselector/ProfileSelectAccountFragment.java
@@ -15,10 +15,12 @@
  */
 
 package com.android.settings.dashboard.profileselector;
+
 import androidx.fragment.app.Fragment;
 
 import com.android.settings.R;
 import com.android.settings.accounts.AccountPersonalDashboardFragment;
+import com.android.settings.accounts.AccountPrivateDashboardFragment;
 import com.android.settings.accounts.AccountWorkProfileDashboardFragment;
 
 /**
@@ -28,10 +30,12 @@
 
     @Override
     public Fragment[] getFragments() {
-        return new Fragment[] {
-                new AccountPersonalDashboardFragment(),
-                new AccountWorkProfileDashboardFragment()
-        };
+        return ProfileSelectFragment.getFragments(
+                getContext(),
+                null /* bundle */,
+                AccountPersonalDashboardFragment::new,
+                AccountWorkProfileDashboardFragment::new,
+                AccountPrivateDashboardFragment::new);
     }
 
     @Override
diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectFragment.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectFragment.java
index 5c0580d..657cdbf 100644
--- a/src/com/android/settings/dashboard/profileselector/ProfileSelectFragment.java
+++ b/src/com/android/settings/dashboard/profileselector/ProfileSelectFragment.java
@@ -17,21 +17,28 @@
 package com.android.settings.dashboard.profileselector;
 
 import static android.app.admin.DevicePolicyResources.Strings.Settings.PERSONAL_CATEGORY_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.PRIVATE_CATEGORY_HEADER;
 import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_CATEGORY_HEADER;
 import static android.content.Intent.EXTRA_USER_ID;
 
 import android.annotation.IntDef;
 import android.app.Activity;
 import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.pm.UserInfo;
 import android.os.Bundle;
+import android.os.Flags;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.fragment.app.Fragment;
 import androidx.recyclerview.widget.RecyclerView;
@@ -42,12 +49,14 @@
 import com.android.settings.SettingsActivity;
 import com.android.settings.Utils;
 import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.privatespace.PrivateSpaceMaintainer;
 
 import com.google.android.material.tabs.TabLayout;
 import com.google.android.material.tabs.TabLayoutMediator;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
 
 /**
  * Base fragment class for profile settings.
@@ -77,9 +86,14 @@
         int WORK = 1 << 1;
 
         /**
-         * It is personal and work profile
+         * It is private profile
          */
-        int ALL = PERSONAL | WORK;
+        int PRIVATE = 1 << 2;
+
+        /**
+         * It is personal, work, and private profile
+         */
+        int ALL = PERSONAL | WORK | PRIVATE;
     }
 
     /**
@@ -97,6 +111,11 @@
      */
     public static final int WORK_TAB = 1;
 
+    /**
+     * Used in fragment argument with Extra key {@link SettingsActivity.EXTRA_SHOW_FRAGMENT_TAB}
+     */
+    public static final int PRIVATE_TAB = 2;
+
     private ViewGroup mContentView;
 
     private ViewPager2 mViewPager;
@@ -215,12 +234,20 @@
             if (isWorkProfile) {
                 return WORK_TAB;
             }
+            UserInfo userInfo = UserManager.get(activity).getUserInfo(userId);
+            if (Flags.allowPrivateProfile() && userInfo != null && userInfo.isPrivateProfile()) {
+                return PRIVATE_TAB;
+            }
         }
         // Start intent from a specific user eg: adb shell --user 10
         final int intentUser = activity.getIntent().getContentUserHint();
         if (UserManager.get(activity).isManagedProfile(intentUser)) {
             return WORK_TAB;
         }
+        UserInfo userInfo = UserManager.get(activity).getUserInfo(intentUser);
+        if (Flags.allowPrivateProfile() && userInfo != null && userInfo.isPrivateProfile()) {
+            return PRIVATE_TAB;
+        }
 
         return PERSONAL_TAB;
     }
@@ -229,13 +256,114 @@
         final DevicePolicyManager devicePolicyManager =
                 getContext().getSystemService(DevicePolicyManager.class);
 
-        if (position == WORK_TAB) {
+        if (Flags.allowPrivateProfile()) {
+            int tabForPosition =
+                    ((ViewPagerAdapter) mViewPager.getAdapter()).getTabForPosition(position);
+
+            if (tabForPosition == WORK_TAB) {
+                return devicePolicyManager.getResources().getString(WORK_CATEGORY_HEADER,
+                        () -> getContext().getString(
+                                com.android.settingslib.R.string.category_work));
+            }
+
+            if (tabForPosition == PRIVATE_TAB) {
+                return devicePolicyManager.getResources().getString(PRIVATE_CATEGORY_HEADER,
+                        () -> getContext()
+                                .getString(com.android.settingslib.R.string.category_private));
+            }
+
+        } else if (position == WORK_TAB) {
             return devicePolicyManager.getResources().getString(WORK_CATEGORY_HEADER,
                     () -> getContext().getString(com.android.settingslib.R.string.category_work));
+
+        }
+        return devicePolicyManager.getResources().getString(PERSONAL_CATEGORY_HEADER,
+                () -> getContext().getString(
+                        com.android.settingslib.R.string.category_personal));
+    }
+
+    /** Creates fragments of passed types, and returns them in an array. */
+    @NonNull static Fragment[] getFragments(
+            Context context,
+            @Nullable Bundle bundle,
+            FragmentConstructor personalFragmentConstructor,
+            FragmentConstructor workFragmentConstructor,
+            FragmentConstructor privateFragmentConstructor) {
+        return getFragments(
+                context,
+                bundle,
+                personalFragmentConstructor,
+                workFragmentConstructor,
+                privateFragmentConstructor,
+                new PrivateSpaceInfoProvider() {},
+                new ManagedProfileInfoProvider() {});
+    }
+
+    /**
+     * Creates fragments of passed types, and returns them in an array. This overload exists only
+     * for helping with testing.
+     */
+    @NonNull static Fragment[] getFragments(
+            Context context,
+            @Nullable Bundle bundle,
+            FragmentConstructor personalFragmentConstructor,
+            FragmentConstructor workFragmentConstructor,
+            FragmentConstructor privateFragmentConstructor,
+            PrivateSpaceInfoProvider privateSpaceInfoProvider,
+            ManagedProfileInfoProvider managedProfileInfoProvider) {
+        Fragment[] result = new Fragment[0];
+        ArrayList<Fragment> fragments = new ArrayList<>();
+
+        try {
+            final Bundle personalOnly = bundle != null ? bundle : new Bundle();
+            personalOnly.putInt(EXTRA_PROFILE, ProfileType.PERSONAL);
+            final Fragment personalFragment =
+                    personalFragmentConstructor.constructAndGetFragment();
+            personalFragment.setArguments(personalOnly);
+            fragments.add(personalFragment);
+
+            if (managedProfileInfoProvider.getManagedProfile(context) != null) {
+                final Bundle workOnly = bundle != null ? bundle : new Bundle();
+                workOnly.putInt(EXTRA_PROFILE, ProfileType.WORK);
+                final Fragment workFragment =
+                        workFragmentConstructor.constructAndGetFragment();
+                workFragment.setArguments(workOnly);
+                fragments.add(workFragment);
+            }
+
+            if (Flags.allowPrivateProfile()
+                    && !privateSpaceInfoProvider.isPrivateSpaceLocked(context)) {
+                final Bundle privateOnly = bundle != null ? bundle : new Bundle();
+                privateOnly.putInt(EXTRA_PROFILE, ProfileType.PRIVATE);
+                final Fragment privateFragment =
+                        privateFragmentConstructor.constructAndGetFragment();
+                privateFragment.setArguments(privateOnly);
+                fragments.add(privateFragment);
+            }
+
+            result = new Fragment[fragments.size()];
+            fragments.toArray(result);
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to create fragment");
         }
 
-        return devicePolicyManager.getResources().getString(PERSONAL_CATEGORY_HEADER,
-                () -> getContext().getString(com.android.settingslib.R.string.category_personal));
+        return result;
+    }
+
+    interface FragmentConstructor {
+        Fragment constructAndGetFragment();
+    }
+
+    interface PrivateSpaceInfoProvider {
+        default boolean isPrivateSpaceLocked(Context context) {
+            return PrivateSpaceMaintainer.getInstance(context).isPrivateSpaceLocked();
+        }
+    }
+
+    interface ManagedProfileInfoProvider {
+        default UserHandle getManagedProfile(Context context) {
+            return Utils.getManagedProfile(context.getSystemService(UserManager.class));
+        }
     }
 
     static class ViewPagerAdapter extends FragmentStateAdapter {
@@ -256,5 +384,22 @@
         public int getItemCount() {
             return mChildFragments.length;
         }
+
+        private int getTabForPosition(int position) {
+            if (position >= mChildFragments.length) {
+                Log.e(TAG, "tab requested for out of bound position " + position);
+                return PERSONAL_TAB;
+            }
+            @ProfileType
+            int profileType = mChildFragments[position].getArguments().getInt(EXTRA_PROFILE);
+
+            if (profileType == ProfileType.WORK) {
+                return WORK_TAB;
+            }
+            if (profileType == ProfileType.PRIVATE) {
+                return PRIVATE_TAB;
+            }
+            return PERSONAL_TAB;
+        }
     }
 }
diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectKeyboardFragment.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectKeyboardFragment.java
index c4386e6..d35692c 100644
--- a/src/com/android/settings/dashboard/profileselector/ProfileSelectKeyboardFragment.java
+++ b/src/com/android/settings/dashboard/profileselector/ProfileSelectKeyboardFragment.java
@@ -16,8 +16,6 @@
 
 package com.android.settings.dashboard.profileselector;
 
-import android.os.Bundle;
-
 import androidx.fragment.app.Fragment;
 
 import com.android.settings.R;
@@ -39,19 +37,11 @@
 
     @Override
     public Fragment[] getFragments() {
-        final Bundle personalOnly = new Bundle();
-        personalOnly.putInt(EXTRA_PROFILE, ProfileType.PERSONAL);
-        final Fragment personalFragment = new AvailableVirtualKeyboardFragment();
-        personalFragment.setArguments(personalOnly);
-
-        final Bundle workOnly = new Bundle();
-        workOnly.putInt(EXTRA_PROFILE, ProfileType.WORK);
-        final Fragment workFragment = new AvailableVirtualKeyboardFragment();
-        workFragment.setArguments(workOnly);
-
-        return new Fragment[]{
-                personalFragment,
-                workFragment
-        };
+        return ProfileSelectFragment.getFragments(
+                getContext(),
+                null /* bundle */,
+                AvailableVirtualKeyboardFragment::new,
+                AvailableVirtualKeyboardFragment::new,
+                AvailableVirtualKeyboardFragment::new);
     }
 }
diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectLocationFragment.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectLocationFragment.java
index 28fb97b..feaec74 100644
--- a/src/com/android/settings/dashboard/profileselector/ProfileSelectLocationFragment.java
+++ b/src/com/android/settings/dashboard/profileselector/ProfileSelectLocationFragment.java
@@ -45,20 +45,12 @@
 
     @Override
     public Fragment[] getFragments() {
-
-        final Bundle workOnly = new Bundle();
-        workOnly.putInt(EXTRA_PROFILE, ProfileSelectFragment.ProfileType.WORK);
-        final Fragment workFragment = new LocationWorkProfileSettings();
-        workFragment.setArguments(workOnly);
-
-        final Bundle personalOnly = new Bundle();
-        personalOnly.putInt(EXTRA_PROFILE, ProfileSelectFragment.ProfileType.PERSONAL);
-        final Fragment personalFragment = new LocationPersonalSettings();
-        personalFragment.setArguments(personalOnly);
-        return new Fragment[]{
-                personalFragment,
-                workFragment
-        };
+        return ProfileSelectFragment.getFragments(
+                getContext(),
+                null /* bundle */,
+                LocationPersonalSettings::new,
+                LocationWorkProfileSettings::new,
+                LocationPersonalSettings::new);
     }
 
     @Override
diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectLocationServicesFragment.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectLocationServicesFragment.java
index 111e4ce..8e48c7b 100644
--- a/src/com/android/settings/dashboard/profileselector/ProfileSelectLocationServicesFragment.java
+++ b/src/com/android/settings/dashboard/profileselector/ProfileSelectLocationServicesFragment.java
@@ -16,8 +16,6 @@
 
 package com.android.settings.dashboard.profileselector;
 
-import android.os.Bundle;
-
 import androidx.fragment.app.Fragment;
 
 import com.android.settings.R;
@@ -31,19 +29,12 @@
 
     @Override
     public Fragment[] getFragments() {
-        final Bundle workOnly = new Bundle();
-        workOnly.putInt(EXTRA_PROFILE, ProfileType.WORK);
-        final Fragment workFragment = new LocationServicesForWork();
-        workFragment.setArguments(workOnly);
-
-        final Bundle personalOnly = new Bundle();
-        personalOnly.putInt(EXTRA_PROFILE, ProfileType.PERSONAL);
-        final Fragment personalFragment = new LocationServices();
-        personalFragment.setArguments(personalOnly);
-        return new Fragment[]{
-                personalFragment, // 0
-                workFragment
-        };
+        return ProfileSelectFragment.getFragments(
+                getContext(),
+                null /* bundle */,
+                LocationServices::new,
+                LocationServicesForWork::new,
+                LocationServices::new);
     }
 
     @Override
diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectManageApplications.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectManageApplications.java
index 36aa9c5..4c82f6c 100644
--- a/src/com/android/settings/dashboard/profileselector/ProfileSelectManageApplications.java
+++ b/src/com/android/settings/dashboard/profileselector/ProfileSelectManageApplications.java
@@ -31,19 +31,12 @@
 
     @Override
     public Fragment[] getFragments() {
-        final Bundle workOnly = getArguments() != null ? getArguments().deepCopy() : new Bundle();
-        workOnly.putInt(EXTRA_PROFILE, ProfileSelectFragment.ProfileType.WORK);
-        final Fragment workFragment = new ManageApplications();
-        workFragment.setArguments(workOnly);
-
-        final Bundle personalOnly = getArguments() != null ? getArguments() : new Bundle();
-        personalOnly.putInt(EXTRA_PROFILE, ProfileSelectFragment.ProfileType.PERSONAL);
-        final Fragment personalFragment = new ManageApplications();
-        personalFragment.setArguments(personalOnly);
-        return new Fragment[]{
-                personalFragment, //0
-                workFragment
-        };
+        return ProfileSelectFragment.getFragments(
+                getContext(),
+                getArguments(),
+                ManageApplications::new,
+                ManageApplications::new,
+                ManageApplications::new);
     }
 
     @Override
diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectPhysicalKeyboardFragment.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectPhysicalKeyboardFragment.java
index 3c1546e..239d609 100644
--- a/src/com/android/settings/dashboard/profileselector/ProfileSelectPhysicalKeyboardFragment.java
+++ b/src/com/android/settings/dashboard/profileselector/ProfileSelectPhysicalKeyboardFragment.java
@@ -51,22 +51,11 @@
 
     @Override
     public Fragment[] getFragments() {
-        final Bundle personalOnly = new Bundle();
-        personalOnly.putInt(EXTRA_PROFILE, ProfileType.PERSONAL);
-        final Fragment personalFragment = new NewKeyboardLayoutEnabledLocalesFragment();
-        personalOnly.putParcelable(
-                Settings.EXTRA_INPUT_DEVICE_IDENTIFIER, mInputDeviceIdentifier);
-        personalFragment.setArguments(personalOnly);
-
-        final Bundle workOnly = new Bundle();
-        workOnly.putInt(EXTRA_PROFILE, ProfileType.WORK);
-        final Fragment workFragment = new NewKeyboardLayoutEnabledLocalesFragment();
-        workOnly.putParcelable(Settings.EXTRA_INPUT_DEVICE_IDENTIFIER, mInputDeviceIdentifier);
-        workFragment.setArguments(workOnly);
-
-        return new Fragment[]{
-                personalFragment,
-                workFragment
-        };
+        return ProfileSelectFragment.getFragments(
+                getContext(),
+                null /* bundle */,
+                NewKeyboardLayoutEnabledLocalesFragment::new,
+                NewKeyboardLayoutEnabledLocalesFragment::new,
+                NewKeyboardLayoutEnabledLocalesFragment::new);
     }
 }
diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectStorageFragment.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectStorageFragment.java
index 5babf30..b523e7e 100644
--- a/src/com/android/settings/dashboard/profileselector/ProfileSelectStorageFragment.java
+++ b/src/com/android/settings/dashboard/profileselector/ProfileSelectStorageFragment.java
@@ -193,20 +193,12 @@
             return mFragments;
         }
 
-        final Bundle workBundle = new Bundle();
-        workBundle.putInt(EXTRA_PROFILE, ProfileType.WORK);
-        final Fragment workFragment = new StorageCategoryFragment();
-        workFragment.setArguments(workBundle);
-
-        final Bundle personalBundle = new Bundle();
-        personalBundle.putInt(EXTRA_PROFILE, ProfileType.PERSONAL);
-        final Fragment personalFragment = new StorageCategoryFragment();
-        personalFragment.setArguments(personalBundle);
-
-        mFragments = new Fragment[] {
-            personalFragment,
-            workFragment
-        };
+        mFragments = ProfileSelectFragment.getFragments(
+                getContext(),
+                null /* bundle */,
+                StorageCategoryFragment::new,
+                StorageCategoryFragment::new,
+                StorageCategoryFragment::new);
         return mFragments;
     }
 
diff --git a/src/com/android/settings/datausage/ChartDataUsagePreferenceController.kt b/src/com/android/settings/datausage/ChartDataUsagePreferenceController.kt
index 0479be4..49235b5 100644
--- a/src/com/android/settings/datausage/ChartDataUsagePreferenceController.kt
+++ b/src/com/android/settings/datausage/ChartDataUsagePreferenceController.kt
@@ -74,7 +74,7 @@
         preference.setTime(startTime, endTime)
         lifecycleScope.launch {
             val chartData = withContext(Dispatchers.Default) {
-                repository.querySummary(startTime, endTime)
+                repository.queryChartData(startTime, endTime)
             }
             preference.setNetworkCycleData(chartData)
         }
diff --git a/src/com/android/settings/datausage/DataUsageList.kt b/src/com/android/settings/datausage/DataUsageList.kt
index 9ac7161..1eb883c 100644
--- a/src/com/android/settings/datausage/DataUsageList.kt
+++ b/src/com/android/settings/datausage/DataUsageList.kt
@@ -195,8 +195,7 @@
         lastDisplayedUsageData = usageData
         Log.d(TAG, "showing cycle $usageData")
 
-        val totalPhrase = DataUsageUtils.formatDataUsage(requireContext(), usageData.usage)
-        usageAmount.title = getString(R.string.data_used_template, totalPhrase)
+        usageAmount.title = usageData.getDataUsedString(requireContext())
 
         updateChart(usageData)
         updateApps(usageData)
diff --git a/src/com/android/settings/datausage/lib/NetworkCycleDataRepository.kt b/src/com/android/settings/datausage/lib/NetworkCycleDataRepository.kt
index 1ff7a81..4e97c6d 100644
--- a/src/com/android/settings/datausage/lib/NetworkCycleDataRepository.kt
+++ b/src/com/android/settings/datausage/lib/NetworkCycleDataRepository.kt
@@ -23,15 +23,13 @@
 import android.text.format.DateUtils
 import android.util.Range
 import com.android.settingslib.NetworkPolicyEditor
-import kotlinx.coroutines.async
-import kotlinx.coroutines.awaitAll
-import kotlinx.coroutines.coroutineScope
+import com.android.settingslib.spa.framework.util.asyncMap
 
 interface INetworkCycleDataRepository {
     suspend fun loadCycles(): List<NetworkUsageData>
     fun getCycles(): List<Range<Long>>
     fun getPolicy(): NetworkPolicy?
-    suspend fun querySummary(startTime: Long, endTime: Long): NetworkCycleChartData?
+    suspend fun queryChartData(startTime: Long, endTime: Long): NetworkCycleChartData?
 }
 
 class NetworkCycleDataRepository(
@@ -46,6 +44,8 @@
     override suspend fun loadCycles(): List<NetworkUsageData> =
         getCycles().queryUsage().filter { it.usage > 0 }
 
+    fun loadFirstCycle(): NetworkUsageData? = getCycles().firstOrNull()?.let { queryUsage(it) }
+
     override fun getCycles(): List<Range<Long>> {
         val policy = getPolicy() ?: return queryCyclesAsFourWeeks()
         return policy.cycleIterator().asSequence().map {
@@ -68,7 +68,7 @@
             getPolicy(networkTemplate)
         }
 
-    override suspend fun querySummary(startTime: Long, endTime: Long): NetworkCycleChartData? {
+    override suspend fun queryChartData(startTime: Long, endTime: Long): NetworkCycleChartData? {
         val usage = networkStatsRepository.querySummaryForDevice(startTime, endTime)
         if (usage > 0L) {
             return NetworkCycleChartData(
@@ -83,17 +83,14 @@
         return null
     }
 
-    private suspend fun List<Range<Long>>.queryUsage(): List<NetworkUsageData> = coroutineScope {
-        map { range ->
-            async {
-                NetworkUsageData(
-                    startTime = range.lower,
-                    endTime = range.upper,
-                    usage = networkStatsRepository.querySummaryForDevice(range.lower, range.upper),
-                )
-            }
-        }.awaitAll()
-    }
+    private suspend fun List<Range<Long>>.queryUsage(): List<NetworkUsageData> =
+        asyncMap { queryUsage(it) }
+
+    fun queryUsage(range: Range<Long>) = NetworkUsageData(
+        startTime = range.lower,
+        endTime = range.upper,
+        usage = networkStatsRepository.querySummaryForDevice(range.lower, range.upper),
+    )
 
     private fun bucketRange(startTime: Long, endTime: Long, bucketSize: Long): List<Range<Long>> {
         val buckets = mutableListOf<Range<Long>>()
diff --git a/src/com/android/settings/datausage/lib/NetworkUsageData.kt b/src/com/android/settings/datausage/lib/NetworkUsageData.kt
index 5bdd7ed..93cde5f 100644
--- a/src/com/android/settings/datausage/lib/NetworkUsageData.kt
+++ b/src/com/android/settings/datausage/lib/NetworkUsageData.kt
@@ -16,7 +16,11 @@
 
 package com.android.settings.datausage.lib
 
+import android.content.Context
+import android.text.format.DateUtils
 import android.util.Range
+import com.android.settings.R
+import com.android.settings.datausage.DataUsageUtils
 
 /**
  * Base data structure representing usage data in a period.
@@ -27,6 +31,21 @@
     val usage: Long,
 ) {
     val timeRange = Range(startTime, endTime)
+
+    fun formatStartDate(context: Context): String =
+        DateUtils.formatDateTime(context, startTime, DATE_FORMAT)
+
+    fun formatDateRange(context: Context): String =
+        DateUtils.formatDateRange(context, startTime, endTime, DATE_FORMAT)
+
+    fun formatUsage(context: Context): CharSequence = DataUsageUtils.formatDataUsage(context, usage)
+
+    fun getDataUsedString(context: Context): String =
+        context.getString(R.string.data_used_template, formatUsage(context))
+
+    private companion object {
+        const val DATE_FORMAT = DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_ABBREV_MONTH
+    }
 }
 
 fun List<NetworkUsageData>.aggregate(): NetworkUsageData? = when {
diff --git a/src/com/android/settings/deviceinfo/StorageCategoryFragment.java b/src/com/android/settings/deviceinfo/StorageCategoryFragment.java
index 52f453d..1280057 100644
--- a/src/com/android/settings/deviceinfo/StorageCategoryFragment.java
+++ b/src/com/android/settings/deviceinfo/StorageCategoryFragment.java
@@ -37,6 +37,7 @@
 import com.android.settings.Utils;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
+import com.android.settings.dashboard.profileselector.ProfileSelectFragment.ProfileType;
 import com.android.settings.deviceinfo.storage.ManageStoragePreferenceController;
 import com.android.settings.deviceinfo.storage.NonCurrentUserController;
 import com.android.settings.deviceinfo.storage.StorageAsyncLoader;
@@ -85,7 +86,7 @@
 
     private StorageItemPreferenceController mPreferenceController;
     private List<NonCurrentUserController> mNonCurrentUsers;
-    private boolean mIsWorkProfile;
+    private @ProfileType int mProfileType;
     private int mUserId;
     private boolean mIsLoadedFromCache;
     private StorageCacheHelper mStorageCacheHelper;
@@ -163,9 +164,9 @@
         // These member variables are initialized befoer super.onAttach for
         // createPreferenceControllers to work correctly.
         mUserManager = context.getSystemService(UserManager.class);
-        mIsWorkProfile = getArguments().getInt(ProfileSelectFragment.EXTRA_PROFILE)
-                == ProfileSelectFragment.ProfileType.WORK;
-        mUserId = Utils.getCurrentUserId(mUserManager, mIsWorkProfile);
+        mProfileType = getArguments().getInt(ProfileSelectFragment.EXTRA_PROFILE);
+        mUserId = Utils.getCurrentUserIdOfType(mUserManager, mProfileType);
+
         mStorageCacheHelper = new StorageCacheHelper(getContext(), mUserId);
 
         super.onAttach(context);
@@ -229,8 +230,12 @@
 
     @Override
     public int getMetricsCategory() {
-        return mIsWorkProfile ? SettingsEnums.SETTINGS_STORAGE_CATEGORY_WORK :
-                SettingsEnums.SETTINGS_STORAGE_CATEGORY;
+        if (mProfileType == ProfileSelectFragment.ProfileType.WORK) {
+            return SettingsEnums.SETTINGS_STORAGE_CATEGORY_WORK;
+        } else if (mProfileType == ProfileSelectFragment.ProfileType.PRIVATE) {
+            return SettingsEnums.SETTINGS_STORAGE_CATEGORY_PRIVATE;
+        }
+        return SettingsEnums.SETTINGS_STORAGE_CATEGORY;
     }
 
     @Override
@@ -248,11 +253,12 @@
         final List<AbstractPreferenceController> controllers = new ArrayList<>();
         final StorageManager sm = context.getSystemService(StorageManager.class);
         mPreferenceController = new StorageItemPreferenceController(context, this,
-                null /* volume */, new StorageManagerVolumeProvider(sm), mIsWorkProfile);
+                null /* volume */, new StorageManagerVolumeProvider(sm), mProfileType);
         controllers.add(mPreferenceController);
 
-        mNonCurrentUsers = mIsWorkProfile ? EMPTY_LIST :
-                NonCurrentUserController.getNonCurrentUserControllers(context, mUserManager);
+        mNonCurrentUsers = mProfileType == ProfileSelectFragment.ProfileType.PERSONAL
+                ? NonCurrentUserController.getNonCurrentUserControllers(context, mUserManager)
+                : EMPTY_LIST;
         controllers.addAll(mNonCurrentUsers);
         return controllers;
     }
diff --git a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
index f31f2be..0da3667 100644
--- a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
+++ b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
@@ -308,7 +308,6 @@
         // These member variables are initialized befoer super.onAttach for
         // createPreferenceControllers to work correctly.
         mUserManager = context.getSystemService(UserManager.class);
-        mIsWorkProfile = false;
         mUserId = UserHandle.myUserId();
         mStorageCacheHelper = new StorageCacheHelper(getContext(), mUserId);
 
@@ -423,7 +422,7 @@
         final List<AbstractPreferenceController> controllers = new ArrayList<>();
         final StorageManager sm = context.getSystemService(StorageManager.class);
         mPreferenceController = new StorageItemPreferenceController(context, this,
-                null /* volume */, new StorageManagerVolumeProvider(sm), mIsWorkProfile);
+                null /* volume */, new StorageManagerVolumeProvider(sm));
         controllers.add(mPreferenceController);
 
         mNonCurrentUsers = NonCurrentUserController.getNonCurrentUserControllers(context,
@@ -467,8 +466,7 @@
                     final UserManager userManager = context.getSystemService(UserManager.class);
                     final List<AbstractPreferenceController> controllers = new ArrayList<>();
                     controllers.add(new StorageItemPreferenceController(context, null /* host */,
-                            null /* volume */, new StorageManagerVolumeProvider(sm),
-                            false /* isWorkProfile */));
+                            null /* volume */, new StorageManagerVolumeProvider(sm)));
                     controllers.addAll(NonCurrentUserController.getNonCurrentUserControllers(
                             context, userManager));
                     return controllers;
diff --git a/src/com/android/settings/deviceinfo/TopLevelStoragePreferenceController.java b/src/com/android/settings/deviceinfo/TopLevelStoragePreferenceController.java
index ccae7e9..1955f36 100644
--- a/src/com/android/settings/deviceinfo/TopLevelStoragePreferenceController.java
+++ b/src/com/android/settings/deviceinfo/TopLevelStoragePreferenceController.java
@@ -27,6 +27,7 @@
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settings.core.BasePreferenceController;
+import com.android.settings.dashboard.profileselector.ProfileSelectFragment.ProfileType;
 import com.android.settings.deviceinfo.storage.StorageCacheHelper;
 import com.android.settingslib.deviceinfo.PrivateStorageInfo;
 import com.android.settingslib.deviceinfo.StorageManagerVolumeProvider;
@@ -62,8 +63,8 @@
 
     @VisibleForTesting
     protected Future refreshSummaryThread(Preference preference) {
-        int userId = Utils.getCurrentUserId(mContext.getSystemService(UserManager.class),
-                /* isWorkProfile */ false);
+        int userId = Utils.getCurrentUserIdOfType(
+                mContext.getSystemService(UserManager.class), ProfileType.PERSONAL);
         final StorageCacheHelper storageCacheHelper = new StorageCacheHelper(mContext, userId);
         long cachedUsedSize = storageCacheHelper.retrieveUsedSize();
         long cachedTotalSize = storageCacheHelper.retrieveCachedSize().totalSize;
diff --git a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
index a57cd5b..fd42417 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
@@ -17,11 +17,13 @@
 package com.android.settings.deviceinfo.storage;
 
 import static com.android.settings.dashboard.profileselector.ProfileSelectFragment.PERSONAL_TAB;
+import static com.android.settings.dashboard.profileselector.ProfileSelectFragment.PRIVATE_TAB;
 import static com.android.settings.dashboard.profileselector.ProfileSelectFragment.WORK_TAB;
 
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
@@ -47,6 +49,7 @@
 import com.android.settings.applications.manageapplications.ManageApplications;
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
 import com.android.settings.deviceinfo.StorageItemPreference;
 import com.android.settings.deviceinfo.storage.StorageUtils.SystemInfoFragment;
 import com.android.settings.overlay.FeatureFactory;
@@ -108,33 +111,33 @@
     private final Fragment mFragment;
     private final MetricsFeatureProvider mMetricsFeatureProvider;
     private final StorageVolumeProvider mSvp;
-    private VolumeInfo mVolume;
+    @Nullable private VolumeInfo mVolume;
     private int mUserId;
     private long mUsedBytes;
     private long mTotalSize;
 
-    private List<StorageItemPreference> mPrivateStorageItemPreferences;
-    private PreferenceScreen mScreen;
+    @Nullable private List<StorageItemPreference> mPrivateStorageItemPreferences;
+    @Nullable private PreferenceScreen mScreen;
     @VisibleForTesting
-    Preference mPublicStoragePreference;
+    @Nullable Preference mPublicStoragePreference;
     @VisibleForTesting
-    StorageItemPreference mImagesPreference;
+    @Nullable StorageItemPreference mImagesPreference;
     @VisibleForTesting
-    StorageItemPreference mVideosPreference;
+    @Nullable StorageItemPreference mVideosPreference;
     @VisibleForTesting
-    StorageItemPreference mAudioPreference;
+    @Nullable StorageItemPreference mAudioPreference;
     @VisibleForTesting
-    StorageItemPreference mAppsPreference;
+    @Nullable StorageItemPreference mAppsPreference;
     @VisibleForTesting
-    StorageItemPreference mGamesPreference;
+    @Nullable StorageItemPreference mGamesPreference;
     @VisibleForTesting
-    StorageItemPreference mDocumentsAndOtherPreference;
+    @Nullable StorageItemPreference mDocumentsAndOtherPreference;
     @VisibleForTesting
-    StorageItemPreference mSystemPreference;
+    @Nullable StorageItemPreference mSystemPreference;
     @VisibleForTesting
-    StorageItemPreference mTrashPreference;
+    @Nullable StorageItemPreference mTrashPreference;
 
-    private boolean mIsWorkProfile;
+    private final int mProfileType;
 
     private StorageCacheHelper mStorageCacheHelper;
     // The mIsDocumentsPrefShown being used here is to prevent a flicker problem from displaying
@@ -142,15 +145,24 @@
     private boolean mIsDocumentsPrefShown;
     private boolean mIsPreferenceOrderedBySize;
 
-    public StorageItemPreferenceController(Context context, Fragment hostFragment,
-            VolumeInfo volume, StorageVolumeProvider svp, boolean isWorkProfile) {
+    public StorageItemPreferenceController(
+            Context context, Fragment hostFragment, VolumeInfo volume, StorageVolumeProvider svp) {
+        this(context, hostFragment, volume, svp, ProfileSelectFragment.ProfileType.PERSONAL);
+    }
+
+    public StorageItemPreferenceController(
+            Context context,
+            Fragment hostFragment,
+            @Nullable VolumeInfo volume,
+            StorageVolumeProvider svp,
+            @ProfileSelectFragment.ProfileType int profileType) {
         super(context);
         mPackageManager = context.getPackageManager();
         mUserManager = context.getSystemService(UserManager.class);
         mFragment = hostFragment;
         mVolume = volume;
         mSvp = svp;
-        mIsWorkProfile = isWorkProfile;
+        mProfileType = profileType;
         mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
         mUserId = getCurrentUserId();
         mIsDocumentsPrefShown = isDocumentsPrefShown();
@@ -168,7 +180,7 @@
 
     @VisibleForTesting
     int getCurrentUserId() {
-        return Utils.getCurrentUserId(mUserManager, mIsWorkProfile);
+        return Utils.getCurrentUserIdOfType(mUserManager, mProfileType);
     }
 
     @Override
@@ -229,7 +241,9 @@
         mVolume = volume;
 
         if (mPublicStoragePreference != null) {
-            mPublicStoragePreference.setVisible(isValidPublicVolume() && !mIsWorkProfile);
+            mPublicStoragePreference.setVisible(
+                    isValidPublicVolume()
+                            && mProfileType == ProfileSelectFragment.ProfileType.PERSONAL);
         }
 
         // If isValidPrivateVolume() is true, these preferences will become visible at
@@ -327,9 +341,16 @@
      * Sets the user id for which this preference controller is handling.
      */
     public void setUserId(UserHandle userHandle) {
-        if (mIsWorkProfile && !mUserManager.isManagedProfile(userHandle.getIdentifier())) {
+        if (mProfileType == ProfileSelectFragment.ProfileType.WORK
+                && !mUserManager.isManagedProfile(userHandle.getIdentifier())) {
             throw new IllegalArgumentException("Only accept work profile userHandle");
         }
+
+        UserInfo userInfo = mUserManager.getUserInfo(userHandle.getIdentifier());
+        if (mProfileType == ProfileSelectFragment.ProfileType.PRIVATE
+                && (userInfo == null || userInfo.isPrivateProfile())) {
+            throw new IllegalArgumentException("Only accept private profile userHandle");
+        }
         mUserId = userHandle.getIdentifier();
 
         tintPreference(mPublicStoragePreference);
@@ -498,8 +519,13 @@
 
     private Bundle getWorkAnnotatedBundle(int additionalCapacity) {
         final Bundle args = new Bundle(1 + additionalCapacity);
-        args.putInt(SettingsActivity.EXTRA_SHOW_FRAGMENT_TAB,
-                mIsWorkProfile ? WORK_TAB : PERSONAL_TAB);
+        if (mProfileType == ProfileSelectFragment.ProfileType.WORK) {
+            args.putInt(SettingsActivity.EXTRA_SHOW_FRAGMENT_TAB, WORK_TAB);
+        } else if (mProfileType == ProfileSelectFragment.ProfileType.PRIVATE) {
+            args.putInt(SettingsActivity.EXTRA_SHOW_FRAGMENT_TAB, PRIVATE_TAB);
+        } else {
+            args.putInt(SettingsActivity.EXTRA_SHOW_FRAGMENT_TAB, PERSONAL_TAB);
+        }
         return args;
     }
 
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java
index f25e16f..eebf1f5 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java
@@ -131,14 +131,14 @@
             final PowerUsageFeatureProvider featureProvider,
             final @NonNull Set<String> systemAppsPackageNames,
             final @NonNull Set<Integer> systemAppsUids) {
+        combineIntoUninstalledApps(context, mAppEntries);
         combineIntoSystemApps(
                 context, featureProvider, systemAppsPackageNames, systemAppsUids, mAppEntries);
         combineSystemItemsIntoOthers(context, featureProvider, mSystemEntries);
     }
 
     private static void purgeBatteryDiffData(
-            final PowerUsageFeatureProvider featureProvider,
-            final List<BatteryDiffEntry> entries) {
+            final PowerUsageFeatureProvider featureProvider, final List<BatteryDiffEntry> entries) {
         final double screenOnTimeThresholdInMs =
                 featureProvider.getBatteryUsageListScreenOnTimeThresholdInMs();
         final double consumePowerThreshold =
@@ -155,7 +155,7 @@
             final String packageName = entry.getPackageName();
             final Integer componentId = entry.mComponentId;
             if ((screenOnTimeInMs < screenOnTimeThresholdInMs
-                    && comsumePower < consumePowerThreshold)
+                            && comsumePower < consumePowerThreshold)
                     || ConvertUtils.FAKE_PACKAGE_NAME.equals(packageName)
                     || hideSystemComponentSet.contains(componentId)
                     || (packageName != null && hideApplicationSet.contains(packageName))) {
@@ -178,18 +178,23 @@
         final Iterator<BatteryDiffEntry> appListIterator = appEntries.iterator();
         while (appListIterator.hasNext()) {
             final BatteryDiffEntry batteryDiffEntry = appListIterator.next();
-            if (needsCombineInSystemApp(batteryDiffEntry, systemAppsAllowlist,
-                    systemAppsPackageNames, systemAppsUids)) {
+            if (needsCombineInSystemApp(
+                    batteryDiffEntry,
+                    systemAppsAllowlist,
+                    systemAppsPackageNames,
+                    systemAppsUids)) {
                 if (systemAppsDiffEntry == null) {
-                    systemAppsDiffEntry = new BatteryDiffEntry(context,
-                            BatteryDiffEntry.SYSTEM_APPS_KEY, BatteryDiffEntry.SYSTEM_APPS_KEY,
-                            ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
+                    systemAppsDiffEntry =
+                            new BatteryDiffEntry(
+                                    context,
+                                    BatteryDiffEntry.SYSTEM_APPS_KEY,
+                                    BatteryDiffEntry.SYSTEM_APPS_KEY,
+                                    ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
                 }
                 systemAppsDiffEntry.mConsumePower += batteryDiffEntry.mConsumePower;
                 systemAppsDiffEntry.mForegroundUsageTimeInMs +=
                         batteryDiffEntry.mForegroundUsageTimeInMs;
-                systemAppsDiffEntry.setTotalConsumePower(
-                        batteryDiffEntry.getTotalConsumePower());
+                systemAppsDiffEntry.setTotalConsumePower(batteryDiffEntry.getTotalConsumePower());
                 appListIterator.remove();
             }
         }
@@ -198,6 +203,35 @@
         }
     }
 
+    private static void combineIntoUninstalledApps(
+            final Context context, final @NonNull List<BatteryDiffEntry> appEntries) {
+        BatteryDiffEntry uninstalledAppDiffEntry = null;
+        final Iterator<BatteryDiffEntry> appListIterator = appEntries.iterator();
+        while (appListIterator.hasNext()) {
+            final BatteryDiffEntry batteryDiffEntry = appListIterator.next();
+            if (!batteryDiffEntry.isUninstalledEntry()) {
+                continue;
+            }
+
+            if (uninstalledAppDiffEntry == null) {
+                uninstalledAppDiffEntry =
+                        new BatteryDiffEntry(
+                                context,
+                                BatteryDiffEntry.UNINSTALLED_APPS_KEY,
+                                BatteryDiffEntry.UNINSTALLED_APPS_KEY,
+                                ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
+            }
+            uninstalledAppDiffEntry.mConsumePower += batteryDiffEntry.mConsumePower;
+            uninstalledAppDiffEntry.mForegroundUsageTimeInMs +=
+                    batteryDiffEntry.mForegroundUsageTimeInMs;
+            uninstalledAppDiffEntry.setTotalConsumePower(batteryDiffEntry.getTotalConsumePower());
+            appListIterator.remove();
+        }
+        if (uninstalledAppDiffEntry != null) {
+            appEntries.add(uninstalledAppDiffEntry);
+        }
+    }
+
     private static void combineSystemItemsIntoOthers(
             final Context context,
             final PowerUsageFeatureProvider featureProvider,
@@ -210,17 +244,20 @@
         while (systemListIterator.hasNext()) {
             final BatteryDiffEntry batteryDiffEntry = systemListIterator.next();
             final int componentId = batteryDiffEntry.mComponentId;
-            if (othersSystemComponentSet.contains(componentId) || (
-                    componentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
+            if (othersSystemComponentSet.contains(componentId)
+                    || (componentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
                             && othersCustomComponentNameSet.contains(
                                     batteryDiffEntry.getAppLabel()))) {
                 if (othersDiffEntry == null) {
-                    othersDiffEntry = new BatteryDiffEntry(context, BatteryDiffEntry.OTHERS_KEY,
-                            BatteryDiffEntry.OTHERS_KEY, ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
+                    othersDiffEntry =
+                            new BatteryDiffEntry(
+                                    context,
+                                    BatteryDiffEntry.OTHERS_KEY,
+                                    BatteryDiffEntry.OTHERS_KEY,
+                                    ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
                 }
                 othersDiffEntry.mConsumePower += batteryDiffEntry.mConsumePower;
-                othersDiffEntry.setTotalConsumePower(
-                        batteryDiffEntry.getTotalConsumePower());
+                othersDiffEntry.setTotalConsumePower(batteryDiffEntry.getTotalConsumePower());
                 systemListIterator.remove();
             }
         }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntry.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntry.java
index b284ea5..971ada9 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntry.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntry.java
@@ -22,6 +22,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
 
@@ -43,22 +44,33 @@
     static Locale sCurrentLocale = null;
     // Caches app label and icon to improve loading performance.
     static final Map<String, BatteryEntry.NameAndIcon> sResourceCache = new HashMap<>();
+
     // Whether a specific item is valid to launch restriction page?
     @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
     static final Map<String, Boolean> sValidForRestriction = new HashMap<>();
+
     /** A comparator for {@link BatteryDiffEntry} based on the sorting key. */
     static final Comparator<BatteryDiffEntry> COMPARATOR =
             (a, b) -> Double.compare(b.getSortingKey(), a.getSortingKey());
+
     static final String SYSTEM_APPS_KEY = "A|SystemApps";
+    static final String UNINSTALLED_APPS_KEY = "A|UninstalledApps";
     static final String OTHERS_KEY = "S|Others";
 
     // key -> (label_id, icon_id)
-    private static final Map<String, Pair<Integer, Integer>> SPECIAL_ENTRY_MAP = Map.of(
-            SYSTEM_APPS_KEY,
-            Pair.create(R.string.battery_usage_system_apps, R.drawable.ic_power_system),
-            OTHERS_KEY,
-            Pair.create(R.string.battery_usage_others,
-                    R.drawable.ic_settings_battery_usage_others));
+    private static final Map<String, Pair<Integer, Integer>> SPECIAL_ENTRY_MAP =
+            Map.of(
+                    SYSTEM_APPS_KEY,
+                    Pair.create(
+                            R.string.battery_usage_system_apps, R.drawable.ic_power_system),
+                    UNINSTALLED_APPS_KEY,
+                    Pair.create(
+                            R.string.battery_usage_uninstalled_apps,
+                            R.drawable.ic_battery_uninstalled),
+                    OTHERS_KEY,
+                    Pair.create(
+                            R.string.battery_usage_others,
+                            R.drawable.ic_settings_battery_usage_others));
 
     public long mUid;
     public long mUserId;
@@ -146,8 +158,7 @@
     /** Sets the total consumed power in a specific time slot. */
     public void setTotalConsumePower(double totalConsumePower) {
         mTotalConsumePower = totalConsumePower;
-        mPercentage = totalConsumePower == 0
-                ? 0 : (mConsumePower / mTotalConsumePower) * 100.0;
+        mPercentage = totalConsumePower == 0 ? 0 : (mConsumePower / mTotalConsumePower) * 100.0;
         mAdjustPercentageOffset = 0;
     }
 
@@ -173,8 +184,21 @@
 
     /** Gets the key for sorting */
     public double getSortingKey() {
-        return getKey() != null && SPECIAL_ENTRY_MAP.containsKey(getKey())
-                ? -1 : getPercentage() + getAdjustPercentageOffset();
+        String key = getKey();
+        if (key == null) {
+            return getPercentage() + getAdjustPercentageOffset();
+        }
+
+        // For special entries, put them to the end of the list.
+        switch (key) {
+            case UNINSTALLED_APPS_KEY:
+            case OTHERS_KEY:
+                return -1;
+            case SYSTEM_APPS_KEY:
+                return -2;
+            default:
+                return getPercentage() + getAdjustPercentageOffset();
+        }
     }
 
     /** Clones a new instance. */
@@ -222,8 +246,8 @@
 
     /** Gets the searching package name for UID battery type. */
     public String getPackageName() {
-        final String packageName = mDefaultPackageName != null
-                ? mDefaultPackageName : mLegacyPackageName;
+        final String packageName =
+                mDefaultPackageName != null ? mDefaultPackageName : mLegacyPackageName;
         if (packageName == null) {
             return packageName;
         }
@@ -231,7 +255,8 @@
         // From "com.opera.browser:privileged_process0" to "com.opera.browser"
         final String[] splitPackageNames = packageName.split(":");
         return splitPackageNames != null && splitPackageNames.length > 0
-                ? splitPackageNames[0] : packageName;
+                ? splitPackageNames[0]
+                : packageName;
     }
 
     /** Whether this item is valid for users to launch restriction page? */
@@ -255,6 +280,17 @@
         }
     }
 
+    /** Whether the current BatteryDiffEntry is uninstalled app or not. */
+    public boolean isUninstalledEntry() {
+        final String packageName = getPackageName();
+        if (TextUtils.isEmpty(packageName) || isSystemEntry()) {
+            return false;
+        }
+
+        final int uid = BatteryUtils.getInstance(mContext).getPackageUid(packageName);
+        return uid == BatteryUtils.UID_REMOVED_APPS || uid == BatteryUtils.UID_NULL;
+    }
+
     void loadLabelAndIcon() {
         if (mIsLoaded) {
             return;
@@ -286,8 +322,7 @@
             mAppIconId = pair.second;
             mAppIcon = mContext.getDrawable(mAppIconId);
             sResourceCache.put(
-                    getKey(),
-                    new BatteryEntry.NameAndIcon(mAppLabel, mAppIcon, mAppIconId));
+                    getKey(), new BatteryEntry.NameAndIcon(mAppLabel, mAppIcon, mAppIconId));
             return;
         }
 
@@ -301,7 +336,7 @@
                     mAppLabel = nameAndIconForUser.mName;
                     sResourceCache.put(
                             getKey(),
-                            new BatteryEntry.NameAndIcon(mAppLabel, mAppIcon, /*iconId=*/ 0));
+                            new BatteryEntry.NameAndIcon(mAppLabel, mAppIcon, /* iconId= */ 0));
                 }
                 break;
             case ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY:
@@ -329,7 +364,7 @@
                 if (mAppLabel != null || mAppIcon != null) {
                     sResourceCache.put(
                             getKey(),
-                            new BatteryEntry.NameAndIcon(mAppLabel, mAppIcon, /*iconId=*/ 0));
+                            new BatteryEntry.NameAndIcon(mAppLabel, mAppIcon, /* iconId= */ 0));
                 }
                 break;
         }
@@ -354,16 +389,20 @@
         }
         try {
             mValidForRestriction =
-                    mContext.getPackageManager().getPackageInfo(
-                            getPackageName(),
-                            PackageManager.MATCH_DISABLED_COMPONENTS
-                                    | PackageManager.MATCH_ANY_USER
-                                    | PackageManager.GET_SIGNATURES
-                                    | PackageManager.GET_PERMISSIONS)
+                    mContext.getPackageManager()
+                                    .getPackageInfo(
+                                            getPackageName(),
+                                            PackageManager.MATCH_DISABLED_COMPONENTS
+                                                    | PackageManager.MATCH_ANY_USER
+                                                    | PackageManager.GET_SIGNATURES
+                                                    | PackageManager.GET_PERMISSIONS)
                             != null;
         } catch (Exception e) {
-            Log.e(TAG, String.format("getPackageInfo() error %s for package=%s",
-                    e.getCause(), getPackageName()));
+            Log.e(
+                    TAG,
+                    String.format(
+                            "getPackageInfo() error %s for package=%s",
+                            e.getCause(), getPackageName()));
             mValidForRestriction = false;
         }
     }
@@ -371,8 +410,11 @@
     private BatteryEntry.NameAndIcon getCache() {
         final Locale locale = Locale.getDefault();
         if (sCurrentLocale != locale) {
-            Log.d(TAG, String.format("clearCache() locale is changed from %s to %s",
-                    sCurrentLocale, locale));
+            Log.d(
+                    TAG,
+                    String.format(
+                            "clearCache() locale is changed from %s to %s",
+                            sCurrentLocale, locale));
             sCurrentLocale = locale;
             clearCache();
         }
@@ -421,8 +463,11 @@
             mDefaultPackageName = nameAndIcon.mPackageName;
             if (mDefaultPackageName != null
                     && !mDefaultPackageName.equals(nameAndIcon.mPackageName)) {
-                Log.w(TAG, String.format("found different package: %s | %s",
-                        mDefaultPackageName, nameAndIcon.mPackageName));
+                Log.w(
+                        TAG,
+                        String.format(
+                                "found different package: %s | %s",
+                                mDefaultPackageName, nameAndIcon.mPackageName));
             }
         }
     }
@@ -459,7 +504,8 @@
 
     private Drawable getBadgeIconForUser(Drawable icon) {
         final int userId = UserHandle.getUserId((int) mUid);
-        return userId == UserHandle.USER_OWNER ? icon :
-                mUserManager.getBadgedIconForUser(icon, new UserHandle(userId));
+        return userId == UserHandle.USER_OWNER
+                ? icon
+                : mUserManager.getBadgedIconForUser(icon, new UserHandle(userId));
     }
 }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java
index 63f0d40..006928a 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java
@@ -63,6 +63,10 @@
         }
         final String action = intent.getAction();
         Log.d(TAG, "onReceive:" + action);
+        if (DatabaseUtils.isWorkProfile(context)) {
+            Log.w(TAG, "do nothing for work profile action=" + action);
+            return;
+        }
         DatabaseUtils.recordDateTime(context, action);
         final String fullChargeIntentAction = FeatureFactory.getFeatureFactory()
                 .getPowerUsageFeatureProvider()
diff --git a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
index a1987c9..4eef3a0 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
@@ -111,8 +111,8 @@
             values.put(BatteryHistEntry.KEY_UID, Long.valueOf(entry.getUid()));
             values.put(BatteryHistEntry.KEY_USER_ID,
                     Long.valueOf(UserHandle.getUserId(entry.getUid())));
-            values.put(BatteryHistEntry.KEY_PACKAGE_NAME,
-                    entry.getDefaultPackageName());
+            final String packageName = entry.getDefaultPackageName();
+            values.put(BatteryHistEntry.KEY_PACKAGE_NAME, packageName != null ? packageName : "");
             values.put(BatteryHistEntry.KEY_CONSUMER_TYPE,
                     Integer.valueOf(entry.getConsumerType()));
         } else {
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
index 32cd1b9..6099192 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
@@ -626,17 +626,20 @@
             final List<BatteryHistEntry> batteryHistEntryList,
             final @NonNull Set<String> systemAppsPackageNames,
             final @NonNull Set<Integer> systemAppsUids) {
+        final List<BatteryDiffEntry> appEntries = new ArrayList<>();
+        final List<BatteryDiffEntry> systemEntries = new ArrayList<>();
         if (batteryHistEntryList == null || batteryHistEntryList.isEmpty()) {
             Log.w(TAG, "batteryHistEntryList is null or empty in generateBatteryDiffData()");
-            return null;
+            return new BatteryDiffData(context, startTimestamp, getCurrentTimeMillis(),
+                    /* startBatteryLevel =*/ 100, getCurrentLevel(context), /* screenOnTime= */ 0L,
+                    appEntries, systemEntries, systemAppsPackageNames, systemAppsUids,
+                    /* isAccumulated= */ false);
         }
         final int currentUserId = context.getUserId();
         final UserHandle userHandle =
                 Utils.getManagedProfile(context.getSystemService(UserManager.class));
         final int workProfileUserId =
                 userHandle != null ? userHandle.getIdentifier() : Integer.MIN_VALUE;
-        final List<BatteryDiffEntry> appEntries = new ArrayList<>();
-        final List<BatteryDiffEntry> systemEntries = new ArrayList<>();
 
         for (BatteryHistEntry entry : batteryHistEntryList) {
             final boolean isFromOtherUsers = isConsumedFromOtherUsers(
@@ -670,11 +673,6 @@
                 }
             }
         }
-
-        // If there is no data, return null instead of empty item.
-        if (appEntries.isEmpty() && systemEntries.isEmpty()) {
-            return null;
-        }
         return new BatteryDiffData(context, startTimestamp, getCurrentTimeMillis(),
                 /* startBatteryLevel =*/ 100, getCurrentLevel(context), /* screenOnTime= */ 0L,
                 appEntries, systemEntries, systemAppsPackageNames, systemAppsUids,
@@ -1318,7 +1316,9 @@
                 // We should not get the empty list since we have at least one fake data to record
                 // the battery level and status in each time slot, the empty list is used to
                 // represent there is no enough data to apply interpolation arithmetic.
-                return null;
+                return new BatteryDiffData(context, startTimestamp, endTimestamp, startBatteryLevel,
+                        endBatteryLevel, /* screenOnTime= */ 0L, appEntries, systemEntries,
+                        systemAppsPackageNames, systemAppsUids, /* isAccumulated= */ false);
             }
             allBatteryHistEntryKeys.addAll(slotBatteryHistMap.keySet());
         }
@@ -1458,12 +1458,6 @@
                 appEntries.add(currentBatteryDiffEntry);
             }
         }
-
-        // If there is no data, return null instead of empty item.
-        if (appEntries.isEmpty() && systemEntries.isEmpty()) {
-            return null;
-        }
-
         return new BatteryDiffData(context, startTimestamp, endTimestamp, startBatteryLevel,
                 endBatteryLevel, slotScreenOnTime, appEntries, systemEntries,
                 systemAppsPackageNames, systemAppsUids, /* isAccumulated= */ false);
@@ -1563,9 +1557,9 @@
             }
         }
 
-        return diffEntryList.isEmpty() ? null : new BatteryDiffData(context, startTimestamp,
-                endTimestamp, startBatteryLevel, endBatteryLevel, totalScreenOnTime, appEntries,
-                systemEntries, /* systemAppsPackageNames= */ new ArraySet<>(),
+        return new BatteryDiffData(context, startTimestamp, endTimestamp, startBatteryLevel,
+                endBatteryLevel, totalScreenOnTime, appEntries, systemEntries,
+                /* systemAppsPackageNames= */ new ArraySet<>(),
                 /* systemAppsUids= */ new ArraySet<>(), /* isAccumulated= */ true);
     }
 
diff --git a/src/com/android/settings/inputmethod/AvailableVirtualKeyboardFragment.java b/src/com/android/settings/inputmethod/AvailableVirtualKeyboardFragment.java
index 6efbc6d..7e27529 100644
--- a/src/com/android/settings/inputmethod/AvailableVirtualKeyboardFragment.java
+++ b/src/com/android/settings/inputmethod/AvailableVirtualKeyboardFragment.java
@@ -84,6 +84,16 @@
                 newUserAwareContext = context.createContextAsUser(UserHandle.of(newUserId), 0);
                 break;
             }
+            case ProfileSelectFragment.ProfileType.PRIVATE: {
+                // If the user is a private profile user, use currentUserId directly. Or get the
+                // private profile userId instead.
+                newUserId = userManager.isPrivateProfile()
+                        ? currentUserId
+                        : Utils.getCurrentUserIdOfType(
+                                userManager, ProfileSelectFragment.ProfileType.PRIVATE);
+                newUserAwareContext = context.createContextAsUser(UserHandle.of(newUserId), 0);
+                break;
+            }
             case ProfileSelectFragment.ProfileType.PERSONAL: {
                 // Use the parent user of the current user if the current user is profile.
                 final UserHandle currentUser = UserHandle.of(currentUserId);
diff --git a/src/com/android/settings/inputmethod/NewKeyboardLayoutEnabledLocalesFragment.java b/src/com/android/settings/inputmethod/NewKeyboardLayoutEnabledLocalesFragment.java
index f007bc8..abe640b 100644
--- a/src/com/android/settings/inputmethod/NewKeyboardLayoutEnabledLocalesFragment.java
+++ b/src/com/android/settings/inputmethod/NewKeyboardLayoutEnabledLocalesFragment.java
@@ -77,6 +77,15 @@
                         ? currentUserId : Utils.getManagedProfileId(userManager, currentUserId);
                 break;
             }
+            case ProfileSelectFragment.ProfileType.PRIVATE: {
+                // If the user is a private profile user, use currentUserId directly. Or get the
+                // private profile userId instead.
+                newUserId = userManager.isPrivateProfile()
+                        ? currentUserId
+                        : Utils.getCurrentUserIdOfType(
+                                userManager, ProfileSelectFragment.ProfileType.PRIVATE);
+                break;
+            }
             case ProfileSelectFragment.ProfileType.PERSONAL: {
                 final UserHandle primaryUser = userManager.getPrimaryUser().getUserHandle();
                 newUserId = primaryUser.getIdentifier();
diff --git a/src/com/android/settings/localepicker/LocaleNotificationDataManager.java b/src/com/android/settings/localepicker/LocaleNotificationDataManager.java
index 09d6280..4d948f1 100644
--- a/src/com/android/settings/localepicker/LocaleNotificationDataManager.java
+++ b/src/com/android/settings/localepicker/LocaleNotificationDataManager.java
@@ -31,7 +31,7 @@
  * information.
  */
 public class LocaleNotificationDataManager {
-    private static final String LOCALE_NOTIFICATION = "locale_notification";
+    public static final String LOCALE_NOTIFICATION = "locale_notification";
     private Context mContext;
 
     /**
diff --git a/src/com/android/settings/network/apn/ApnEditPageProvider.kt b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
index ad16ae3..753de22 100644
--- a/src/com/android/settings/network/apn/ApnEditPageProvider.kt
+++ b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
@@ -19,6 +19,10 @@
 import android.net.Uri
 import android.os.Bundle
 import androidx.compose.foundation.layout.Column
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Done
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.getValue
@@ -70,7 +74,7 @@
         val apnDataCur = remember {
             mutableStateOf(apnDataInit)
         }
-        ApnPage(apnDataCur)
+        ApnPage(apnDataInit, apnDataCur, uriInit)
     }
 
     fun getRoute(
@@ -83,7 +87,7 @@
 }
 
 @Composable
-fun ApnPage(apnDataCur: MutableState<ApnData>) {
+fun ApnPage(apnDataInit: ApnData, apnDataCur: MutableState<ApnData>, uriInit: Uri) {
     var apnData by apnDataCur
     val context = LocalContext.current
     val authTypeOptions = stringArrayResource(R.array.apn_auth_entries).toList()
@@ -93,6 +97,11 @@
     }
     RegularScaffold(
         title = stringResource(id = R.string.apn_edit),
+        actions = {
+            IconButton(onClick = {
+                validateAndSaveApnData(apnDataInit, apnData, context, uriInit)
+            }) { Icon(imageVector = Icons.Outlined.Done, contentDescription = "Save APN") }
+        }
     ) {
         Column() {
             SettingsOutlinedTextField(
@@ -156,7 +165,7 @@
                 value = apnData.apnType,
                 label = stringResource(R.string.apn_type),
                 enabled = apnData.apnTypeEnabled
-            ) { apnData = apnData.copy(apn = it) } // TODO: updateApnType
+            ) { apnData = apnData.copy(apnType = updateApnType(apnData.copy(apnType = it))) }
             SettingsExposedDropdownMenuBox(
                 label = stringResource(R.string.apn_protocol),
                 options = apnProtocolOptions,
diff --git a/src/com/android/settings/network/apn/ApnRepository.kt b/src/com/android/settings/network/apn/ApnRepository.kt
index f758439..e0121b4 100644
--- a/src/com/android/settings/network/apn/ApnRepository.kt
+++ b/src/com/android/settings/network/apn/ApnRepository.kt
@@ -16,11 +16,13 @@
 
 package com.android.settings.network.apn
 
+import android.content.ContentValues
 import android.content.Context
 import android.net.Uri
 import android.provider.Telephony
 import android.util.Log
 import com.android.settings.R
+import com.android.settingslib.utils.ThreadUtils
 import java.util.Locale
 
 const val NAME_INDEX = 1
@@ -162,3 +164,34 @@
         }
     }
 }
+
+fun convertOptions2Protocol(protocolIndex: Int, context: Context): String {
+    val apnProtocolValues = context.resources.getStringArray(R.array.apn_protocol_values).toList()
+
+    return if (protocolIndex == -1) {
+        ""
+    } else {
+        try {
+            apnProtocolValues[protocolIndex]
+        } catch (e: ArrayIndexOutOfBoundsException) {
+            ""
+        }
+    }
+}
+
+fun updateApnDataToDatabase(newApn: Boolean, values: ContentValues, context: Context, uriInit: Uri) {
+    ThreadUtils.postOnBackgroundThread {
+        if (newApn) {
+            // Add a new apn to the database
+            val newUri = context.contentResolver.insert(uriInit, values)
+            if (newUri == null) {
+                Log.e(TAG, "Can't add a new apn to database $uriInit")
+            }
+        } else {
+            // Update the existing apn
+            context.contentResolver.update(
+                uriInit, values, null /* where */, null /* selection Args */
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/network/apn/ApnStatus.kt b/src/com/android/settings/network/apn/ApnStatus.kt
index c5ca45a..991529a 100644
--- a/src/com/android/settings/network/apn/ApnStatus.kt
+++ b/src/com/android/settings/network/apn/ApnStatus.kt
@@ -16,6 +16,7 @@
 
 package com.android.settings.network.apn
 
+import android.content.ContentValues
 import android.content.Context
 import android.net.Uri
 import android.os.Bundle
@@ -26,7 +27,6 @@
 import android.util.Log
 import com.android.internal.util.ArrayUtils
 import com.android.settings.R
-import java.util.Arrays
 import java.util.Locale
 
 data class ApnData(
@@ -68,7 +68,32 @@
     val newApn: Boolean = false,
     val subId: Int = -1,
     val customizedConfig: CustomizedConfig = CustomizedConfig()
-)
+) {
+    fun getContentValues(context: Context): ContentValues {
+        val values = ContentValues()
+        values.put(Telephony.Carriers.NAME, name)
+        values.put(Telephony.Carriers.APN, apn)
+        values.put(Telephony.Carriers.PROXY, proxy)
+        values.put(Telephony.Carriers.PORT, port)
+        values.put(Telephony.Carriers.MMSPROXY, mmsProxy)
+        values.put(Telephony.Carriers.MMSPORT, mmsPort)
+        values.put(Telephony.Carriers.USER, userName)
+        values.put(Telephony.Carriers.SERVER, server)
+        values.put(Telephony.Carriers.PASSWORD, passWord)
+        values.put(Telephony.Carriers.MMSC, mmsc)
+        values.put(Telephony.Carriers.AUTH_TYPE, authType)
+        values.put(Telephony.Carriers.PROTOCOL, convertOptions2Protocol(apnProtocol, context))
+        values.put(
+            Telephony.Carriers.ROAMING_PROTOCOL,
+            convertOptions2Protocol(apnRoaming, context)
+        )
+        values.put(Telephony.Carriers.TYPE, apnType)
+        values.put(Telephony.Carriers.NETWORK_TYPE_BITMASK, networkType)
+        values.put(Telephony.Carriers.CARRIER_ENABLED, apnEnable)
+        values.put(Telephony.Carriers.EDITED_STATUS, Telephony.Carriers.USER_EDITED)
+        return values
+    }
+}
 
 data class CustomizedConfig(
     val newApn: Boolean = false,
@@ -82,6 +107,67 @@
 )
 
 /**
+ * APN types for data connections.  These are usage categories for an APN
+ * entry.  One APN entry may support multiple APN types, eg, a single APN
+ * may service regular internet traffic ("default") as well as MMS-specific
+ * connections.<br></br>
+ * APN_TYPE_ALL is a special type to indicate that this APN entry can
+ * service all data connections.
+ */
+const val APN_TYPE_ALL = "*"
+
+/** APN type for default data traffic  */
+const val APN_TYPE_DEFAULT = "default"
+
+/** APN type for MMS traffic  */
+const val APN_TYPE_MMS = "mms"
+
+/** APN type for SUPL assisted GPS  */
+const val APN_TYPE_SUPL = "supl"
+
+/** APN type for DUN traffic  */
+const val APN_TYPE_DUN = "dun"
+
+/** APN type for HiPri traffic  */
+const val APN_TYPE_HIPRI = "hipri"
+
+/** APN type for FOTA  */
+const val APN_TYPE_FOTA = "fota"
+
+/** APN type for IMS  */
+const val APN_TYPE_IMS = "ims"
+
+/** APN type for CBS  */
+const val APN_TYPE_CBS = "cbs"
+
+/** APN type for IA Initial Attach APN  */
+const val APN_TYPE_IA = "ia"
+
+/** APN type for Emergency PDN. This is not an IA apn, but is used
+ * for access to carrier services in an emergency call situation.  */
+const val APN_TYPE_EMERGENCY = "emergency"
+
+/** APN type for Mission Critical Services  */
+const val APN_TYPE_MCX = "mcx"
+
+/** APN type for XCAP  */
+const val APN_TYPE_XCAP = "xcap"
+val APN_TYPES = arrayOf(
+    APN_TYPE_DEFAULT,
+    APN_TYPE_MMS,
+    APN_TYPE_SUPL,
+    APN_TYPE_DUN,
+    APN_TYPE_HIPRI,
+    APN_TYPE_FOTA,
+    APN_TYPE_IMS,
+    APN_TYPE_CBS,
+    APN_TYPE_IA,
+    APN_TYPE_EMERGENCY,
+    APN_TYPE_MCX,
+    APN_TYPE_XCAP
+)
+
+/**
  * Initialize ApnData according to the arguments.
  * @param arguments The data passed in when the user calls PageProvider.
  * @param uriInit The decoded user incoming uri data in Page.
@@ -124,6 +210,116 @@
 }
 
 /**
+ * Validates the apn data and save it to the database if it's valid.
+ *
+ *
+ *
+ * A dialog with error message will be displayed if the APN data is invalid.
+ *
+ * @return true if there is no error
+ */
+fun validateAndSaveApnData(
+    apnDataInit: ApnData,
+    apnData: ApnData,
+    context: Context,
+    uriInit: Uri
+): Boolean {
+    // Nothing to do if it's a read only APN
+    if (apnData.customizedConfig.readOnlyApn) {
+        return true
+    }
+    val errorMsg = validateApnData(apnData, context)
+    if (errorMsg != null) {
+        //TODO: showError(this)
+        return false
+    }
+    if (apnData.newApn || (apnData != apnDataInit)) {
+        Log.d(TAG, "validateAndSaveApnData: apnData ${apnData.name}")
+        updateApnDataToDatabase(apnData.newApn, apnData.getContentValues(context), context, uriInit)
+    }
+    return true
+}
+
+/**
+ * Validates whether the apn data is valid.
+ *
+ * @return An error message if the apn data is invalid, otherwise return null.
+ */
+fun validateApnData(apnData: ApnData, context: Context): String? {
+    var errorMsg: String? = null
+    val name = apnData.name
+    val apn = apnData.apn
+    if (name == "") {
+        errorMsg = context.resources.getString(R.string.error_name_empty)
+    } else if (apn == "") {
+        errorMsg = context.resources.getString(R.string.error_apn_empty)
+    }
+    if (errorMsg == null) {
+        // if carrier does not allow editing certain apn types, make sure type does not include
+        // those
+        if (!ArrayUtils.isEmpty(apnData.customizedConfig.readOnlyApnTypes)
+            && apnTypesMatch(
+                apnData.customizedConfig.readOnlyApnTypes,
+                getUserEnteredApnType(apnData.apnType, apnData.customizedConfig.readOnlyApnTypes)
+            )
+        ) {
+            val stringBuilder = StringBuilder()
+            for (type in apnData.customizedConfig.readOnlyApnTypes) {
+                stringBuilder.append(type).append(", ")
+                Log.d(TAG, "validateApnData: appending type: $type")
+            }
+            // remove last ", "
+            if (stringBuilder.length >= 2) {
+                stringBuilder.delete(stringBuilder.length - 2, stringBuilder.length)
+            }
+            errorMsg = String.format(
+                context.resources.getString(R.string.error_adding_apn_type),
+                stringBuilder
+            )
+        }
+    }
+    return errorMsg
+}
+
+private fun getUserEnteredApnType(apnType: String, readOnlyApnTypes: List<String>): String {
+    // if user has not specified a type, map it to "ALL APN TYPES THAT ARE NOT READ-ONLY"
+    // but if user enter empty type, map it just for default
+    var userEnteredApnType = apnType
+    if (userEnteredApnType != "") userEnteredApnType =
+        userEnteredApnType.trim { it <= ' ' }
+    if (TextUtils.isEmpty(userEnteredApnType) || APN_TYPE_ALL == userEnteredApnType) {
+        userEnteredApnType = getEditableApnType(readOnlyApnTypes)
+    }
+    Log.d(
+        TAG, "getUserEnteredApnType: changed apn type to editable apn types: "
+            + userEnteredApnType
+    )
+    return userEnteredApnType
+}
+
+private fun getEditableApnType(readOnlyApnTypes: List<String>): String {
+    val editableApnTypes = StringBuilder()
+    var first = true
+    for (apnType in APN_TYPES) {
+        // add APN type if it is not read-only and is not wild-cardable
+        if (!readOnlyApnTypes.contains(apnType)
+            && apnType != APN_TYPE_IA
+            && apnType != APN_TYPE_EMERGENCY
+            && apnType != APN_TYPE_MCX
+            && apnType != APN_TYPE_IMS
+        ) {
+            if (first) {
+                first = false
+            } else {
+                editableApnTypes.append(",")
+            }
+            editableApnTypes.append(apnType)
+        }
+    }
+    return editableApnTypes.toString()
+}
+
+/**
  * Initialize CustomizedConfig information through subId.
  * @param subId subId information obtained from arguments.
  *
@@ -189,7 +385,7 @@
     return customizedConfig
 }
 
-fun disableInit(apnDataInit : ApnData): ApnData {
+fun disableInit(apnDataInit: ApnData): ApnData {
     var apnData = apnDataInit
     val isUserEdited = apnDataInit.edited == Telephony.Carriers.USER_EDITED
     Log.d(TAG, "disableInit: EDITED $isUserEdited")
@@ -198,10 +394,16 @@
             || apnTypesMatch(apnDataInit.customizedConfig.readOnlyApnTypes, apnDataInit.apnType))
     ) {
         Log.d(TAG, "disableInit: read-only APN")
-        apnData = apnDataInit.copy(customizedConfig = apnDataInit.customizedConfig.copy(readOnlyApn = true))
+        apnData =
+            apnDataInit.copy(customizedConfig = apnDataInit.customizedConfig.copy(readOnlyApn = true))
         apnData = disableAllFields(apnData)
     } else if (!ArrayUtils.isEmpty(apnData.customizedConfig.readOnlyApnFields)) {
-        Log.d(TAG, "disableInit: mReadOnlyApnFields ${apnData.customizedConfig.readOnlyApnFields.joinToString(", ")})")
+        Log.d(
+            TAG,
+            "disableInit: mReadOnlyApnFields ${
+                apnData.customizedConfig.readOnlyApnFields.joinToString(", ")
+            })"
+        )
         apnData = disableFields(apnData.customizedConfig.readOnlyApnFields, apnData)
     }
     return apnData
@@ -210,7 +412,7 @@
 /**
  * Disables all fields so that user cannot modify the APN
  */
-private fun disableAllFields(apnDataInit : ApnData): ApnData {
+private fun disableAllFields(apnDataInit: ApnData): ApnData {
     var apnData = apnDataInit
     apnData = apnData.copy(nameEnabled = false)
     apnData = apnData.copy(apnEnabled = false)
@@ -236,7 +438,7 @@
  *
  * @param apnFields fields to be disabled
  */
-private fun disableFields(apnFields: List<String>, apnDataInit : ApnData): ApnData {
+private fun disableFields(apnFields: List<String>, apnDataInit: ApnData): ApnData {
     var apnData = apnDataInit
     for (apnField in apnFields) {
         apnData = disableByFieldName(apnField, apnDataInit)
@@ -244,7 +446,7 @@
     return apnData
 }
 
-private fun disableByFieldName(apnField: String, apnDataInit : ApnData): ApnData {
+private fun disableByFieldName(apnField: String, apnDataInit: ApnData): ApnData {
     var apnData = apnDataInit
     when (apnField) {
         Telephony.Carriers.NAME -> apnData = apnData.copy(nameEnabled = false)
@@ -262,52 +464,47 @@
         Telephony.Carriers.PROTOCOL -> apnData = apnData.copy(apnProtocolEnabled = false)
         Telephony.Carriers.ROAMING_PROTOCOL -> apnData = apnData.copy(apnRoamingEnabled = false)
         Telephony.Carriers.CARRIER_ENABLED -> apnData = apnData.copy(apnEnableEnabled = false)
-        Telephony.Carriers.BEARER, Telephony.Carriers.BEARER_BITMASK -> apnData = apnData.copy(networkTypeEnabled =
-            false)
+        Telephony.Carriers.BEARER, Telephony.Carriers.BEARER_BITMASK,
+        Telephony.Carriers.NETWORK_TYPE_BITMASK -> apnData = apnData.copy(
+            networkTypeEnabled =
+            false
+        )
     }
     return apnData
 }
 
-private fun apnTypesMatch(apnTypesArray: List<String>, apnTypesCur: String?): Boolean {
-    if (ArrayUtils.isEmpty(apnTypesArray)) {
-        return false
-    }
-    val apnTypesArrayLowerCase = arrayOfNulls<String>(
-        apnTypesArray.size
-    )
-    for (i in apnTypesArray.indices) {
-        apnTypesArrayLowerCase[i] = apnTypesArray[i].lowercase(Locale.getDefault())
-    }
-    if (hasAllApns(apnTypesArrayLowerCase) || TextUtils.isEmpty(apnTypesCur)) {
-        return true
-    }
-    val apnTypesList: List<*> = listOf(*apnTypesArrayLowerCase)
-    val apnTypesArrayCur =
-        apnTypesCur!!.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
-    for (apn in apnTypesArrayCur) {
-        if (apnTypesList.contains(apn.trim { it <= ' ' }.lowercase(Locale.getDefault()))) {
-            Log.d(TAG, "apnTypesMatch: true because match found for " + apn.trim { it <= ' ' })
-            return true
-        }
-    }
-    Log.d(TAG, "apnTypesMatch: false")
-    return false
+private fun apnTypesMatch(apnTypeList: List<String>, apnType: String): Boolean {
+    val normalizeApnTypeList = apnTypeList.map(::normalizeApnType)
+    return hasAllApns(normalizeApnTypeList) ||
+        apnType.split(",").map(::normalizeApnType).all { it in normalizeApnTypeList }
 }
 
-fun hasAllApns(apnTypes: Array<String?>): Boolean {
-    if (ArrayUtils.isEmpty(apnTypes)) {
-        return false
-    }
-    val apnList: List<*> = Arrays.asList(*apnTypes)
-    if (apnList.contains(ApnEditor.APN_TYPE_ALL)) {
-        Log.d(TAG, "hasAllApns: true because apnList.contains(APN_TYPE_ALL)")
+fun hasAllApns(apnTypes: List<String>): Boolean {
+    if (APN_TYPE_ALL in apnTypes) {
+        Log.d(TAG, "hasAllApns: true because apnTypes.contains(APN_TYPE_ALL)")
         return true
     }
-    for (apn in ApnEditor.APN_TYPES) {
-        if (!apnList.contains(apn)) {
-            return false
-        }
-    }
-    Log.d(TAG, "hasAllApns: true")
-    return true
+    return APN_TYPES.all { it in apnTypes }
+}
+
+private fun normalizeApnType(apnType: String): String =
+    apnType.trim().lowercase(Locale.getDefault())
+
+fun updateApnType(apnData: ApnData): String {
+    return if (apnData.apnType == "" && apnData.customizedConfig.defaultApnTypes.isNotEmpty())
+        getEditableApnType(apnData)
+    else
+        apnData.apnType
+}
+
+private fun getEditableApnType(apnData: ApnData): String {
+    val customizedConfig = apnData.customizedConfig
+    return customizedConfig.defaultApnTypes.filterNot { apnType ->
+        customizedConfig.readOnlyApnTypes.contains(apnType) || apnType in listOf(
+            APN_TYPE_IA,
+            APN_TYPE_EMERGENCY,
+            APN_TYPE_MCX,
+            APN_TYPE_IMS,
+        )
+    }.joinToString()
 }
\ No newline at end of file
diff --git a/src/com/android/settings/network/telephony/DataUsagePreferenceController.kt b/src/com/android/settings/network/telephony/DataUsagePreferenceController.kt
index 34433c4..d133955 100644
--- a/src/com/android/settings/network/telephony/DataUsagePreferenceController.kt
+++ b/src/com/android/settings/network/telephony/DataUsagePreferenceController.kt
@@ -31,7 +31,8 @@
 import com.android.settings.R
 import com.android.settings.datausage.DataUsageUtils
 import com.android.settings.datausage.lib.DataUsageLib
-import com.android.settingslib.net.DataUsageController
+import com.android.settings.datausage.lib.NetworkCycleDataRepository
+import com.android.settings.datausage.lib.NetworkStatsRepository.Companion.AllTimeRange
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
@@ -45,9 +46,6 @@
     private lateinit var preference: Preference
     private var networkTemplate: NetworkTemplate? = null
 
-    @VisibleForTesting
-    var dataUsageControllerFactory: (Context) -> DataUsageController = { DataUsageController(it) }
-
     fun init(subId: Int) {
         mSubId = subId
     }
@@ -103,25 +101,21 @@
         else -> null
     }
 
+    @VisibleForTesting
+    fun createNetworkCycleDataRepository(): NetworkCycleDataRepository? =
+        networkTemplate?.let { NetworkCycleDataRepository(mContext, it) }
+
     private fun getDataUsageSummary(): String? {
-        val networkTemplate = networkTemplate ?: return null
-        val controller = dataUsageControllerFactory(mContext).apply {
-            setSubscriptionId(mSubId)
-        }
-        val usageInfo = controller.getDataUsageInfo(networkTemplate)
-        if (usageInfo != null && usageInfo.usageLevel > 0) {
+        val repository = createNetworkCycleDataRepository() ?: return null
+        repository.loadFirstCycle()?.takeIf { it.usage > 0 }?.let { usageData ->
             return mContext.getString(
                 R.string.data_usage_template,
-                DataUsageUtils.formatDataUsage(mContext, usageInfo.usageLevel),
-                usageInfo.period,
+                usageData.formatUsage(mContext),
+                usageData.formatDateRange(mContext),
             )
         }
 
-        return controller.getHistoricalUsageLevel(networkTemplate).takeIf { it > 0 }?.let {
-            mContext.getString(
-                R.string.data_used_template,
-                DataUsageUtils.formatDataUsage(mContext, it),
-            )
-        }
+        return repository.queryUsage(AllTimeRange).takeIf { it.usage > 0 }
+            ?.getDataUsedString(mContext)
     }
 }
diff --git a/src/com/android/settings/notification/PoliteNotifVibrateUnlockedToggleController.java b/src/com/android/settings/notification/PoliteNotifVibrateUnlockedToggleController.java
new file mode 100644
index 0000000..aaa8f87
--- /dev/null
+++ b/src/com/android/settings/notification/PoliteNotifVibrateUnlockedToggleController.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
+import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
+
+import android.content.Context;
+import android.os.Vibrator;
+import android.provider.Settings;
+
+import com.android.server.notification.Flags;
+import com.android.settings.R;
+import com.android.settings.core.TogglePreferenceController;
+
+/**
+ * Controls the toggle that determines whether notifications
+ * should only vibrate (no sound) when the device is unlocked.
+ */
+public class PoliteNotifVibrateUnlockedToggleController extends TogglePreferenceController {
+
+    public PoliteNotifVibrateUnlockedToggleController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        // TODO: b/291897570 - remove this when the feature flag is removed!
+        if (!Flags.politeNotifications()) {
+            return CONDITIONALLY_UNAVAILABLE;
+        }
+        return mContext.getSystemService(Vibrator.class).hasVibrator() ? AVAILABLE
+                : UNSUPPORTED_ON_DEVICE;
+    }
+
+    @Override
+    public boolean isChecked() {
+        return Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, OFF) != OFF;
+    }
+
+    @Override
+    public boolean setChecked(boolean isChecked) {
+        return Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, (isChecked ? ON : OFF));
+    }
+
+    @Override
+    public int getSliceHighlightMenuRes() {
+        return R.string.menu_key_accessibility;
+    }
+}
diff --git a/src/com/android/settings/notification/PoliteNotifWorkProfileToggleController.java b/src/com/android/settings/notification/PoliteNotifWorkProfileToggleController.java
new file mode 100644
index 0000000..65b4fb8
--- /dev/null
+++ b/src/com/android/settings/notification/PoliteNotifWorkProfileToggleController.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
+import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.server.notification.Flags;
+import com.android.settings.R;
+import com.android.settings.core.TogglePreferenceController;
+
+/**
+ * Controls the toggle that determines whether notification cooldown
+ * should apply to work profiles.
+ */
+public class PoliteNotifWorkProfileToggleController extends TogglePreferenceController {
+
+    private final int mManagedProfileId;
+
+    public PoliteNotifWorkProfileToggleController(Context context, String preferenceKey) {
+        this(context, preferenceKey, new AudioHelper(context));
+    }
+
+    @VisibleForTesting
+    PoliteNotifWorkProfileToggleController(Context context, String preferenceKey,
+                AudioHelper helper) {
+        super(context, preferenceKey);
+        mManagedProfileId = helper.getManagedProfileId(UserManager.get(mContext));
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        // TODO: b/291897570 - remove this when the feature flag is removed!
+        if (!Flags.politeNotifications()) {
+            return CONDITIONALLY_UNAVAILABLE;
+        }
+
+        return (mManagedProfileId != UserHandle.USER_NULL) ? AVAILABLE : DISABLED_FOR_USER;
+    }
+
+    @Override
+    public boolean isChecked() {
+        return Settings.System.getIntForUser(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_ENABLED, OFF, mManagedProfileId) != OFF;
+    }
+
+    @Override
+    public boolean setChecked(boolean isChecked) {
+        return Settings.System.putIntForUser(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_ENABLED, (isChecked ? ON : OFF),
+                mManagedProfileId);
+    }
+
+    @Override
+    public int getSliceHighlightMenuRes() {
+        return R.string.menu_key_accessibility;
+    }
+}
diff --git a/src/com/android/settings/notification/PoliteNotificationFilterController.java b/src/com/android/settings/notification/PoliteNotificationFilterController.java
new file mode 100644
index 0000000..8093f55
--- /dev/null
+++ b/src/com/android/settings/notification/PoliteNotificationFilterController.java
@@ -0,0 +1,114 @@
+/*
+ * 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.notification;
+
+import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
+import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.util.Log;
+
+import androidx.preference.ListPreference;
+import androidx.preference.Preference;
+
+import com.android.server.notification.Flags;
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.core.PreferenceControllerMixin;
+
+
+/**
+ * Controls whether polite notifications are enabled and apply to all apps or just to conversations.
+ */
+public class PoliteNotificationFilterController extends BasePreferenceController
+        implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
+    static final String TAG = "PoliteNotificationFilterController";
+
+    private static final int POLITE_NOTIFICATIONS_ALL = 0;
+    private static final int POLITE_NOTIFICATIONS_CONVERSATIONS = 1;
+    private static final int POLITE_NOTIFICATIONS_DISABLED = 2;
+
+    public PoliteNotificationFilterController(Context context, String key) {
+        super(context, key);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        // TODO: b/291897570 - remove this when the feature flag is removed!
+        return Flags.politeNotifications() ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+        final ListPreference pref = (ListPreference) preference;
+
+        if (isPoliteNotifDisabled()) {
+            pref.setValue(Integer.toString(POLITE_NOTIFICATIONS_DISABLED));
+        } else if (shouldApplyForAllApps()) {
+            pref.setValue(Integer.toString(POLITE_NOTIFICATIONS_ALL));
+        } else {
+            pref.setValue(Integer.toString(POLITE_NOTIFICATIONS_CONVERSATIONS));
+        }
+    }
+
+    @Override
+    public CharSequence getSummary() {
+        if (isPoliteNotifDisabled()) {
+            return mContext.getString(R.string.notification_polite_disabled_summary);
+        }
+        if (shouldApplyForAllApps()) {
+            return mContext.getString(R.string.notification_polite_all_apps_summary);
+        } else {
+            return mContext.getString(R.string.notification_polite_conversations_summary);
+        }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        final int prefValue = Integer.parseInt((String) newValue);
+        if (prefValue == POLITE_NOTIFICATIONS_ALL) {
+            Settings.System.putInt(mContext.getContentResolver(),
+                    Settings.System.NOTIFICATION_COOLDOWN_ENABLED, ON);
+            Settings.System.putInt(mContext.getContentResolver(),
+                    Settings.System.NOTIFICATION_COOLDOWN_ALL, ON);
+        } else if (prefValue == POLITE_NOTIFICATIONS_CONVERSATIONS) {
+            Settings.System.putInt(mContext.getContentResolver(),
+                    Settings.System.NOTIFICATION_COOLDOWN_ENABLED, ON);
+            Settings.System.putInt(mContext.getContentResolver(),
+                    Settings.System.NOTIFICATION_COOLDOWN_ALL, OFF);
+        } else if (prefValue == POLITE_NOTIFICATIONS_DISABLED) {
+            Settings.System.putInt(mContext.getContentResolver(),
+                    Settings.System.NOTIFICATION_COOLDOWN_ENABLED, OFF);
+        } else {
+            Log.e(TAG, "Unexpected preference value: " + prefValue);
+        }
+        refreshSummary(preference);
+        return true;
+    }
+
+    private boolean isPoliteNotifDisabled() {
+        return Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_ENABLED, ON) == OFF;
+    }
+
+    private boolean shouldApplyForAllApps() {
+        return Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_ALL, ON) != OFF;
+    }
+}
diff --git a/src/com/android/settings/notification/PoliteNotificationsPreferenceController.java b/src/com/android/settings/notification/PoliteNotificationsPreferenceController.java
new file mode 100644
index 0000000..e6e0947
--- /dev/null
+++ b/src/com/android/settings/notification/PoliteNotificationsPreferenceController.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.content.Context;
+
+import com.android.server.notification.Flags;
+import com.android.settings.core.BasePreferenceController;
+
+// TODO: b/291897570 - remove controller when the feature flag is removed!
+/**
+ * Controller for polite notifications settings page.
+ */
+public class PoliteNotificationsPreferenceController extends BasePreferenceController {
+
+    public PoliteNotificationsPreferenceController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return Flags.politeNotifications() ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+    }
+
+}
diff --git a/src/com/android/settings/notification/PoliteNotificationsPreferenceFragment.java b/src/com/android/settings/notification/PoliteNotificationsPreferenceFragment.java
new file mode 100644
index 0000000..449e678
--- /dev/null
+++ b/src/com/android/settings/notification/PoliteNotificationsPreferenceFragment.java
@@ -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.notification;
+
+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;
+
+/**
+ * Fragment for polite notifications.
+ */
+@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
+public class PoliteNotificationsPreferenceFragment extends DashboardFragment {
+
+    private static final String POLITE_NOTIF_PREFERENCE_KEY = "polite_notifications_pref_dlg";
+
+    @Override
+    public int getMetricsCategory() {
+        return SettingsEnums.SETTINGS_POLITE_NOTIFICATIONS;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.polite_notifications_settings;
+    }
+    @Override
+    protected String getLogTag() {
+        return "PoliteNotificationsPreferenceFragment";
+    }
+
+    public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+            new BaseSearchIndexProvider(R.xml.polite_notifications_settings);
+}
diff --git a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
index 709814d..0abf8f7 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
@@ -131,7 +131,17 @@
         return false;
     }
 
-    static synchronized PrivateSpaceMaintainer getInstance(Context context) {
+    /** Returns true when the PS is locked or when PS doesn't exist, false otherwise. */
+    public synchronized boolean isPrivateSpaceLocked() {
+        if (!doesPrivateSpaceExist()) {
+            return true;
+        }
+
+        return mUserManager.isQuietModeEnabled(mUserHandle);
+    }
+
+    /** Returns the instance of {@link PrivateSpaceMaintainer} */
+    public static synchronized PrivateSpaceMaintainer getInstance(Context context) {
         if (sPrivateSpaceMaintainer == null) {
             sPrivateSpaceMaintainer = new PrivateSpaceMaintainer(context);
         }
diff --git a/src/com/android/settings/spa/app/appinfo/AppDataUsagePreference.kt b/src/com/android/settings/spa/app/appinfo/AppDataUsagePreference.kt
index 6b14da0..ceb3986 100644
--- a/src/com/android/settings/spa/app/appinfo/AppDataUsagePreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppDataUsagePreference.kt
@@ -19,8 +19,6 @@
 import android.content.Context
 import android.content.pm.ApplicationInfo
 import android.net.NetworkTemplate
-import android.text.format.DateUtils
-import android.text.format.Formatter
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
@@ -114,8 +112,8 @@
         } else {
             context.getString(
                 R.string.data_summary_format,
-                Formatter.formatFileSize(context, appUsageData.usage, Formatter.FLAG_IEC_UNITS),
-                DateUtils.formatDateTime(context, appUsageData.startTime, DATE_FORMAT),
+                appUsageData.formatUsage(context),
+                appUsageData.formatStartDate(context),
             )
         }
     }
@@ -128,8 +126,4 @@
             AppInfoSettingsProvider.METRICS_CATEGORY,
         )
     }
-
-    private companion object {
-        const val DATE_FORMAT = DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_ABBREV_MONTH
-    }
 }
diff --git a/src/com/android/settings/tts/TtsEnginePreferenceFragment.java b/src/com/android/settings/tts/TtsEnginePreferenceFragment.java
index 33648fe..f6416db 100644
--- a/src/com/android/settings/tts/TtsEnginePreferenceFragment.java
+++ b/src/com/android/settings/tts/TtsEnginePreferenceFragment.java
@@ -165,16 +165,16 @@
     private void updateDefaultEngine(String engine) {
         Log.d(TAG, "Updating default synth to : " + engine);
 
-        // Keep track of the previous engine that was being used. So that
-        // we can reuse the previous engine.
-        //
-        // Note that if TextToSpeech#getCurrentEngine is not null, it means at
-        // the very least that we successfully bound to the engine service.
-        mPreviousEngine = mTts.getCurrentEngine();
-
         // Step 1: Shut down the existing TTS engine.
         Log.i(TAG, "Shutting down current tts engine");
         if (mTts != null) {
+            // Keep track of the previous engine that was being used. So that
+            // we can reuse the previous engine.
+            //
+            // Note that if TextToSpeech#getCurrentEngine is not null, it means at
+            // the very least that we successfully bound to the engine service.
+            mPreviousEngine = mTts.getCurrentEngine();
+
             try {
                 mTts.shutdown();
                 mTts = null;
diff --git a/src/com/android/settings/users/AutoSyncPrivateDataPreferenceController.kt b/src/com/android/settings/users/AutoSyncPrivateDataPreferenceController.kt
new file mode 100644
index 0000000..3eabafe
--- /dev/null
+++ b/src/com/android/settings/users/AutoSyncPrivateDataPreferenceController.kt
@@ -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.users
+
+import android.content.Context
+import androidx.preference.PreferenceFragmentCompat
+import com.android.settings.Utils
+import com.android.settings.dashboard.profileselector.ProfileSelectFragment
+
+class AutoSyncPrivateDataPreferenceController(
+        context: Context?, parent: PreferenceFragmentCompat?)
+    : AutoSyncDataPreferenceController(context, parent) {
+    init {
+        mUserHandle = Utils
+                .getProfileOfType(mUserManager, ProfileSelectFragment.ProfileType.PRIVATE)
+    }
+
+    override fun getPreferenceKey(): String {
+        return KEY_AUTO_SYNC_PRIVATE_ACCOUNT
+    }
+
+    override fun isAvailable(): Boolean {
+        return (mUserHandle != null
+                && mUserManager.getUserInfo(mUserHandle.identifier).isPrivateProfile)
+    }
+
+    companion object {
+        private const val KEY_AUTO_SYNC_PRIVATE_ACCOUNT = "auto_sync_private_account_data"
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/wifi/WifiConfigController.java b/src/com/android/settings/wifi/WifiConfigController.java
index b2b7346..d2beec8 100644
--- a/src/com/android/settings/wifi/WifiConfigController.java
+++ b/src/com/android/settings/wifi/WifiConfigController.java
@@ -653,6 +653,9 @@
             case AccessPoint.SECURITY_EAP:
             case AccessPoint.SECURITY_EAP_WPA3_ENTERPRISE:
             case AccessPoint.SECURITY_EAP_SUITE_B:
+                if (mEapMethodSpinner == null || mPhase2Spinner == null) {
+                    break;
+                }
                 if (mAccessPointSecurity == AccessPoint.SECURITY_EAP_SUITE_B) {
                     // allowedSuiteBCiphers will be set according to certificate type
                     config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B);
diff --git a/tests/anomaly-tester/Android.bp b/tests/anomaly-tester/Android.bp
index f17f4b2..0b05e04 100644
--- a/tests/anomaly-tester/Android.bp
+++ b/tests/anomaly-tester/Android.bp
@@ -18,7 +18,7 @@
         "androidx.test.rules",
         "mockito-target",
         "androidx.test.uiautomator_uiautomator",
-        "truth-prebuilt",
+        "truth",
     ],
 
     srcs: ["**/*.java"],
diff --git a/tests/componenttests/Android.bp b/tests/componenttests/Android.bp
index 5c03aa9..300a338 100644
--- a/tests/componenttests/Android.bp
+++ b/tests/componenttests/Android.bp
@@ -19,7 +19,7 @@
     ],
 
     static_libs: [
-        "truth-prebuilt",
+        "truth",
         "androidx.test.core",
         "androidx.test.espresso.core",
         "androidx.test.espresso.intents-nodeps",
diff --git a/tests/robotests/Android.bp b/tests/robotests/Android.bp
index fbfd888..68793a1 100644
--- a/tests/robotests/Android.bp
+++ b/tests/robotests/Android.bp
@@ -25,6 +25,8 @@
         "androidx.fragment_fragment-testing",
         "frameworks-base-testutils",
         "androidx.fragment_fragment",
+        "androidx.lifecycle_lifecycle-runtime-testing",
+        "kotlinx_coroutines_test",
     ],
 
     aaptflags: ["--extra-packages com.android.settings"],
@@ -59,16 +61,21 @@
         "androidx.test.rules",
         "androidx.test.runner",
         "flag-junit",
+        "flag-junit-base",
         "aconfig_settings_flags_lib",
         "platform-test-annotations",
         "Settings-testutils2",
+        "notification_flags_lib",
     ],
 
     libs: [
         "ims-common",
     ],
 
-    java_resource_dirs: ["config", "resources"],
+    java_resource_dirs: [
+        "config",
+        "resources",
+    ],
 
     instrumentation_for: "SettingsRoboTestStub",
 
@@ -93,6 +100,6 @@
         "Robolectric_all-target_upstream",
         "Settings-core",
         "mockito-robolectric-prebuilt",
-        "truth-prebuilt",
+        "truth",
     ],
 }
diff --git a/tests/robotests/src/com/android/settings/applications/AppDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/applications/AppDashboardFragmentTest.java
index 0eca43c..5137516 100644
--- a/tests/robotests/src/com/android/settings/applications/AppDashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/applications/AppDashboardFragmentTest.java
@@ -16,12 +16,24 @@
 
 package com.android.settings.applications;
 
+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 android.content.Context;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import com.android.settings.applications.appcompat.UserAspectRatioAppsPreferenceController;
 import com.android.settings.testutils.XmlTestUtils;
 import com.android.settings.testutils.shadow.ShadowUserManager;
+import com.android.settings.widget.PreferenceCategoryController;
 import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.drawer.CategoryKey;
 
@@ -31,6 +43,7 @@
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
+import org.robolectric.shadows.androidx.fragment.FragmentController;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -66,4 +79,22 @@
 
         assertThat(preferenceScreenKeys).containsAtLeastElementsIn(preferenceKeys);
     }
+
+    @Test
+    @Config(shadows = ShadowUserManager.class)
+    public void testAdvancedAppsCategory() {
+        AppDashboardFragment fragment = FragmentController.of(new AppDashboardFragment(),
+                new Bundle()).create().get();
+        UserAspectRatioAppsPreferenceController controller =
+                mock(UserAspectRatioAppsPreferenceController.class);
+        final PreferenceCategoryController advancedController =
+                fragment.getAdvancedAppsPreferenceCategoryController();
+        advancedController.setChildren(List.of(controller));
+
+        when(controller.getAvailabilityStatus()).thenReturn(AVAILABLE);
+        assertTrue(advancedController.isAvailable());
+
+        when(controller.getAvailabilityStatus()).thenReturn(CONDITIONALLY_UNAVAILABLE);
+        assertFalse(advancedController.isAvailable());
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint2/fragment/FingerprintEnrollIntroFragmentTest.kt b/tests/robotests/src/com/android/settings/biometrics/fingerprint2/fragment/FingerprintEnrollIntroFragmentTest.kt
new file mode 100644
index 0000000..cea6676
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint2/fragment/FingerprintEnrollIntroFragmentTest.kt
@@ -0,0 +1,173 @@
+/*
+ * 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.fingerprint2.fragment
+
+import android.content.Context
+import android.os.Bundle
+import androidx.fragment.app.testing.FragmentScenario
+import androidx.fragment.app.testing.launchFragmentInContainer
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.ViewMatchers.Visibility
+import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
+import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import androidx.test.runner.AndroidJUnit4
+import com.android.settings.R
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.fragment.FingerprintEnrollIntroV2Fragment
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollNavigationViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintGatekeeperViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintScrollViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.GatekeeperInfo
+import com.android.settings.testutils2.FakeFingerprintManagerInteractor
+import com.google.android.setupdesign.GlifLayout
+import com.google.android.setupdesign.template.RequireScrollMixin
+import kotlinx.coroutines.test.StandardTestDispatcher
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class FingerprintEnrollIntroFragmentTest {
+  private var context: Context = ApplicationProvider.getApplicationContext()
+  private var interactor = FakeFingerprintManagerInteractor()
+
+  private val gatekeeperViewModel =
+    FingerprintGatekeeperViewModel(
+      GatekeeperInfo.GatekeeperPasswordInfo(byteArrayOf(1, 2, 3), 100L),
+      interactor
+    )
+  private val backgroundDispatcher = StandardTestDispatcher()
+  private lateinit var fragmentScenario: FragmentScenario<FingerprintEnrollIntroV2Fragment>
+
+  private val navigationViewModel =
+    FingerprintEnrollNavigationViewModel(
+      backgroundDispatcher,
+      interactor,
+      gatekeeperViewModel,
+      canSkipConfirm = true,
+    )
+  private var fingerprintViewModel = FingerprintEnrollViewModel(interactor, backgroundDispatcher)
+  private var fingerprintScrollViewModel = FingerprintScrollViewModel()
+
+  @Before
+  fun setup() {
+    val factory =
+      object : ViewModelProvider.Factory {
+        @Suppress("UNCHECKED_CAST")
+        override fun <T : ViewModel> create(
+          modelClass: Class<T>,
+        ): T {
+          return when (modelClass) {
+            FingerprintEnrollViewModel::class.java -> fingerprintViewModel
+            FingerprintScrollViewModel::class.java -> fingerprintScrollViewModel
+            FingerprintEnrollNavigationViewModel::class.java -> navigationViewModel
+            FingerprintGatekeeperViewModel::class.java -> gatekeeperViewModel
+            else -> null
+          }
+            as T
+        }
+      }
+
+    fragmentScenario =
+      launchFragmentInContainer(Bundle(), R.style.SudThemeGlif) {
+        FingerprintEnrollIntroV2Fragment(factory)
+      }
+  }
+
+  @Test
+  fun testScrollToBottomButtonChangesText() {
+    fragmentScenario.onFragment { fragment ->
+      onView(withText("I agree")).check(doesNotExist())
+      val someView = (fragment.requireView().findViewById<GlifLayout>(R.id.setup_wizard_layout))!!
+      val scrollMixin = someView.getMixin(RequireScrollMixin::class.java)!!
+      val listener = scrollMixin.onRequireScrollStateChangedListener
+      // This actually changes the button text
+      listener.onRequireScrollStateChanged(false)
+
+      onView(withText("I agree")).check(matches(isDisplayed()))
+    }
+  }
+
+  @Test
+  fun testBasicTitle() {
+    onView(withText(R.string.security_settings_fingerprint_enroll_introduction_title))
+      .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
+  }
+
+  @Test
+  fun testFooterMessageTwo() {
+    onView(withId(R.id.footer_message_2))
+      .check(
+        matches(
+          withText(
+            context.getString(
+              (R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_2)
+            )
+          )
+        )
+      )
+  }
+
+  @Test
+  fun testFooterMessageThree() {
+    onView(withId(R.id.footer_message_3))
+      .check(
+        matches(
+          withText(
+            context.getString(
+              (R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_3)
+            )
+          )
+        )
+      )
+  }
+
+  @Test
+  fun testFooterMessageFour() {
+    onView(withId(R.id.footer_message_4))
+      .check(
+        matches(
+          withText(
+            context.getString(
+              (R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_4)
+            )
+          )
+        )
+      )
+  }
+
+  @Test
+  fun testFooterMessageFive() {
+    onView(withId(R.id.footer_message_5))
+      .check(
+        matches(
+          withText(
+            context.getString(
+              (R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_5)
+            )
+          )
+        )
+      )
+  }
+}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherControllerTest.java
index 2bd2d74..c02ec32 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherControllerTest.java
@@ -20,6 +20,8 @@
 
 import static org.mockito.Mockito.when;
 
+import android.bluetooth.BluetoothDevice;
+
 import com.android.settings.R;
 import com.android.settings.applications.SpacePreference;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -65,7 +67,7 @@
 
     /** Test the pair other side button title during initialization. */
     @Test
-    public void init_leftSideDevice_pairRightSideButtonTitle() {
+    public void init_deviceIsLeftSide_showPairRightSideTitle() {
         when(mCachedDevice.getDeviceSide()).thenReturn(HearingAidInfo.DeviceSide.SIDE_LEFT);
 
         mController.init(mScreen);
@@ -76,7 +78,7 @@
 
     /** Test the pair other side button title during initialization. */
     @Test
-    public void init_rightSideDevice_pairLeftSideButtonTitle() {
+    public void init_deviceIsRightSide_showPairLeftSideTitle() {
         when(mCachedDevice.getDeviceSide()).thenReturn(HearingAidInfo.DeviceSide.SIDE_RIGHT);
 
         mController.init(mScreen);
@@ -87,7 +89,7 @@
 
     /** Test the pair other side button visibility during initialization. */
     @Test
-    public void init_isNotConnectedHearingAidDevice_preferenceIsNotVisible() {
+    public void init_deviceIsNotConnectedHearingAid_preferenceIsNotVisible() {
         when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(false);
 
         mController.init(mScreen);
@@ -99,13 +101,13 @@
     /**
      * Test if the controller is available.
      * Conditions:
-     *      1. Hearing aids is not connected
+     *      1. The device is not a connected hearing aid
      * Expected result:
      *      The controller is not available. No need to show pair other side hint for
-     *      not connected device.
+     *      non-hearing aid device or not connected device.
      */
     @Test
-    public void isAvailable_isNotConnectedHearingAidDevice_notAvailable() {
+    public void isAvailable_deviceIsNotConnectedHearingAid_notAvailable() {
         when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(false);
 
         assertThat(mController.isAvailable()).isFalse();
@@ -114,13 +116,13 @@
     /**
      * Test if the controller is available.
      * Conditions:
-     *      1. Monaural hearing aids
+     *      1. Monaural hearing aid
      * Expected result:
      *      The controller is not available. No need to show pair other side hint for
      *      monaural device.
      */
     @Test
-    public void isAvailable_isConnectedHearingAidDevice_isMonaural_notAvailable() {
+    public void isAvailable_deviceIsConnectedHearingAid_isMonaural_notAvailable() {
         when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
         when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_MONAURAL);
 
@@ -130,18 +132,17 @@
     /**
      * Test if the controller is available.
      * Conditions:
-     *      1. Binaural ASHA hearing aids
+     *      1. Binaural hearing aids
      *      2. Sub device is added
-     *      3. Sub device is connected
+     *      3. Sub device is bonded
      * Expected result:
-     *      The controller is not available. Both sides are already paired and connected.
+     *      The controller is not available. Both sides are already paired.
      */
     @Test
-    public void isAvailable_ashaDevice_otherDeviceIsConnected_notAvailable() {
+    public void isAvailable_deviceIsConnectedHearingAid_subDeviceIsBonded_notAvailable() {
         when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
-        when(mCachedDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
         when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_BINAURAL);
-        when(mSubCachedDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
+        when(mSubCachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
         when(mCachedDevice.getSubDevice()).thenReturn(mSubCachedDevice);
 
         assertThat(mController.isAvailable()).isFalse();
@@ -150,18 +151,17 @@
     /**
      * Test if the controller is available.
      * Conditions:
-     *      1. Binaural ASHA hearing aids
+     *      1. Binaural hearing aids
      *      2. Sub device is added
-     *      3. Sub device is not connected
+     *      3. Sub device is not bonded
      * Expected result:
      *      The controller is available. Need to show the hint to pair the other side.
      */
     @Test
-    public void isAvailable_ashaDevice_otherDeviceIsNotConnected_available() {
+    public void isAvailable_deviceIsConnectedHearingAid_subDeviceIsNotBonded_available() {
         when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
-        when(mCachedDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
         when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_BINAURAL);
-        when(mSubCachedDevice.isConnectedAshaHearingAidDevice()).thenReturn(false);
+        when(mSubCachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
         when(mCachedDevice.getSubDevice()).thenReturn(mSubCachedDevice);
 
         assertThat(mController.isAvailable()).isTrue();
@@ -170,74 +170,55 @@
     /**
      * Test if the controller is available.
      * Conditions:
-     *      1. Binaural ASHA hearing aids
-     *      2. No sub device added
+     *      1. Binaural hearing aids
+     *      2. Member device is added
+     *      3. Member device is bonded
+     * Expected result:
+     *      The controller is not available. Both sides are already paired.
+     */
+    @Test
+    public void isAvailable_deviceIsConnectedHearingAid_memberDeviceIsBonded_notAvailable() {
+        when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
+        when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_BINAURAL);
+        when(mSubCachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        when(mCachedDevice.getMemberDevice()).thenReturn(Set.of(mSubCachedDevice));
+
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    /**
+     * Test if the controller is available.
+     * Conditions:
+     *      1. Binaural hearing aids
+     *      2. Member device is added
+     *      3. Member device is not bonded
      * Expected result:
      *      The controller is available. Need to show the hint to pair the other side.
      */
     @Test
-    public void isAvailable_ashaDevice_otherDeviceIsNotExist_available() {
+    public void isAvailable_deviceIsConnectedHearingAid_memberDeviceIsNotBonded_available() {
         when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
-        when(mCachedDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
+        when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_BINAURAL);
+        when(mSubCachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
+        when(mCachedDevice.getMemberDevice()).thenReturn(Set.of(mSubCachedDevice));
+
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    /**
+     * Test if the controller is available.
+     * Conditions:
+     *      1. Binaural hearing aids
+     *      2. No sub device is added
+     *      2. No member device is added
+     * Expected result:
+     *      The controller is available. Need to show the hint to pair the other side.
+     */
+    @Test
+    public void isAvailable_deviceIsConnectedHearingAid_otherDeviceIsNotExist_available() {
+        when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
         when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_BINAURAL);
         when(mCachedDevice.getSubDevice()).thenReturn(null);
-
-        assertThat(mController.isAvailable()).isTrue();
-    }
-
-    /**
-     * Test if the controller is available.
-     * Conditions:
-     *      1. Binaural LE Audio hearing aids
-     *      2. Member device is added
-     *      3. Member device is connected
-     * Expected result:
-     *      The controller is not available. Both sides are already paired and connected.
-     */
-    @Test
-    public void isAvailable_leAudioDevice_otherDeviceIsConnected_notAvailable() {
-        when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
-        when(mCachedDevice.isConnectedLeAudioHearingAidDevice()).thenReturn(true);
-        when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_BINAURAL);
-        when(mSubCachedDevice.isConnectedLeAudioHearingAidDevice()).thenReturn(true);
-        when(mCachedDevice.getMemberDevice()).thenReturn(Set.of(mSubCachedDevice));
-
-        assertThat(mController.isAvailable()).isFalse();
-    }
-
-    /**
-     * Test if the controller is available.
-     * Conditions:
-     *      1. Binaural LE Audio hearing aids
-     *      2. Member device is added
-     *      3. Member device is not connected
-     * Expected result:
-     *      The controller is available. Need to show the hint to pair the other side.
-     */
-    @Test
-    public void isAvailable_leAudioDevice_otherDeviceIsNotConnected_available() {
-        when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
-        when(mCachedDevice.isConnectedLeAudioHearingAidDevice()).thenReturn(true);
-        when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_BINAURAL);
-        when(mSubCachedDevice.isConnectedLeAudioHearingAidDevice()).thenReturn(false);
-        when(mCachedDevice.getMemberDevice()).thenReturn(Set.of(mSubCachedDevice));
-
-        assertThat(mController.isAvailable()).isTrue();
-    }
-
-    /**
-     * Test if the controller is available.
-     * Conditions:
-     *      1. Binaural LE Audio hearing aids
-     *      2. No member device added
-     * Expected result:
-     *      The controller is available. Need to show the hint to pair the other side.
-     */
-    @Test
-    public void isAvailable_leAudioDevice_otherDeviceIsNotExist_available() {
-        when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
-        when(mCachedDevice.isConnectedLeAudioHearingAidDevice()).thenReturn(true);
-        when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_BINAURAL);
         when(mCachedDevice.getMemberDevice()).thenReturn(new HashSet<>());
 
         assertThat(mController.isAvailable()).isTrue();
@@ -245,7 +226,7 @@
 
     /** Test the pair other side button title after refreshing. */
     @Test
-    public void refresh_rightSideDevice_pairLeftSideButtonTitle() {
+    public void refresh_deviceIsRightSide_showPairLeftSideTitle() {
         when(mCachedDevice.getDeviceSide()).thenReturn(HearingAidInfo.DeviceSide.SIDE_RIGHT);
         mController.init(mScreen);
 
@@ -257,7 +238,7 @@
 
     /** Test the pair other side button visibility after refreshing. */
     @Test
-    public void refresh_isNotConnectedHearingAidDevice_preferenceIsNotVisible() {
+    public void refresh_deviceIsNotConnectedHearingAid_preferenceIsNotVisible() {
         when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(false);
         mController.init(mScreen);
 
diff --git a/tests/robotests/src/com/android/settings/dashboard/profileselector/ProfileSelectFragmentTest.java b/tests/robotests/src/com/android/settings/dashboard/profileselector/ProfileSelectFragmentTest.java
index b595d06..056935c 100644
--- a/tests/robotests/src/com/android/settings/dashboard/profileselector/ProfileSelectFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/profileselector/ProfileSelectFragmentTest.java
@@ -19,6 +19,7 @@
 import static android.content.Intent.EXTRA_USER_ID;
 
 import static com.android.settings.dashboard.profileselector.ProfileSelectFragment.PERSONAL_TAB;
+import static com.android.settings.dashboard.profileselector.ProfileSelectFragment.PRIVATE_TAB;
 import static com.android.settings.dashboard.profileselector.ProfileSelectFragment.WORK_TAB;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -29,6 +30,9 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
+import android.os.Flags;
+import android.os.UserHandle;
+import android.platform.test.flag.junit.SetFlagsRule;
 
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentActivity;
@@ -38,6 +42,7 @@
 import com.android.settings.testutils.shadow.ShadowUserManager;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.MockitoAnnotations;
@@ -60,6 +65,7 @@
     private TestProfileSelectFragment mFragment;
     private FragmentActivity mActivity;
     private ShadowUserManager mUserManager;
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     @Before
     public void setUp() {
@@ -86,6 +92,14 @@
     }
 
     @Test
+    public void getTabId_setArgumentPrivate_setCorrectTab() {
+        final Bundle bundle = new Bundle();
+        bundle.putInt(SettingsActivity.EXTRA_SHOW_FRAGMENT_TAB, PRIVATE_TAB);
+
+        assertThat(mFragment.getTabId(mActivity, bundle)).isEqualTo(PRIVATE_TAB);
+    }
+
+    @Test
     public void getTabId_setArgumentPersonal_setCorrectTab() {
         final Bundle bundle = new Bundle();
         bundle.putInt(SettingsActivity.EXTRA_SHOW_FRAGMENT_TAB, PERSONAL_TAB);
@@ -105,6 +119,16 @@
     }
 
     @Test
+    public void getTabId_setPrivateId_getCorrectTab() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+        final Bundle bundle = new Bundle();
+        bundle.putInt(EXTRA_USER_ID, 11);
+        mUserManager.setPrivateProfile(11, "private", 0);
+
+        assertThat(mFragment.getTabId(mActivity, bundle)).isEqualTo(PRIVATE_TAB);
+    }
+
+    @Test
     public void getTabId_setPersonalId_getCorrectTab() {
         final Bundle bundle = new Bundle();
         bundle.putInt(EXTRA_USER_ID, 0);
@@ -124,12 +148,120 @@
         assertThat(mFragment.getTabId(mActivity, null)).isEqualTo(WORK_TAB);
     }
 
+    @Test
+    public void testGetFragments_whenOnlyPersonal_returnsOneFragment() {
+        mSetFlagsRule.disableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+        Fragment[] fragments = ProfileSelectFragment.getFragments(
+                mContext,
+                null /* bundle */,
+                TestProfileSelectFragment::new,
+                TestProfileSelectFragment::new,
+                TestProfileSelectFragment::new);
+        assertThat(fragments).hasLength(1);
+    }
+
+    @Test
+    public void testGetFragments_whenPrivateDisabled_returnsOneFragment() {
+        Fragment[] fragments = ProfileSelectFragment.getFragments(
+                mContext,
+                null /* bundle */,
+                TestProfileSelectFragment::new,
+                TestProfileSelectFragment::new,
+                TestProfileSelectFragment::new,
+                new ProfileSelectFragment.PrivateSpaceInfoProvider() {
+                    @Override
+                    public boolean isPrivateSpaceLocked(Context context) {
+                        return true;
+                    }
+                },
+                new ProfileSelectFragment.ManagedProfileInfoProvider() {
+                    @Override
+                    public UserHandle getManagedProfile(Context context) {
+                        return null;
+                    }
+                });
+        assertThat(fragments).hasLength(1);
+    }
+
+    @Test
+    public void testGetFragments_whenPrivateEnabled_returnsTwoFragments() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+        Fragment[] fragments = ProfileSelectFragment.getFragments(
+                mContext,
+                null /* bundle */,
+                TestProfileSelectFragment::new,
+                TestProfileSelectFragment::new,
+                TestProfileSelectFragment::new,
+                new ProfileSelectFragment.PrivateSpaceInfoProvider() {
+                    @Override
+                    public boolean isPrivateSpaceLocked(Context context) {
+                        return false;
+                    }
+                },
+                new ProfileSelectFragment.ManagedProfileInfoProvider() {
+                    @Override
+                    public UserHandle getManagedProfile(Context context) {
+                        return null;
+                    }
+                });
+        assertThat(fragments).hasLength(2);
+    }
+
+    @Test
+    public void testGetFragments_whenManagedProfile_returnsTwoFragments() {
+        mSetFlagsRule.disableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+        Fragment[] fragments = ProfileSelectFragment.getFragments(
+                mContext,
+                null /* bundle */,
+                TestProfileSelectFragment::new,
+                TestProfileSelectFragment::new,
+                TestProfileSelectFragment::new,
+                new ProfileSelectFragment.PrivateSpaceInfoProvider() {
+                    @Override
+                    public boolean isPrivateSpaceLocked(Context context) {
+                        return false;
+                    }
+                },
+                new ProfileSelectFragment.ManagedProfileInfoProvider() {
+                    @Override
+                    public UserHandle getManagedProfile(Context context) {
+                        return new UserHandle(123);
+                    }
+                });
+        assertThat(fragments).hasLength(2);
+    }
+
+    @Test
+    public void testGetFragments_whenAllProfiles_returnsThreeFragments() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+        Fragment[] fragments = ProfileSelectFragment.getFragments(
+                mContext,
+                null /* bundle */,
+                TestProfileSelectFragment::new,
+                TestProfileSelectFragment::new,
+                TestProfileSelectFragment::new,
+                new ProfileSelectFragment.PrivateSpaceInfoProvider() {
+                    @Override
+                    public boolean isPrivateSpaceLocked(Context context) {
+                        return false;
+                    }
+                },
+                new ProfileSelectFragment.ManagedProfileInfoProvider() {
+                    @Override
+                    public UserHandle getManagedProfile(Context context) {
+                        return new UserHandle(123);
+                    }
+                });
+        assertThat(fragments).hasLength(3);
+    }
+
     public static class TestProfileSelectFragment extends ProfileSelectFragment {
 
         @Override
         public Fragment[] getFragments() {
             return new Fragment[]{
                     new SettingsPreferenceFragmentTest.TestFragment(), //0
+                    new SettingsPreferenceFragmentTest.TestFragment(),
                     new SettingsPreferenceFragmentTest.TestFragment()
             };
         }
diff --git a/tests/robotests/src/com/android/settings/dashboard/profileselector/ProfileSelectLocationFragmentTest.java b/tests/robotests/src/com/android/settings/dashboard/profileselector/ProfileSelectLocationFragmentTest.java
index f463bec..fa2782f 100644
--- a/tests/robotests/src/com/android/settings/dashboard/profileselector/ProfileSelectLocationFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/profileselector/ProfileSelectLocationFragmentTest.java
@@ -39,10 +39,12 @@
 
     @Test
     public void getFragments_containsCorrectBundle() {
-        assertThat(mProfileSelectLocationFragment.getFragments().length).isEqualTo(2);
+        assertThat(mProfileSelectLocationFragment.getFragments().length).isEqualTo(3);
         assertThat(mProfileSelectLocationFragment.getFragments()[0].getArguments().getInt(
                 EXTRA_PROFILE, -1)).isEqualTo(ProfileSelectFragment.ProfileType.PERSONAL);
         assertThat(mProfileSelectLocationFragment.getFragments()[1].getArguments().getInt(
                 EXTRA_PROFILE, -1)).isEqualTo(ProfileSelectFragment.ProfileType.WORK);
+        assertThat(mProfileSelectLocationFragment.getFragments()[1].getArguments().getInt(
+                EXTRA_PROFILE, -1)).isEqualTo(ProfileSelectFragment.ProfileType.PRIVATE);
     }
 }
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
index 5db0243..b61f5ab 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
@@ -52,6 +52,7 @@
 import com.android.settings.SubSettings;
 import com.android.settings.applications.manageapplications.ManageApplications;
 import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
+import com.android.settings.dashboard.profileselector.ProfileSelectFragment.ProfileType;
 import com.android.settings.deviceinfo.StorageItemPreference;
 import com.android.settings.testutils.shadow.ShadowUserManager;
 import com.android.settingslib.deviceinfo.StorageVolumeProvider;
@@ -99,7 +100,7 @@
         // Note: null is passed as the Lifecycle because we are handling it outside of the normal
         //       Settings fragment lifecycle for test purposes.
         mController = new StorageItemPreferenceController(mContext, mFragment, mVolume, mSvp,
-                false /* isWorkProfile */);
+                ProfileSelectFragment.ProfileType.PERSONAL);
         mPreference = new StorageItemPreference(mContext);
 
         // Inflate the preference and the widget.
@@ -175,7 +176,7 @@
         mPreference.setKey(StorageItemPreferenceController.IMAGES_KEY);
         final Context mockContext = getMockContext();
         mController = new StorageItemPreferenceController(mockContext, mFragment, mVolume,
-                mSvp, false /* isWorkProfile */);
+                mSvp, ProfileSelectFragment.ProfileType.PERSONAL);
         mController.handlePreferenceTreeClick(mPreference);
 
         final ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
@@ -192,7 +193,7 @@
         mPreference.setKey(StorageItemPreferenceController.AUDIO_KEY);
         final Context mockContext = getMockContext();
         mController = new StorageItemPreferenceController(mockContext, mFragment, mVolume,
-                mSvp, false /* isWorkProfile */);
+                mSvp, ProfileSelectFragment.ProfileType.PERSONAL);
         mController.handlePreferenceTreeClick(mPreference);
 
         final ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
@@ -242,7 +243,7 @@
     @Test
     public void launchAppsIntent_forWork_settingsIntent() {
         mController = new FakeStorageItemPreferenceController(mContext, mFragment, mVolume, mSvp,
-                true /* isWorkProfile */);
+                ProfileType.WORK);
         mPreference.setKey(StorageItemPreferenceController.APPS_KEY);
         mController.handlePreferenceTreeClick(mPreference);
 
@@ -272,7 +273,7 @@
         mPreference.setKey(StorageItemPreferenceController.DOCUMENTS_AND_OTHER_KEY);
         final Context mockContext = getMockContext();
         mController = new StorageItemPreferenceController(mockContext, mFragment, mVolume,
-                mSvp, false /* isWorkProfile */);
+                mSvp, ProfileSelectFragment.ProfileType.PERSONAL);
         mController.handlePreferenceTreeClick(mPreference);
 
         final ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
@@ -307,7 +308,7 @@
         mPreference.setKey(StorageItemPreferenceController.VIDEOS_KEY);
         final Context mockContext = getMockContext();
         mController = new StorageItemPreferenceController(mockContext, mFragment, mVolume,
-                mSvp, false /* isWorkProfile */);
+                mSvp, ProfileSelectFragment.ProfileType.PERSONAL);
         mController.handlePreferenceTreeClick(mPreference);
 
         final ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
@@ -457,8 +458,8 @@
         private static final int CURRENT_USER_ID = 10;
 
         FakeStorageItemPreferenceController(Context context, Fragment hostFragment,
-                VolumeInfo volume, StorageVolumeProvider svp, boolean isWorkProfile) {
-            super(context, hostFragment, volume, svp, isWorkProfile);
+                VolumeInfo volume, StorageVolumeProvider svp, @ProfileType int profileType) {
+            super(context, hostFragment, volume, svp, profileType);
         }
 
         @Override
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffDataTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffDataTest.java
index d4bae29..04d601f 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffDataTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffDataTest.java
@@ -60,10 +60,11 @@
     @Test
     public void needsCombineInSystemApp_isHidden_returnTrue() {
         final BatteryDiffEntry hiddenDiffEntry =
-                createBatteryDiffEntry(mContext, /*consumePower=*/ 0, /*isHidden=*/ true);
+                createBatteryDiffEntry(mContext, /* consumePower= */ 0, /* isHidden= */ true);
 
-        final boolean needsCombineInSystemApp = BatteryDiffData.needsCombineInSystemApp(
-                hiddenDiffEntry, List.of(), Set.of(), Set.of());
+        final boolean needsCombineInSystemApp =
+                BatteryDiffData.needsCombineInSystemApp(
+                        hiddenDiffEntry, List.of(), Set.of(), Set.of());
 
         assertThat(needsCombineInSystemApp).isTrue();
     }
@@ -71,13 +72,17 @@
     @Test
     public void needsCombineInSystemApp_isSystemApp_returnTrue() {
         final BatteryDiffEntry batteryDiffEntry =
-                createBatteryDiffEntry(mContext, /*consumePower=*/ 0, /*isHidden=*/ false);
+                createBatteryDiffEntry(mContext, /* consumePower= */ 0, /* isHidden= */ false);
         doReturn(mAppEntry).when(mApplicationsState).getEntry(anyString(), anyInt());
         mAppEntry.info = mApplicationInfo;
         mApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
 
-        final boolean needsCombineInSystemApp = BatteryDiffData.needsCombineInSystemApp(
-                batteryDiffEntry, List.of(), Set.of(ConvertUtils.FAKE_PACKAGE_NAME), Set.of());
+        final boolean needsCombineInSystemApp =
+                BatteryDiffData.needsCombineInSystemApp(
+                        batteryDiffEntry,
+                        List.of(),
+                        Set.of(ConvertUtils.FAKE_PACKAGE_NAME),
+                        Set.of());
 
         assertThat(needsCombineInSystemApp).isTrue();
     }
@@ -85,13 +90,14 @@
     @Test
     public void needsCombineInSystemApp_notSystemApp_returnFalse() {
         final BatteryDiffEntry batteryDiffEntry =
-                createBatteryDiffEntry(mContext, /*consumePower=*/ 0, /*isHidden=*/ false);
+                createBatteryDiffEntry(mContext, /* consumePower= */ 0, /* isHidden= */ false);
         doReturn(mAppEntry).when(mApplicationsState).getEntry(anyString(), anyInt());
         mAppEntry.info = mApplicationInfo;
         mApplicationInfo.flags = 0;
 
-        final boolean needsCombineInSystemApp = BatteryDiffData.needsCombineInSystemApp(
-                batteryDiffEntry, List.of(), Set.of(), Set.of());
+        final boolean needsCombineInSystemApp =
+                BatteryDiffData.needsCombineInSystemApp(
+                        batteryDiffEntry, List.of(), Set.of(), Set.of());
 
         assertThat(needsCombineInSystemApp).isFalse();
     }
@@ -100,11 +106,11 @@
     public void processPercentsAndSort_sumLessThan100_adjustTo100() {
         List<BatteryDiffEntry> batteryDiffEntries = new ArrayList<>();
         batteryDiffEntries.add(
-                createBatteryDiffEntry(mContext, /*consumePower=*/ 33.33, /*isHidden=*/ false));
+                createBatteryDiffEntry(mContext, /* consumePower= */ 33.33, /* isHidden= */ false));
         batteryDiffEntries.add(
-                createBatteryDiffEntry(mContext, /*consumePower=*/ 33.34, /*isHidden=*/ false));
+                createBatteryDiffEntry(mContext, /* consumePower= */ 33.34, /* isHidden= */ false));
         batteryDiffEntries.add(
-                createBatteryDiffEntry(mContext, /*consumePower=*/ 33.33, /*isHidden=*/ false));
+                createBatteryDiffEntry(mContext, /* consumePower= */ 33.33, /* isHidden= */ false));
 
         BatteryDiffData.processAndSortEntries(batteryDiffEntries);
 
@@ -120,11 +126,11 @@
     public void processPercentsAndSort_sumGreaterThan100_adjustTo100() {
         List<BatteryDiffEntry> batteryDiffEntries = new ArrayList<>();
         batteryDiffEntries.add(
-                createBatteryDiffEntry(mContext, /*consumePower=*/ 48.5, /*isHidden=*/ false));
+                createBatteryDiffEntry(mContext, /* consumePower= */ 48.5, /* isHidden= */ false));
         batteryDiffEntries.add(
-                createBatteryDiffEntry(mContext, /*consumePower=*/ 3, /*isHidden=*/ false));
+                createBatteryDiffEntry(mContext, /* consumePower= */ 3, /* isHidden= */ false));
         batteryDiffEntries.add(
-                createBatteryDiffEntry(mContext, /*consumePower=*/ 48.5, /*isHidden=*/ false));
+                createBatteryDiffEntry(mContext, /* consumePower= */ 48.5, /* isHidden= */ false));
 
         BatteryDiffData.processAndSortEntries(batteryDiffEntries);
 
@@ -136,47 +142,101 @@
         assertThat(batteryDiffEntries.get(2).getAdjustPercentageOffset()).isEqualTo(0);
     }
 
+    @Test
+    public void processPercentsAndSort_uninstalledApps_sortAsExpected() {
+        List<BatteryDiffEntry> batteryDiffEntries = new ArrayList<>();
+        batteryDiffEntries.add(
+                createBatteryDiffEntry(mContext, /* consumePower= */ 28.5, /* key= */ "APP_1"));
+        batteryDiffEntries.add(
+                createBatteryDiffEntry(
+                        mContext,
+                        /* consumePower= */ 20,
+                        BatteryDiffEntry.UNINSTALLED_APPS_KEY));
+        batteryDiffEntries.add(
+                createBatteryDiffEntry(mContext, /* consumePower= */ 3, /* key= */ "APP_2"));
+        batteryDiffEntries.add(
+                createBatteryDiffEntry(
+                        mContext,
+                        /* consumePower= */ 28.5,
+                        BatteryDiffEntry.SYSTEM_APPS_KEY));
+        batteryDiffEntries.add(
+                createBatteryDiffEntry(mContext, /* consumePower= */ 20, /* key= */ "APP_3"));
+
+        BatteryDiffData.processAndSortEntries(batteryDiffEntries);
+
+        assertThat(batteryDiffEntries.get(0).getKey()).isEqualTo("APP_1");
+        assertThat(batteryDiffEntries.get(1).getKey()).isEqualTo("APP_3");
+        assertThat(batteryDiffEntries.get(2).getKey()).isEqualTo("APP_2");
+        assertThat(batteryDiffEntries.get(3).getKey())
+                .isEqualTo(BatteryDiffEntry.UNINSTALLED_APPS_KEY);
+        assertThat(batteryDiffEntries.get(4).getKey()).isEqualTo(BatteryDiffEntry.SYSTEM_APPS_KEY);
+    }
+
     private static BatteryDiffEntry createBatteryDiffEntry(
             Context context, double consumePower, boolean isHidden) {
+        return createBatteryDiffEntry(context, consumePower, isHidden, /* key= */ null);
+    }
+
+    private static BatteryDiffEntry createBatteryDiffEntry(
+            Context context, double consumePower, String key) {
+        return createBatteryDiffEntry(context, consumePower, /* isHidden= */ false, key);
+    }
+
+    private static BatteryDiffEntry createBatteryDiffEntry(
+            Context context, double consumePower, boolean isHidden, String key) {
         final int currentUserId = context.getUserId();
-        final BatteryHistEntry batteryHistEntry = createBatteryHistEntry(
-                ConvertUtils.FAKE_PACKAGE_NAME, "fake_label", consumePower,
-                /*foregroundUsageConsumePower=*/ 0, /*foregroundServiceUsageConsumePower=*/ 0,
-                /*backgroundUsageConsumePower=*/ 0, /*cachedUsageConsumePower=*/ 0,
-                /*uid=*/ 0L, currentUserId, ConvertUtils.CONSUMER_TYPE_UID_BATTERY,
-                /*foregroundUsageTimeInMs=*/ 0L,  /*backgroundUsageTimeInMs=*/ 0L, isHidden);
+        final BatteryHistEntry batteryHistEntry =
+                createBatteryHistEntry(
+                        ConvertUtils.FAKE_PACKAGE_NAME,
+                        "fake_label",
+                        consumePower,
+                        /* foregroundUsageConsumePower= */ 0,
+                        /* foregroundServiceUsageConsumePower= */ 0,
+                        /* backgroundUsageConsumePower= */ 0,
+                        /* cachedUsageConsumePower= */ 0,
+                        /* uid= */ 0L,
+                        currentUserId,
+                        ConvertUtils.CONSUMER_TYPE_UID_BATTERY,
+                        /* foregroundUsageTimeInMs= */ 0L,
+                        /* backgroundUsageTimeInMs= */ 0L,
+                        isHidden);
         return new BatteryDiffEntry(
                 context,
                 batteryHistEntry.mUid,
                 batteryHistEntry.mUserId,
-                batteryHistEntry.getKey(),
+                key == null ? batteryHistEntry.getKey() : key,
                 batteryHistEntry.mIsHidden,
                 batteryHistEntry.mDrainType,
                 batteryHistEntry.mPackageName,
                 batteryHistEntry.mAppLabel,
                 batteryHistEntry.mConsumerType,
-                /*foregroundUsageTimeInMs=*/ 0,
-                /*backgroundUsageTimeInMs=*/ 0,
-                /*screenOnTimeInMs=*/ 0,
-                /*consumePower=*/ consumePower,
-                /*foregroundUsageConsumePower=*/ 0,
-                /*foregroundServiceUsageConsumePower=*/ 0,
-                /*backgroundUsageConsumePower=*/ 0,
-                /*cachedUsageConsumePower=*/ 0);
+                /* foregroundUsageTimeInMs= */ 0,
+                /* backgroundUsageTimeInMs= */ 0,
+                /* screenOnTimeInMs= */ 0,
+                consumePower,
+                /* foregroundUsageConsumePower= */ 0,
+                /* foregroundServiceUsageConsumePower= */ 0,
+                /* backgroundUsageConsumePower= */ 0,
+                /* cachedUsageConsumePower= */ 0);
     }
 
     private static BatteryHistEntry createBatteryHistEntry(
-            final String packageName, final String appLabel, final double consumePower,
+            final String packageName,
+            final String appLabel,
+            final double consumePower,
             final double foregroundUsageConsumePower,
             final double foregroundServiceUsageConsumePower,
-            final double backgroundUsageConsumePower, final double cachedUsageConsumePower,
-            final long uid, final long userId, final int consumerType,
-            final long foregroundUsageTimeInMs, final long backgroundUsageTimeInMs,
+            final double backgroundUsageConsumePower,
+            final double cachedUsageConsumePower,
+            final long uid,
+            final long userId,
+            final int consumerType,
+            final long foregroundUsageTimeInMs,
+            final long backgroundUsageTimeInMs,
             final boolean isHidden) {
         // Only insert required fields.
         final BatteryInformation batteryInformation =
-                BatteryInformation
-                        .newBuilder()
+                BatteryInformation.newBuilder()
                         .setAppLabel(appLabel)
                         .setConsumePower(consumePower)
                         .setForegroundUsageConsumePower(foregroundUsageConsumePower)
@@ -192,7 +252,8 @@
         values.put(BatteryHistEntry.KEY_UID, uid);
         values.put(BatteryHistEntry.KEY_USER_ID, userId);
         values.put(BatteryHistEntry.KEY_CONSUMER_TYPE, consumerType);
-        values.put(BatteryHistEntry.KEY_BATTERY_INFORMATION,
+        values.put(
+                BatteryHistEntry.KEY_BATTERY_INFORMATION,
                 ConvertUtils.convertBatteryInformationToString(batteryInformation));
         return new BatteryHistEntry(values);
     }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntryTest.java
index 9bb4b73..2cafadb 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntryTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntryTest.java
@@ -58,35 +58,37 @@
 @Config(shadows = {BatteryDiffEntryTest.ShadowUserHandle.class})
 public final class BatteryDiffEntryTest {
 
+    private static final int UID = 100;
+    private static final int UNINSTALLED_UID = 101;
+    private static final String PACKAGE_NAME = "com.android.testing";
+    private static final String UNINSTALLED_PACKAGE_NAME = "com.android.testing.uninstalled";
+
     private Context mContext;
 
-    @Mock
-    private ApplicationInfo mMockAppInfo;
-    @Mock
-    private PackageManager mMockPackageManager;
-    @Mock
-    private UserManager mMockUserManager;
-    @Mock
-    private Drawable mMockDrawable;
-    @Mock
-    private Drawable mMockDrawable2;
-    @Mock
-    private Drawable mMockBadgedDrawable;
-    @Mock
-    private BatteryHistEntry mBatteryHistEntry;
-    @Mock
-    private PackageInfo mMockPackageInfo;
-    @Mock
-    private ConstantState mMockConstantState;
+    @Mock private ApplicationInfo mMockAppInfo;
+    @Mock private PackageManager mMockPackageManager;
+    @Mock private UserManager mMockUserManager;
+    @Mock private Drawable mMockDrawable;
+    @Mock private Drawable mMockDrawable2;
+    @Mock private Drawable mMockBadgedDrawable;
+    @Mock private BatteryHistEntry mBatteryHistEntry;
+    @Mock private PackageInfo mMockPackageInfo;
+    @Mock private ConstantState mMockConstantState;
 
     @Before
-    public void setUp() {
+    public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         ShadowUserHandle.reset();
         mContext = spy(RuntimeEnvironment.application);
         doReturn(mContext).when(mContext).getApplicationContext();
         doReturn(mMockUserManager).when(mContext).getSystemService(UserManager.class);
         doReturn(mMockPackageManager).when(mContext).getPackageManager();
+        doReturn(UID)
+                .when(mMockPackageManager)
+                .getPackageUid(PACKAGE_NAME, PackageManager.GET_META_DATA);
+        doReturn(BatteryUtils.UID_NULL)
+                .when(mMockPackageManager)
+                .getPackageUid(UNINSTALLED_PACKAGE_NAME, PackageManager.GET_META_DATA);
         BatteryDiffEntry.clearCache();
     }
 
@@ -95,22 +97,22 @@
         final BatteryDiffEntry entry =
                 new BatteryDiffEntry(
                         mContext,
-                        /*uid=*/ 0,
-                        /*userId=*/ 0,
-                        /*key=*/ "key",
-                        /*isHidden=*/ false,
-                        /*componentId=*/ -1,
-                        /*legacyPackageName=*/ null,
-                        /*legacyLabel=*/ null,
+                        /* uid= */ 0,
+                        /* userId= */ 0,
+                        /* key= */ "key",
+                        /* isHidden= */ false,
+                        /* componentId= */ -1,
+                        /* legacyPackageName= */ null,
+                        /* legacyLabel= */ null,
                         /*consumerType*/ ConvertUtils.CONSUMER_TYPE_UID_BATTERY,
-                        /*foregroundUsageTimeInMs=*/ 10001L,
-                        /*backgroundUsageTimeInMs=*/ 20002L,
-                        /*screenOnTimeInMs=*/ 30003L,
-                        /*consumePower=*/ 22.0,
-                        /*foregroundUsageConsumePower=*/ 10.0,
-                        /*foregroundServiceUsageConsumePower=*/ 10.0,
-                        /*backgroundUsageConsumePower=*/ 1.0,
-                        /*cachedUsageConsumePower=*/ 1.0);
+                        /* foregroundUsageTimeInMs= */ 10001L,
+                        /* backgroundUsageTimeInMs= */ 20002L,
+                        /* screenOnTimeInMs= */ 30003L,
+                        /* consumePower= */ 22.0,
+                        /* foregroundUsageConsumePower= */ 10.0,
+                        /* foregroundServiceUsageConsumePower= */ 10.0,
+                        /* backgroundUsageConsumePower= */ 1.0,
+                        /* cachedUsageConsumePower= */ 1.0);
         entry.setTotalConsumePower(100.0);
 
         assertThat(entry.getPercentage()).isEqualTo(22.0);
@@ -121,22 +123,22 @@
         final BatteryDiffEntry entry =
                 new BatteryDiffEntry(
                         mContext,
-                        /*uid=*/ 0,
-                        /*userId=*/ 0,
-                        /*key=*/ "key",
-                        /*isHidden=*/ false,
-                        /*componentId=*/ -1,
-                        /*legacyPackageName=*/ null,
-                        /*legacyLabel=*/ null,
+                        /* uid= */ 0,
+                        /* userId= */ 0,
+                        /* key= */ "key",
+                        /* isHidden= */ false,
+                        /* componentId= */ -1,
+                        /* legacyPackageName= */ null,
+                        /* legacyLabel= */ null,
                         /*consumerType*/ ConvertUtils.CONSUMER_TYPE_UID_BATTERY,
-                        /*foregroundUsageTimeInMs=*/ 10001L,
-                        /*backgroundUsageTimeInMs=*/ 20002L,
-                        /*screenOnTimeInMs=*/ 30003L,
-                        /*consumePower=*/ 22.0,
-                        /*foregroundUsageConsumePower=*/ 10.0,
-                        /*foregroundServiceUsageConsumePower=*/ 10.0,
-                        /*backgroundUsageConsumePower=*/ 1.0,
-                        /*cachedUsageConsumePower=*/ 1.0);
+                        /* foregroundUsageTimeInMs= */ 10001L,
+                        /* backgroundUsageTimeInMs= */ 20002L,
+                        /* screenOnTimeInMs= */ 30003L,
+                        /* consumePower= */ 22.0,
+                        /* foregroundUsageConsumePower= */ 10.0,
+                        /* foregroundServiceUsageConsumePower= */ 10.0,
+                        /* backgroundUsageConsumePower= */ 1.0,
+                        /* cachedUsageConsumePower= */ 1.0);
         entry.setTotalConsumePower(0);
 
         assertThat(entry.getPercentage()).isEqualTo(0);
@@ -149,22 +151,22 @@
         BatteryDiffEntry systemAppsBatteryDiffEntry =
                 new BatteryDiffEntry(
                         mContext,
-                        /*uid=*/ 0,
-                        /*userId=*/ 0,
-                        /*key=*/ BatteryDiffEntry.SYSTEM_APPS_KEY,
-                        /*isHidden=*/ false,
-                        /*componentId=*/ -1,
-                        /*legacyPackageName=*/ null,
-                        /*legacyLabel=*/ BatteryDiffEntry.SYSTEM_APPS_KEY,
+                        /* uid= */ 0,
+                        /* userId= */ 0,
+                        /* key= */ BatteryDiffEntry.SYSTEM_APPS_KEY,
+                        /* isHidden= */ false,
+                        /* componentId= */ -1,
+                        /* legacyPackageName= */ null,
+                        /* legacyLabel= */ BatteryDiffEntry.SYSTEM_APPS_KEY,
                         /*consumerType*/ ConvertUtils.CONSUMER_TYPE_UID_BATTERY,
-                        /*foregroundUsageTimeInMs=*/ 0,
-                        /*backgroundUsageTimeInMs=*/ 0,
-                        /*screenOnTimeInMs=*/ 0,
-                        /*consumePower=*/ 0,
-                        /*foregroundUsageConsumePower=*/ 0,
-                        /*foregroundServiceUsageConsumePower=*/ 0,
-                        /*backgroundUsageConsumePower=*/ 0,
-                        /*cachedUsageConsumePower=*/ 0);
+                        /* foregroundUsageTimeInMs= */ 0,
+                        /* backgroundUsageTimeInMs= */ 0,
+                        /* screenOnTimeInMs= */ 0,
+                        /* consumePower= */ 0,
+                        /* foregroundUsageConsumePower= */ 0,
+                        /* foregroundServiceUsageConsumePower= */ 0,
+                        /* backgroundUsageConsumePower= */ 0,
+                        /* cachedUsageConsumePower= */ 0);
         systemAppsBatteryDiffEntry.mConsumePower = 16;
         systemAppsBatteryDiffEntry.setTotalConsumePower(100);
         entryList.add(systemAppsBatteryDiffEntry);
@@ -184,14 +186,14 @@
     public void testLoadLabelAndIcon_forSystemBattery_returnExpectedResult() {
         final String expectedName = "Ambient display";
         // Generates fake testing data.
-        final ContentValues values = getContentValuesWithType(
-                ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
+        final ContentValues values =
+                getContentValuesWithType(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
         final BatteryInformation batteryInformation =
-                BatteryInformation
-                        .newBuilder()
+                BatteryInformation.newBuilder()
                         .setDrainType(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY)
                         .build();
-        values.put(BatteryHistEntry.KEY_BATTERY_INFORMATION,
+        values.put(
+                BatteryHistEntry.KEY_BATTERY_INFORMATION,
                 ConvertUtils.convertBatteryInformationToString(batteryInformation));
         final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values);
 
@@ -215,8 +217,8 @@
         final String expectedName = "Removed user";
         doReturn(null).when(mMockUserManager).getUserInfo(1001);
         // Generates fake testing data.
-        final ContentValues values = getContentValuesWithType(
-                ConvertUtils.CONSUMER_TYPE_USER_BATTERY);
+        final ContentValues values =
+                getContentValuesWithType(ConvertUtils.CONSUMER_TYPE_USER_BATTERY);
         values.put(BatteryHistEntry.KEY_USER_ID, Integer.valueOf(1001));
         final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values);
 
@@ -240,14 +242,12 @@
     public void testGetAppLabel_loadDataFromApplicationInfo() throws Exception {
         final String expectedAppLabel = "fake app label";
         final String fakePackageName = "com.fake.google.com";
-        final ContentValues values = getContentValuesWithType(
-                ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
+        final ContentValues values =
+                getContentValuesWithType(ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
         values.put(BatteryHistEntry.KEY_UID, /*invalid uid*/ 10001);
         values.put(BatteryHistEntry.KEY_PACKAGE_NAME, fakePackageName);
-        doReturn(mMockAppInfo).when(mMockPackageManager)
-                .getApplicationInfo(fakePackageName, 0);
-        doReturn(expectedAppLabel).when(mMockPackageManager)
-                .getApplicationLabel(mMockAppInfo);
+        doReturn(mMockAppInfo).when(mMockPackageManager).getApplicationInfo(fakePackageName, 0);
+        doReturn(expectedAppLabel).when(mMockPackageManager).getApplicationLabel(mMockAppInfo);
         final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values);
 
         final BatteryDiffEntry entry = createBatteryDiffEntry(10, batteryHistEntry);
@@ -267,8 +267,8 @@
     @Test
     public void testGetAppLabel_loadDataFromPreDefinedNameAndUid() {
         final String expectedAppLabel = "Android OS";
-        final ContentValues values = getContentValuesWithType(
-                ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
+        final ContentValues values =
+                getContentValuesWithType(ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
         final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values);
 
         final BatteryDiffEntry entry = createBatteryDiffEntry(10, batteryHistEntry);
@@ -284,14 +284,12 @@
     @Test
     public void testGetAppLabel_nullAppLabel_returnAppLabelInBatteryHistEntry() {
         final String expectedAppLabel = "fake app label";
-        final ContentValues values = getContentValuesWithType(
-                ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
+        final ContentValues values =
+                getContentValuesWithType(ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
         final BatteryInformation batteryInformation =
-                BatteryInformation
-                        .newBuilder()
-                        .setAppLabel(expectedAppLabel)
-                        .build();
-        values.put(BatteryHistEntry.KEY_BATTERY_INFORMATION,
+                BatteryInformation.newBuilder().setAppLabel(expectedAppLabel).build();
+        values.put(
+                BatteryHistEntry.KEY_BATTERY_INFORMATION,
                 ConvertUtils.convertBatteryInformationToString(batteryInformation));
         final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values);
 
@@ -304,8 +302,8 @@
 
     @Test
     public void testGetAppIcon_nonUidConsumer_returnAppIconInBatteryDiffEntry() {
-        final ContentValues values = getContentValuesWithType(
-                ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
+        final ContentValues values =
+                getContentValuesWithType(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
         final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values);
         mockConstantState(mMockDrawable);
 
@@ -324,7 +322,8 @@
         final BatteryDiffEntry entry = createBatteryDiffEntry(mMockDrawable);
         mockConstantState(mMockDrawable);
         mockConstantState(mMockBadgedDrawable);
-        doReturn(mMockBadgedDrawable).when(mMockUserManager)
+        doReturn(mMockBadgedDrawable)
+                .when(mMockUserManager)
                 .getBadgedIconForUser(eq(mMockDrawable), any());
 
         entry.mAppIcon = null;
@@ -350,9 +349,8 @@
     public void testClearCache_clearDataForResourcesAndFlags() {
         BatteryDiffEntry.sResourceCache.put(
                 "fake application key",
-                new BatteryEntry.NameAndIcon("app label", null, /*iconId=*/ 0));
-        BatteryDiffEntry.sValidForRestriction.put(
-                "fake application key", Boolean.valueOf(false));
+                new BatteryEntry.NameAndIcon("app label", null, /* iconId= */ 0));
+        BatteryDiffEntry.sValidForRestriction.put("fake application key", Boolean.valueOf(false));
 
         BatteryDiffEntry.clearCache();
 
@@ -386,7 +384,8 @@
         final BatteryDiffEntry entry =
                 createBatteryDiffEntry(
                         ConvertUtils.CONSUMER_TYPE_USER_BATTERY,
-                        /*uid=*/ 0, /*isHidden=*/ false);
+                        /* uid= */ 0,
+                        /* isHidden= */ false);
         assertThat(entry.isSystemEntry()).isTrue();
     }
 
@@ -395,7 +394,8 @@
         final BatteryDiffEntry entry =
                 createBatteryDiffEntry(
                         ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY,
-                        /*uid=*/ 0, /*isHidden=*/ false);
+                        /* uid= */ 0,
+                        /* isHidden= */ false);
         assertThat(entry.isSystemEntry()).isTrue();
     }
 
@@ -404,7 +404,8 @@
         final BatteryDiffEntry entry =
                 createBatteryDiffEntry(
                         ConvertUtils.CONSUMER_TYPE_UID_BATTERY,
-                        /*uid=*/ 123, /*isHidden=*/ false);
+                        /* uid= */ 123,
+                        /* isHidden= */ false);
         assertThat(entry.isSystemEntry()).isFalse();
     }
 
@@ -413,39 +414,80 @@
         final BatteryDiffEntry entry =
                 createBatteryDiffEntry(
                         ConvertUtils.CONSUMER_TYPE_UID_BATTERY,
-                        /*uid=*/ 1230, /*isHidden=*/ false);
+                        /* uid= */ 1230,
+                        /* isHidden= */ false);
         assertThat(entry.isSystemEntry()).isFalse();
     }
 
     @Test
+    public void testIsUninstalledEntry_systemApp_returnFalse() {
+        final BatteryDiffEntry entry =
+                createBatteryDiffEntry(
+                        ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY,
+                        /* uid= */ 0,
+                        /* isHidden= */ false);
+        assertThat(entry.isSystemEntry()).isTrue();
+        assertThat(entry.isUninstalledEntry()).isFalse();
+    }
+
+    @Test
+    public void testIsUninstalledEntry_installedApp_returnFalse() throws Exception {
+        final ContentValues values =
+                getContentValuesWithType(ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
+        values.put(BatteryHistEntry.KEY_UID, UID);
+        values.put(BatteryHistEntry.KEY_PACKAGE_NAME, PACKAGE_NAME);
+        final BatteryDiffEntry entry = createBatteryDiffEntry(10, new BatteryHistEntry(values));
+
+        assertThat(entry.isSystemEntry()).isFalse();
+        assertThat(entry.isUninstalledEntry()).isFalse();
+    }
+
+    @Test
+    public void testIsUninstalledEntry_uninstalledApp_returnTrue() throws Exception {
+        doReturn(BatteryUtils.UID_NULL)
+                .when(mMockPackageManager)
+                .getPackageUid(PACKAGE_NAME, PackageManager.GET_META_DATA);
+        final ContentValues values =
+                getContentValuesWithType(ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
+        values.put(BatteryHistEntry.KEY_UID, UNINSTALLED_UID);
+        values.put(BatteryHistEntry.KEY_PACKAGE_NAME, UNINSTALLED_PACKAGE_NAME);
+        final BatteryDiffEntry entry = createBatteryDiffEntry(10, new BatteryHistEntry(values));
+
+        assertThat(entry.isSystemEntry()).isFalse();
+        assertThat(entry.isUninstalledEntry()).isTrue();
+    }
+
+    @Test
     public void testUpdateRestrictionFlagState_updateFlagAsExpected() throws Exception {
         final String expectedAppLabel = "fake app label";
         final String fakePackageName = "com.fake.google.com";
-        final ContentValues values = getContentValuesWithType(
-                ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
+        final ContentValues values =
+                getContentValuesWithType(ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
         values.put(BatteryHistEntry.KEY_UID, /*invalid uid*/ 10001);
         values.put(BatteryHistEntry.KEY_PACKAGE_NAME, fakePackageName);
-        final BatteryDiffEntry entry =
-                createBatteryDiffEntry(10, new BatteryHistEntry(values));
+        final BatteryDiffEntry entry = createBatteryDiffEntry(10, new BatteryHistEntry(values));
 
         entry.updateRestrictionFlagState();
         // Sets false if the app entry cannot be found.
         assertThat(entry.mValidForRestriction).isFalse();
 
-        doReturn(BatteryUtils.UID_NULL).when(mMockPackageManager).getPackageUid(
-                entry.getPackageName(), PackageManager.GET_META_DATA);
+        doReturn(BatteryUtils.UID_NULL)
+                .when(mMockPackageManager)
+                .getPackageUid(entry.getPackageName(), PackageManager.GET_META_DATA);
         entry.updateRestrictionFlagState();
         // Sets false if the app is invalid package name.
         assertThat(entry.mValidForRestriction).isFalse();
 
-        doReturn(1000).when(mMockPackageManager).getPackageUid(
-                entry.getPackageName(), PackageManager.GET_META_DATA);
+        doReturn(1000)
+                .when(mMockPackageManager)
+                .getPackageUid(entry.getPackageName(), PackageManager.GET_META_DATA);
         entry.updateRestrictionFlagState();
         // Sets false if the app PackageInfo cannot be found.
         assertThat(entry.mValidForRestriction).isFalse();
 
-        doReturn(mMockPackageInfo).when(mMockPackageManager).getPackageInfo(
-                eq(entry.getPackageName()), anyInt());
+        doReturn(mMockPackageInfo)
+                .when(mMockPackageManager)
+                .getPackageInfo(eq(entry.getPackageName()), anyInt());
         entry.updateRestrictionFlagState();
         // Sets true if package is valid and PackageInfo can be found.
         assertThat(entry.mValidForRestriction).isTrue();
@@ -454,11 +496,10 @@
     @Test
     public void testGetPackageName_returnExpectedResult() {
         final String expectedPackageName = "com.fake.google.com";
-        final ContentValues values = getContentValuesWithType(
-                ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
+        final ContentValues values =
+                getContentValuesWithType(ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
         values.put(BatteryHistEntry.KEY_PACKAGE_NAME, expectedPackageName);
-        final BatteryDiffEntry entry =
-                createBatteryDiffEntry(10, new BatteryHistEntry(values));
+        final BatteryDiffEntry entry = createBatteryDiffEntry(10, new BatteryHistEntry(values));
 
         assertThat(entry.getPackageName()).isEqualTo(expectedPackageName);
     }
@@ -466,59 +507,56 @@
     @Test
     public void testGetPackageName_withProcessName_returnExpectedResult() {
         final String expectedPackageName = "com.fake.google.com";
-        final ContentValues values = getContentValuesWithType(
-                ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
-        values.put(
-                BatteryHistEntry.KEY_PACKAGE_NAME,
-                expectedPackageName + ":privileged_process0");
-        final BatteryDiffEntry entry =
-                createBatteryDiffEntry(10, new BatteryHistEntry(values));
+        final ContentValues values =
+                getContentValuesWithType(ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
+        values.put(BatteryHistEntry.KEY_PACKAGE_NAME, expectedPackageName + ":privileged_process0");
+        final BatteryDiffEntry entry = createBatteryDiffEntry(10, new BatteryHistEntry(values));
 
         assertThat(entry.getPackageName()).isEqualTo(expectedPackageName);
     }
 
-    private BatteryDiffEntry createBatteryDiffEntry(
-            int consumerType, long uid, boolean isHidden) {
+    private BatteryDiffEntry createBatteryDiffEntry(int consumerType, long uid, boolean isHidden) {
         return new BatteryDiffEntry(
                 mContext,
-                /*uid=*/ uid,
-                /*userId=*/ 0,
-                /*key=*/ "key",
-                /*isHidden=*/ isHidden,
-                /*componentId=*/ -1,
-                /*legacyPackageName=*/ null,
-                /*legacyLabel=*/ null,
+                /* uid= */ uid,
+                /* userId= */ 0,
+                /* key= */ "key",
+                /* isHidden= */ isHidden,
+                /* componentId= */ -1,
+                /* legacyPackageName= */ null,
+                /* legacyLabel= */ null,
                 /*consumerType*/ consumerType,
-                /*foregroundUsageTimeInMs=*/ 0,
-                /*backgroundUsageTimeInMs=*/ 0,
-                /*screenOnTimeInMs=*/ 0,
-                /*consumePower=*/ 0,
-                /*foregroundUsageConsumePower=*/ 0,
-                /*foregroundServiceUsageConsumePower=*/ 0,
-                /*backgroundUsageConsumePower=*/ 0,
-                /*cachedUsageConsumePower=*/ 0);
+                /* foregroundUsageTimeInMs= */ 0,
+                /* backgroundUsageTimeInMs= */ 0,
+                /* screenOnTimeInMs= */ 0,
+                /* consumePower= */ 0,
+                /* foregroundUsageConsumePower= */ 0,
+                /* foregroundServiceUsageConsumePower= */ 0,
+                /* backgroundUsageConsumePower= */ 0,
+                /* cachedUsageConsumePower= */ 0);
     }
 
     private BatteryDiffEntry createBatteryDiffEntry(
             double consumePower, BatteryHistEntry batteryHistEntry) {
-        final BatteryDiffEntry entry = new BatteryDiffEntry(
-                mContext,
-                batteryHistEntry.mUid,
-                batteryHistEntry.mUserId,
-                batteryHistEntry.getKey(),
-                batteryHistEntry.mIsHidden,
-                batteryHistEntry.mDrainType,
-                batteryHistEntry.mPackageName,
-                batteryHistEntry.mAppLabel,
-                batteryHistEntry.mConsumerType,
-                /*foregroundUsageTimeInMs=*/ 0,
-                /*backgroundUsageTimeInMs=*/ 0,
-                /*screenOnTimeInMs=*/ 0,
-                consumePower,
-                /*foregroundUsageConsumePower=*/ 0,
-                /*foregroundServiceUsageConsumePower=*/ 0,
-                /*backgroundUsageConsumePower=*/ 0,
-                /*cachedUsageConsumePower=*/ 0);
+        final BatteryDiffEntry entry =
+                new BatteryDiffEntry(
+                        mContext,
+                        batteryHistEntry.mUid,
+                        batteryHistEntry.mUserId,
+                        batteryHistEntry.getKey(),
+                        batteryHistEntry.mIsHidden,
+                        batteryHistEntry.mDrainType,
+                        batteryHistEntry.mPackageName,
+                        batteryHistEntry.mAppLabel,
+                        batteryHistEntry.mConsumerType,
+                        /* foregroundUsageTimeInMs= */ 0,
+                        /* backgroundUsageTimeInMs= */ 0,
+                        /* screenOnTimeInMs= */ 0,
+                        consumePower,
+                        /* foregroundUsageConsumePower= */ 0,
+                        /* foregroundServiceUsageConsumePower= */ 0,
+                        /* backgroundUsageConsumePower= */ 0,
+                        /* cachedUsageConsumePower= */ 0);
         entry.setTotalConsumePower(100.0);
         return entry;
     }
@@ -530,15 +568,14 @@
     }
 
     private BatteryDiffEntry createBatteryDiffEntry(Drawable drawable) throws Exception {
-        final ContentValues values = getContentValuesWithType(
-                ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
+        final ContentValues values =
+                getContentValuesWithType(ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
         values.put(BatteryHistEntry.KEY_UID, 1001);
         values.put(BatteryHistEntry.KEY_PACKAGE_NAME, "com.a.b.c");
         final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values);
         doReturn(drawable).when(mMockPackageManager).getDefaultActivityIcon();
         doReturn(null).when(mMockPackageManager).getApplicationInfo("com.a.b.c", 0);
-        doReturn(new String[]{"com.a.b.c"}).when(mMockPackageManager)
-                .getPackagesForUid(1001);
+        doReturn(new String[] {"com.a.b.c"}).when(mMockPackageManager).getPackagesForUid(1001);
         return createBatteryDiffEntry(10, batteryHistEntry);
     }
 
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiverTest.java
index f00c049..c4cb89e 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiverTest.java
@@ -30,6 +30,7 @@
 import android.os.SystemClock;
 import android.text.format.DateUtils;
 
+import com.android.settings.testutils.BatteryTestUtils;
 import com.android.settings.testutils.FakeFeatureFactory;
 
 import org.junit.Before;
@@ -68,6 +69,16 @@
     }
 
     @Test
+    public void onReceive_workProfile_doNothing() {
+        BatteryTestUtils.setWorkProfile(mContext);
+
+        mBatteryUsageBroadcastReceiver.onReceive(mContext,
+                new Intent(BatteryUsageBroadcastReceiver.ACTION_BATTERY_UNPLUGGING));
+
+        assertThat(mBatteryUsageBroadcastReceiver.mFetchBatteryUsageData).isFalse();
+    }
+
+    @Test
     public void onReceive_aospNotFullCharged_notFetchUsageData() {
         when(mFakeFeatureFactory.powerUsageFeatureProvider.getFullChargeIntentAction())
                 .thenReturn(Intent.ACTION_BATTERY_LEVEL_CHANGED);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
index cd594d3..c648cde 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
@@ -32,6 +32,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.database.MatrixCursor;
+import android.os.BatteryConsumer;
 import android.os.BatteryManager;
 import android.os.BatteryUsageStats;
 import android.os.LocaleList;
@@ -80,8 +81,7 @@
     }
 
     @Test
-    public void convertBatteryEntryToContentValues_returnsExpectedContentValues() {
-        final int expectedType = 3;
+    public void convertBatteryEntryToContentValues_appEntry_returnsExpectedContentValues() {
         when(mMockBatteryEntry.getUid()).thenReturn(1001);
         when(mMockBatteryEntry.getLabel()).thenReturn("Settings");
         when(mMockBatteryEntry.getDefaultPackageName())
@@ -96,9 +96,9 @@
         mMockBatteryEntry.mPercent = 0.3;
         when(mMockBatteryEntry.getTimeInForegroundMs()).thenReturn(1234L);
         when(mMockBatteryEntry.getTimeInBackgroundMs()).thenReturn(5689L);
-        when(mMockBatteryEntry.getPowerComponentId()).thenReturn(expectedType);
+        when(mMockBatteryEntry.getPowerComponentId()).thenReturn(-1);
         when(mMockBatteryEntry.getConsumerType())
-                .thenReturn(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
+                .thenReturn(ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
 
         final ContentValues values =
                 ConvertUtils.convertBatteryEntryToContentValues(
@@ -122,7 +122,7 @@
                 .isEqualTo("com.google.android.settings.battery");
         assertThat(values.getAsLong(BatteryHistEntry.KEY_TIMESTAMP)).isEqualTo(10001L);
         assertThat(values.getAsInteger(BatteryHistEntry.KEY_CONSUMER_TYPE))
-                .isEqualTo(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
+                .isEqualTo(ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
         assertThat(values.getAsBoolean(BatteryHistEntry.KEY_IS_FULL_CHARGE_CYCLE_START)).isTrue();
         assertThat(batteryInformation.getAppLabel()).isEqualTo("Settings");
         assertThat(batteryInformation.getIsHidden()).isTrue();
@@ -137,7 +137,71 @@
         assertThat(batteryInformation.getPercentOfTotal()).isEqualTo(0.3);
         assertThat(batteryInformation.getForegroundUsageTimeInMs()).isEqualTo(1234L);
         assertThat(batteryInformation.getBackgroundUsageTimeInMs()).isEqualTo(5689L);
-        assertThat(batteryInformation.getDrainType()).isEqualTo(expectedType);
+        assertThat(batteryInformation.getDrainType()).isEqualTo(-1);
+        assertThat(deviceBatteryState.getBatteryLevel()).isEqualTo(12);
+        assertThat(deviceBatteryState.getBatteryStatus())
+                .isEqualTo(BatteryManager.BATTERY_STATUS_FULL);
+        assertThat(deviceBatteryState.getBatteryHealth())
+                .isEqualTo(BatteryManager.BATTERY_HEALTH_COLD);
+    }
+
+    @Test
+    public void convertBatteryEntryToContentValues_systemEntry_returnsExpectedContentValues() {
+        when(mMockBatteryEntry.getUid()).thenReturn(-1);
+        when(mMockBatteryEntry.getLabel()).thenReturn("CPU");
+        when(mMockBatteryEntry.getDefaultPackageName()).thenReturn(null);
+        when(mMockBatteryEntry.getPowerComponentId()).thenReturn(
+                BatteryConsumer.POWER_COMPONENT_CPU);
+        when(mBatteryUsageStats.getConsumedPower()).thenReturn(5.1);
+        when(mMockBatteryEntry.getConsumedPower()).thenReturn(1.1);
+        when(mMockBatteryEntry.getConsumedPowerInForeground()).thenReturn(1.2);
+        when(mMockBatteryEntry.getConsumedPowerInForegroundService()).thenReturn(1.3);
+        when(mMockBatteryEntry.getConsumedPowerInBackground()).thenReturn(1.4);
+        when(mMockBatteryEntry.getConsumedPowerInCached()).thenReturn(1.5);
+        mMockBatteryEntry.mPercent = 0.3;
+        when(mMockBatteryEntry.getTimeInForegroundMs()).thenReturn(1234L);
+        when(mMockBatteryEntry.getTimeInBackgroundMs()).thenReturn(5689L);
+        when(mMockBatteryEntry.getConsumerType())
+                .thenReturn(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
+
+        final ContentValues values =
+                ConvertUtils.convertBatteryEntryToContentValues(
+                        mMockBatteryEntry,
+                        mBatteryUsageStats,
+                        /*batteryLevel=*/ 12,
+                        /*batteryStatus=*/ BatteryManager.BATTERY_STATUS_FULL,
+                        /*batteryHealth=*/ BatteryManager.BATTERY_HEALTH_COLD,
+                        /*bootTimestamp=*/ 101L,
+                        /*timestamp=*/ 10001L,
+                        /*isFullChargeStart=*/ true);
+        final BatteryInformation batteryInformation =
+                ConvertUtils.getBatteryInformation(
+                        values, BatteryHistEntry.KEY_BATTERY_INFORMATION);
+        final DeviceBatteryState deviceBatteryState = batteryInformation.getDeviceBatteryState();
+
+        assertThat(values.getAsLong(BatteryHistEntry.KEY_UID)).isEqualTo(-1);
+        assertThat(values.getAsLong(BatteryHistEntry.KEY_USER_ID))
+                .isEqualTo(UserHandle.getUserId(1001));
+        assertThat(values.getAsString(BatteryHistEntry.KEY_PACKAGE_NAME)).isEqualTo("");
+        assertThat(values.getAsLong(BatteryHistEntry.KEY_TIMESTAMP)).isEqualTo(10001L);
+        assertThat(values.getAsInteger(BatteryHistEntry.KEY_CONSUMER_TYPE))
+                .isEqualTo(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
+        assertThat(values.getAsBoolean(BatteryHistEntry.KEY_IS_FULL_CHARGE_CYCLE_START)).isTrue();
+        assertThat(batteryInformation.getAppLabel()).isEqualTo("CPU");
+        assertThat(batteryInformation.getIsHidden()).isFalse();
+        assertThat(batteryInformation.getBootTimestamp()).isEqualTo(101L);
+        assertThat(batteryInformation.getZoneId()).isEqualTo(TimeZone.getDefault().getID());
+        assertThat(batteryInformation.getTotalPower()).isEqualTo(5.1);
+        assertThat(batteryInformation.getConsumePower()).isEqualTo(1.1);
+        assertThat(batteryInformation.getForegroundUsageConsumePower()).isEqualTo(1.2);
+        assertThat(batteryInformation.getForegroundServiceUsageConsumePower()).isEqualTo(1.3);
+        assertThat(batteryInformation.getBackgroundUsageConsumePower()).isEqualTo(1.4);
+        assertThat(batteryInformation.getCachedUsageConsumePower()).isEqualTo(1.5);
+        assertThat(batteryInformation.getPercentOfTotal()).isEqualTo(0.3);
+        assertThat(batteryInformation.getForegroundUsageTimeInMs()).isEqualTo(1234L);
+        assertThat(batteryInformation.getBackgroundUsageTimeInMs()).isEqualTo(5689L);
+        assertThat(batteryInformation.getDrainType()).isEqualTo(
+                BatteryConsumer.POWER_COMPONENT_CPU);
         assertThat(deviceBatteryState.getBatteryLevel()).isEqualTo(12);
         assertThat(deviceBatteryState.getBatteryStatus())
                 .isEqualTo(BatteryManager.BATTERY_STATUS_FULL);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java
index c4394f7..636a7a3 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java
@@ -1397,12 +1397,14 @@
     }
 
     @Test
-    public void generateBatteryDiffData_emptyBatteryEntryList_returnNull() {
-        assertThat(DataProcessor.generateBatteryDiffData(mContext,
+    public void generateBatteryDiffData_emptyBatteryEntryList_returnEmptyBatteryDiffData() {
+        final BatteryDiffData batteryDiffData = DataProcessor.generateBatteryDiffData(mContext,
                 System.currentTimeMillis(),
                 DataProcessor.convertToBatteryHistEntry(null, mBatteryUsageStats),
                 /* systemAppsPackageNames= */ Set.of(),
-                /* systemAppsUids= */ Set.of())).isNull();
+                /* systemAppsUids= */ Set.of());
+        assertThat(batteryDiffData.getAppDiffEntryList()).isEmpty();
+        assertThat(batteryDiffData.getSystemDiffEntryList()).isEmpty();
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/notification/PoliteNotifVibrateUnlockedToggleControllerTest.java b/tests/robotests/src/com/android/settings/notification/PoliteNotifVibrateUnlockedToggleControllerTest.java
new file mode 100644
index 0000000..f92ed25
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/PoliteNotifVibrateUnlockedToggleControllerTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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.notification;
+
+import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
+import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
+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.os.Vibrator;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+
+import com.android.server.notification.Flags;
+import com.android.settings.core.BasePreferenceController;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class PoliteNotifVibrateUnlockedToggleControllerTest {
+
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+    private static final String PREFERENCE_KEY = "preference_key";
+
+    private PoliteNotifVibrateUnlockedToggleController mController;
+    private Context mContext;
+    @Mock
+    private Vibrator mVibrator;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        mController = new PoliteNotifVibrateUnlockedToggleController(mContext, PREFERENCE_KEY);
+        when(mContext.getSystemService(Vibrator.class)).thenReturn(mVibrator);
+    }
+
+    @Test
+    public void isAvailable_flagEnabled_vibrationSupported_shouldReturnTrue() {
+        // TODO: b/291907312 - remove feature flags
+        mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+        when(mVibrator.hasVibrator()).thenReturn(true);
+        assertThat(mController.isAvailable()).isTrue();
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(
+                BasePreferenceController.AVAILABLE);
+    }
+
+    @Test
+    public void isAvailable_flagEnabled_vibrationNotSupported_shouldReturnFalse() {
+        // TODO: b/291907312 - remove feature flags
+        mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+        when(mVibrator.hasVibrator()).thenReturn(false);
+        assertThat(mController.isAvailable()).isFalse();
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(
+                BasePreferenceController.UNSUPPORTED_ON_DEVICE);
+    }
+
+    @Test
+    public void isAvailable_flagDisabled_shouldReturnFalse() {
+        // TODO: b/291907312 - remove feature flags
+        mSetFlagsRule.disableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+        assertThat(mController.isAvailable()).isFalse();
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(
+                BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
+    }
+
+    @Test
+    public void isChecked_vibrateEnabled_shouldReturnTrue() {
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, ON);
+        assertThat(mController.isChecked()).isTrue();
+    }
+
+    @Test
+    public void isChecked_vibrateDisabled_shouldReturnFalse() {
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, OFF);
+        assertThat(mController.isChecked()).isFalse();
+    }
+
+    @Test
+    public void setChecked_setTrue_shouldEnableVibrateSetting() {
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, OFF);
+        mController.setChecked(true);
+        assertThat(Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, OFF)).isEqualTo(ON);
+    }
+
+    @Test
+    public void setChecked_setFalse_shouldDisableVibrateSetting() {
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, ON);
+        mController.setChecked(false);
+        assertThat(Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, ON)).isEqualTo(OFF);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/PoliteNotifWorkProfileToggleControllerTest.java b/tests/robotests/src/com/android/settings/notification/PoliteNotifWorkProfileToggleControllerTest.java
new file mode 100644
index 0000000..5cda1ea
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/PoliteNotifWorkProfileToggleControllerTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
+import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+
+import com.android.server.notification.Flags;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.testutils.shadow.ShadowSystemSettings;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(RobolectricTestRunner.class)
+public class PoliteNotifWorkProfileToggleControllerTest {
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+    private static final String PREFERENCE_KEY = "preference_key";
+
+    private Context mContext;
+    PoliteNotifWorkProfileToggleController mController;
+    @Mock
+    private AudioHelper mAudioHelper;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        when(mAudioHelper.getManagedProfileId(any())).thenReturn(UserHandle.MIN_SECONDARY_USER_ID);
+        mController = new PoliteNotifWorkProfileToggleController(mContext, PREFERENCE_KEY,
+                mAudioHelper);
+    }
+
+    @Test
+    public void isAvailable_flagEnabled_workProfileExists_shouldReturnTrue() {
+        // TODO: b/291907312 - remove feature flags
+        mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+        assertThat(mController.isAvailable()).isTrue();
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(
+                BasePreferenceController.AVAILABLE);
+    }
+
+    @Test
+    public void isAvailable_flagEnabled_workProfileMissing_shouldReturnFalse() {
+        // TODO: b/291907312 - remove feature flags
+        mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+        when(mAudioHelper.getManagedProfileId(any())).thenReturn(UserHandle.USER_NULL);
+        mController = new PoliteNotifWorkProfileToggleController(mContext, PREFERENCE_KEY,
+                mAudioHelper);
+        assertThat(mController.isAvailable()).isFalse();
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(
+                BasePreferenceController.DISABLED_FOR_USER);
+    }
+
+    @Test
+    public void isAvailable_flagDisabled_shouldReturnFalse() {
+        // TODO: b/291907312 - remove feature flags
+        mSetFlagsRule.disableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+        assertThat(mController.isAvailable()).isFalse();
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(
+                BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
+    }
+
+    @Test
+    @Config(shadows = ShadowSystemSettings.class)
+    public void isChecked_enabledForWorkProfile_shouldReturnTrue() {
+        Settings.System.putIntForUser(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_ENABLED, ON,
+                UserHandle.MIN_SECONDARY_USER_ID);
+        assertThat(mController.isChecked()).isTrue();
+    }
+
+    @Test
+    @Config(shadows = ShadowSystemSettings.class)
+    public void isChecked_disabledForWorkProfile_shouldReturnFalse() {
+        Settings.System.putIntForUser(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_ENABLED, OFF,
+                UserHandle.MIN_SECONDARY_USER_ID);
+        assertThat(mController.isChecked()).isFalse();
+    }
+
+    @Test
+    @Config(shadows = ShadowSystemSettings.class)
+    public void setChecked_setTrue_shouldEnablePoliteNotifForWorkProfile() {
+        Settings.System.putIntForUser(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_ENABLED, OFF,
+                UserHandle.MIN_SECONDARY_USER_ID);
+        mController.setChecked(true);
+        assertThat(Settings.System.getIntForUser(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_ENABLED, ON,
+                UserHandle.MIN_SECONDARY_USER_ID)).isEqualTo(ON);
+    }
+
+    @Test
+    @Config(shadows = ShadowSystemSettings.class)
+    public void setChecked_setFalse_shouldDisablePoliteNotifForWorkProfile() {
+        Settings.System.putIntForUser(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_ENABLED, ON,
+                UserHandle.MIN_SECONDARY_USER_ID);
+        mController.setChecked(false);
+        assertThat(Settings.System.getIntForUser(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_ENABLED, ON,
+                UserHandle.MIN_SECONDARY_USER_ID)).isEqualTo(OFF);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/PoliteNotificationFilterControllerTest.java b/tests/robotests/src/com/android/settings/notification/PoliteNotificationFilterControllerTest.java
new file mode 100644
index 0000000..646e927
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/PoliteNotificationFilterControllerTest.java
@@ -0,0 +1,165 @@
+/*
+ * 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.notification;
+
+import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
+import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+
+import androidx.preference.ListPreference;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.server.notification.Flags;
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class PoliteNotificationFilterControllerTest {
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+    private static final String PREFERENCE_KEY = "preference_key";
+    private static final int POLITE_NOTIFICATIONS_ALL = 0;
+    private static final int POLITE_NOTIFICATIONS_CONVERSATIONS = 1;
+    private static final int POLITE_NOTIFICATIONS_DISABLED = 2;
+
+    @Mock
+    private PreferenceScreen mScreen;
+
+    private PoliteNotificationFilterController mController;
+    private Preference mPreference;
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+
+        mController = new PoliteNotificationFilterController(mContext, PREFERENCE_KEY);
+        mPreference = new Preference(mContext);
+        mPreference.setKey(mController.getPreferenceKey());
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
+    }
+
+    @Test
+    public void isAvailable_flagEnabled_shouldReturnTrue() {
+        // TODO: b/291907312 - remove feature flags
+        mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+        assertThat(mController.isAvailable()).isTrue();
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(
+                BasePreferenceController.AVAILABLE);
+    }
+
+    @Test
+    public void isAvailable_flagDisabled_shouldReturnFalse() {
+        // TODO: b/291907312 - remove feature flags
+        mSetFlagsRule.disableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+        assertThat(mController.isAvailable()).isFalse();
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(
+                BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
+    }
+
+    @Test
+    public void updateState_politeNotificationDisabled() {
+        final ListPreference preference = mock(ListPreference.class);
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_ENABLED, OFF);
+        mController.updateState(preference);
+
+        verify(preference).setValue(Integer.toString(POLITE_NOTIFICATIONS_DISABLED));
+        assertThat(mController.getSummary().toString()).isEqualTo(
+                mContext.getString(R.string.notification_polite_disabled_summary));
+    }
+
+    @Test
+    public void updateState_politeNotificationEnabled_applyAllApps() {
+        final ListPreference preference = mock(ListPreference.class);
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_ENABLED, ON);
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_ALL, ON);
+        mController.updateState(preference);
+
+        verify(preference).setValue(Integer.toString(POLITE_NOTIFICATIONS_ALL));
+        assertThat(mController.getSummary().toString()).isEqualTo(
+                mContext.getString(R.string.notification_polite_all_apps_summary));
+    }
+
+    @Test
+    public void updateState_politeNotificationEnabled_applyOnlyConversations() {
+        final ListPreference preference = mock(ListPreference.class);
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_ENABLED, ON);
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_ALL, OFF);
+        mController.updateState(preference);
+
+        verify(preference).setValue(Integer.toString(POLITE_NOTIFICATIONS_CONVERSATIONS));
+        assertThat(mController.getSummary().toString()).isEqualTo(
+                mContext.getString(R.string.notification_polite_conversations_summary));
+    }
+
+    @Test
+    public void onPreferenceChanged_firstItemSelected_shouldEnableForAll() {
+        mController.displayPreference(mScreen);
+        mController.onPreferenceChange(mPreference, "0");
+
+        assertThat(Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_ENABLED, OFF)).isEqualTo(ON);
+        assertThat(Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_ALL, OFF)).isEqualTo(ON);
+    }
+
+    @Test
+    public void onPreferenceChanged_secondItemSelected_shouldEnableForConversationsOnly() {
+        mController.displayPreference(mScreen);
+        mController.onPreferenceChange(mPreference, "1");
+
+        assertThat(Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_ENABLED, OFF)).isEqualTo(ON);
+        assertThat(Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_ALL, OFF)).isEqualTo(OFF);
+    }
+
+    @Test
+    public void onPreferenceChanged_thirdItemSelected_shouldDisable() {
+        mController.displayPreference(mScreen);
+        mController.onPreferenceChange(mPreference, "2");
+
+        assertThat(Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_ENABLED, OFF)).isEqualTo(OFF);
+    }
+
+}
diff --git a/tests/robotests/src/com/android/settings/notification/PoliteNotificationsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/PoliteNotificationsPreferenceControllerTest.java
new file mode 100644
index 0000000..f6c5e16
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/PoliteNotificationsPreferenceControllerTest.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.notification;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import com.android.server.notification.Flags;
+import com.android.settings.core.BasePreferenceController;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class PoliteNotificationsPreferenceControllerTest {
+
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+    private static final String PREFERENCE_KEY = "preference_key";
+
+    private PoliteNotificationsPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mController = new PoliteNotificationsPreferenceController(RuntimeEnvironment.application,
+                PREFERENCE_KEY);
+    }
+
+    @Test
+    public void isAvailable_flagEnabled_shouldReturnTrue() {
+        // TODO: b/291907312 - remove feature flags
+        mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+        assertThat(mController.isAvailable()).isTrue();
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(
+                BasePreferenceController.AVAILABLE);
+    }
+
+    @Test
+    public void isAvailable_flagDisabled_shouldReturnFalse() {
+        // TODO: b/291907312 - remove feature flags
+        mSetFlagsRule.disableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+        assertThat(mController.isAvailable()).isFalse();
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(
+                BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
+    }
+
+}
diff --git a/tests/robotests/src/com/android/settings/tts/TtsEnginePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/tts/TtsEnginePreferenceFragmentTest.java
index 00b8e86..1cb188c 100644
--- a/tests/robotests/src/com/android/settings/tts/TtsEnginePreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/tts/TtsEnginePreferenceFragmentTest.java
@@ -24,6 +24,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
 
 import android.content.Context;
 import android.content.Intent;
@@ -31,6 +32,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.os.Bundle;
+import android.os.Looper;
 import android.speech.tts.TextToSpeech;
 import android.speech.tts.TtsEngines;
 
@@ -43,6 +45,7 @@
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implements;
 import org.robolectric.shadow.api.Shadow;
 import org.robolectric.shadows.ShadowPackageManager;
 import org.robolectric.shadows.androidx.fragment.FragmentController;
@@ -50,12 +53,15 @@
 @RunWith(RobolectricTestRunner.class)
 public class TtsEnginePreferenceFragmentTest {
 
+    @Implements(TextToSpeech.class)
+    public static class NoOpShadowTextToSpeech {}
+
     private Context mContext;
     private TtsEnginePreferenceFragment mTtsEnginePreferenceFragment;
 
     @Before
     public void setUp() {
-        mContext = RuntimeEnvironment.application;
+        mContext = RuntimeEnvironment.systemContext;
 
         final ResolveInfo info = new ResolveInfo();
         final ServiceInfo serviceInfo = spy(new ServiceInfo());
@@ -76,17 +82,19 @@
     }
 
     @Test
+    @Config(shadows = {NoOpShadowTextToSpeech.class})
     public void getCandidates_AddEngines_returnCorrectEngines() {
         mTtsEnginePreferenceFragment = FragmentController.of(new TtsEnginePreferenceFragment(),
                 new Bundle())
                 .create()
                 .get();
+        shadowOf(Looper.getMainLooper()).idle();
 
         assertThat(mTtsEnginePreferenceFragment.getCandidates().size()).isEqualTo(1);
     }
 
     @Test
-    @Config(shadows = {ShadowTtsEngines.class})
+    @Config(shadows = {ShadowTtsEngines.class, NoOpShadowTextToSpeech.class})
     public void getDefaultKey_validKey_returnCorrectKey() {
         final String TEST_ENGINE = "test_engine";
         final TtsEngines engine = mock(TtsEngines.class);
@@ -95,13 +103,14 @@
                 new Bundle())
                 .create()
                 .get();
+        shadowOf(Looper.getMainLooper()).idle();
         when(engine.getDefaultEngine()).thenReturn(TEST_ENGINE);
 
         assertThat(mTtsEnginePreferenceFragment.getDefaultKey()).isEqualTo(TEST_ENGINE);
     }
 
     @Test
-    @Config(shadows = {ShadowTtsEngines.class})
+    @Config(shadows = {ShadowTtsEngines.class, NoOpShadowTextToSpeech.class})
     public void setDefaultKey_validKey_callingTtsEngineFunction() {
         final TtsEngines engine = mock(TtsEngines.class);
         ShadowTtsEngines.setInstance(engine);
@@ -109,6 +118,7 @@
                 new Bundle())
                 .create()
                 .get();
+        shadowOf(Looper.getMainLooper()).idle();
 
         mTtsEnginePreferenceFragment.setDefaultKey(mContext.getPackageName());
 
@@ -116,14 +126,17 @@
     }
 
     @Test
+    @Config(shadows = {NoOpShadowTextToSpeech.class})
     public void setDefaultKey_validKey_updateCheckedState() {
-        mTtsEnginePreferenceFragment = spy(FragmentController.of(new TtsEnginePreferenceFragment(),
+        mTtsEnginePreferenceFragment = FragmentController.of(new TtsEnginePreferenceFragment(),
                 new Bundle())
                 .create()
-                .get());
+                .get();
+        shadowOf(Looper.getMainLooper()).idle();
+        TtsEnginePreferenceFragment fragmentSpy = spy(mTtsEnginePreferenceFragment);
 
-        mTtsEnginePreferenceFragment.setDefaultKey(mContext.getPackageName());
+        fragmentSpy.setDefaultKey(mContext.getPackageName());
 
-        verify(mTtsEnginePreferenceFragment).updateCheckedState(mContext.getPackageName());
+        verify(fragmentSpy).updateCheckedState(mContext.getPackageName());
     }
 }
diff --git a/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowUserManager.java b/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowUserManager.java
index c8d2866..ce6dc6a 100644
--- a/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowUserManager.java
+++ b/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowUserManager.java
@@ -17,6 +17,7 @@
 package com.android.settings.testutils.shadow;
 
 import static android.os.Build.VERSION_CODES.LOLLIPOP;
+import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE;
 
 import android.annotation.UserIdInt;
 import android.content.pm.UserInfo;
@@ -223,6 +224,10 @@
         mManagedProfiles.addAll(profileIds);
     }
 
+    public void setPrivateProfile(int id, String name, int flags) {
+        mUserProfileInfos.add(new UserInfo(id, name, null, flags, USER_TYPE_PROFILE_PRIVATE));
+    }
+
     public void setUserSwitcherEnabled(boolean userSwitchEnabled) {
         mUserSwitchEnabled = userSwitchEnabled;
     }
diff --git a/tests/spa_unit/src/com/android/settings/datausage/ChartDataUsagePreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/datausage/ChartDataUsagePreferenceControllerTest.kt
index e0eb789..ae09ef9 100644
--- a/tests/spa_unit/src/com/android/settings/datausage/ChartDataUsagePreferenceControllerTest.kt
+++ b/tests/spa_unit/src/com/android/settings/datausage/ChartDataUsagePreferenceControllerTest.kt
@@ -43,7 +43,7 @@
         override fun getCycles() = emptyList<Range<Long>>()
         override fun getPolicy() = null
 
-        override suspend fun querySummary(startTime: Long, endTime: Long) = when {
+        override suspend fun queryChartData(startTime: Long, endTime: Long) = when {
             startTime == START_TIME && endTime == END_TIME -> CycleChartDate
             else -> null
         }
diff --git a/tests/spa_unit/src/com/android/settings/datausage/DataUsageListHeaderControllerTest.kt b/tests/spa_unit/src/com/android/settings/datausage/DataUsageListHeaderControllerTest.kt
index 581f7ba..3580e68 100644
--- a/tests/spa_unit/src/com/android/settings/datausage/DataUsageListHeaderControllerTest.kt
+++ b/tests/spa_unit/src/com/android/settings/datausage/DataUsageListHeaderControllerTest.kt
@@ -51,7 +51,7 @@
         override suspend fun loadCycles() = emptyList<NetworkUsageData>()
         override fun getCycles() = emptyList<Range<Long>>()
         override fun getPolicy() = null
-        override suspend fun querySummary(startTime: Long, endTime: Long) = null
+        override suspend fun queryChartData(startTime: Long, endTime: Long) = null
     }
 
     private val header =
diff --git a/tests/spa_unit/src/com/android/settings/datausage/lib/NetworkCycleDataRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/datausage/lib/NetworkCycleDataRepositoryTest.kt
index feb195d..5678503 100644
--- a/tests/spa_unit/src/com/android/settings/datausage/lib/NetworkCycleDataRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/datausage/lib/NetworkCycleDataRepositoryTest.kt
@@ -98,7 +98,7 @@
 
     @Test
     fun querySummary() = runTest {
-        val summary = repository.querySummary(CYCLE3_START_TIME, CYCLE4_END_TIME)
+        val summary = repository.queryChartData(CYCLE3_START_TIME, CYCLE4_END_TIME)
 
         assertThat(summary).isEqualTo(
             NetworkCycleChartData(
diff --git a/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt
index 7cd0f5d..20d67fc 100644
--- a/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt
@@ -17,7 +17,7 @@
 package com.android.settings.network.apn
 
 import android.content.Context
-import androidx.compose.runtime.MutableState
+import android.net.Uri
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.test.assertIsDisplayed
@@ -41,6 +41,7 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
 
 @RunWith(AndroidJUnit4::class)
 class ApnEditPageProviderTest {
@@ -58,16 +59,18 @@
         context.resources.getStringArray(R.array.apn_protocol_entries).toList()
     private val networkType = context.resources.getString(R.string.network_type)
     private val passwordTitle = context.resources.getString(R.string.apn_password)
-    private val apnData = mutableStateOf(
-        ApnData(
-            name = apnName,
-            mmsc = mmsc,
-            mmsProxy = mmsProxy,
-            apnType = apnType,
-            apnRoaming = apnProtocolOptions.indexOf(apnRoaming),
-            apnEnable = true
-        )
+    private val apnInit = ApnData(
+        name = apnName,
+        mmsc = mmsc,
+        mmsProxy = mmsProxy,
+        apnType = apnType,
+        apnRoaming = apnProtocolOptions.indexOf(apnRoaming),
+        apnEnable = true
     )
+    private val apnData = mutableStateOf(
+        apnInit
+    )
+    private val uri = mock<Uri> {}
 
     @Test
     fun apnEditPageProvider_name() {
@@ -77,9 +80,7 @@
     @Test
     fun title_displayed() {
         composeTestRule.setContent {
-            ApnPage(remember {
-                apnData
-            })
+            ApnPage(apnInit, remember { apnData }, uri)
         }
         composeTestRule.onNodeWithText(context.getString(R.string.apn_edit)).assertIsDisplayed()
     }
@@ -87,9 +88,7 @@
     @Test
     fun name_displayed() {
         composeTestRule.setContent {
-            ApnPage(remember {
-                apnData
-            })
+            ApnPage(apnInit, remember { apnData }, uri)
         }
         composeTestRule.onNodeWithText(apnName, true).assertIsDisplayed()
     }
@@ -97,9 +96,7 @@
     @Test
     fun mmsc_displayed() {
         composeTestRule.setContent {
-            ApnPage(remember {
-                apnData
-            })
+            ApnPage(apnInit, remember { apnData }, uri)
         }
         composeTestRule.onRoot().onChild().onChildAt(0)
             .performScrollToNode(hasText(mmsc, true))
@@ -109,9 +106,7 @@
     @Test
     fun mms_proxy_displayed() {
         composeTestRule.setContent {
-            ApnPage(remember {
-                apnData
-            })
+            ApnPage(apnInit, remember { apnData }, uri)
         }
         composeTestRule.onRoot().onChild().onChildAt(0)
             .performScrollToNode(hasText(mmsProxy, true))
@@ -121,9 +116,7 @@
     @Test
     fun apn_type_displayed() {
         composeTestRule.setContent {
-            ApnPage(remember {
-                apnData
-            })
+            ApnPage(apnInit, remember { apnData }, uri)
         }
         composeTestRule.onRoot().onChild().onChildAt(0)
             .performScrollToNode(hasText(apnType, true))
@@ -133,9 +126,7 @@
     @Test
     fun apn_roaming_displayed() {
         composeTestRule.setContent {
-            ApnPage(remember {
-                apnData
-            })
+            ApnPage(apnInit, remember { apnData }, uri)
         }
         composeTestRule.onRoot().onChild().onChildAt(0)
             .performScrollToNode(hasText(apnRoaming, true))
@@ -145,9 +136,7 @@
     @Test
     fun carrier_enabled_displayed() {
         composeTestRule.setContent {
-            ApnPage(remember {
-                apnData
-            })
+            ApnPage(apnInit, remember { apnData }, uri)
         }
         composeTestRule.onRoot().onChild().onChildAt(0)
             .performScrollToNode(hasText(apnEnable, true))
@@ -157,9 +146,7 @@
     @Test
     fun carrier_enabled_isChecked() {
         composeTestRule.setContent {
-            ApnPage(remember {
-                apnData
-            })
+            ApnPage(apnInit, remember { apnData }, uri)
         }
         composeTestRule.onRoot().onChild().onChildAt(0)
             .performScrollToNode(hasText(apnEnable, true))
@@ -169,9 +156,7 @@
     @Test
     fun carrier_enabled_checkChanged() {
         composeTestRule.setContent {
-            ApnPage(remember {
-                apnData
-            })
+            ApnPage(apnInit, remember { apnData }, uri)
         }
         composeTestRule.onRoot().onChild().onChildAt(0)
             .performScrollToNode(hasText(apnEnable, true))
@@ -182,9 +167,7 @@
     @Test
     fun network_type_displayed() {
         composeTestRule.setContent {
-            ApnPage(remember {
-                apnData
-            })
+            ApnPage(apnInit, remember { apnData }, uri)
         }
         composeTestRule.onRoot().onChild().onChildAt(0)
             .performScrollToNode(hasText(networkType, true))
@@ -193,12 +176,8 @@
 
     @Test
     fun network_type_changed() {
-        var apnDataa: MutableState<ApnData> = apnData
         composeTestRule.setContent {
-            apnDataa = remember {
-                apnData
-            }
-            ApnPage(apnDataa)
+            ApnPage(apnInit, remember { apnData }, uri)
         }
         composeTestRule.onRoot().onChild().onChildAt(0)
             .performScrollToNode(hasText(networkType, true))
@@ -211,12 +190,8 @@
 
     @Test
     fun network_type_changed_back2Default() {
-        var apnDataa: MutableState<ApnData> = apnData
         composeTestRule.setContent {
-            apnDataa = remember {
-                apnData
-            }
-            ApnPage(apnDataa)
+            ApnPage(apnInit, remember { apnData }, uri)
         }
         composeTestRule.onRoot().onChild().onChildAt(0)
             .performScrollToNode(hasText(networkType, true))
@@ -234,9 +209,7 @@
     @Test
     fun password_displayed() {
         composeTestRule.setContent {
-            ApnPage(remember {
-                apnData
-            })
+            ApnPage(apnInit, remember { apnData }, uri)
         }
         composeTestRule.onRoot().onChild().onChildAt(0)
             .performScrollToNode(hasText(passwordTitle, true))
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/DataUsagePreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/DataUsagePreferenceControllerTest.kt
index a6d1531..069145d 100644
--- a/tests/spa_unit/src/com/android/settings/network/telephony/DataUsagePreferenceControllerTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/DataUsagePreferenceControllerTest.kt
@@ -25,7 +25,7 @@
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.testing.TestLifecycleOwner
 import androidx.preference.Preference
-import androidx.preference.PreferenceScreen
+import androidx.preference.PreferenceManager
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.dx.mockito.inline.extended.ExtendedMockito
@@ -33,45 +33,46 @@
 import com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE
 import com.android.settings.datausage.DataUsageUtils
 import com.android.settings.datausage.lib.DataUsageLib
-import com.android.settingslib.net.DataUsageController
-import com.android.settingslib.net.DataUsageController.DataUsageInfo
+import com.android.settings.datausage.lib.NetworkCycleDataRepository
+import com.android.settings.datausage.lib.NetworkUsageData
 import com.android.settingslib.spa.testutils.waitUntil
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.runBlocking
 import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.Mock
-import org.mockito.Mockito.any
-import org.mockito.Mockito.doNothing
-import org.mockito.Mockito.verify
 import org.mockito.MockitoSession
-import org.mockito.Spy
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doNothing
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.stub
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
 import org.mockito.quality.Strictness
-import org.mockito.Mockito.`when` as whenever
 
 @RunWith(AndroidJUnit4::class)
 class DataUsagePreferenceControllerTest {
 
     private lateinit var mockSession: MockitoSession
 
-    @Spy
-    private val context: Context = ApplicationProvider.getApplicationContext()
+    private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+        doNothing().whenever(mock).startActivity(any())
+    }
 
-    private lateinit var controller: DataUsagePreferenceController
+    private val preference = Preference(context).apply { key = TEST_KEY }
+    private val preferenceScreen = PreferenceManager(context).createPreferenceScreen(context)
+    private val networkTemplate = mock<NetworkTemplate>()
+    private val repository = mock<NetworkCycleDataRepository> {
+        on { queryUsage(any()) } doReturn NetworkUsageData(START_TIME, END_TIME, 0L)
+    }
 
-    private val preference = Preference(context)
-
-    @Mock
-    private lateinit var networkTemplate: NetworkTemplate
-
-    @Mock
-    private lateinit var dataUsageController: DataUsageController
-
-    @Mock
-    private lateinit var preferenceScreen: PreferenceScreen
+    private val controller = spy(DataUsagePreferenceController(context, TEST_KEY)) {
+        doReturn(repository).whenever(mock).createNetworkCycleDataRepository()
+    }
 
     @Before
     fun setUp() {
@@ -85,17 +86,15 @@
 
         whenever(SubscriptionManager.isValidSubscriptionId(SUB_ID)).thenReturn(true)
         ExtendedMockito.doReturn(true).`when` { DataUsageUtils.hasMobileData(context) }
-        ExtendedMockito.doReturn(networkTemplate)
-            .`when` { DataUsageLib.getMobileTemplate(context, SUB_ID) }
-        preference.key = TEST_KEY
-        whenever(preferenceScreen.findPreference<Preference>(TEST_KEY)).thenReturn(preference)
+        ExtendedMockito.doReturn(networkTemplate).`when` {
+            DataUsageLib.getMobileTemplate(context, SUB_ID)
+        }
 
-        controller =
-            DataUsagePreferenceController(context, TEST_KEY).apply {
-                init(SUB_ID)
-                displayPreference(preferenceScreen)
-                dataUsageControllerFactory = { dataUsageController }
-            }
+        preferenceScreen.addPreference(preference)
+        controller.apply {
+            init(SUB_ID)
+            displayPreference(preferenceScreen)
+        }
     }
 
     @After
@@ -116,26 +115,25 @@
     }
 
     @Test
-    fun handlePreferenceTreeClick_startActivity() = runTest {
-        val usageInfo = DataUsageInfo().apply {
-            usageLevel = DataUnit.MEBIBYTES.toBytes(1)
+    fun handlePreferenceTreeClick_startActivity() = runBlocking {
+        val usageData = NetworkUsageData(START_TIME, END_TIME, 1L)
+        repository.stub {
+            on { loadFirstCycle() } doReturn usageData
         }
-        whenever(dataUsageController.getDataUsageInfo(networkTemplate)).thenReturn(usageInfo)
-        doNothing().`when`(context).startActivity(any())
         controller.onViewCreated(TestLifecycleOwner(initialState = Lifecycle.State.STARTED))
         waitUntil { preference.summary != null }
 
         controller.handlePreferenceTreeClick(preference)
 
-        val captor = ArgumentCaptor.forClass(Intent::class.java)
-        verify(context).startActivity(captor.capture())
-        val intent = captor.value
+        val intent = argumentCaptor<Intent> {
+            verify(context).startActivity(capture())
+        }.firstValue
         assertThat(intent.action).isEqualTo(Settings.ACTION_MOBILE_DATA_USAGE)
         assertThat(intent.getIntExtra(Settings.EXTRA_SUB_ID, 0)).isEqualTo(SUB_ID)
     }
 
     @Test
-    fun updateState_invalidSubId_disabled() = runTest {
+    fun updateState_invalidSubId_disabled() = runBlocking {
         controller.init(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
 
         controller.onViewCreated(TestLifecycleOwner(initialState = Lifecycle.State.STARTED))
@@ -144,9 +142,11 @@
     }
 
     @Test
-    fun updateState_noUsageData_shouldDisablePreference() = runTest {
-        val usageInfo = DataUsageInfo()
-        whenever(dataUsageController.getDataUsageInfo(networkTemplate)).thenReturn(usageInfo)
+    fun updateState_noUsageData_shouldDisablePreference() = runBlocking {
+        val usageData = NetworkUsageData(START_TIME, END_TIME, 0L)
+        repository.stub {
+            on { loadFirstCycle() } doReturn usageData
+        }
 
         controller.onViewCreated(TestLifecycleOwner(initialState = Lifecycle.State.STARTED))
 
@@ -154,11 +154,11 @@
     }
 
     @Test
-    fun updateState_shouldUseIecUnit() = runTest {
-        val usageInfo = DataUsageInfo().apply {
-            usageLevel = DataUnit.MEBIBYTES.toBytes(1)
+    fun updateState_shouldUseIecUnit() = runBlocking {
+        val usageData = NetworkUsageData(START_TIME, END_TIME, DataUnit.MEBIBYTES.toBytes(1))
+        repository.stub {
+            on { loadFirstCycle() } doReturn usageData
         }
-        whenever(dataUsageController.getDataUsageInfo(networkTemplate)).thenReturn(usageInfo)
 
         controller.onViewCreated(TestLifecycleOwner(initialState = Lifecycle.State.STARTED))
 
@@ -168,5 +168,7 @@
     private companion object {
         const val TEST_KEY = "test_key"
         const val SUB_ID = 2
+        const val START_TIME = 10L
+        const val END_TIME = 30L
     }
 }
diff --git a/tests/uitests/Android.bp b/tests/uitests/Android.bp
index 407b5ec..f149519 100644
--- a/tests/uitests/Android.bp
+++ b/tests/uitests/Android.bp
@@ -47,7 +47,7 @@
         "settings-helper",
         "sysui-helper",
         "timeresult-helper-lib",
-        "truth-prebuilt",
+        "truth",
     ],
 
     //sdk_version: "current",
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index 1b14a73..0f045a8 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -27,7 +27,7 @@
         "mockito-target-minus-junit4",
         "platform-test-annotations",
         "platform-test-rules",
-        "truth-prebuilt",
+        "truth",
         "kotlinx_coroutines_test",
         "flag-junit",
         "Settings-testutils2",
@@ -36,7 +36,7 @@
     ],
 
     errorprone: {
-        javacflags: ["-Xep:CheckReturnValue:WARN"]
+        javacflags: ["-Xep:CheckReturnValue:WARN"],
     },
     // Include all test java/kotlin files.
     srcs: [
diff --git a/tests/unit/src/com/android/settings/applications/appcompat/UserAspectRatioManagerTest.java b/tests/unit/src/com/android/settings/applications/appcompat/UserAspectRatioManagerTest.java
index 61c4507..cf2a1f0 100644
--- a/tests/unit/src/com/android/settings/applications/appcompat/UserAspectRatioManagerTest.java
+++ b/tests/unit/src/com/android/settings/applications/appcompat/UserAspectRatioManagerTest.java
@@ -17,7 +17,6 @@
 package com.android.settings.applications.appcompat;
 
 import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_16_9;
-import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_4_3;
 import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN;
 import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN;
 import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
@@ -243,9 +242,6 @@
         assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_SPLIT_SCREEN,
                 mPackageName)).isEqualTo(ResourcesUtils.getResourcesString(mContext,
                         "user_aspect_ratio_half_screen"));
-        // R,string.user_aspect_ratio_4_3
-        assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_4_3, mPackageName))
-                .isEqualTo(ResourcesUtils.getResourcesString(mContext, "user_aspect_ratio_4_3"));
         assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_FULLSCREEN,
                 mPackageName)).isEqualTo(ResourcesUtils.getResourcesString(mContext,
                         "user_aspect_ratio_fullscreen"));