Merge "Migrate VpnSettings from RestrictedSettingsFragment to RestrictedDashboardFragment." into main
diff --git a/Android.bp b/Android.bp
index baf9914..861f95f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -84,6 +84,7 @@
         "net-utils-framework-common",
         "app-usage-event-protos-lite",
         "battery-event-protos-lite",
+        "power-anomaly-event-protos-lite",
         "settings-contextual-card-protos-lite",
         "settings-log-bridge-protos-lite",
         "settings-telephony-protos-lite",
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 6702ed1..cae7096 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -644,7 +644,6 @@
         <activity android:name="Settings$FaceSettingsActivity"
             android:label="@string/security_settings_face_preference_title"
             android:exported="true"
-            android:configChanges="orientation|screenSize"
             android:icon="@drawable/ic_face_header">
             <intent-filter>
                 <action android:name="android.settings.FACE_SETTINGS" />
@@ -660,7 +659,6 @@
                   android:label="@string/security_settings_face_preference_title"
                   android:exported="false"
                   android:icon="@drawable/ic_face_header"
-                  android:configChanges="orientation|screenSize"
                   android:taskAffinity="com.android.settings.root">
             <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                        android:value="com.android.settings.biometrics.face.FaceSettings" />
diff --git a/res/drawable/battery_tips_all_rounded_bg.xml b/res/drawable/battery_tips_all_rounded_bg.xml
new file mode 100644
index 0000000..4f61f54
--- /dev/null
+++ b/res/drawable/battery_tips_all_rounded_bg.xml
@@ -0,0 +1,22 @@
+<?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.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="@color/settingslib_dialog_background" />
+    <corners android:radius="@dimen/battery_tips_card_corner_radius_normal" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/battery_tips_half_rounded_bottom_bg.xml b/res/drawable/battery_tips_half_rounded_bottom_bg.xml
new file mode 100644
index 0000000..7766de6
--- /dev/null
+++ b/res/drawable/battery_tips_half_rounded_bottom_bg.xml
@@ -0,0 +1,27 @@
+<?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.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="@color/settingslib_dialog_background"/>
+    <corners
+        android:topLeftRadius="@dimen/battery_tips_card_corner_radius_small"
+        android:topRightRadius="@dimen/battery_tips_card_corner_radius_small"
+        android:bottomLeftRadius="@dimen/battery_tips_card_corner_radius_normal"
+        android:bottomRightRadius="@dimen/battery_tips_card_corner_radius_normal"
+        />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/battery_tips_half_rounded_top_bg.xml b/res/drawable/battery_tips_half_rounded_top_bg.xml
new file mode 100644
index 0000000..aba1a4f
--- /dev/null
+++ b/res/drawable/battery_tips_half_rounded_top_bg.xml
@@ -0,0 +1,27 @@
+<?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.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="@color/settingslib_dialog_background"/>
+    <corners
+        android:topLeftRadius="@dimen/battery_tips_card_corner_radius_normal"
+        android:topRightRadius="@dimen/battery_tips_card_corner_radius_normal"
+        android:bottomLeftRadius="@dimen/battery_tips_card_corner_radius_small"
+        android:bottomRightRadius="@dimen/battery_tips_card_corner_radius_small"
+        />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/ic_battery_charger.xml b/res/drawable/ic_battery_charger.xml
new file mode 100644
index 0000000..4406a56
--- /dev/null
+++ b/res/drawable/ic_battery_charger.xml
@@ -0,0 +1,25 @@
+<!--
+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="48dp"
+        android:height="48dp"
+        android:viewportWidth="960"
+        android:viewportHeight="960">
+    <path
+      android:fillColor="?android:attr/colorAccent"
+      android:pathData="M442,780L518,780L518,698L660,542L660,351Q660,351 660,351Q660,351 660,351L300,351Q300,351 300,351Q300,351 300,351L300,542L442,697.7L442,780ZM382,840L382,722L240,566L240,351Q240,326.25 257.63,308.63Q275.25,291 300,291L372,291L342,321L342,120L402,120L402,291L558,291L558,120L618,120L618,321L588,291L660,291Q684.75,291 702.38,308.63Q720,326.25 720,351L720,566L578,722L578,840L382,840ZM480,565L480,565L480,565L480,565Q480,565 480,565Q480,565 480,565L480,565Q480,565 480,565Q480,565 480,565L480,565L480,565L480,565L480,565Z"/>
+</vector>
+
diff --git a/res/drawable/ic_battery_tips_close.xml b/res/drawable/ic_battery_tips_close.xml
new file mode 100644
index 0000000..7ef571b
--- /dev/null
+++ b/res/drawable/ic_battery_tips_close.xml
@@ -0,0 +1,25 @@
+<!--
+  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="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="?android:attr/textColorSecondary"
+        android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12 19,6.41z"/>
+</vector>
diff --git a/res/drawable/ic_battery_tips_close_icon.xml b/res/drawable/ic_battery_tips_close_icon.xml
new file mode 100644
index 0000000..b766474
--- /dev/null
+++ b/res/drawable/ic_battery_tips_close_icon.xml
@@ -0,0 +1,32 @@
+<!--
+  Copyright (C) 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp">
+    <item>
+        <shape android:shape="oval">
+            <size
+                android:width="24dp"
+                android:height="24dp" />
+            <solid android:color="?android:attr/colorBackground" />
+        </shape>
+    </item>
+    <item android:drawable="@drawable/ic_battery_tips_close"
+        android:gravity="center"
+        android:width="16dp"
+        android:height="16dp"/>
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/ic_battery_tips_lightbulb.xml b/res/drawable/ic_battery_tips_lightbulb.xml
new file mode 100644
index 0000000..f1449f9
--- /dev/null
+++ b/res/drawable/ic_battery_tips_lightbulb.xml
@@ -0,0 +1,25 @@
+<!--
+  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="32dp"
+    android:height="32dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="?android:attr/colorAccent"
+        android:pathData="M7,20h4c0,1.1 -0.9,2 -2,2S7,21.1 7,20zM5,19h8v-2H5V19zM16.5,9.5c0,3.82 -2.66,5.86 -3.77,6.5H5.27C4.16,15.36 1.5,13.32 1.5,9.5C1.5,5.36 4.86,2 9,2S16.5,5.36 16.5,9.5zM14.5,9.5C14.5,6.47 12.03,4 9,4S3.5,6.47 3.5,9.5c0,2.47 1.49,3.89 2.35,4.5h6.3C13.01,13.39 14.5,11.97 14.5,9.5zM21.37,7.37L20,8l1.37,0.63L22,10l0.63,-1.37L24,8l-1.37,-0.63L22,6L21.37,7.37zM19,6l0.94,-2.06L22,3l-2.06,-0.94L19,0l-0.94,2.06L16,3l2.06,0.94L19,6z"/>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_battery_tips_thumb_down.xml b/res/drawable/ic_battery_tips_thumb_down.xml
new file mode 100644
index 0000000..cd7656b
--- /dev/null
+++ b/res/drawable/ic_battery_tips_thumb_down.xml
@@ -0,0 +1,25 @@
+<!--
+  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="24dp"
+    android:height="24dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960">
+    <path
+        android:fillColor="?android:attr/colorAccent"
+        android:pathData="M242,120L686,120L686,632L408,920L369,889Q363,884 360,875Q357,866 357,853L357,843L402,632L103,632Q79,632 61,614Q43,596 43,572L43,490.16Q43,483 41.5,475.5Q40,468 43,461L169,171Q177.88,149.75 198.6,134.88Q219.31,120 242,120ZM626,180L229,180Q229,180 229,180Q229,180 229,180L103,479L103,572Q103,572 103,572Q103,572 103,572L476,572L423,821L626,607L626,180ZM626,607L626,607L626,572L626,572Q626,572 626,572Q626,572 626,572L626,479L626,180Q626,180 626,180Q626,180 626,180L626,180L626,607ZM686,632L686,572L819,572L819,180L686,180L686,120L879,120L879,632L686,632Z" />
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_battery_tips_thumb_up.xml b/res/drawable/ic_battery_tips_thumb_up.xml
new file mode 100644
index 0000000..b1d4cb6
--- /dev/null
+++ b/res/drawable/ic_battery_tips_thumb_up.xml
@@ -0,0 +1,25 @@
+<!--
+  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="24dp"
+    android:height="24dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960">
+    <path
+        android:fillColor="?android:attr/colorAccent"
+        android:pathData="M716,840L272,840L272,328L550,40L589,71Q595,76 598,85Q601,94 601,107L601,117L556,328L855,328Q879,328 897,346Q915,364 915,388L915,469.84Q915,477 916.5,484.5Q918,492 915,499L789,789Q780.12,810.25 759.41,825.13Q738.69,840 716,840ZM332,780L729,780Q729,780 729,780Q729,780 729,780L855,481L855,388Q855,388 855,388Q855,388 855,388L482,388L535,139L332,353L332,780ZM332,353L332,353L332,388L332,388Q332,388 332,388Q332,388 332,388L332,481L332,780Q332,780 332,780Q332,780 332,780L332,780L332,353ZM272,328L272,388L139,388L139,780L272,780L272,840L79,840L79,328L272,328Z" />
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_lock_none.xml b/res/drawable/ic_lock_none.xml
index 31069b7..54b9bb4 100644
--- a/res/drawable/ic_lock_none.xml
+++ b/res/drawable/ic_lock_none.xml
@@ -18,7 +18,8 @@
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24"
-    android:viewportHeight="24">
+    android:viewportHeight="24"
+    android:tint="?android:attr/colorControlNormal">
   <path
       android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6h2c0,-1.66 1.34,-3 3,-3s3,1.34 3,3v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM18,20L6,20L6,10h12v10z"
       android:fillColor="?android:attr/colorAccent"/>
diff --git a/res/drawable/ic_lock_pin.xml b/res/drawable/ic_lock_pin.xml
index 587f49c..4614f53 100644
--- a/res/drawable/ic_lock_pin.xml
+++ b/res/drawable/ic_lock_pin.xml
@@ -18,7 +18,8 @@
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24"
-    android:viewportHeight="24">
+    android:viewportHeight="24"
+    android:tint="?android:attr/colorControlNormal">
   <path
       android:pathData="M6,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,20c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM6,20c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM6,14c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,14c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM16,6c0,1.1 0.9,2 2,2s2,-0.9 2,-2 -0.9,-2 -2,-2 -2,0.9 -2,2zM12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM18,14c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM18,20c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2z"
       android:fillColor="?android:attr/colorAccent"/>
diff --git a/res/drawable/ic_lock_swipe.xml b/res/drawable/ic_lock_swipe.xml
index f7e78b8..fb8302d 100644
--- a/res/drawable/ic_lock_swipe.xml
+++ b/res/drawable/ic_lock_swipe.xml
@@ -18,7 +18,8 @@
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24"
-    android:viewportHeight="24">
+    android:viewportHeight="24"
+    android:tint="?android:attr/colorControlNormal">
   <path
       android:pathData="M20.5,2v2.02C18.18,2.13 15.22,1 12,1S5.82,2.13 3.5,4.02V2H2v3.5V7h1.5H7V5.5H4.09c2.11,-1.86 4.88,-3 7.91,-3s5.79,1.14 7.91,3H17V7h3.5H22V5.5V2H20.5z"
       android:fillColor="?android:attr/colorAccent"/>
diff --git a/res/drawable/ic_password.xml b/res/drawable/ic_password.xml
index 341e544..cf3b408 100644
--- a/res/drawable/ic_password.xml
+++ b/res/drawable/ic_password.xml
@@ -18,7 +18,8 @@
     android:width="24dp"
     android:height="24dp"
     android:viewportHeight="24.0"
-    android:viewportWidth="24.0">
+    android:viewportWidth="24.0"
+    android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="?android:attr/colorAccent"
         android:pathData="M21.5,9.39l-1.63,0l0.81,-1.42l-0.86,-0.5l-0.82,1.42l-0.82,-1.42l-0.86,0.5l0.81,1.42l-1.63,0l0,1l1.63,0l-0.81,1.41l0.86,0.5l0.82,-1.41l0.82,1.41l0.86,-0.5l-0.81,-1.41l1.63,0z" />
diff --git a/res/drawable/ic_pattern.xml b/res/drawable/ic_pattern.xml
index 788eaa7..e56fb00 100644
--- a/res/drawable/ic_pattern.xml
+++ b/res/drawable/ic_pattern.xml
@@ -18,7 +18,8 @@
     android:width="24dp"
     android:height="24dp"
     android:viewportHeight="24.0"
-    android:viewportWidth="24.0">
+    android:viewportWidth="24.0"
+    android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="?android:attr/colorAccent"
         android:pathData="M4,4m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0" />
diff --git a/res/drawable/ic_pin.xml b/res/drawable/ic_pin.xml
index 682e934..8520ec1 100644
--- a/res/drawable/ic_pin.xml
+++ b/res/drawable/ic_pin.xml
@@ -18,7 +18,8 @@
     android:width="24dp"
     android:height="24dp"
     android:viewportHeight="24.0"
-    android:viewportWidth="24.0">
+    android:viewportWidth="24.0"
+    android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="?android:attr/colorAccent"
         android:pathData="M20,4L4,4A2,2 0,0 0,2 6L2,18a2,2 0,0 0,2 2L20,20a2,2 0,0 0,2 -2L22,6A2,2 0,0 0,20 4ZM7.1,15L5.9,15L5.9,10.2L4.7,10.2L4.7,9L7.1,9v6ZM13.2,11.4A1.2,1.2 0,0 1,12 12.6L10.8,12.6v1.2h2.4L13.2,15L9.6,15L9.6,12.6a1.2,1.2 0,0 1,1.2 -1.2L12,11.4L12,10.2L9.6,10.2L9.6,9L12,9a1.2,1.2 0,0 1,1.2 1.2v1.2ZM19.3,11.1a0.9,0.9 0,0 1,-0.9 0.9,0.9 0.9,0 0,1 0.9,0.9v0.9A1.2,1.2 0,0 1,18.1 15L15.7,15L15.7,13.8h2.4L18.1,12.6L16.9,12.6L16.9,11.4h1.2L18.1,10.2L15.7,10.2L15.7,9h2.4a1.2,1.2 0,0 1,1.2 1.2v0.9Z" />
diff --git a/res/layout/battery_tips_card.xml b/res/layout/battery_tips_card.xml
new file mode 100644
index 0000000..d2edb51
--- /dev/null
+++ b/res/layout/battery_tips_card.xml
@@ -0,0 +1,125 @@
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/battery_tips_card"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+    <LinearLayout
+        android:id="@+id/tips_card"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@drawable/battery_tips_all_rounded_bg"
+        android:orientation="vertical"
+        android:padding="24dp">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+
+            <ImageView
+                android:id="@+id/icon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical|start"
+                android:src="@drawable/ic_battery_tips_lightbulb" />
+
+            <Space
+                android:layout_width="0dp"
+                android:layout_height="0dp"
+                android:layout_weight="1" />
+
+            <ImageButton
+                android:id="@+id/dismiss_button"
+                style="@style/Banner.Dismiss.SettingsLib"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical|end"
+                android:layout_marginEnd="0dp"
+                android:src="@drawable/ic_battery_tips_close_icon" />
+        </LinearLayout>
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="8dp"
+            android:maxLines="2"
+            android:textAlignment="viewStart"
+            android:textAppearance="?android:attr/textAppearanceLarge"
+            android:textColor="?android:attr/textColorPrimary" />
+
+        <TextView
+            android:id="@+id/summary"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="8dp"
+            android:gravity="start"
+            android:maxLines="10"
+            android:textAlignment="viewStart"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="?android:attr/textColorSecondary" />
+
+        <com.google.android.material.button.MaterialButton
+            android:id="@+id/action_button"
+            style="@style/Widget.Material3.Button.OutlinedButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="end"
+            android:layout_marginTop="8dp"
+            android:text="@string/battery_tips_card_action_button"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="?android:attr/textColorPrimary"
+            android:textStyle="bold"
+            app:strokeColor="?android:attr/colorAccent"
+            app:strokeWidth="1dp" />
+    </LinearLayout>
+
+    <Space
+        android:layout_width="0dp"
+        android:layout_height="1dp"/>
+
+    <LinearLayout
+        android:id="@+id/feedback_card"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@drawable/battery_tips_half_rounded_bottom_bg"
+        android:gravity="center_vertical|start"
+        android:orientation="horizontal"
+        android:paddingHorizontal="24dp"
+        android:paddingVertical="16dp"
+        android:visibility="gone">
+
+        <TextView
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="0dp"
+            android:layout_marginEnd="20dp"
+            android:layout_weight="1"
+            android:text="@string/battery_tips_card_feedback_info"
+            android:textAlignment="viewStart"
+            android:textColor="?android:attr/textColorPrimary"
+            android:textStyle="bold"/>
+
+        <ImageButton
+            android:id="@+id/thumb_up"
+            style="@style/Banner.Dismiss.SettingsLib"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical|end"
+            android:layout_marginEnd="20dp"
+            android:src="@drawable/ic_battery_tips_thumb_up" />
+
+        <ImageButton
+            android:id="@+id/thumb_down"
+            style="@style/Banner.Dismiss.SettingsLib"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical|end"
+            android:src="@drawable/ic_battery_tips_thumb_down" />
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/res/values/config.xml b/res/values/config.xml
index f20bce0..8d9d4b9 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -608,6 +608,31 @@
         <item>3</item>
     </integer-array>
 
+    <!-- App aspect ratio settings screen, user aspect ratio override options. Must be the same
+         length and order as config_userAspectRatioOverrideValues below. -->
+    <string-array name="config_userAspectRatioOverrideEntries" translatable="false">
+        <item>@string/user_aspect_ratio_app_default</item>
+        <item>@string/user_aspect_ratio_fullscreen</item>
+        <item>@string/user_aspect_ratio_half_screen</item>
+        <item>@string/user_aspect_ratio_device_size</item>
+        <item>@string/user_aspect_ratio_16_9</item>
+        <item>@string/user_aspect_ratio_4_3</item>
+        <item>@string/user_aspect_ratio_3_2</item>
+    </string-array>
+
+    <!-- App aspect ratio settings screen, user aspect ratio override options. Must be the same
+         length and order as config_userAspectRatioOverrideEntries above. The values must
+         correspond to PackageManager.UserMinAspectRatio -->
+    <integer-array name="config_userAspectRatioOverrideValues" translatable="false">
+        <item>0</item> <!-- USER_MIN_ASPECT_RATIO_UNSET -->
+        <item>6</item> <!-- USER_MIN_ASPECT_RATIO_FULLSCREEN -->
+        <item>1</item> <!-- USER_MIN_ASPECT_RATIO_SPLIT_SCREEN -->
+        <item>2</item> <!-- USER_MIN_ASPECT_RATIO_DISPLAY_SIZE -->
+        <item>4</item> <!-- USER_MIN_ASPECT_RATIO_16_9 -->
+        <item>3</item> <!-- USER_MIN_ASPECT_RATIO_4_3 -->
+        <item>5</item> <!-- USER_MIN_ASPECT_RATIO_3_2 -->
+    </integer-array>
+
     <!-- The settings/preference description for each settable device state defined in the array
          "config_perDeviceStateRotationLockDefaults".
          The item in position "i" describes the auto-rotation setting for the device state also in
@@ -664,6 +689,9 @@
     <!-- Whether to enable the advanced vpn feature. The default is not to. -->
     <bool name="config_advanced_vpn_enabled">false</bool>
 
+    <!-- Whether to show the VPN options menu. The default is to show it. -->
+    <bool name="config_show_vpn_options">true</bool>
+
     <!-- An array of uid name for which packages exempt from Wi-Fi permission check. -->
     <string-array name="config_exempt_wifi_permission_uid_name" translatable="false">
         <item>@string/config_settingsintelligence_package_name</item>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index a7a124a..e272c15 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -373,6 +373,10 @@
     <dimen name="chartview_trapezoid_margin_start">1dp</dimen>
     <dimen name="chartview_trapezoid_margin_bottom">2dp</dimen>
 
+    <!-- Battery tips card view component -->
+    <dimen name="battery_tips_card_corner_radius_small">4dp</dimen>
+    <dimen name="battery_tips_card_corner_radius_normal">24dp</dimen>
+
     <!-- Dimensions for Dream settings cards -->
     <dimen name="dream_item_min_column_width">174dp</dimen>
     <dimen name="dream_item_corner_radius">28dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 177a5cb..0863249 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -9686,6 +9686,24 @@
     <!-- Preference summary for battery usage list page[CHAR_LIMIT=50]-->
     <string name="app_battery_usage_summary">Set battery usage for apps</string>
 
+    <!-- Label of action button in battery tips card [CHAR LIMIT=NONE] -->
+    <string name="battery_tips_card_action_button" translatable="false">Optimize</string>
+
+    <!-- Feedback card message in battery tips card [CHAR LIMIT=NONE] -->
+    <string name="battery_tips_card_feedback_info" translatable="false">Is this message helpful?</string>
+
+    <!-- Title of battery tips: adaptive brightness [CHAR LIMIT=NONE] -->
+    <string name="battery_tips_adaptive_brightness_title" translatable="false">Turn on adaptive brightness to extend battery life</string>
+
+    <!-- Summary of battery tips: adaptive brightness [CHAR LIMIT=NONE] -->
+    <string name="battery_tips_adaptive_brightness_summary" translatable="false">It will help reduce your daily battery drain by 10%</string>
+
+    <!-- Title of battery tips: reduce screen timeout [CHAR LIMIT=NONE] -->
+    <string name="battery_tips_screen_timeout_title" translatable="false">Reduce screen timeout to extend battery life</string>
+
+    <!-- Summary of battery tips: reduce screen timeout [CHAR LIMIT=NONE] -->
+    <string name="battery_tips_screen_timeout_summary" translatable="false">It will help reduce your daily battery drain by 10%</string>
+
     <!-- Filter title for battery unrestricted[CHAR_LIMIT=50]-->
     <string name="filter_battery_unrestricted_title">Unrestricted</string>
 
@@ -11029,6 +11047,8 @@
     <string name="sim_action_cancel">Cancel</string>
     <!-- Button which will disconnect the user from one mobile network and immediately connect to another. [CHAR LIMIT=30] -->
     <string name="sim_switch_button">Switch</string>
+    <!-- Button text to turn off the sim. [CHAR LIMIT=30] -->
+    <string name="sim_action_turn_off">Turn off</string>
     <!-- Title of DSDS activation failure dialog [CHAR LIMIT=40] -->
     <string name="dsds_activation_failure_title">Can\u2019t activate SIM</string>
     <!-- Body text of DSDS activation failure dialog. Users could toggle the selected SIM again or reboot to recover. [CHAR LIMIT=NONE] -->
@@ -12111,6 +12131,33 @@
     other {Apps installed more than # months ago}
     }</string>
 
+    <!-- App Aspect Ratio (User Aspect Ratio Override) -->
+    <!-- [CHAR LIMIT=60] Aspect ratio title setting to choose app aspect ratio -->
+    <string name="aspect_ratio_title">Aspect ratio</string>
+    <!-- [CHAR LIMIT=NONE] Aspect ratio setting summary to choose aspect ratio for apps unoptimized for device -->
+    <string name="aspect_ratio_summary">Choose an aspect ratio to view this app if it hasn\'t been designed to fit your <xliff:g id="device_name">%1$s</xliff:g></string>
+    <!-- [CHAR LIMIT=NONE] Aspect ratio suggested apps filter label -->
+    <string name="user_aspect_ratio_suggested_apps_label">Suggested apps</string>
+    <!-- [CHAR LIMIT=NONE] Filter label for apps that have user aspect ratio override applied -->
+    <string name="user_aspect_ratio_overridden_apps_label">Apps you have overridden</string>
+    <!-- [CHAR LIMIT=NONE] App default aspect ratio entry -->
+    <string name="user_aspect_ratio_app_default">App default</string>
+    <!-- [CHAR LIMIT=NONE] Fullscreen aspect ratio entry -->
+    <string name="user_aspect_ratio_fullscreen">Full screen</string>
+    <!-- [CHAR LIMIT=NONE] Half screen aspect ratio entry -->
+    <string name="user_aspect_ratio_half_screen">Half screen</string>
+    <!-- [CHAR LIMIT=NONE] Device display size aspect ratio entry -->
+    <string name="user_aspect_ratio_device_size">Device aspect ratio</string>
+    <!-- [CHAR LIMIT=NONE] 16:9 aspect ratio entry -->
+    <string name="user_aspect_ratio_16_9">16:9</string>
+    <!-- [CHAR LIMIT=NONE] 3:2 aspect ratio entry -->
+    <string name="user_aspect_ratio_3_2">3:2</string>
+    <!-- [CHAR LIMIT=NONE] 4:3 aspect ratio entry -->
+    <string name="user_aspect_ratio_4_3">4:3</string>
+    <!-- [CHAR LIMIT=NONE] Warning description for app info aspect ratio page -->
+    <string name="app_aspect_ratio_footer">The app will restart when you change aspect ratio. You may lose unsaved changes.</string>
+
+
     <!-- Accessibility label for fingerprint sensor [CHAR LIMIT=NONE] -->
     <string name="accessibility_fingerprint_label">Fingerprint sensor</string>
 
@@ -12220,4 +12267,9 @@
 
     <!--  Warning text about the visibility of device name. [CHAR LIMIT=NONE] -->
     <string name="about_phone_device_name_warning">Your device name is visible to apps you installed. It may also be seen by other people when you connect to Bluetooth devices, connect to a Wi-Fi network or set up a Wi-Fi hotspot.</string>
+
+    <!-- Developer settings: grammatical gender title [CHAR LIMIT=50]-->
+    <string name="grammatical_gender_title">Grammatical gender</string>
+    <!-- Developer settings: select Grammatical gender dialog title [CHAR LIMIT=50]-->
+    <string name="grammatical_gender_dialog_title">Select Grammatical gender</string>
 </resources>
diff --git a/res/xml/apps.xml b/res/xml/apps.xml
index 03212c9..73bcbc9 100644
--- a/res/xml/apps.xml
+++ b/res/xml/apps.xml
@@ -80,6 +80,17 @@
         android:order="10"/>
 
     <Preference
+        android:key="aspect_ratio_apps"
+        android:title="@string/aspect_ratio_title"
+        android:summary="@string/summary_placeholder"
+        android:order="14"
+        settings:controller="com.android.settings.applications.appcompat.UserAspectRatioAppsPreferenceController"
+        android:fragment="com.android.settings.applications.manageapplications.ManageApplications">
+        <extra android:name="classname"
+               android:value="com.android.settings.Settings$UserAspectRatioAppListActivity"/>
+    </Preference>
+
+    <Preference
         android:key="hibernated_apps"
         android:title="@string/unused_apps"
         android:summary="@string/summary_placeholder"
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index 32acac6..ab1ee41 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -146,6 +146,13 @@
             android:key="quick_settings_tiles"
             android:title="@string/quick_settings_developer_tiles"
             android:fragment="com.android.settings.development.qstile.DevelopmentTileConfigFragment" />
+
+        <ListPreference
+            android:key="grammatical_gender"
+            android:title="@string/grammatical_gender_title"
+            android:dialogTitle="@string/grammatical_gender_dialog_title"
+            android:entries="@array/grammatical_gender_entries"
+            android:entryValues="@array/grammatical_gender_values" />
     </PreferenceCategory>
 
     <PreferenceCategory
diff --git a/res/xml/power_usage_advanced.xml b/res/xml/power_usage_advanced.xml
index 2a1a23c..c129453 100644
--- a/res/xml/power_usage_advanced.xml
+++ b/res/xml/power_usage_advanced.xml
@@ -21,6 +21,18 @@
     android:title="@string/advanced_battery_title"
     settings:keywords="@string/keywords_battery_usage">
 
+    <PreferenceCategory
+        android:key="battery_tips_category"
+        settings:controller=
+            "com.android.settings.fuelgauge.batteryusage.BatteryTipsController"
+        settings:isPreferenceVisible="false">
+
+        <com.android.settings.fuelgauge.batteryusage.BatteryTipsCardPreference
+            android:key="battery_tips_card"
+            settings:isPreferenceVisible="false" />
+
+    </PreferenceCategory>
+
     <com.android.settings.fuelgauge.batteryusage.BatteryHistoryPreference
         android:key="battery_chart"
         settings:controller=
diff --git a/res/xml/user_aspect_ratio_details.xml b/res/xml/user_aspect_ratio_details.xml
new file mode 100644
index 0000000..fc921dd
--- /dev/null
+++ b/res/xml/user_aspect_ratio_details.xml
@@ -0,0 +1,59 @@
+<?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/aspect_ratio_title">
+
+    <com.android.settingslib.widget.ActionButtonsPreference
+        android:key="header_view" />
+
+    <com.android.settingslib.widget.SelectorWithWidgetPreference
+        android:key="app_default_pref"
+        android:title="@string/user_aspect_ratio_app_default"/>
+
+    <com.android.settingslib.widget.SelectorWithWidgetPreference
+        android:key="fullscreen_pref"
+        android:title="@string/user_aspect_ratio_fullscreen"/>
+
+    <com.android.settingslib.widget.SelectorWithWidgetPreference
+        android:key="half_screen_pref"
+        android:title="@string/user_aspect_ratio_half_screen"/>
+
+    <com.android.settingslib.widget.SelectorWithWidgetPreference
+        android:key="display_size_pref"
+        android:title="@string/user_aspect_ratio_device_size"/>
+
+    <com.android.settingslib.widget.SelectorWithWidgetPreference
+        android:key="16_9_pref"
+        android:title="@string/user_aspect_ratio_16_9"/>
+
+    <com.android.settingslib.widget.SelectorWithWidgetPreference
+        android:key="4_3_pref"
+        android:title="@string/user_aspect_ratio_4_3"/>
+
+    <com.android.settingslib.widget.SelectorWithWidgetPreference
+        android:key="3_2_pref"
+        android:title="@string/user_aspect_ratio_3_2"/>
+
+    <com.android.settingslib.widget.FooterPreference
+        android:title="@string/app_aspect_ratio_footer"
+        android:selectable="false"
+        settings:searchable="false"/>
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 405931c..57d91f0 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -51,26 +51,9 @@
     public static class MemtagPageActivity extends SettingsActivity { /* empty */}
     public static class BluetoothSettingsActivity extends SettingsActivity { /* empty */ }
     public static class CreateShortcutActivity extends SettingsActivity { /* empty */ }
-    public static class FaceSettingsActivity extends SettingsActivity {
-        @Override
-        protected void onCreate(Bundle savedState) {
-            setTheme(SetupWizardUtils.getTheme(this, getIntent()));
-            setTheme(R.style.SettingsPreferenceTheme_SetupWizard);
-            ThemeHelper.trySetDynamicColor(this);
-            super.onCreate(savedState);
-        }
-    }
+    public static class FaceSettingsActivity extends SettingsActivity { /* empty */ }
     /** Container for {@link FaceSettings} to use with a pre-defined task affinity. */
-    public static class FaceSettingsInternalActivity extends SettingsActivity {
-        @Override
-        protected void onCreate(Bundle savedState) {
-            setTheme(SetupWizardUtils.getTheme(this, getIntent()));
-            setTheme(R.style.SettingsPreferenceTheme_SetupWizard);
-            ThemeHelper.trySetDynamicColor(this);
-            super.onCreate(savedState);
-        }
-    }
-
+    public static class FaceSettingsInternalActivity extends SettingsActivity { /* empty */ }
     public static class FingerprintSettingsActivity extends SettingsActivity { /* empty */ }
     public static class FingerprintSettingsActivityV2 extends SettingsActivity { /* empty */ }
     public static class CombinedBiometricSettingsActivity extends SettingsActivity { /* empty */ }
@@ -377,6 +360,8 @@
     public static class NotificationAppListActivity extends SettingsActivity { /* empty */ }
     /** Activity to manage Cloned Apps page */
     public static class ClonedAppsListActivity extends SettingsActivity { /* empty */ }
+    /** Activity to manage Aspect Ratio app list page */
+    public static class UserAspectRatioAppListActivity extends SettingsActivity { /* empty */ }
     public static class NotificationReviewPermissionsActivity extends SettingsActivity { /* empty */ }
     public static class AppNotificationSettingsActivity extends SettingsActivity { /* empty */ }
     public static class ChannelNotificationSettingsActivity extends SettingsActivity { /* empty */ }
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 087c181..bc061e3 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -238,7 +238,7 @@
         String tag = getMetricsTag();
 
         return new SharedPreferencesLogger(this, tag,
-                FeatureFactory.getFactory(this).getMetricsFeatureProvider(),
+                FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(),
                 lookupMetricsCategory());
     }
 
@@ -292,8 +292,8 @@
     protected void createUiFromIntent(Bundle savedState, Intent intent) {
         long startTime = System.currentTimeMillis();
 
-        final FeatureFactory factory = FeatureFactory.getFactory(this);
-        mDashboardFeatureProvider = factory.getDashboardFeatureProvider(this);
+        final FeatureFactory factory = FeatureFactory.getFeatureFactory();
+        mDashboardFeatureProvider = factory.getDashboardFeatureProvider();
 
         if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
             getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
diff --git a/src/com/android/settings/accessibility/HearingDevicePairingDetail.java b/src/com/android/settings/accessibility/HearingDevicePairingDetail.java
index de86dcf..117a8ed 100644
--- a/src/com/android/settings/accessibility/HearingDevicePairingDetail.java
+++ b/src/com/android/settings/accessibility/HearingDevicePairingDetail.java
@@ -28,7 +28,8 @@
 import com.android.settings.bluetooth.BluetoothDevicePairingDetailBase;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 
-import java.util.Collections;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * HearingDevicePairingDetail is a page to scan hearing devices. This page shows scanning icons and
@@ -42,10 +43,16 @@
 
     public HearingDevicePairingDetail() {
         super();
-        final ScanFilter filter = new ScanFilter.Builder()
-                .setServiceData(BluetoothUuid.HEARING_AID, new byte[]{0}, new byte[]{0})
-                .build();
-        setFilter(Collections.singletonList(filter));
+        final List<ScanFilter> filterList = new ArrayList<>();
+        // Filters for ASHA hearing aids
+        filterList.add(new ScanFilter.Builder().setServiceUuid(BluetoothUuid.HEARING_AID).build());
+        filterList.add(new ScanFilter.Builder()
+                .setServiceData(BluetoothUuid.HEARING_AID, new byte[0]).build());
+        // Filters for LE audio hearing aids
+        filterList.add(new ScanFilter.Builder().setServiceUuid(BluetoothUuid.HAS).build());
+        filterList.add(new ScanFilter.Builder()
+                .setServiceData(BluetoothUuid.HAS, new byte[0]).build());
+        setFilter(filterList);
     }
 
     @Override
diff --git a/src/com/android/settings/applications/appcompat/UserAspectRatioAppsPreferenceController.java b/src/com/android/settings/applications/appcompat/UserAspectRatioAppsPreferenceController.java
new file mode 100644
index 0000000..ff68fb0
--- /dev/null
+++ b/src/com/android/settings/applications/appcompat/UserAspectRatioAppsPreferenceController.java
@@ -0,0 +1,48 @@
+/*
+ * 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.appcompat;
+
+import android.content.Context;
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+
+/**
+ * Preference controller for
+ * {@link com.android.settings.spa.app.appcompat.UserAspectRatioAppsPageProvider}
+ */
+public class UserAspectRatioAppsPreferenceController extends BasePreferenceController {
+
+    public UserAspectRatioAppsPreferenceController(@NonNull Context context,
+            @NonNull String preferenceKey) {
+        super(context, preferenceKey);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return UserAspectRatioManager.isFeatureEnabled(mContext)
+                ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+    }
+
+    @Override
+    public CharSequence getSummary() {
+        return mContext.getResources().getString(R.string.aspect_ratio_summary, Build.MODEL);
+    }
+}
diff --git a/src/com/android/settings/applications/appcompat/UserAspectRatioDetails.java b/src/com/android/settings/applications/appcompat/UserAspectRatioDetails.java
new file mode 100644
index 0000000..f8406f9
--- /dev/null
+++ b/src/com/android/settings/applications/appcompat/UserAspectRatioDetails.java
@@ -0,0 +1,224 @@
+/*
+ * 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.appcompat;
+
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_16_9;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_4_3;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE;
+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;
+
+import android.app.ActivityManager;
+import android.app.IActivityManager;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.R;
+import com.android.settings.applications.AppInfoWithHeader;
+import com.android.settingslib.widget.ActionButtonsPreference;
+import com.android.settingslib.widget.SelectorWithWidgetPreference;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * App specific activity to show aspect ratio overrides
+ */
+public class UserAspectRatioDetails extends AppInfoWithHeader implements
+        SelectorWithWidgetPreference.OnClickListener {
+    private static final String TAG = UserAspectRatioDetails.class.getSimpleName();
+
+    private static final String KEY_HEADER_BUTTONS = "header_view";
+    private static final String KEY_PREF_FULLSCREEN = "fullscreen_pref";
+    private static final String KEY_PREF_HALF_SCREEN = "half_screen_pref";
+    private static final String KEY_PREF_DISPLAY_SIZE = "display_size_pref";
+    private static final String KEY_PREF_16_9 = "16_9_pref";
+    private static final String KEY_PREF_4_3 = "4_3_pref";
+    @VisibleForTesting
+    static final String KEY_PREF_DEFAULT = "app_default_pref";
+    @VisibleForTesting
+    static final String KEY_PREF_3_2 = "3_2_pref";
+
+    private final List<SelectorWithWidgetPreference> mAspectRatioPreferences = new ArrayList<>();
+
+    @NonNull private UserAspectRatioManager mUserAspectRatioManager;
+    @NonNull private String mSelectedKey = KEY_PREF_DEFAULT;
+
+    @Override
+    public void onCreate(@NonNull Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mUserAspectRatioManager = new UserAspectRatioManager(getContext());
+        initPreferences();
+        try {
+            final int userAspectRatio = mUserAspectRatioManager
+                    .getUserMinAspectRatioValue(mPackageName, mUserId);
+            mSelectedKey = getSelectedKey(userAspectRatio);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to get user min aspect ratio");
+        }
+        refreshUi();
+    }
+
+    @Override
+    public void onRadioButtonClicked(@NonNull SelectorWithWidgetPreference selected) {
+        final String selectedKey = selected.getKey();
+        if (mSelectedKey.equals(selectedKey)) {
+            return;
+        }
+        final int userAspectRatio = getSelectedUserMinAspectRatio(selectedKey);
+        try {
+            getAspectRatioManager().setUserMinAspectRatio(mPackageName, mUserId, userAspectRatio);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to set user min aspect ratio");
+            return;
+        }
+        // Only update to selected aspect ratio if nothing goes wrong
+        mSelectedKey = selectedKey;
+        updateAllPreferences(mSelectedKey);
+        Log.d(TAG, "Killing application process " + mPackageName);
+        try {
+            final IActivityManager am = ActivityManager.getService();
+            am.stopAppForUser(mPackageName, mUserId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to stop application " + mPackageName);
+        }
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        // TODO(b/292566895): add metrics for logging
+        return 0;
+    }
+
+    @Override
+    protected boolean refreshUi() {
+        if (mPackageInfo == null || mPackageInfo.applicationInfo == null) {
+            return false;
+        }
+        updateAllPreferences(mSelectedKey);
+        return true;
+    }
+
+    @Override
+    protected AlertDialog createDialog(int id, int errorCode) {
+        return null;
+    }
+
+    private void launchApplication() {
+        Intent launchIntent = mPm.getLaunchIntentForPackage(mPackageName)
+                .addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP);
+        if (launchIntent != null) {
+            getContext().startActivityAsUser(launchIntent, new UserHandle(mUserId));
+        }
+    }
+
+    @PackageManager.UserMinAspectRatio
+    private int getSelectedUserMinAspectRatio(@NonNull String selectedKey) {
+        switch (selectedKey) {
+            case KEY_PREF_FULLSCREEN:
+                return USER_MIN_ASPECT_RATIO_FULLSCREEN;
+            case KEY_PREF_HALF_SCREEN:
+                return USER_MIN_ASPECT_RATIO_SPLIT_SCREEN;
+            case KEY_PREF_DISPLAY_SIZE:
+                return USER_MIN_ASPECT_RATIO_DISPLAY_SIZE;
+            case KEY_PREF_3_2:
+                return USER_MIN_ASPECT_RATIO_3_2;
+            case KEY_PREF_4_3:
+                return USER_MIN_ASPECT_RATIO_4_3;
+            case KEY_PREF_16_9:
+                return USER_MIN_ASPECT_RATIO_16_9;
+            default:
+                return USER_MIN_ASPECT_RATIO_UNSET;
+        }
+    }
+
+    @NonNull
+    private String getSelectedKey(@PackageManager.UserMinAspectRatio int userMinAspectRatio) {
+        switch (userMinAspectRatio) {
+            case USER_MIN_ASPECT_RATIO_FULLSCREEN:
+                return KEY_PREF_FULLSCREEN;
+            case USER_MIN_ASPECT_RATIO_SPLIT_SCREEN:
+                return KEY_PREF_HALF_SCREEN;
+            case USER_MIN_ASPECT_RATIO_DISPLAY_SIZE:
+                return KEY_PREF_DISPLAY_SIZE;
+            case USER_MIN_ASPECT_RATIO_3_2:
+                return KEY_PREF_3_2;
+            case USER_MIN_ASPECT_RATIO_4_3:
+                return KEY_PREF_4_3;
+            case USER_MIN_ASPECT_RATIO_16_9:
+                return KEY_PREF_16_9;
+            default:
+                return KEY_PREF_DEFAULT;
+        }
+    }
+
+    private void initPreferences() {
+        addPreferencesFromResource(R.xml.user_aspect_ratio_details);
+
+        ((ActionButtonsPreference) findPreference(KEY_HEADER_BUTTONS))
+                .setButton1Text(R.string.launch_instant_app)
+                .setButton1Icon(R.drawable.ic_settings_open)
+                .setButton1OnClickListener(v -> launchApplication());
+
+        addPreference(KEY_PREF_DEFAULT, USER_MIN_ASPECT_RATIO_UNSET);
+        addPreference(KEY_PREF_FULLSCREEN, USER_MIN_ASPECT_RATIO_FULLSCREEN);
+        addPreference(KEY_PREF_DISPLAY_SIZE, USER_MIN_ASPECT_RATIO_DISPLAY_SIZE);
+        addPreference(KEY_PREF_HALF_SCREEN, USER_MIN_ASPECT_RATIO_SPLIT_SCREEN);
+        addPreference(KEY_PREF_16_9, USER_MIN_ASPECT_RATIO_16_9);
+        addPreference(KEY_PREF_4_3, USER_MIN_ASPECT_RATIO_4_3);
+        addPreference(KEY_PREF_3_2, USER_MIN_ASPECT_RATIO_3_2);
+    }
+
+    private void addPreference(@NonNull String key,
+            @PackageManager.UserMinAspectRatio int aspectRatio) {
+        final SelectorWithWidgetPreference pref = findPreference(key);
+        if (pref == null) {
+            return;
+        }
+        if (!mUserAspectRatioManager.containsAspectRatioOption(aspectRatio)) {
+            pref.setVisible(false);
+            return;
+        }
+        pref.setTitle(mUserAspectRatioManager.getUserMinAspectRatioEntry(aspectRatio));
+        pref.setOnClickListener(this);
+        mAspectRatioPreferences.add(pref);
+    }
+
+    private void updateAllPreferences(@NonNull String selectedKey) {
+        for (SelectorWithWidgetPreference pref : mAspectRatioPreferences) {
+            pref.setChecked(selectedKey.equals(pref.getKey()));
+        }
+    }
+
+    @VisibleForTesting
+    UserAspectRatioManager getAspectRatioManager() {
+        return mUserAspectRatioManager;
+    }
+}
diff --git a/src/com/android/settings/applications/appcompat/UserAspectRatioManager.java b/src/com/android/settings/applications/appcompat/UserAspectRatioManager.java
new file mode 100644
index 0000000..c132fd0
--- /dev/null
+++ b/src/com/android/settings/applications/appcompat/UserAspectRatioManager.java
@@ -0,0 +1,224 @@
+/*
+ * 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.appcompat;
+
+import android.app.AppGlobals;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.RemoteException;
+import android.provider.DeviceConfig;
+import android.util.ArrayMap;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.settings.R;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Helper class for handling app aspect ratio override
+ * {@link PackageManager.UserMinAspectRatio} set by user
+ */
+public class UserAspectRatioManager {
+    private static final Intent LAUNCHER_ENTRY_INTENT =
+            new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER);
+
+    // TODO(b/288142656): Enable user aspect ratio settings by default
+    private static final boolean DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_SETTINGS = false;
+    @VisibleForTesting
+    static final String KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS =
+            "enable_app_compat_user_aspect_ratio_settings";
+    static final String KEY_ENABLE_USER_ASPECT_RATIO_FULLSCREEN =
+            "enable_app_compat_user_aspect_ratio_fullscreen";
+    private static final boolean DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_FULLSCREEN = true;
+
+    private final Context mContext;
+    private final IPackageManager mIPm;
+    /** Apps that have launcher entry defined in manifest */
+    private final List<ResolveInfo> mInfoHasLauncherEntryList;
+    private final Map<Integer, String> mUserAspectRatioMap;
+
+    public UserAspectRatioManager(@NonNull Context context) {
+        mContext = context;
+        mIPm = AppGlobals.getPackageManager();
+        mInfoHasLauncherEntryList = context.getPackageManager().queryIntentActivities(
+                UserAspectRatioManager.LAUNCHER_ENTRY_INTENT, PackageManager.GET_META_DATA);
+        mUserAspectRatioMap = getUserMinAspectRatioMapping();
+    }
+
+    /**
+     * Whether user aspect ratio settings is enabled for device.
+     */
+    public static boolean isFeatureEnabled(Context context) {
+        final boolean isBuildTimeFlagEnabled = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_appCompatUserAppAspectRatioSettingsIsEnabled);
+        return getValueFromDeviceConfig(KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS,
+                DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_SETTINGS) && isBuildTimeFlagEnabled;
+    }
+
+    /**
+     * @return user-specific {@link PackageManager.UserMinAspectRatio} override for an app
+     */
+    @PackageManager.UserMinAspectRatio
+    public int getUserMinAspectRatioValue(@NonNull String packageName, int uid)
+            throws RemoteException {
+        final int aspectRatio = mIPm.getUserMinAspectRatio(packageName, uid);
+        return containsAspectRatioOption(aspectRatio)
+                ? aspectRatio : PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
+    }
+
+    /**
+     * @return corresponding string for {@link PackageManager.UserMinAspectRatio} value
+     */
+    @NonNull
+    public String getUserMinAspectRatioEntry(@PackageManager.UserMinAspectRatio int aspectRatio) {
+        if (!containsAspectRatioOption(aspectRatio))  {
+            return mUserAspectRatioMap.get(PackageManager.USER_MIN_ASPECT_RATIO_UNSET);
+        }
+        return mUserAspectRatioMap.get(aspectRatio);
+    }
+
+    /**
+     * @return corresponding aspect ratio string for package name and user
+     */
+    @NonNull
+    public String getUserMinAspectRatioEntry(@NonNull String packageName, int uid)
+            throws RemoteException {
+        final int aspectRatio = getUserMinAspectRatioValue(packageName, uid);
+        return getUserMinAspectRatioEntry(aspectRatio);
+    }
+
+    /**
+     * Whether user aspect ratio option is specified in
+     * {@link R.array.config_userAspectRatioOverrideValues}
+     * and is enabled by device config
+     */
+    public boolean containsAspectRatioOption(@PackageManager.UserMinAspectRatio int option) {
+        if (option == PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN
+                && !isFullscreenOptionEnabled()) {
+            return false;
+        }
+        return mUserAspectRatioMap.containsKey(option);
+    }
+
+    /**
+     * Sets user-specified {@link PackageManager.UserMinAspectRatio} override for an app
+     */
+    public void setUserMinAspectRatio(@NonNull String packageName, int uid,
+            @PackageManager.UserMinAspectRatio int aspectRatio) throws RemoteException {
+        mIPm.setUserMinAspectRatio(packageName, uid, aspectRatio);
+    }
+
+    /**
+     * Whether an app's aspect ratio can be overridden by user. Only apps with launcher entry
+     * will be overridable.
+     */
+    public boolean canDisplayAspectRatioUi(@NonNull ApplicationInfo app) {
+        boolean hasLauncherEntry = mInfoHasLauncherEntryList.stream()
+                .anyMatch(info -> info.activityInfo.packageName.equals(app.packageName));
+        return hasLauncherEntry;
+    }
+
+    /**
+     * Whether fullscreen option in per-app user aspect ratio settings is enabled
+     */
+    @VisibleForTesting
+    boolean isFullscreenOptionEnabled() {
+        final boolean isBuildTimeFlagEnabled = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_appCompatUserAppAspectRatioFullscreenIsEnabled);
+        return isBuildTimeFlagEnabled && getValueFromDeviceConfig(
+                KEY_ENABLE_USER_ASPECT_RATIO_FULLSCREEN,
+                DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_FULLSCREEN);
+    }
+
+    private static boolean getValueFromDeviceConfig(String name, boolean defaultValue) {
+        return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER, name, defaultValue);
+    }
+
+    @NonNull
+    private Map<Integer, String> getUserMinAspectRatioMapping() {
+        final String[] userMinAspectRatioStrings = mContext.getResources().getStringArray(
+                R.array.config_userAspectRatioOverrideEntries);
+        final int[] userMinAspectRatioValues = mContext.getResources().getIntArray(
+                R.array.config_userAspectRatioOverrideValues);
+        if (userMinAspectRatioStrings.length != userMinAspectRatioValues.length) {
+            throw new RuntimeException(
+                    "config_userAspectRatioOverride options cannot be different length");
+        }
+
+        final Map<Integer, String> userMinAspectRatioMap = new ArrayMap<>();
+        for (int i = 0; i < userMinAspectRatioValues.length; i++) {
+            final int aspectRatioVal = userMinAspectRatioValues[i];
+            final String aspectRatioString = getAspectRatioStringOrDefault(
+                    userMinAspectRatioStrings[i], aspectRatioVal);
+            switch (aspectRatioVal) {
+                // Only map known values of UserMinAspectRatio and ignore unknown entries
+                case PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN:
+                case PackageManager.USER_MIN_ASPECT_RATIO_UNSET:
+                case PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN:
+                case PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE:
+                case PackageManager.USER_MIN_ASPECT_RATIO_4_3:
+                case PackageManager.USER_MIN_ASPECT_RATIO_16_9:
+                case PackageManager.USER_MIN_ASPECT_RATIO_3_2:
+                    userMinAspectRatioMap.put(aspectRatioVal, aspectRatioString);
+            }
+        }
+        if (!userMinAspectRatioMap.containsKey(PackageManager.USER_MIN_ASPECT_RATIO_UNSET)) {
+            throw new RuntimeException("config_userAspectRatioOverrideValues options must have"
+                    + " USER_MIN_ASPECT_RATIO_UNSET value");
+        }
+        return userMinAspectRatioMap;
+    }
+
+    @NonNull
+    private String getAspectRatioStringOrDefault(@Nullable String aspectRatioString,
+            @PackageManager.UserMinAspectRatio int aspectRatioVal) {
+        if (aspectRatioString != null) {
+            return aspectRatioString;
+        }
+        // Options are customized per device and if strings are set to @null, use default
+        switch (aspectRatioVal) {
+            case PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN:
+                return mContext.getString(R.string.user_aspect_ratio_fullscreen);
+            case PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN:
+                return mContext.getString(R.string.user_aspect_ratio_half_screen);
+            case PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE:
+                return mContext.getString(R.string.user_aspect_ratio_device_size);
+            case PackageManager.USER_MIN_ASPECT_RATIO_4_3:
+                return mContext.getString(R.string.user_aspect_ratio_4_3);
+            case PackageManager.USER_MIN_ASPECT_RATIO_16_9:
+                return mContext.getString(R.string.user_aspect_ratio_16_9);
+            case PackageManager.USER_MIN_ASPECT_RATIO_3_2:
+                return mContext.getString(R.string.user_aspect_ratio_3_2);
+            default:
+                return mContext.getString(R.string.user_aspect_ratio_app_default);
+        }
+    }
+
+    @VisibleForTesting
+    void addInfoHasLauncherEntry(@NonNull ResolveInfo infoHasLauncherEntry) {
+        mInfoHasLauncherEntryList.add(infoHasLauncherEntry);
+    }
+}
diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java
index 548ca55..d734a27 100644
--- a/src/com/android/settings/applications/manageapplications/ManageApplications.java
+++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java
@@ -269,6 +269,7 @@
     public static final int LIST_TYPE_CLONED_APPS = 17;
     public static final int LIST_TYPE_NFC_TAG_APPS = 18;
     public static final int LIST_TYPE_TURN_SCREEN_ON = 19;
+    public static final int LIST_TYPE_USER_ASPECT_RATIO_APPS = 20;
 
     // List types that should show instant apps.
     public static final Set<Integer> LIST_TYPES_WITH_INSTANT = new ArraySet<>(Arrays.asList(
diff --git a/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt b/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt
index 6574f69..216ce47 100644
--- a/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt
+++ b/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt
@@ -20,6 +20,7 @@
 import android.util.FeatureFlagUtils
 import com.android.settings.Settings.AlarmsAndRemindersActivity
 import com.android.settings.Settings.AppBatteryUsageActivity
+import com.android.settings.Settings.UserAspectRatioAppListActivity
 import com.android.settings.Settings.ChangeNfcTagAppsActivity
 import com.android.settings.Settings.ChangeWifiStateActivity
 import com.android.settings.Settings.ClonedAppsListActivity
@@ -40,6 +41,7 @@
 import com.android.settings.applications.manageapplications.ManageApplications.LIST_MANAGE_EXTERNAL_STORAGE
 import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_ALARMS_AND_REMINDERS
 import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_APPS_LOCALE
+import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_USER_ASPECT_RATIO_APPS
 import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_BATTERY_OPTIMIZATION
 import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_CLONED_APPS
 import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_GAMES
@@ -57,6 +59,7 @@
 import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_WIFI_ACCESS
 import com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_WRITE_SETTINGS
 import com.android.settings.spa.app.AllAppListPageProvider
+import com.android.settings.spa.app.appcompat.UserAspectRatioAppsPageProvider
 import com.android.settings.spa.app.specialaccess.AlarmsAndRemindersAppListProvider
 import com.android.settings.spa.app.specialaccess.AllFilesAccessAppListProvider
 import com.android.settings.spa.app.specialaccess.DisplayOverOtherAppsAppListProvider
@@ -65,6 +68,7 @@
 import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider
 import com.android.settings.spa.app.specialaccess.NfcTagAppsSettingsProvider
 import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider
+import com.android.settings.spa.app.storage.StorageAppListPageProvider
 import com.android.settings.spa.notification.AppListNotificationsPageProvider
 import com.android.settings.spa.system.AppLanguagesPageProvider
 
@@ -92,6 +96,7 @@
         ClonedAppsListActivity::class to LIST_TYPE_CLONED_APPS,
         ChangeNfcTagAppsActivity::class to LIST_TYPE_NFC_TAG_APPS,
         TurnScreenOnSettingsActivity::class to LIST_TYPE_TURN_SCREEN_ON,
+        UserAspectRatioAppListActivity::class to LIST_TYPE_USER_ASPECT_RATIO_APPS,
     )
 
     @JvmField
@@ -114,6 +119,10 @@
             LIST_TYPE_APPS_LOCALE -> AppLanguagesPageProvider.name
             LIST_TYPE_MAIN -> AllAppListPageProvider.name
             LIST_TYPE_NFC_TAG_APPS -> NfcTagAppsSettingsProvider.getAppListRoute()
+            LIST_TYPE_USER_ASPECT_RATIO_APPS -> UserAspectRatioAppsPageProvider.name
+            // TODO(b/292165031) enable once sorting is supported
+            //LIST_TYPE_STORAGE -> StorageAppListPageProvider.Apps.name
+            //LIST_TYPE_GAMES -> StorageAppListPageProvider.Games.name
             else -> null
         }
     }
diff --git a/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java b/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java
index 2a350f4..46f534d 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java
@@ -236,6 +236,9 @@
     protected void onResume() {
         super.onResume();
 
+        //reset mNextClick to make sure introduction page would be closed correctly
+        mNextClicked = false;
+
         final int errorMsg = checkMaxEnrolled();
         if (errorMsg == 0) {
             mErrorText.setText(null);
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
index 8444ad6..3c4eb10 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
@@ -480,10 +480,8 @@
                         R.string.security_settings_fingerprint_enroll_introduction_v3_message,
                         DeviceHelper.getDeviceName(getActivity()));
                 column.mLearnMoreClickListener = learnMoreClickListener;
-                if (isSfps()) {
-                    column.mLearnMoreOverrideText = getText(
-                            R.string.security_settings_fingerprint_settings_footer_learn_more);
-                }
+                column.mLearnMoreOverrideText = getText(
+                        R.string.security_settings_fingerprint_settings_footer_learn_more);
                 mFooterColumns.add(column);
             }
         }
diff --git a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollProgressBarDrawable.java b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollProgressBarDrawable.java
index aa3f770..75251cf 100644
--- a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollProgressBarDrawable.java
+++ b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollProgressBarDrawable.java
@@ -202,6 +202,7 @@
             return;
         }
 
+        mShowingHelp = showingHelp;
         if (mShowingHelp) {
             if (mVibrator != null && mIsAccessibilityEnabled) {
                 mVibrator.vibrate(Process.myUid(), mContext.getOpPackageName(),
@@ -228,7 +229,6 @@
             }
         }
 
-        mShowingHelp = showingHelp;
         mRemainingSteps = remainingSteps;
         mTotalSteps = totalSteps;
 
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
index ba2786e..1fd09a3 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
@@ -89,7 +89,7 @@
         mManager = manager;
         mProfileManager = mManager.getProfileManager();
         mCachedDevice = device;
-        mAllOfCachedDevices = Utils.getAllOfCachedBluetoothDevices(mContext, mCachedDevice);
+        mAllOfCachedDevices = Utils.getAllOfCachedBluetoothDevices(mManager, mCachedDevice);
         lifecycle.addObserver(this);
     }
 
@@ -481,7 +481,7 @@
         for (CachedBluetoothDevice item : mAllOfCachedDevices) {
             item.unregisterCallback(this);
         }
-        mAllOfCachedDevices = Utils.getAllOfCachedBluetoothDevices(mContext, mCachedDevice);
+        mAllOfCachedDevices = Utils.getAllOfCachedBluetoothDevices(mManager, mCachedDevice);
         for (CachedBluetoothDevice item : mAllOfCachedDevices) {
             item.registerCallback(this);
         }
diff --git a/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java
index e30bbfb..f72494f 100644
--- a/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java
+++ b/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java
@@ -88,6 +88,7 @@
 
     @VisibleForTesting
     LayoutPreference mLayoutPreference;
+    LocalBluetoothManager mManager;
     private CachedBluetoothDevice mCachedDevice;
     private List<CachedBluetoothDevice> mAllOfCachedDevices;
     @VisibleForTesting
@@ -152,8 +153,9 @@
     public void init(CachedBluetoothDevice cachedBluetoothDevice,
             LocalBluetoothManager bluetoothManager) {
         mCachedDevice = cachedBluetoothDevice;
+        mManager = bluetoothManager;
         mProfileManager = bluetoothManager.getProfileManager();
-        mAllOfCachedDevices = Utils.getAllOfCachedBluetoothDevices(mContext, mCachedDevice);
+        mAllOfCachedDevices = Utils.getAllOfCachedBluetoothDevices(mManager, mCachedDevice);
     }
 
     @VisibleForTesting
@@ -300,7 +302,7 @@
         for (CachedBluetoothDevice item : mAllOfCachedDevices) {
             item.unregisterCallback(this);
         }
-        mAllOfCachedDevices = Utils.getAllOfCachedBluetoothDevices(mContext, mCachedDevice);
+        mAllOfCachedDevices = Utils.getAllOfCachedBluetoothDevices(mManager, mCachedDevice);
         for (CachedBluetoothDevice item : mAllOfCachedDevices) {
             item.registerCallback(this);
         }
diff --git a/src/com/android/settings/bluetooth/Utils.java b/src/com/android/settings/bluetooth/Utils.java
index 79a2de0..f1d6b20 100644
--- a/src/com/android/settings/bluetooth/Utils.java
+++ b/src/com/android/settings/bluetooth/Utils.java
@@ -235,7 +235,8 @@
      * @param cachedBluetoothDevice The main cachedBluetoothDevice.
      * @return all cachedBluetoothDevices with the same groupId.
      */
-    public static List<CachedBluetoothDevice> getAllOfCachedBluetoothDevices(Context context,
+    public static List<CachedBluetoothDevice> getAllOfCachedBluetoothDevices(
+            LocalBluetoothManager localBtMgr,
             CachedBluetoothDevice cachedBluetoothDevice) {
         List<CachedBluetoothDevice> cachedBluetoothDevices = new ArrayList<>();
         if (cachedBluetoothDevice == null) {
@@ -248,7 +249,6 @@
             return cachedBluetoothDevices;
         }
 
-        final LocalBluetoothManager localBtMgr = Utils.getLocalBtManager(context);
         if (localBtMgr == null) {
             Log.e(TAG, "getAllOfCachedBluetoothDevices: no LocalBluetoothManager");
             return cachedBluetoothDevices;
diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java
index d4acfa1..9d673b1 100644
--- a/src/com/android/settings/dashboard/DashboardFragment.java
+++ b/src/com/android/settings/dashboard/DashboardFragment.java
@@ -94,8 +94,8 @@
         super.onAttach(context);
         mSuppressInjectedTileKeys = Arrays.asList(context.getResources().getStringArray(
                 R.array.config_suppress_injected_tile_keys));
-        mDashboardFeatureProvider = FeatureFactory.getFactory(context).
-                getDashboardFeatureProvider(context);
+        mDashboardFeatureProvider =
+                FeatureFactory.getFeatureFactory().getDashboardFeatureProvider();
         // Load preference controllers from code
         final List<AbstractPreferenceController> controllersFromCode =
                 createPreferenceControllers(context);
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index 047b219..01606f2 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -744,6 +744,7 @@
         controllers.add(new ContrastPreferenceController(
                 context, context.getSystemService(UiModeManager.class)));
         controllers.add(new ForceEnableNotesRolePreferenceController(context));
+        controllers.add(new GrammaticalGenderPreferenceController(context));
 
         return controllers;
     }
diff --git a/src/com/android/settings/development/GrammaticalGenderPreferenceController.java b/src/com/android/settings/development/GrammaticalGenderPreferenceController.java
new file mode 100644
index 0000000..993b318
--- /dev/null
+++ b/src/com/android/settings/development/GrammaticalGenderPreferenceController.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.development;
+
+import android.app.ActivityManager;
+import android.app.IActivityManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.text.TextUtils;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.ListPreference;
+import androidx.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.development.DeveloperOptionsPreferenceController;
+
+/**
+ * Preference controller to control Grammatical Gender
+ */
+public class GrammaticalGenderPreferenceController extends DeveloperOptionsPreferenceController
+        implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
+
+    private static final String GRAMMATICAL_GENDER_KEY =
+            "grammatical_gender";
+    @VisibleForTesting
+    static final String GRAMMATICAL_GENDER_PROPERTY = "persist.sys.grammatical_gender";
+    private final String[] mListValues;
+    private final String[] mListSummaries;
+
+    private IActivityManager mActivityManager;
+
+    public GrammaticalGenderPreferenceController(Context context) {
+        this(context, ActivityManager.getService());
+    }
+
+    @VisibleForTesting
+    GrammaticalGenderPreferenceController(Context context,
+            IActivityManager activityManager) {
+        super(context);
+
+        mListValues = context.getResources().getStringArray(R.array.grammatical_gender_values);
+        mListSummaries = context.getResources().getStringArray(R.array.grammatical_gender_entries);
+        mActivityManager = activityManager;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return GRAMMATICAL_GENDER_KEY;
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        SystemProperties.set(GRAMMATICAL_GENDER_PROPERTY, newValue.toString());
+        updateState(mPreference);
+        try {
+            Configuration config = mActivityManager.getConfiguration();
+            config.setGrammaticalGender(Integer.parseInt(newValue.toString()));
+            mActivityManager.updatePersistentConfiguration(config);
+        } catch (RemoteException ex) {
+            // intentional no-op
+        }
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        final ListPreference listPreference = (ListPreference) preference;
+        final String currentValue = SystemProperties.get(GRAMMATICAL_GENDER_PROPERTY);
+        int index = 0; // Defaults to Not Selected
+        for (int i = 0; i < mListValues.length; i++) {
+            if (TextUtils.equals(currentValue, mListValues[i])) {
+                index = i;
+                break;
+            }
+        }
+        listPreference.setValue(mListValues[index]);
+        listPreference.setSummary(mListSummaries[index]);
+    }
+}
diff --git a/src/com/android/settings/dream/WhenToDreamPicker.java b/src/com/android/settings/dream/WhenToDreamPicker.java
index 13cdadf..3052d20 100644
--- a/src/com/android/settings/dream/WhenToDreamPicker.java
+++ b/src/com/android/settings/dream/WhenToDreamPicker.java
@@ -50,7 +50,7 @@
 
     @Override
     public int getMetricsCategory() {
-        return SettingsEnums.DREAM;
+        return SettingsEnums.SETTINGS_WHEN_TO_DREAM;
     }
 
     @Override
diff --git a/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelper.java b/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelper.java
index 2376c80..0f4634e 100644
--- a/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelper.java
+++ b/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelper.java
@@ -24,6 +24,7 @@
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.DialogInterface;
 import android.os.Process;
 import android.os.UserHandle;
 import android.view.LayoutInflater;
@@ -88,10 +89,11 @@
 
     public AlertDialog.Builder prepareDialogBuilder(String restriction,
             EnforcedAdmin enforcedAdmin) {
+        DialogInterface.OnClickListener listener = mActionDisabledByAdminController
+                .getPositiveButtonListener(mActivity, enforcedAdmin);
         AlertDialog.Builder builder = new AlertDialog.Builder(mActivity)
-                .setPositiveButton(R.string.suggestion_button_close,
-                        mActionDisabledByAdminController
-                                .getPositiveButtonListener(mActivity, enforcedAdmin))
+                .setPositiveButton(listener == null
+                        ? R.string.suggestion_button_close : R.string.okay, listener)
                 .setView(mDialogView);
         prepareDialogBuilder(builder, restriction, enforcedAdmin);
         return builder;
diff --git a/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java b/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java
index 4b9e6ef..8697e43 100644
--- a/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java
+++ b/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java
@@ -16,8 +16,8 @@
 
 package com.android.settings.fuelgauge;
 
-import android.content.ContentResolver;
 import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.provider.Settings;
@@ -25,8 +25,6 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.settings.R;
-import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry;
 import com.android.settings.fuelgauge.batterysaver.BatterySaverScheduleRadioButtonsController;
 import com.android.settingslib.fuelgauge.BatterySaverUtils;
 
@@ -41,6 +39,7 @@
 
     @Override
     public void onReceive(Context context, Intent intent) {
+        Log.d(TAG, "onReceive: " + intent + " owner: " + BatteryBackupHelper.isOwner());
         if (intent != null
                 && Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())
                 && BatteryBackupHelper.isOwner()) {
diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
index c06e7f0..258ded1 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.os.Bundle;
 import android.util.ArrayMap;
 import android.util.SparseIntArray;
 
@@ -37,6 +38,16 @@
     boolean isBatteryUsageEnabled();
 
     /**
+     * Check whether the battery tips card is enabled in the battery usage page
+     */
+    boolean isBatteryTipsEnabled();
+
+    /**
+     * Check whether the feedback card is enabled in the battery tips card
+     */
+    boolean isBatteryTipsFeedbackEnabled();
+
+    /**
      * Returns a threshold (in milliseconds) for the minimal screen on time in battery usage list
      */
     double getBatteryUsageListScreenOnTimeThresholdInMs();
@@ -129,9 +140,14 @@
     boolean delayHourlyJobWhenBooting();
 
     /**
-     * Insert device usage data for anomaly detection
+     * Insert settings configuration data for anomaly detection
      */
-    void insertSettingsData(Context context);
+    void insertSettingsData(Context context, double displayDrain);
+
+    /**
+     * Returns {@link Bundle} for settings anomaly detection result
+     */
+    Bundle detectSettingsAnomaly(Context context, double displayDrain);
 
     /**
      * Gets an intent for one time bypass charge limited to resume charging.
diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
index 89d793a..9b5bb5e 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.os.Bundle;
 import android.os.Process;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -75,6 +76,16 @@
     }
 
     @Override
+    public boolean isBatteryTipsEnabled() {
+        return false;
+    }
+
+    @Override
+    public boolean isBatteryTipsFeedbackEnabled() {
+        return true;
+    }
+
+    @Override
     public double getBatteryUsageListScreenOnTimeThresholdInMs() {
         return 0;
     }
@@ -161,7 +172,12 @@
     }
 
     @Override
-    public void insertSettingsData(Context context)  {}
+    public void insertSettingsData(Context context, double displayDrain) {}
+
+    @Override
+    public Bundle detectSettingsAnomaly(Context context, double displayDrain) {
+        return null;
+    }
 
     @Override
     public Set<Integer> getOthersSystemComponentSet() {
diff --git a/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceController.java b/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceController.java
index f449723..7287302 100644
--- a/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceController.java
@@ -18,6 +18,7 @@
 
 import android.content.ComponentName;
 import android.content.Context;
+import android.os.BatteryManager;
 import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
@@ -139,7 +140,10 @@
         if (Utils.containsIncompatibleChargers(mContext, TAG)) {
             return mContext.getString(R.string.battery_info_status_not_charging);
         }
-        if (!info.discharging && info.chargeLabel != null) {
+        if (info.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING) {
+            // Present status only if no remaining time or status anomalous
+            return info.statusLabel;
+        } else if (!info.discharging && info.chargeLabel != null) {
             return info.chargeLabel;
         } else if (info.remainingLabel == null) {
             return info.batteryPercentString;
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/IncompatibleChargerTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/IncompatibleChargerTip.java
index 1c5616f..48cfb7a 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/IncompatibleChargerTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/IncompatibleChargerTip.java
@@ -52,7 +52,7 @@
 
     @Override
     public int getIconId() {
-        return R.drawable.ic_battery_alert_theme;
+        return R.drawable.ic_battery_charger;
     }
 
     @Override
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
index 17d9c8a..b7e1885 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
@@ -98,6 +98,20 @@
         void onScreenOnTimeUpdated(Long screenOnTime, String slotTimestamp);
     }
 
+    /**
+     * A callback listener for the battery tips card is updated.
+     * This happens when battery tips card is ready.
+     */
+    public interface OnBatteryTipsUpdatedListener {
+        /**
+         * The callback function for the battery tips card is updated.
+         * @param title the title of the battery tip card
+         * @param summary the summary of the battery tip card
+         */
+        void onBatteryTipsUpdated(String title, String summary);
+    }
+
+
     @VisibleForTesting
     Context mPrefContext;
     @VisibleForTesting
@@ -119,6 +133,7 @@
     private List<BatteryChartViewModel> mHourlyViewModels;
     private OnBatteryUsageUpdatedListener mOnBatteryUsageUpdatedListener;
     private OnScreenOnTimeUpdatedListener mOnScreenOnTimeUpdatedListener;
+    private OnBatteryTipsUpdatedListener mOnBatteryTipsUpdatedListener;
 
     private final SettingsActivity mActivity;
     private final MetricsFeatureProvider mMetricsFeatureProvider;
@@ -209,6 +224,10 @@
         mOnScreenOnTimeUpdatedListener = listener;
     }
 
+    void setOnBatteryTipsUpdatedListener(OnBatteryTipsUpdatedListener listener) {
+        mOnBatteryTipsUpdatedListener = listener;
+    }
+
     void setBatteryHistoryMap(
             final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
         Log.d(TAG, "setBatteryHistoryMap() " + (batteryHistoryMap == null ? "null"
@@ -344,6 +363,10 @@
             }
             mOnBatteryUsageUpdatedListener.onBatteryUsageUpdated(
                     slotUsageData, getSlotInformation(), isBatteryUsageMapNullOrEmpty());
+
+            if (mOnBatteryTipsUpdatedListener != null) {
+                mOnBatteryTipsUpdatedListener.onBatteryTipsUpdated(null, null);
+            }
         }
         return true;
     }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java
new file mode 100644
index 0000000..89e2ce9
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.batteryusage;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.TextView;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.settings.R;
+import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
+import com.android.settings.overlay.FeatureFactory;
+
+import com.google.android.material.button.MaterialButton;
+
+/**
+ * A preference for displaying the battery tips card view.
+ */
+public class BatteryTipsCardPreference extends Preference implements View.OnClickListener {
+
+    private static final String TAG = "BatteryTipsCardPreference";
+
+    private final PowerUsageFeatureProvider mPowerUsageFeatureProvider;
+
+    private MaterialButton mActionButton;
+    private ImageButton mDismissButton;
+    private ImageButton mThumbUpButton;
+    private ImageButton mThumbDownButton;
+    private CharSequence mTitle;
+    private CharSequence mSummary;
+
+    public BatteryTipsCardPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setLayoutResource(R.layout.battery_tips_card);
+        setSelectable(false);
+        mPowerUsageFeatureProvider = FeatureFactory.getFeatureFactory()
+            .getPowerUsageFeatureProvider();
+    }
+
+    @Override
+    public void setTitle(CharSequence title) {
+        mTitle = title;
+        notifyChanged();
+    }
+
+    @Override
+    public void setSummary(CharSequence summary) {
+        mSummary = summary;
+        notifyChanged();
+    }
+
+    @Override
+    public void onClick(View view) {
+        // TODO: replace with the settings anomaly obtained from detectSettingsAnomaly();
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder view) {
+        super.onBindViewHolder(view);
+
+        ((TextView) view.findViewById(R.id.title)).setText(mTitle);
+        ((TextView) view.findViewById(R.id.summary)).setText(mSummary);
+
+        mActionButton = (MaterialButton) view.findViewById(R.id.action_button);
+        mActionButton.setOnClickListener(this);
+        mDismissButton = (ImageButton) view.findViewById(R.id.dismiss_button);
+        mDismissButton.setOnClickListener(this);
+
+        if (!mPowerUsageFeatureProvider.isBatteryTipsFeedbackEnabled()) {
+            return;
+        }
+        view.findViewById(R.id.tips_card)
+                .setBackgroundResource(R.drawable.battery_tips_half_rounded_top_bg);
+        view.findViewById(R.id.feedback_card).setVisibility(View.VISIBLE);
+
+        mThumbUpButton = (ImageButton) view.findViewById(R.id.thumb_up);
+        mThumbUpButton.setOnClickListener(this);
+        mThumbDownButton = (ImageButton) view.findViewById(R.id.thumb_down);
+        mThumbDownButton.setOnClickListener(this);
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsController.java
new file mode 100644
index 0000000..87c43a1
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsController.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.batteryusage;
+
+import android.content.Context;
+
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
+import com.android.settings.overlay.FeatureFactory;
+
+/** Controls the update for battery tips card */
+public class BatteryTipsController extends BasePreferenceController {
+
+    private static final String TAG = "BatteryTipsController";
+    private static final String ROOT_PREFERENCE_KEY = "battery_tips_category";
+    private static final String CARD_PREFERENCE_KEY = "battery_tips_card";
+
+    private final PowerUsageFeatureProvider mPowerUsageFeatureProvider;
+
+    private Context mPrefContext;
+    private BatteryTipsCardPreference mCardPreference;
+
+    public BatteryTipsController(Context context) {
+        super(context, ROOT_PREFERENCE_KEY);
+        mPowerUsageFeatureProvider = FeatureFactory.getFeatureFactory()
+            .getPowerUsageFeatureProvider();
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AVAILABLE;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mPrefContext = screen.getContext();
+        mCardPreference = screen.findPreference(CARD_PREFERENCE_KEY);
+    }
+
+    /**
+     * Update the card visibility and contents.
+     * @param title a string not extend 2 lines.
+     * @param summary a string not extend 10 lines.
+     */
+    // TODO: replace parameters with SettingsAnomaly Data Proto
+    public void handleBatteryTipsCardUpdated(String title, String summary) {
+        if (!mPowerUsageFeatureProvider.isBatteryTipsEnabled()) {
+            mCardPreference.setVisible(false);
+            return;
+        }
+        if (title == null || summary == null) {
+            mCardPreference.setVisible(false);
+            return;
+        }
+        mCardPreference.setTitle(title);
+        mCardPreference.setSummary(summary);
+        mCardPreference.setVisible(true);
+    }
+
+}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java b/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java
index 67462b3..ebf1543 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java
@@ -83,6 +83,10 @@
             recheckIntent.setClass(context, BootBroadcastReceiver.class);
             final long delayedTime = getRescheduleTimeForBootAction(context);
             mHandler.postDelayed(() -> context.sendBroadcast(recheckIntent), delayedTime);
+
+            // Refreshes the usage source from UsageStatsManager when booting.
+            DatabaseUtils.removeUsageSource(context);
+
             BatteryUsageLogUtils.writeLog(context, Action.RECHECK_JOB, "delay:" + delayedTime);
         } else if (ACTION_SETUP_WIZARD_FINISHED.equals(action)) {
             ElapsedTimeUtils.storeSuwFinishedTimestamp(context, System.currentTimeMillis());
diff --git a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
index 2c98c4b..3fc44cc 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
@@ -17,7 +17,6 @@
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
-import android.app.usage.IUsageStatsManager;
 import android.app.usage.UsageEvents.Event;
 import android.app.usage.UsageStatsManager;
 import android.content.ContentValues;
@@ -27,7 +26,6 @@
 import android.os.BatteryUsageStats;
 import android.os.Build;
 import android.os.LocaleList;
-import android.os.RemoteException;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.text.format.DateFormat;
@@ -67,6 +65,12 @@
     public static final int CONSUMER_TYPE_USER_BATTERY = 2;
     public static final int CONSUMER_TYPE_SYSTEM_BATTERY = 3;
 
+    public static final int DEFAULT_USAGE_SOURCE = UsageStatsManager.USAGE_SOURCE_CURRENT_ACTIVITY;
+    public static final int EMPTY_USAGE_SOURCE = -1;
+
+    @VisibleForTesting
+    static int sUsageSource = EMPTY_USAGE_SOURCE;
+
     private ConvertUtils() {
     }
 
@@ -181,8 +185,7 @@
     /** Converts to {@link AppUsageEvent} from {@link Event} */
     @Nullable
     public static AppUsageEvent convertToAppUsageEvent(
-            Context context, final IUsageStatsManager usageStatsManager, final Event event,
-            final long userId) {
+            Context context, final Event event, final long userId) {
         final String packageName = event.getPackageName();
         if (packageName == null) {
             // See b/190609174: Event package names should never be null, but sometimes they are.
@@ -207,7 +210,7 @@
         }
 
         final String effectivePackageName =
-                getEffectivePackageName(usageStatsManager, packageName, taskRootPackageName);
+                getEffectivePackageName(context, packageName, taskRootPackageName);
         try {
             final long uid = context
                     .getPackageManager()
@@ -323,9 +326,8 @@
      */
     @VisibleForTesting
     static String getEffectivePackageName(
-            final IUsageStatsManager usageStatsManager, final String packageName,
-            final String taskRootPackageName) {
-        int usageSource = getUsageSource(usageStatsManager);
+            Context context, final String packageName, final String taskRootPackageName) {
+        final int usageSource = getUsageSource(context);
         switch (usageSource) {
             case UsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVITY:
                 return !TextUtils.isEmpty(taskRootPackageName)
@@ -370,18 +372,11 @@
         }
     }
 
-    /**
-     * Returns what App Usage Observers will consider the source of usage for an activity.
-     *
-     * @see UsageStatsManager#getUsageSource()
-     */
-    private static int getUsageSource(final IUsageStatsManager usageStatsManager) {
-        try {
-            return usageStatsManager.getUsageSource();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to getUsageSource", e);
-            return UsageStatsManager.USAGE_SOURCE_CURRENT_ACTIVITY;
+    private static int getUsageSource(Context context) {
+        if (sUsageSource == EMPTY_USAGE_SOURCE) {
+            sUsageSource = DatabaseUtils.getUsageSource(context);
         }
+        return sUsageSource;
     }
 
     private static AppUsageEventType getAppUsageEventType(final int eventType) {
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
index 0f67e6a..1c851fd 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
@@ -397,8 +397,8 @@
         }
         // Generates the indexed AppUsagePeriod list data for each corresponding time slot for
         // further use.
-        mAppUsagePeriodMap = DataProcessor.generateAppUsagePeriodMap(mRawStartTimestamp,
-                mHourlyBatteryLevelsPerDay, mAppUsageEventList, mBatteryEventList);
+        mAppUsagePeriodMap = DataProcessor.generateAppUsagePeriodMap(
+                mContext, mHourlyBatteryLevelsPerDay, mAppUsageEventList, mBatteryEventList);
     }
 
     private void tryToGenerateFinalDataAndApplyCallback() {
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
index 6de3f59..f1a8063 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
@@ -32,7 +32,6 @@
 import android.os.BatteryUsageStatsQuery;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.UidBatteryConsumer;
 import android.os.UserBatteryConsumer;
 import android.os.UserHandle;
@@ -78,8 +77,6 @@
     private static final int MIN_DAILY_DATA_SIZE = 2;
     private static final int MIN_TIMESTAMP_DATA_SIZE = 2;
     private static final int MAX_DIFF_SECONDS_OF_UPPER_TIMESTAMP = 5;
-    // Maximum total time value for each hourly slot cumulative data at most 2 hours.
-    private static final float TOTAL_HOURLY_TIME_THRESHOLD = DateUtils.HOUR_IN_MILLIS * 2;
     private static final long MIN_TIME_SLOT = DateUtils.HOUR_IN_MILLIS * 2;
     private static final String MEDIASERVER_PACKAGE_NAME = "mediaserver";
     private static final String ANDROID_CORE_APPS_SHARED_USER_ID = "android.uid.shared";
@@ -111,11 +108,6 @@
     @VisibleForTesting
     static Set<String> sTestSystemAppsPackageNames;
 
-    @VisibleForTesting
-    static IUsageStatsManager sUsageStatsManager =
-            IUsageStatsManager.Stub.asInterface(
-                    ServiceManager.getService(Context.USAGE_STATS_SERVICE));
-
     public static final String CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER =
             "CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER";
 
@@ -271,7 +263,7 @@
     @Nullable
     public static Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>>
             generateAppUsagePeriodMap(
-                    final long rawStartTimestamp,
+                    Context context,
                     final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay,
                     final List<AppUsageEvent> appUsageEventList,
                     final List<BatteryEvent> batteryEventList) {
@@ -305,7 +297,7 @@
                 // The value could be null when there is no data in the hourly slot.
                 dailyMap.put(
                         hourlyIndex,
-                        buildAppUsagePeriodList(hourlyAppUsageEventList, batteryEventList,
+                        buildAppUsagePeriodList(context, hourlyAppUsageEventList, batteryEventList,
                                 startTimestamp, endTimestamp));
             }
         }
@@ -346,8 +338,7 @@
                             break;
                         }
                         final AppUsageEvent appUsageEvent =
-                                ConvertUtils.convertToAppUsageEvent(
-                                        context, sUsageStatsManager, event, userId);
+                                ConvertUtils.convertToAppUsageEvent(context, event, userId);
                         if (appUsageEvent != null) {
                             numEventsFetched++;
                             appUsageEventList.add(appUsageEvent);
@@ -661,8 +652,8 @@
     @VisibleForTesting
     @Nullable
     static Map<Long, Map<String, List<AppUsagePeriod>>> buildAppUsagePeriodList(
-            final List<AppUsageEvent> appUsageEvents, final List<BatteryEvent> batteryEventList,
-            final long startTime, final long endTime) {
+            Context context, final List<AppUsageEvent> appUsageEvents,
+            final List<BatteryEvent> batteryEventList, final long startTime, final long endTime) {
         if (appUsageEvents.isEmpty()) {
             return null;
         }
@@ -702,7 +693,7 @@
             final AppUsageEvent firstEvent = usageEvents.get(0);
             final long eventUserId = firstEvent.getUserId();
             final String packageName = getEffectivePackageName(
-                    sUsageStatsManager,
+                    context,
                     firstEvent.getPackageName(),
                     firstEvent.getTaskRootPackageName());
             usageEvents.addAll(deviceEvents);
@@ -975,7 +966,7 @@
         final long startTime = DatabaseUtils.getAppUsageStartTimestampOfUser(
                 context, userID, earliestTimestamp);
         return loadAppUsageEventsForUserFromService(
-                sUsageStatsManager, startTime, now, userID, callingPackage);
+                DatabaseUtils.sUsageStatsManager, startTime, now, userID, callingPackage);
     }
 
     @Nullable
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
index 8d1a2f9..ea1f3ed 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
@@ -15,6 +15,8 @@
  */
 
 package com.android.settings.fuelgauge.batteryusage;
+import android.app.usage.IUsageStatsManager;
+import android.app.usage.UsageStatsManager;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
@@ -28,6 +30,8 @@
 import android.os.BatteryUsageStats;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserManager;
 import android.util.Log;
@@ -63,6 +67,7 @@
     static final int DATA_RETENTION_INTERVAL_DAY = 9;
     static final String KEY_LAST_LOAD_FULL_CHARGE_TIME = "last_load_full_charge_time";
     static final String KEY_LAST_UPLOAD_FULL_CHARGE_TIME = "last_upload_full_charge_time";
+    static final String KEY_LAST_USAGE_SOURCE = "last_usage_source";
 
     /** An authority name of the battery content provider. */
     public static final String AUTHORITY = "com.android.settings.battery.usage.provider";
@@ -74,8 +79,6 @@
     public static final String BATTERY_STATE_TABLE = "BatteryState";
     /** A path name for app usage latest timestamp query. */
     public static final String APP_USAGE_LATEST_TIMESTAMP_PATH = "appUsageLatestTimestamp";
-    /** A class name for battery usage data provider. */
-    public static final String SETTINGS_PACKAGE_PATH = "com.android.settings";
     /** Key for query parameter timestamp used in BATTERY_CONTENT_URI **/
     public static final String QUERY_KEY_TIMESTAMP = "timestamp";
     /** Key for query parameter userid used in APP_USAGE_EVENT_URI **/
@@ -114,6 +117,11 @@
     @VisibleForTesting
     static Supplier<Cursor> sFakeSupplier;
 
+    @VisibleForTesting
+    static IUsageStatsManager sUsageStatsManager =
+            IUsageStatsManager.Stub.asInterface(
+                    ServiceManager.getService(Context.USAGE_STATS_SERVICE));
+
     private DatabaseUtils() {
     }
 
@@ -468,6 +476,37 @@
                 SHARED_PREFS_FILE, Context.MODE_PRIVATE);
     }
 
+    static void removeUsageSource(Context context) {
+        final SharedPreferences sharedPreferences = getSharedPreferences(context);
+        if (sharedPreferences != null && sharedPreferences.contains(KEY_LAST_USAGE_SOURCE)) {
+            sharedPreferences.edit().remove(KEY_LAST_USAGE_SOURCE).apply();
+        }
+    }
+
+    /**
+     * Returns what App Usage Observers will consider the source of usage for an activity.
+     *
+     * @see UsageStatsManager#getUsageSource()
+     */
+    static int getUsageSource(Context context) {
+        final SharedPreferences sharedPreferences = getSharedPreferences(context);
+        if (sharedPreferences != null && sharedPreferences.contains(KEY_LAST_USAGE_SOURCE)) {
+            return sharedPreferences
+                    .getInt(KEY_LAST_USAGE_SOURCE, ConvertUtils.DEFAULT_USAGE_SOURCE);
+        }
+        int usageSource = ConvertUtils.DEFAULT_USAGE_SOURCE;
+
+        try {
+            usageSource = sUsageStatsManager.getUsageSource();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to getUsageSource", e);
+        }
+        if (sharedPreferences != null) {
+            sharedPreferences.edit().putInt(KEY_LAST_USAGE_SOURCE, usageSource).apply();
+        }
+        return usageSource;
+    }
+
     static void recordDateTime(Context context, String preferenceKey) {
         final SharedPreferences sharedPreferences = getSharedPreferences(context);
         if (sharedPreferences != null) {
@@ -564,7 +603,7 @@
 
     private static Map<Long, Map<String, BatteryHistEntry>> loadHistoryMapFromContentProvider(
             Context context, Uri batteryStateUri) {
-        context = DatabaseUtils.getParentContext(context);
+        context = getParentContext(context);
         if (context == null) {
             return null;
         }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java
index 7c4478e..5a96fb4 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java
@@ -34,6 +34,8 @@
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
 import com.android.settings.fuelgauge.BatteryBroadcastReceiver;
+import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.search.SearchIndexable;
@@ -143,6 +145,16 @@
         controllers.add(screenOnTimeController);
         controllers.add(batteryUsageBreakdownController);
         setBatteryChartPreferenceController();
+
+        final PowerUsageFeatureProvider powerUsageFeatureProvider =
+                FeatureFactory.getFeatureFactory().getPowerUsageFeatureProvider();
+        if (powerUsageFeatureProvider.isBatteryTipsEnabled()) {
+            BatteryTipsController batteryTipsController = new BatteryTipsController(context);
+            mBatteryChartPreferenceController.setOnBatteryTipsUpdatedListener(
+                    batteryTipsController::handleBatteryTipsCardUpdated);
+            controllers.add(batteryTipsController);
+        }
+
         return controllers;
     }
 
diff --git a/src/com/android/settings/fuelgauge/protos/Android.bp b/src/com/android/settings/fuelgauge/protos/Android.bp
index 3af2aef..1f3cdd9 100644
--- a/src/com/android/settings/fuelgauge/protos/Android.bp
+++ b/src/com/android/settings/fuelgauge/protos/Android.bp
@@ -30,3 +30,11 @@
     },
     srcs: ["fuelgauge_usage_state.proto"],
 }
+
+java_library {
+    name: "power-anomaly-event-protos-lite",
+    proto: {
+        type: "lite",
+    },
+    srcs: ["power_anomaly_event.proto"],
+}
diff --git a/src/com/android/settings/fuelgauge/protos/power_anomaly_event.proto b/src/com/android/settings/fuelgauge/protos/power_anomaly_event.proto
new file mode 100644
index 0000000..b4277c4
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/protos/power_anomaly_event.proto
@@ -0,0 +1,37 @@
+syntax = "proto2";
+
+option java_multiple_files = true;
+option java_package = "com.android.settings.fuelgauge.batteryusage";
+option java_outer_classname = "PowerAnomalyEventProto";
+
+message PowerAnomalyEvent {
+  optional int64 timestamp = 1;
+  optional string type = 2;  // e.g. settings, apps
+  optional string key = 3;  // e.g. brightness, significant_increase
+  optional float score = 4;
+  oneof info {
+    WarningBannerInfo warning_banner_info = 5;
+    WarningItemInfo warning_item_info = 6;
+  }
+}
+
+message WarningBannerInfo {
+  optional string title_string = 1;
+  optional string description_string = 2;
+  optional string main_button_string = 3;
+  optional string main_button_action = 4;
+  optional string cancel_button_string = 5;
+  optional string cancel_button_action = 6;
+}
+
+message WarningItemInfo {
+  optional int64 start_timestamp = 1;
+  optional int64 end_timestamp = 2;
+  optional string top_card_string = 3;
+  optional string title_string = 4;
+  optional string description_string = 5;
+  optional string main_button_string = 6;
+  optional string main_button_action = 7;
+  optional string cancel_button_string = 8;
+  optional string cancel_button_action = 9;
+}
diff --git a/src/com/android/settings/media/MediaDeviceUpdateWorker.java b/src/com/android/settings/media/MediaDeviceUpdateWorker.java
index 6fa512e..d4aef47 100644
--- a/src/com/android/settings/media/MediaDeviceUpdateWorker.java
+++ b/src/com/android/settings/media/MediaDeviceUpdateWorker.java
@@ -40,7 +40,6 @@
 import com.android.settingslib.media.MediaDevice;
 import com.android.settingslib.utils.ThreadUtils;
 
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
@@ -224,18 +223,8 @@
         return mLocalMediaManager.getSessionName();
     }
 
-    List<RoutingSessionInfo> getActiveRemoteMediaDevice() {
-        final List<RoutingSessionInfo> sessionInfos = new ArrayList<>();
-        for (RoutingSessionInfo info : mLocalMediaManager.getActiveMediaSession()) {
-            if (!info.isSystemSession()) {
-                if (DEBUG) {
-                    Log.d(TAG, "getActiveRemoteMediaDevice() info : " + info.toString()
-                            + ", package name : " + info.getClientPackageName());
-                }
-                sessionInfos.add(info);
-            }
-        }
-        return sessionInfos;
+    List<RoutingSessionInfo> getActiveRemoteMediaDevices() {
+        return mLocalMediaManager.getRemoteRoutingSessions();
     }
 
     /**
diff --git a/src/com/android/settings/media/RemoteMediaSlice.java b/src/com/android/settings/media/RemoteMediaSlice.java
index 193a6c7..a9980e8 100644
--- a/src/com/android/settings/media/RemoteMediaSlice.java
+++ b/src/com/android/settings/media/RemoteMediaSlice.java
@@ -100,8 +100,9 @@
             Log.e(TAG, "Unable to get the slice worker.");
             return listBuilder.build();
         }
+
         // Only displaying remote devices
-        final List<RoutingSessionInfo> infos = getWorker().getActiveRemoteMediaDevice();
+        final List<RoutingSessionInfo> infos = getWorker().getActiveRemoteMediaDevices();
         if (infos.isEmpty()) {
             Log.d(TAG, "No active remote media device");
             return listBuilder.build();
diff --git a/src/com/android/settings/network/apn/ApnEditor.java b/src/com/android/settings/network/apn/ApnEditor.java
index 82584f6..5ff4b35 100644
--- a/src/com/android/settings/network/apn/ApnEditor.java
+++ b/src/com/android/settings/network/apn/ApnEditor.java
@@ -435,15 +435,20 @@
             return false;
         }
 
-        if (hasAllApns(apnTypesArray1) || TextUtils.isEmpty(apnTypes2)) {
+        final String[] apnTypesArray1LowerCase = new String[apnTypesArray1.length];
+        for (int i = 0; i < apnTypesArray1.length; i++) {
+            apnTypesArray1LowerCase[i] = apnTypesArray1[i].toLowerCase();
+        }
+
+        if (hasAllApns(apnTypesArray1LowerCase) || TextUtils.isEmpty(apnTypes2)) {
             return true;
         }
 
-        final List apnTypesList1 = Arrays.asList(apnTypesArray1);
+        final List apnTypesList1 = Arrays.asList(apnTypesArray1LowerCase);
         final String[] apnTypesArray2 = apnTypes2.split(",");
 
         for (String apn : apnTypesArray2) {
-            if (apnTypesList1.contains(apn.trim())) {
+            if (apnTypesList1.contains(apn.trim().toLowerCase())) {
                 Log.d(TAG, "apnTypesMatch: true because match found for " + apn.trim());
                 return true;
             }
diff --git a/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java b/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java
index 4160299..6f4d3c3 100644
--- a/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java
+++ b/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java
@@ -379,7 +379,7 @@
                 DIALOG_TAG_DISABLE_SIM_CONFIRMATION,
                 title,
                 null,
-                getString(R.string.condition_turn_off),
+                getString(R.string.sim_action_turn_off),
                 getString(R.string.sim_action_cancel));
     }
 
diff --git a/src/com/android/settings/notification/RemoteVolumeGroupController.java b/src/com/android/settings/notification/RemoteVolumeGroupController.java
index a943d5f..e2faf02 100644
--- a/src/com/android/settings/notification/RemoteVolumeGroupController.java
+++ b/src/com/android/settings/notification/RemoteVolumeGroupController.java
@@ -54,7 +54,7 @@
     static final String SWITCHER_PREFIX = "OUTPUT_SWITCHER";
 
     private PreferenceCategory mPreferenceCategory;
-    private List<RoutingSessionInfo> mRoutingSessionInfos = new ArrayList<>();
+    private final List<RoutingSessionInfo> mRoutingSessionInfos = new ArrayList<>();
 
     @VisibleForTesting
     LocalMediaManager mLocalMediaManager;
@@ -89,11 +89,7 @@
 
     private void initRemoteMediaSession() {
         mRoutingSessionInfos.clear();
-        for (RoutingSessionInfo info : mLocalMediaManager.getActiveMediaSession()) {
-            if (!info.isSystemSession()) {
-                mRoutingSessionInfos.add(info);
-            }
-        }
+        mRoutingSessionInfos.addAll(mLocalMediaManager.getRemoteRoutingSessions());
     }
 
     @Override
diff --git a/src/com/android/settings/notification/SeekBarVolumizerFactory.java b/src/com/android/settings/notification/SeekBarVolumizerFactory.java
new file mode 100644
index 0000000..6fac2c1
--- /dev/null
+++ b/src/com/android/settings/notification/SeekBarVolumizerFactory.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.content.Context;
+import android.net.Uri;
+import android.preference.SeekBarVolumizer;
+
+/**
+ * Testable wrapper around {@link SeekBarVolumizer} constructor.
+ */
+public class SeekBarVolumizerFactory {
+    private final Context mContext;
+
+    public SeekBarVolumizerFactory(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Creates a new SeekBarVolumizer.
+     *
+     * @param streamType of the audio manager.
+     * @param defaultUri of the volume.
+     * @param sbvc callback of the seekbar volumizer.
+     * @return a SeekBarVolumizer.
+     */
+    public SeekBarVolumizer create(int streamType, Uri defaultUri, SeekBarVolumizer.Callback sbvc) {
+        return new SeekBarVolumizer(mContext, streamType, defaultUri, sbvc);
+    }
+}
diff --git a/src/com/android/settings/notification/VolumeSeekBarPreference.java b/src/com/android/settings/notification/VolumeSeekBarPreference.java
index dd9df44..2156c05 100644
--- a/src/com/android/settings/notification/VolumeSeekBarPreference.java
+++ b/src/com/android/settings/notification/VolumeSeekBarPreference.java
@@ -37,6 +37,8 @@
 import com.android.settings.R;
 import com.android.settings.widget.SeekBarPreference;
 
+import java.text.NumberFormat;
+import java.util.Locale;
 import java.util.Objects;
 
 /** A slider preference that directly controls an audio stream volume (no dialog) **/
@@ -47,8 +49,9 @@
 
     protected SeekBar mSeekBar;
     private int mStream;
+    private SeekBarVolumizer mVolumizer;
     @VisibleForTesting
-    SeekBarVolumizer mVolumizer;
+    SeekBarVolumizerFactory mSeekBarVolumizerFactory;
     private Callback mCallback;
     private Listener mListener;
     private ImageView mIconView;
@@ -62,30 +65,36 @@
     private boolean mStopped;
     @VisibleForTesting
     AudioManager mAudioManager;
+    private Locale mLocale;
+    private NumberFormat mNumberFormat;
 
     public VolumeSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
         setLayoutResource(R.layout.preference_volume_slider);
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        mSeekBarVolumizerFactory = new SeekBarVolumizerFactory(context);
     }
 
     public VolumeSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         setLayoutResource(R.layout.preference_volume_slider);
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        mSeekBarVolumizerFactory = new SeekBarVolumizerFactory(context);
     }
 
     public VolumeSeekBarPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
         setLayoutResource(R.layout.preference_volume_slider);
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        mSeekBarVolumizerFactory = new SeekBarVolumizerFactory(context);
     }
 
     public VolumeSeekBarPreference(Context context) {
         super(context);
         setLayoutResource(R.layout.preference_volume_slider);
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        mSeekBarVolumizerFactory = new SeekBarVolumizerFactory(context);
     }
 
     public void setStream(int stream) {
@@ -148,6 +157,7 @@
                 if (mCallback != null) {
                     mCallback.onStreamValueChanged(mStream, progress);
                 }
+                overrideSeekBarStateDescription(formatStateDescription(progress));
             }
             @Override
             public void onMuted(boolean muted, boolean zenMuted) {
@@ -175,7 +185,7 @@
         };
         final Uri sampleUri = mStream == AudioManager.STREAM_MUSIC ? getMediaVolumeUri() : null;
         if (mVolumizer == null) {
-            mVolumizer = new SeekBarVolumizer(getContext(), mStream, sampleUri, sbvc);
+            mVolumizer = mSeekBarVolumizerFactory.create(mStream, sampleUri, sbvc);
         }
         mVolumizer.start();
         mVolumizer.setSeekBar(mSeekBar);
@@ -217,6 +227,33 @@
                 + "/" + R.raw.media_volume);
     }
 
+    @VisibleForTesting
+    CharSequence formatStateDescription(int progress) {
+        // This code follows the same approach in ProgressBar.java, but it rounds down the percent
+        // to match it with what the talkback feature says after any progress change. (b/285458191)
+        // Cache the locale-appropriate NumberFormat.  Configuration locale is guaranteed
+        // non-null, so the first time this is called we will always get the appropriate
+        // NumberFormat, then never regenerate it unless the locale changes on the fly.
+        Locale curLocale = getContext().getResources().getConfiguration().getLocales().get(0);
+        if (mLocale == null || !mLocale.equals(curLocale)) {
+            mLocale = curLocale;
+            mNumberFormat = NumberFormat.getPercentInstance(mLocale);
+        }
+        return mNumberFormat.format(getPercent(progress));
+    }
+
+    @VisibleForTesting
+    double getPercent(float progress) {
+        final float maxProgress = getMax();
+        final float minProgress = getMin();
+        final float diffProgress = maxProgress - minProgress;
+        if (diffProgress <= 0.0f) {
+            return 0.0f;
+        }
+        final float percent = (progress - minProgress) / diffProgress;
+        return Math.floor(Math.max(0.0f, Math.min(1.0f, percent)) * 100) / 100;
+    }
+
     public void setSuppressionText(String text) {
         if (Objects.equals(text, mSuppressionText)) return;
         mSuppressionText = text;
diff --git a/src/com/android/settings/overlay/FeatureFactory.kt b/src/com/android/settings/overlay/FeatureFactory.kt
index f38b5da..5f456e0 100644
--- a/src/com/android/settings/overlay/FeatureFactory.kt
+++ b/src/com/android/settings/overlay/FeatureFactory.kt
@@ -81,7 +81,7 @@
      */
     abstract val batterySettingsFeatureProvider: BatterySettingsFeatureProvider
 
-    abstract fun getDashboardFeatureProvider(context: Context): DashboardFeatureProvider
+    abstract val dashboardFeatureProvider: DashboardFeatureProvider
     abstract val dockUpdaterFeatureProvider: DockUpdaterFeatureProvider
     abstract val applicationFeatureProvider: ApplicationFeatureProvider
     abstract val localeFeatureProvider: LocaleFeatureProvider
diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.kt b/src/com/android/settings/overlay/FeatureFactoryImpl.kt
index b6dd221..0da5871 100644
--- a/src/com/android/settings/overlay/FeatureFactoryImpl.kt
+++ b/src/com/android/settings/overlay/FeatureFactoryImpl.kt
@@ -67,8 +67,6 @@
  * [FeatureFactory] implementation for AOSP Settings.
  */
 open class FeatureFactoryImpl : FeatureFactory() {
-    private val dashboardFeatureProvider by lazy { DashboardFeatureProviderImpl(appContext) }
-
     private val enterprisePrivacyFeatureProvider by lazy {
         EnterprisePrivacyFeatureProviderImpl(
             appContext,
@@ -100,7 +98,7 @@
 
     override val batterySettingsFeatureProvider by lazy { BatterySettingsFeatureProviderImpl() }
 
-    override fun getDashboardFeatureProvider(context: Context) = dashboardFeatureProvider
+    override val dashboardFeatureProvider by lazy { DashboardFeatureProviderImpl(appContext) }
 
     override val dockUpdaterFeatureProvider: DockUpdaterFeatureProvider by lazy {
         DockUpdaterFeatureProviderImpl()
diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java
index 6066efb..12709fe 100644
--- a/src/com/android/settings/password/ChooseLockPassword.java
+++ b/src/com/android/settings/password/ChooseLockPassword.java
@@ -523,6 +523,7 @@
             setupPasswordRequirementsView(headerLayout);
 
             mPasswordRestrictionView.setLayoutManager(new LinearLayoutManager(getActivity()));
+            mPasswordRestrictionView.setAccessibilityLiveRegion(ACCESSIBILITY_LIVE_REGION_POLITE);
             mPasswordEntry = view.findViewById(R.id.password_entry);
             mPasswordEntry.setOnEditorActionListener(this);
             mPasswordEntry.addTextChangedListener(this);
diff --git a/src/com/android/settings/password/ConfirmLockPassword.java b/src/com/android/settings/password/ConfirmLockPassword.java
index 8d0ff14..b203015 100644
--- a/src/com/android/settings/password/ConfirmLockPassword.java
+++ b/src/com/android/settings/password/ConfirmLockPassword.java
@@ -18,12 +18,8 @@
 
 import static android.app.admin.DevicePolicyResources.Strings.Settings.CONFIRM_WORK_PROFILE_PASSWORD_HEADER;
 import static android.app.admin.DevicePolicyResources.Strings.Settings.CONFIRM_WORK_PROFILE_PIN_HEADER;
-import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_PASSWORD;
-import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_PIN;
 import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LAST_PASSWORD_ATTEMPT_BEFORE_WIPE;
 import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LAST_PIN_ATTEMPT_BEFORE_WIPE;
-import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_PASSWORD_REQUIRED;
-import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_PIN_REQUIRED;
 import static android.app.admin.DevicePolicyResources.UNDEFINED;
 
 import static com.android.settings.biometrics.GatekeeperPasswordProvider.containsGatekeeperPasswordHandle;
@@ -75,27 +71,12 @@
 
 public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
 
-    // The index of the array is isStrongAuth << 2 + isManagedProfile << 1 + isAlpha.
+    // The index of the array is isStrongAuth << 1 + isAlpha.
     private static final int[] DETAIL_TEXTS = new int[] {
         R.string.lockpassword_confirm_your_pin_generic,
         R.string.lockpassword_confirm_your_password_generic,
-        R.string.lockpassword_confirm_your_pin_generic_profile,
-        R.string.lockpassword_confirm_your_password_generic_profile,
         R.string.lockpassword_strong_auth_required_device_pin,
         R.string.lockpassword_strong_auth_required_device_password,
-        R.string.lockpassword_strong_auth_required_work_pin,
-        R.string.lockpassword_strong_auth_required_work_password
-    };
-
-    private static final String[] DETAIL_TEXT_OVERRIDES = new String[] {
-            UNDEFINED,
-            UNDEFINED,
-            WORK_PROFILE_CONFIRM_PIN,
-            WORK_PROFILE_CONFIRM_PASSWORD,
-            UNDEFINED,
-            UNDEFINED,
-            WORK_PROFILE_PIN_REQUIRED,
-            WORK_PROFILE_PASSWORD_REQUIRED
     };
 
     public static class InternalActivity extends ConfirmLockPassword {
@@ -200,7 +181,12 @@
                     detailsMessage = getDefaultDetails();
                 }
                 mGlifLayout.setHeaderText(headerMessage);
-                mGlifLayout.setDescriptionText(detailsMessage);
+
+                if (mIsManagedProfile) {
+                    mGlifLayout.getDescriptionTextView().setVisibility(View.GONE);
+                } else {
+                    mGlifLayout.setDescriptionText(detailsMessage);
+                }
                 mCheckBoxLabel = intent.getCharSequenceExtra(KeyguardManager.EXTRA_CHECKBOX_LABEL);
             }
             int currentType = mPasswordEntry.getInputType();
@@ -323,11 +309,9 @@
                         : R.string.lockpassword_remote_validation_pin_details);
             }
             boolean isStrongAuthRequired = isStrongAuthRequired();
-            // Map boolean flags to an index by isStrongAuth << 2 + isManagedProfile << 1 + isAlpha.
-            int index = ((isStrongAuthRequired ? 1 : 0) << 2) + ((mIsManagedProfile ? 1 : 0) << 1)
-                    + (mIsAlpha ? 1 : 0);
-            return mDevicePolicyManager.getResources().getString(
-                    DETAIL_TEXT_OVERRIDES[index], () -> getString(DETAIL_TEXTS[index]));
+            // Map boolean flags to an index by isStrongAuth << 1 + isAlpha.
+            int index = ((isStrongAuthRequired ? 1 : 0) << 1) + (mIsAlpha ? 1 : 0);
+            return getString(DETAIL_TEXTS[index]);
         }
 
         private String getDefaultCheckboxLabel() {
diff --git a/src/com/android/settings/password/ConfirmLockPattern.java b/src/com/android/settings/password/ConfirmLockPattern.java
index ffd7c64..7160d64 100644
--- a/src/com/android/settings/password/ConfirmLockPattern.java
+++ b/src/com/android/settings/password/ConfirmLockPattern.java
@@ -316,12 +316,9 @@
                         R.string.lockpassword_remote_validation_pattern_details);
             }
             final boolean isStrongAuthRequired = isStrongAuthRequired();
-            if (!mIsManagedProfile) {
-                return isStrongAuthRequired
-                        ? getString(R.string.lockpassword_strong_auth_required_device_pattern)
-                        : getString(R.string.lockpassword_confirm_your_pattern_generic);
-            }
-            return null;
+            return isStrongAuthRequired
+                    ? getString(R.string.lockpassword_strong_auth_required_device_pattern)
+                    : getString(R.string.lockpassword_confirm_your_pattern_generic);
         }
 
         private Object[][] getActiveViews() {
@@ -371,7 +368,10 @@
 
                     CharSequence detailsText =
                             mDetailsText == null ? getDefaultDetails() : mDetailsText;
-                    if (detailsText != null) {
+
+                    if (mIsManagedProfile) {
+                        mGlifLayout.getDescriptionTextView().setVisibility(View.GONE);
+                    } else {
                         mGlifLayout.setDescriptionText(detailsText);
                     }
 
diff --git a/src/com/android/settings/search/SettingsSearchIndexablesProvider.java b/src/com/android/settings/search/SettingsSearchIndexablesProvider.java
index d6635a1..3b10c09 100644
--- a/src/com/android/settings/search/SettingsSearchIndexablesProvider.java
+++ b/src/com/android/settings/search/SettingsSearchIndexablesProvider.java
@@ -173,7 +173,7 @@
     public Cursor queryDynamicRawData(String[] projection) {
         final Context context = getContext();
         final List<SearchIndexableRaw> rawList = new ArrayList<>();
-        final Collection<SearchIndexableData> bundles = FeatureFactory.getFactory(context)
+        final Collection<SearchIndexableData> bundles = FeatureFactory.getFeatureFactory()
                 .getSearchFeatureProvider().getSearchIndexableResources().getProviderValues();
 
         for (SearchIndexableData bundle : bundles) {
@@ -200,8 +200,8 @@
         final MatrixCursor cursor = new MatrixCursor(SITE_MAP_COLUMNS);
         final Context context = getContext();
         // Loop through all IA categories and pages and build additional SiteMapPairs
-        final List<DashboardCategory> categories = FeatureFactory.getFactory(context)
-                .getDashboardFeatureProvider(context).getAllCategories();
+        final List<DashboardCategory> categories = FeatureFactory.getFeatureFactory()
+                .getDashboardFeatureProvider().getAllCategories();
         for (DashboardCategory category : categories) {
             // Use the category key to look up parent (which page hosts this key)
             final String parentClass = CATEGORY_KEY_TO_PARENT_MAP.get(category.key);
@@ -271,7 +271,7 @@
     }
 
     private List<String> getNonIndexableKeysFromProvider(Context context) {
-        final Collection<SearchIndexableData> bundles = FeatureFactory.getFactory(context)
+        final Collection<SearchIndexableData> bundles = FeatureFactory.getFeatureFactory()
                 .getSearchFeatureProvider().getSearchIndexableResources().getProviderValues();
 
         final List<String> nonIndexableKeys = new ArrayList<>();
@@ -322,7 +322,7 @@
     }
 
     private List<SearchIndexableResource> getSearchIndexableResourcesFromProvider(Context context) {
-        final Collection<SearchIndexableData> bundles = FeatureFactory.getFactory(context)
+        final Collection<SearchIndexableData> bundles = FeatureFactory.getFeatureFactory()
                 .getSearchFeatureProvider().getSearchIndexableResources().getProviderValues();
         List<SearchIndexableResource> resourceList = new ArrayList<>();
 
@@ -348,7 +348,7 @@
     }
 
     private List<SearchIndexableRaw> getSearchIndexableRawFromProvider(Context context) {
-        final Collection<SearchIndexableData> bundles = FeatureFactory.getFactory(context)
+        final Collection<SearchIndexableData> bundles = FeatureFactory.getFeatureFactory()
                 .getSearchFeatureProvider().getSearchIndexableResources().getProviderValues();
         final List<SearchIndexableRaw> rawList = new ArrayList<>();
 
@@ -392,7 +392,7 @@
     @VisibleForTesting
     List<SearchIndexableRaw> getInjectionIndexableRawData(Context context) {
         final DashboardFeatureProvider dashboardFeatureProvider =
-                FeatureFactory.getFactory(context).getDashboardFeatureProvider(context);
+                FeatureFactory.getFeatureFactory().getDashboardFeatureProvider();
         final List<SearchIndexableRaw> rawList = new ArrayList<>();
         final String currentPackageName = context.getPackageName();
         for (DashboardCategory category : dashboardFeatureProvider.getAllCategories()) {
diff --git a/src/com/android/settings/sim/SimDialogActivity.java b/src/com/android/settings/sim/SimDialogActivity.java
index 7d39938..e7b0185 100644
--- a/src/com/android/settings/sim/SimDialogActivity.java
+++ b/src/com/android/settings/sim/SimDialogActivity.java
@@ -280,8 +280,20 @@
     public void showEnableAutoDataSwitchDialog() {
         final FragmentManager fragmentManager = getSupportFragmentManager();
         SimDialogFragment fragment = createFragment(ENABLE_AUTO_DATA_SWITCH);
-        fragment.show(fragmentManager, Integer.toString(ENABLE_AUTO_DATA_SWITCH));
 
+        if (fragmentManager.isStateSaved()) {
+            Log.w(TAG, "Failed to show EnableAutoDataSwitchDialog. The fragmentManager "
+                    + "is StateSaved.");
+            forceClose();
+            return;
+        }
+        try {
+            fragment.show(fragmentManager, Integer.toString(ENABLE_AUTO_DATA_SWITCH));
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to show EnableAutoDataSwitchDialog.", e);
+            forceClose();
+            return;
+        }
         if (getResources().getBoolean(
                 R.bool.config_auto_data_switch_enables_cross_sim_calling)) {
             // If auto data switch is already enabled on the non-DDS, the dialog for enabling it
diff --git a/src/com/android/settings/spa/SettingsSpaEnvironment.kt b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
index 7b450c9..f08a2de 100644
--- a/src/com/android/settings/spa/SettingsSpaEnvironment.kt
+++ b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
@@ -21,6 +21,7 @@
 import com.android.settings.spa.about.AboutPhonePageProvider
 import com.android.settings.spa.app.AllAppListPageProvider
 import com.android.settings.spa.app.AppsMainPageProvider
+import com.android.settings.spa.app.appcompat.UserAspectRatioAppsPageProvider
 import com.android.settings.spa.app.appinfo.AppInfoSettingsProvider
 import com.android.settings.spa.app.appinfo.CloneAppInfoSettingsProvider
 import com.android.settings.spa.app.backgroundinstall.BackgroundInstalledAppsPageProvider
@@ -35,6 +36,7 @@
 import com.android.settings.spa.app.specialaccess.SpecialAppAccessPageProvider
 import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider
 import com.android.settings.spa.app.specialaccess.UseFullScreenIntentAppListProvider
+import com.android.settings.spa.app.storage.StorageAppListPageProvider
 import com.android.settings.spa.core.instrumentation.SpaLogProvider
 import com.android.settings.spa.development.UsageStatsPageProvider
 import com.android.settings.spa.development.compat.PlatformCompatAppListPageProvider
@@ -87,9 +89,12 @@
                 UsageStatsPageProvider,
                 PlatformCompatAppListPageProvider,
                 BackgroundInstalledAppsPageProvider,
+                UserAspectRatioAppsPageProvider,
                 CloneAppInfoSettingsProvider,
                 NetworkAndInternetPageProvider,
                 AboutPhonePageProvider,
+                StorageAppListPageProvider.Apps,
+                StorageAppListPageProvider.Games,
                 ) + togglePermissionAppListTemplate.createPageProviders(),
             rootPages = listOf(
                 HomePageProvider.createSettingsPage()
diff --git a/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppPreference.kt b/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppPreference.kt
new file mode 100644
index 0000000..3680715
--- /dev/null
+++ b/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppPreference.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.appcompat
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.settings.R
+import com.android.settings.applications.appcompat.UserAspectRatioDetails
+import com.android.settings.applications.appcompat.UserAspectRatioManager
+import com.android.settings.applications.appinfo.AppInfoDashboardFragment
+import com.android.settings.spa.app.appinfo.AppInfoSettingsProvider
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOn
+
+@OptIn(ExperimentalLifecycleComposeApi::class)
+@Composable
+fun UserAspectRatioAppPreference(app: ApplicationInfo) {
+    val context = LocalContext.current
+    val presenter = remember { UserAspectRatioAppPresenter(context, app) }
+    if (!presenter.isAvailableFlow.collectAsStateWithLifecycle(initialValue = false).value) return
+
+    Preference(object : PreferenceModel {
+        override val title = stringResource(R.string.aspect_ratio_title)
+        override val summary = presenter.summaryFlow.collectAsStateWithLifecycle(
+            initialValue = stringResource(R.string.summary_placeholder),
+        )
+        override val onClick = presenter::startActivity
+    })
+}
+
+class UserAspectRatioAppPresenter(
+    private val context: Context,
+    private val app: ApplicationInfo,
+) {
+    private val manager = UserAspectRatioManager(context)
+
+    val isAvailableFlow = flow {
+        emit(UserAspectRatioManager.isFeatureEnabled(context)
+                && manager.canDisplayAspectRatioUi(app))
+    }.flowOn(Dispatchers.IO)
+
+    fun startActivity() =
+        navigateToAppAspectRatioSettings(context, app)
+
+    val summaryFlow = flow {
+        emit(manager.getUserMinAspectRatioEntry(app.packageName, context.userId))
+    }.flowOn(Dispatchers.IO)
+}
+
+fun navigateToAppAspectRatioSettings(context: Context, app: ApplicationInfo) {
+    AppInfoDashboardFragment.startAppInfoFragment(
+        UserAspectRatioDetails::class.java,
+        app,
+        context,
+        AppInfoSettingsProvider.METRICS_CATEGORY,
+    )
+}
\ No newline at end of file
diff --git a/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt b/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt
new file mode 100644
index 0000000..ff90492
--- /dev/null
+++ b/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.appcompat
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.GET_ACTIVITIES
+import android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET
+import android.os.Build
+import android.os.Bundle
+import android.util.Log
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.settings.R
+import com.android.settings.applications.appcompat.UserAspectRatioManager
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
+import com.android.settingslib.spa.framework.common.createSettingsPage
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.compose.rememberContext
+import com.android.settingslib.spa.framework.compose.toState
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.util.asyncMap
+import com.android.settingslib.spa.framework.util.filterItem
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.ui.SettingsBody
+import com.android.settingslib.spa.widget.ui.SpinnerOption
+import com.android.settingslib.spaprivileged.model.app.AppListModel
+import com.android.settingslib.spaprivileged.model.app.AppRecord
+import com.android.settingslib.spaprivileged.model.app.userId
+import com.android.settingslib.spaprivileged.template.app.AppList
+import com.android.settingslib.spaprivileged.template.app.AppListInput
+import com.android.settingslib.spaprivileged.template.app.AppListItem
+import com.android.settingslib.spaprivileged.template.app.AppListItemModel
+import com.android.settingslib.spaprivileged.template.app.AppListPage
+import com.google.common.annotations.VisibleForTesting
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOn
+
+object UserAspectRatioAppsPageProvider : SettingsPageProvider {
+    override val name = "UserAspectRatioAppsPage"
+    private val owner = createSettingsPage()
+
+    override fun isEnabled(arguments: Bundle?): Boolean =
+        UserAspectRatioManager.isFeatureEnabled(SpaEnvironmentFactory.instance.appContext)
+
+    @Composable
+    override fun Page(arguments: Bundle?) =
+        UserAspectRatioAppList()
+
+    @Composable
+    @VisibleForTesting
+    fun EntryItem() =
+        Preference(object : PreferenceModel {
+            override val title = stringResource(R.string.aspect_ratio_title)
+            override val summary = getSummary().toState()
+            override val onClick = navigator(name)
+        })
+
+    @VisibleForTesting
+    fun buildInjectEntry() = SettingsEntryBuilder
+        .createInject(owner)
+        .setSearchDataFn { null }
+        .setUiLayoutFn { EntryItem() }
+
+    @Composable
+    @VisibleForTesting
+    fun getSummary(): String = stringResource(R.string.aspect_ratio_summary, Build.MODEL)
+}
+
+@Composable
+fun UserAspectRatioAppList(
+    appList: @Composable AppListInput<UserAspectRatioAppListItemModel>.() -> Unit
+    = { AppList() },
+) {
+    AppListPage(
+        title = stringResource(R.string.aspect_ratio_title),
+        listModel = rememberContext(::UserAspectRatioAppListModel),
+        appList = appList,
+        header = {
+            Box(Modifier.padding(SettingsDimension.itemPadding)) {
+                SettingsBody(UserAspectRatioAppsPageProvider.getSummary())
+            }
+        }
+    )
+}
+
+data class UserAspectRatioAppListItemModel(
+    override val app: ApplicationInfo,
+    val override: Int,
+    val suggested: Boolean,
+    val canDisplay: Boolean,
+) : AppRecord
+
+class UserAspectRatioAppListModel(private val context: Context)
+    : AppListModel<UserAspectRatioAppListItemModel> {
+
+    private val packageManager = context.packageManager
+    private val userAspectRatioManager = UserAspectRatioManager(context)
+
+    override fun getSpinnerOptions(
+        recordList: List<UserAspectRatioAppListItemModel>
+    ): List<SpinnerOption> {
+        val hasSuggested = recordList.any { it.suggested }
+        val hasOverride = recordList.any { it.override != USER_MIN_ASPECT_RATIO_UNSET }
+        val options = mutableListOf(SpinnerItem.All)
+        // Add suggested filter first as default
+        if (hasSuggested) options.add(0, SpinnerItem.Suggested)
+        if (hasOverride) options += SpinnerItem.Overridden
+        return options.map {
+            SpinnerOption(
+                id = it.ordinal,
+                text = context.getString(it.stringResId),
+            )
+        }
+    }
+
+    @Composable
+    override fun AppListItemModel<UserAspectRatioAppListItemModel>.AppItem() {
+        val app = record.app
+        AppListItem(
+            onClick = { navigateToAppAspectRatioSettings(context, app) }
+        )
+    }
+
+    override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) =
+        userIdFlow.combine(appListFlow) { uid, appList ->
+            appList.asyncMap { app ->
+                UserAspectRatioAppListItemModel(
+                    app = app,
+                    suggested = !app.isSystemApp && getPackageAndActivityInfo(
+                                    app)?.isFixedOrientationOrAspectRatio() == true,
+                    override = userAspectRatioManager.getUserMinAspectRatioValue(
+                                    app.packageName, uid),
+                    canDisplay = userAspectRatioManager.canDisplayAspectRatioUi(app),
+                )
+            }
+        }
+
+    override fun filter(
+        userIdFlow: Flow<Int>,
+        option: Int,
+        recordListFlow: Flow<List<UserAspectRatioAppListItemModel>>
+    ): Flow<List<UserAspectRatioAppListItemModel>> = recordListFlow.filterItem(
+        when (SpinnerItem.values().getOrNull(option)) {
+            SpinnerItem.Suggested -> ({ it.canDisplay && it.suggested })
+            SpinnerItem.Overridden -> ({ it.override != USER_MIN_ASPECT_RATIO_UNSET })
+            else -> ({ it.canDisplay })
+        }
+    )
+
+    @OptIn(ExperimentalLifecycleComposeApi::class)
+    @Composable
+    override fun getSummary(option: Int, record: UserAspectRatioAppListItemModel) : State<String> =
+        remember(record.override) {
+            flow {
+                emit(userAspectRatioManager.getUserMinAspectRatioEntry(record.override))
+            }.flowOn(Dispatchers.IO)
+        }.collectAsStateWithLifecycle(initialValue = stringResource(R.string.summary_placeholder))
+
+    private fun getPackageAndActivityInfo(app: ApplicationInfo): PackageInfo? = try {
+        packageManager.getPackageInfoAsUser(app.packageName, GET_ACTIVITIES_FLAGS, app.userId)
+    } catch (e: Exception) {
+        // Query PackageManager.getPackageInfoAsUser() with GET_ACTIVITIES_FLAGS could cause
+        // exception sometimes. Since we reply on this flag to retrieve the Picture In Picture
+        // packages, we need to catch the exception to alleviate the impact before PackageManager
+        // fixing this issue or provide a better api.
+        Log.e(TAG, "Exception while getPackageInfoAsUser", e)
+        null
+    }
+
+    companion object {
+        private const val TAG = "AspectRatioAppsListModel"
+        private fun PackageInfo.isFixedOrientationOrAspectRatio() =
+            activities?.any { a -> a.isFixedOrientation || a.hasFixedAspectRatio() } ?: false
+        private val GET_ACTIVITIES_FLAGS =
+            PackageManager.PackageInfoFlags.of(GET_ACTIVITIES.toLong())
+    }
+}
+
+private enum class SpinnerItem(val stringResId: Int) {
+    Suggested(R.string.user_aspect_ratio_suggested_apps_label),
+    All(R.string.filter_all_apps),
+    Overridden(R.string.user_aspect_ratio_overridden_apps_label)
+}
\ No newline at end of file
diff --git a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
index d59a4f7..e6df933 100644
--- a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
@@ -35,6 +35,7 @@
 import com.android.settings.applications.AppInfoBase
 import com.android.settings.applications.appinfo.AppInfoDashboardFragment
 import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
+import com.android.settings.spa.app.appcompat.UserAspectRatioAppPreference
 import com.android.settings.spa.app.specialaccess.AlarmsAndRemindersAppListProvider
 import com.android.settings.spa.app.specialaccess.DisplayOverOtherAppsAppListProvider
 import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider
@@ -150,6 +151,7 @@
         }
 
         Category(title = stringResource(R.string.advanced_apps)) {
+            UserAspectRatioAppPreference(app)
             DisplayOverOtherAppsAppListProvider.InfoPageEntryItem(app)
             ModifySystemSettingsAppListProvider.InfoPageEntryItem(app)
             PictureInPictureListProvider.InfoPageEntryItem(app)
diff --git a/src/com/android/settings/spa/app/storage/StorageAppList.kt b/src/com/android/settings/spa/app/storage/StorageAppList.kt
new file mode 100644
index 0000000..8fc3eb5
--- /dev/null
+++ b/src/com/android/settings/spa/app/storage/StorageAppList.kt
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.storage
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.os.Bundle
+import androidx.annotation.StringRes
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import com.android.settings.R
+import com.android.settings.spa.app.appinfo.AppInfoSettingsProvider
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.util.filterItem
+import com.android.settingslib.spa.framework.util.mapItem
+import com.android.settingslib.spaprivileged.model.app.AppEntry
+import com.android.settingslib.spaprivileged.model.app.AppListModel
+import com.android.settingslib.spaprivileged.model.app.AppRecord
+import com.android.settingslib.spaprivileged.template.app.AppList
+import com.android.settingslib.spaprivileged.template.app.AppListInput
+import com.android.settingslib.spaprivileged.template.app.AppListItem
+import com.android.settingslib.spaprivileged.template.app.AppListItemModel
+import com.android.settingslib.spaprivileged.template.app.AppListPage
+import com.android.settingslib.spaprivileged.template.app.calculateSizeBytes
+import com.android.settingslib.spaprivileged.template.app.getStorageSize
+import kotlinx.coroutines.flow.Flow
+
+sealed class StorageAppListPageProvider(private val type: StorageType) : SettingsPageProvider {
+    @Composable
+    override fun Page(arguments: Bundle?) {
+        StorageAppListPage(type)
+    }
+
+    object Apps : StorageAppListPageProvider(StorageType.Apps) {
+        override val name = "StorageAppList"
+    }
+
+    object Games : StorageAppListPageProvider(StorageType.Games) {
+        override val name = "GameStorageAppList"
+    }
+}
+
+sealed class StorageType(
+    @StringRes val titleResource: Int,
+    val filter: (AppRecordWithSize) -> Boolean
+) {
+    object Apps : StorageType(
+        titleResource = R.string.apps_storage,
+        filter = {
+            (it.app.flags and ApplicationInfo.FLAG_IS_GAME) == 0 &&
+            it.app.category != ApplicationInfo.CATEGORY_GAME
+        }
+    )
+    object Games : StorageType(
+        titleResource = R.string.game_storage_settings,
+        filter = {
+            (it.app.flags and ApplicationInfo.FLAG_IS_GAME) != 0 ||
+                it.app.category == ApplicationInfo.CATEGORY_GAME
+        }
+    )
+}
+
+@Composable
+fun StorageAppListPage(
+    type: StorageType,
+    appList: @Composable AppListInput<AppRecordWithSize>.() -> Unit = { AppList() }
+) {
+    val context = LocalContext.current
+    AppListPage(
+        title = stringResource(type.titleResource),
+        listModel = when (type) {
+            StorageType.Apps -> remember(context) { StorageAppListModel(context, type) }
+            StorageType.Games -> remember(context) { StorageAppListModel(context, type) }
+        },
+        showInstantApps = true,
+        matchAnyUserForAdmin = true,
+        appList = appList,
+        moreOptions = {  }, // TODO(b/292165031) Sorting in Options not yet supported
+    )
+}
+
+data class AppRecordWithSize(
+    override val app: ApplicationInfo,
+    val size: Long
+) : AppRecord
+
+class StorageAppListModel(
+    private val context: Context,
+    private val type: StorageType,
+    private val getStorageSummary: @Composable ApplicationInfo.() -> State<String> = {
+        getStorageSize()
+    }
+) : AppListModel<AppRecordWithSize> {
+    override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) =
+        appListFlow.mapItem {
+            AppRecordWithSize(it, it.calculateSizeBytes(context) ?: 0L)
+        }
+
+    override fun filter(
+        userIdFlow: Flow<Int>,
+        option: Int,
+        recordListFlow: Flow<List<AppRecordWithSize>>
+    ): Flow<List<AppRecordWithSize>> = recordListFlow.filterItem { type.filter(it) }
+
+    @Composable
+    override fun getSummary(option: Int, record: AppRecordWithSize): State<String> {
+        val storageSummary = record.app.getStorageSummary()
+        return remember {
+            derivedStateOf {
+                storageSummary.value
+            }
+        }
+    }
+
+    @Composable
+    override fun AppListItemModel<AppRecordWithSize>.AppItem() {
+        AppListItem(onClick = AppInfoSettingsProvider.navigator(app = record.app))
+    }
+
+    override fun getComparator(option: Int) = compareByDescending<AppEntry<AppRecordWithSize>> {
+        it.record.size
+    }.then(super.getComparator(option))
+}
diff --git a/src/com/android/settings/spa/core/instrumentation/SpaLogProvider.kt b/src/com/android/settings/spa/core/instrumentation/SpaLogProvider.kt
index 600a2e6..8659bf5 100644
--- a/src/com/android/settings/spa/core/instrumentation/SpaLogProvider.kt
+++ b/src/com/android/settings/spa/core/instrumentation/SpaLogProvider.kt
@@ -72,10 +72,10 @@
         }
         val sessionSource = extraData.getString(LOG_DATA_SESSION_NAME)
         return when(sessionSource) {
-            SESSION_BROWSE -> SettingsEnums.BROWSE
-            SESSION_SEARCH -> SettingsEnums.SEARCH
-            SESSION_SLICE -> SettingsEnums.SLICE_TYPE
-            SESSION_EXTERNAL -> SettingsEnums.EXTERNAL
+            SESSION_BROWSE -> SettingsEnums.SESSION_BROWSE
+            SESSION_SEARCH -> SettingsEnums.SESSION_SEARCH
+            SESSION_SLICE -> SettingsEnums.SESSION_SLICE_TYPE
+            SESSION_EXTERNAL -> SettingsEnums.SESSION_EXTERNAL
             else -> SettingsEnums.SESSION_UNKNOWN
         }
     }
diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java
index 93b7c78..b0816fd 100644
--- a/src/com/android/settings/users/UserSettings.java
+++ b/src/com/android/settings/users/UserSettings.java
@@ -1628,7 +1628,7 @@
             mRemovingUserId = -1;
             updateUserList();
             if (mCreateUserDialogController.isActive()) {
-                mCreateUserDialogController.clear();
+                mCreateUserDialogController.finish();
             }
         }
     }
diff --git a/src/com/android/settings/vpn2/VpnSettings.java b/src/com/android/settings/vpn2/VpnSettings.java
index 8cec2f4..0389ecd 100644
--- a/src/com/android/settings/vpn2/VpnSettings.java
+++ b/src/com/android/settings/vpn2/VpnSettings.java
@@ -141,6 +141,11 @@
     @Override
     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
         super.onCreateOptionsMenu(menu, inflater);
+
+        if (!getContext().getResources().getBoolean(R.bool.config_show_vpn_options)) {
+            return;
+        }
+
         // Although FEATURE_IPSEC_TUNNELS should always be present in android S and beyond,
         // keep this check here just to be safe.
         if (!getContext().getPackageManager().hasSystemFeature(
diff --git a/src/com/android/settings/wifi/NetworkRequestDialogFragment.java b/src/com/android/settings/wifi/NetworkRequestDialogFragment.java
index 5639047..93d88e9 100644
--- a/src/com/android/settings/wifi/NetworkRequestDialogFragment.java
+++ b/src/com/android/settings/wifi/NetworkRequestDialogFragment.java
@@ -18,8 +18,6 @@
 
 import static com.android.wifitrackerlib.Utils.getSecurityTypesFromScanResult;
 
-import static java.util.stream.Collectors.toList;
-
 import android.app.Dialog;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -273,19 +271,31 @@
     @VisibleForTesting
     void updateWifiEntries() {
         final List<WifiEntry> wifiEntries = new ArrayList<>();
-        if (mWifiPickerTracker.getConnectedWifiEntry() != null) {
-            wifiEntries.add(mWifiPickerTracker.getConnectedWifiEntry());
+        WifiEntry connectedWifiEntry = mWifiPickerTracker.getConnectedWifiEntry();
+        String connectedSsid;
+        if (connectedWifiEntry != null) {
+            connectedSsid = connectedWifiEntry.getSsid();
+            wifiEntries.add(connectedWifiEntry);
+        } else {
+            connectedSsid = null;
         }
         wifiEntries.addAll(mWifiPickerTracker.getWifiEntries());
 
         mFilteredWifiEntries.clear();
         mFilteredWifiEntries.addAll(wifiEntries.stream()
-                .filter(entry -> isMatchedWifiEntry(entry))
+                .filter(entry -> isMatchedWifiEntry(entry, connectedSsid))
                 .limit(mShowLimitedItem ? MAX_NUMBER_LIST_ITEM : Long.MAX_VALUE)
-                .collect(toList()));
+                .toList());
     }
 
-    private boolean isMatchedWifiEntry(WifiEntry entry) {
+    private boolean isMatchedWifiEntry(WifiEntry entry, String connectedSsid) {
+        if (entry.getConnectedState() == WifiEntry.CONNECTED_STATE_DISCONNECTED
+                && TextUtils.equals(entry.getSsid(), connectedSsid)) {
+            // WifiPickerTracker may return a duplicate unsaved network that is separate from
+            // the connecting app-requested network, so make sure we only show the connected
+            // app-requested one.
+            return false;
+        }
         for (MatchWifi wifi : mMatchWifis) {
             if (!TextUtils.equals(entry.getSsid(), wifi.mSsid)) {
                 continue;
diff --git a/src/com/android/settings/wifi/WifiEntryPreference.java b/src/com/android/settings/wifi/WifiEntryPreference.java
index 5b44887..7206666 100644
--- a/src/com/android/settings/wifi/WifiEntryPreference.java
+++ b/src/com/android/settings/wifi/WifiEntryPreference.java
@@ -15,6 +15,8 @@
  */
 package com.android.settings.wifi;
 
+import static com.android.settingslib.wifi.WifiUtils.getHotspotIconResource;
+
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Resources;
@@ -37,6 +39,7 @@
 import com.android.settingslib.Utils;
 import com.android.settingslib.wifi.WifiUtils;
 import com.android.wifitrackerlib.BaseWifiTracker;
+import com.android.wifitrackerlib.HotspotNetworkEntry;
 import com.android.wifitrackerlib.WifiEntry;
 
 /**
@@ -145,13 +148,17 @@
      */
     public void refresh() {
         setTitle(mWifiEntry.getTitle());
-        final int level = mWifiEntry.getLevel();
-        final boolean showX = mWifiEntry.shouldShowXLevelIcon();
-        if (level != mLevel || showX != mShowX) {
-            mLevel = level;
-            mShowX = showX;
-            updateIcon(mShowX, mLevel);
-            notifyChanged();
+        if (mWifiEntry instanceof HotspotNetworkEntry) {
+            updateHotspotIcon(((HotspotNetworkEntry) mWifiEntry).getDeviceType());
+        } else {
+            int level = mWifiEntry.getLevel();
+            boolean showX = mWifiEntry.shouldShowXLevelIcon();
+
+            if (level != mLevel || showX != mShowX) {
+                mLevel = level;
+                mShowX = showX;
+                updateIcon(mShowX, mLevel);
+            }
         }
 
         setSummary(mWifiEntry.getSummary(false /* concise */));
@@ -201,14 +208,7 @@
         return accent ? android.R.attr.colorAccent : android.R.attr.colorControlNormal;
     }
 
-    @VisibleForTesting
-    void updateIcon(boolean showX, int level) {
-        if (level == -1) {
-            setIcon(null);
-            return;
-        }
-
-        final Drawable drawable = mIconInjector.getIcon(showX, level);
+    private void setIconWithTint(Drawable drawable) {
         if (drawable != null) {
             // Must use Drawable#setTintList() instead of Drawable#setTint() to show the grey
             // icon when the preference is disabled.
@@ -219,6 +219,20 @@
         }
     }
 
+    @VisibleForTesting
+    void updateIcon(boolean showX, int level) {
+        if (level == -1) {
+            setIcon(null);
+            return;
+        }
+        setIconWithTint(mIconInjector.getIcon(showX, level));
+    }
+
+    @VisibleForTesting
+    void updateHotspotIcon(int deviceType) {
+        setIconWithTint(getContext().getDrawable(getHotspotIconResource(deviceType)));
+    }
+
     @Nullable
     private StateListDrawable getFrictionStateListDrawable() {
         TypedArray frictionSld;
diff --git a/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java b/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java
index d3a4be7..4fc036b 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java
@@ -27,6 +27,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.provider.Settings;
+import android.text.InputType;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -118,6 +119,8 @@
 
         final String password = wifiNetworkConfig.getPreSharedKey();
         TextView passwordView = view.findViewById(R.id.password);
+        passwordView.setInputType(
+                InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
         if (TextUtils.isEmpty(password)) {
             mSummary.setText(getString(
                     R.string.wifi_dpp_scan_open_network_qr_code_with_another_device,
diff --git a/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java b/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java
new file mode 100644
index 0000000..31ff76c
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.applications.appcompat;
+
+import static com.android.settings.applications.appcompat.UserAspectRatioDetails.KEY_PREF_3_2;
+import static com.android.settings.applications.appcompat.UserAspectRatioDetails.KEY_PREF_DEFAULT;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.IActivityManager;
+import android.content.Context;
+import android.os.RemoteException;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.testutils.shadow.ShadowActivityManager;
+import com.android.settingslib.widget.SelectorWithWidgetPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+/**
+ * To run test: atest SettingsRoboTests:UserAspectRatioDetailsTest
+ */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowActivityManager.class})
+public class UserAspectRatioDetailsTest {
+
+    @Mock
+    private UserAspectRatioManager mUserAspectRatioManager;
+    @Mock
+    private IActivityManager mAm;
+
+    private SelectorWithWidgetPreference mRadioButtonPref;
+    private Context mContext;
+    private UserAspectRatioDetails mFragment;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = spy(ApplicationProvider.getApplicationContext());
+        mFragment = spy(new UserAspectRatioDetails());
+        when(mFragment.getContext()).thenReturn(mContext);
+        when(mFragment.getAspectRatioManager()).thenReturn(mUserAspectRatioManager);
+        ShadowActivityManager.setService(mAm);
+        mRadioButtonPref = new SelectorWithWidgetPreference(mContext);
+    }
+
+    @Test
+    public void onRadioButtonClicked_prefChange_shouldStopActivity() throws RemoteException {
+        // Default was already selected
+        mRadioButtonPref.setKey(KEY_PREF_DEFAULT);
+        mFragment.onRadioButtonClicked(mRadioButtonPref);
+        // Preference changed
+        mRadioButtonPref.setKey(KEY_PREF_3_2);
+        mFragment.onRadioButtonClicked(mRadioButtonPref);
+        // Only triggered once when preference change
+        verify(mAm).stopAppForUser(any(), anyInt());
+    }
+
+    @Test
+    public void onRadioButtonClicked_prefChange_shouldSetAspectRatio() throws RemoteException {
+        // Default was already selected
+        mRadioButtonPref.setKey(KEY_PREF_DEFAULT);
+        mFragment.onRadioButtonClicked(mRadioButtonPref);
+        // Preference changed
+        mRadioButtonPref.setKey(KEY_PREF_3_2);
+        mFragment.onRadioButtonClicked(mRadioButtonPref);
+        // Only triggered once when preference changes
+        verify(mUserAspectRatioManager).setUserMinAspectRatio(
+                any(), anyInt(), anyInt());
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/biometrics/face/FaceSettingsActivityTest.java b/tests/robotests/src/com/android/settings/biometrics/face/FaceSettingsActivityTest.java
deleted file mode 100644
index 2a95f4c..0000000
--- a/tests/robotests/src/com/android/settings/biometrics/face/FaceSettingsActivityTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.face;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.spy;
-
-import android.content.res.Resources;
-import android.os.Bundle;
-
-import com.android.settings.Settings;
-import com.android.settings.testutils.FakeFeatureFactory;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.android.controller.ActivityController;
-
-@RunWith(RobolectricTestRunner.class)
-public class FaceSettingsActivityTest {
-
-    private static final String APPLIED_SETUP_WIZARD_THEME = "SettingsPreferenceTheme.SetupWizard";
-
-    private Settings.FaceSettingsActivity mActivity;
-    private Resources.Theme mTheme;
-
-    @Before
-    public void setUp() {
-        FakeFeatureFactory.setupForTest();
-        mActivity = spy(Settings.FaceSettingsActivity.class);
-    }
-
-    @Test
-    public void verifyFaceSettingsActivity_shouldAppliedSetupWizardTheme() {
-        createActivity();
-
-        assertThat(isThemeApplied(APPLIED_SETUP_WIZARD_THEME)).isTrue();
-    }
-
-    private boolean isThemeApplied(String themeName) {
-        final String [] appliedThemes =  mTheme.getTheme();
-        for (String theme : appliedThemes) {
-            if (theme.contains(themeName)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private void createActivity() {
-        ActivityController.of(mActivity).create(new Bundle());
-        mTheme = mActivity.getTheme();
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java
index 84826e1..006c699 100644
--- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java
@@ -137,6 +137,23 @@
     }
 
     @Test
+    public void fingerprintUdfpsEnrollInitStage_afterOnEnrollmentHelp_shouldVibrate() {
+        initializeActivityFor(TYPE_UDFPS_OPTICAL);
+
+        assertThat(getLayout().getDescriptionText()).isNotEqualTo("");
+
+        mActivity.configureEnrollmentStage(0 /* lottie */);
+        mActivity.onEnrollmentHelp(1/* FINGERPRINT_ACQUIRED_PARTIAL */, mContext.getString(
+                com.android.internal.R.string.fingerprint_acquired_partial));
+
+        verify(mVibrator, never()).vibrate(anyInt(), anyString(), any(), anyString(), any());
+
+        mActivity.onEnrollmentProgressChange(1, 1);
+        verify(mVibrator).vibrate(anyInt(), anyString(), any(), anyString(), any());
+
+    }
+
+    @Test
     public void fingerprintUdfpsOverlayEnrollment_gainFocus_shouldNotCancel() {
         initializeActivityFor(TYPE_UDFPS_OPTICAL);
 
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroductionTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroductionTest.java
index 69f10d6..3eba91c 100644
--- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroductionTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroductionTest.java
@@ -85,6 +85,7 @@
     private Context mContext;
 
     private TestFingerprintEnrollIntroduction mFingerprintEnrollIntroduction;
+    private ActivityController<TestFingerprintEnrollIntroduction> mController;
 
     private static final int MAX_ENROLLMENTS = 5;
     private static final byte[] EXPECTED_TOKEN = new byte[] { 10, 20, 30, 40 };
@@ -121,9 +122,8 @@
 
     void setupFingerprintEnrollIntroWith(@NonNull Intent intent) {
 
-        final ActivityController<TestFingerprintEnrollIntroduction> controller =
-                Robolectric.buildActivity(TestFingerprintEnrollIntroduction.class, intent);
-        mFingerprintEnrollIntroduction = controller.get();
+        mController = Robolectric.buildActivity(TestFingerprintEnrollIntroduction.class, intent);
+        mFingerprintEnrollIntroduction = mController.get();
         mFingerprintEnrollIntroduction.mMockedFingerprintManager = mFingerprintManager;
         mFingerprintEnrollIntroduction.mMockedGatekeeperPasswordProvider =
                 mGatekeeperPasswordProvider;
@@ -137,7 +137,7 @@
         when(mLockPatternUtils.getActivePasswordQuality(userId))
                 .thenReturn(PASSWORD_QUALITY_SOMETHING);
 
-        controller.create();
+        mController.create();
     }
 
     void setFingerprintManagerToHave(int numEnrollments) {
@@ -277,6 +277,18 @@
         }
     }
 
+    @Test
+    public void clickNext_onActivityResult_pause_shouldFinish() {
+        setupFingerprintEnrollIntroWith(newTokenOnlyIntent());
+        mController.resume();
+        mFingerprintEnrollIntroduction.clickNextBtn();
+        mController.pause().stop();
+        assertThat(mFingerprintEnrollIntroduction.shouldFinishWhenBackgrounded()).isEqualTo(false);
+
+        mController.resume().pause().stop();
+        assertThat(mFingerprintEnrollIntroduction.shouldFinishWhenBackgrounded()).isEqualTo(true);
+    }
+
     private Intent newTokenOnlyIntent() {
         return new Intent()
                 .putExtra(EXTRA_KEY_CHALLENGE_TOKEN, new byte[] { 1 });
@@ -362,5 +374,16 @@
         protected void getChallenge(GenerateChallengeCallback callback) {
             callback.onChallengeGenerated(mNewSensorId, mUserId, mNewChallenge);
         }
+
+        @Override
+        protected boolean shouldFinishWhenBackgrounded() {
+            return super.shouldFinishWhenBackgrounded();
+        }
+
+        //mock click next btn
+        public void clickNextBtn() {
+            super.onNextButtonClick(null);
+        }
+
     }
 }
diff --git a/tests/robotests/src/com/android/settings/development/GrammaticalGenderPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/GrammaticalGenderPreferenceControllerTest.java
new file mode 100644
index 0000000..005e6a8
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/GrammaticalGenderPreferenceControllerTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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.development;
+
+
+import static com.android.settings.development.GrammaticalGenderPreferenceController.GRAMMATICAL_GENDER_PROPERTY;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.IActivityManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+
+import androidx.preference.ListPreference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class GrammaticalGenderPreferenceControllerTest {
+    @Mock
+    private ListPreference mPreference;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+    @Mock
+    private IActivityManager mActivityManager;
+    private Configuration mConfiguration;
+    private Context mContext;
+    private GrammaticalGenderPreferenceController mController;
+    private String[] mListValues;
+    private String[] mListSummaries;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        final Resources resources = mContext.getResources();
+        mListValues = resources.getStringArray(R.array.grammatical_gender_values);
+        mListSummaries = resources.getStringArray(R.array.grammatical_gender_entries);
+        mConfiguration = new Configuration();
+        mController = new GrammaticalGenderPreferenceController(mContext, mActivityManager);
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
+                .thenReturn(mPreference);
+        mController.displayPreference(mPreferenceScreen);
+        doReturn(mConfiguration).when(mActivityManager).getConfiguration();
+    }
+
+    @Test
+    public void onPreferenceChange_setNeuter_shouldEnableNeuter() throws RemoteException {
+        mController.onPreferenceChange(mPreference, mListValues[1]);
+        final String currentValue = SystemProperties.get(GRAMMATICAL_GENDER_PROPERTY);
+        assertThat(currentValue).isEqualTo(mListValues[1]);
+        verify(mActivityManager).updatePersistentConfiguration(mConfiguration);
+        assertThat(mConfiguration.getGrammaticalGender())
+                .isEqualTo(Integer.parseInt(mListValues[1]));
+    }
+
+    @Test
+    public void updateState_setNeuter_shouldSetPreferenceToNeuter() {
+        SystemProperties.set(GRAMMATICAL_GENDER_PROPERTY, mListValues[1]);
+
+        mController.updateState(mPreference);
+
+        verify(mPreference).setValue(mListValues[1]);
+        verify(mPreference).setSummary(mListSummaries[1]);
+    }
+
+    @Test
+    public void onPreferenceChange_setFeminine_shouldEnableFeminine() throws RemoteException {
+        mController.onPreferenceChange(mPreference, mListValues[2]);
+        final String currentValue = SystemProperties.get(GRAMMATICAL_GENDER_PROPERTY);
+        assertThat(currentValue).isEqualTo(mListValues[2]);
+        verify(mActivityManager).updatePersistentConfiguration(mConfiguration);
+        assertThat(mConfiguration.getGrammaticalGender())
+                .isEqualTo(Integer.parseInt(mListValues[2]));
+    }
+
+    @Test
+    public void updateState_setFeminine_shouldSetPreferenceToFeminine() {
+        SystemProperties.set(GRAMMATICAL_GENDER_PROPERTY, mListValues[2]);
+
+        mController.updateState(mPreference);
+
+        verify(mPreference).setValue(mListValues[2]);
+        verify(mPreference).setSummary(mListSummaries[2]);
+    }
+
+    @Test
+    public void onPreferenceChange_setMasculine_shouldEnableMasculine() throws RemoteException {
+        mController.onPreferenceChange(mPreference, mListValues[3]);
+        final String currentValue = SystemProperties.get(GRAMMATICAL_GENDER_PROPERTY);
+        assertThat(currentValue).isEqualTo(mListValues[3]);
+        verify(mActivityManager).updatePersistentConfiguration(mConfiguration);
+        assertThat(mConfiguration.getGrammaticalGender())
+                .isEqualTo(Integer.parseInt(mListValues[3]));
+    }
+
+    @Test
+    public void updateState_setMasculine_shouldSetPreferenceToMasculine() {
+        SystemProperties.set(GRAMMATICAL_GENDER_PROPERTY, mListValues[3]);
+
+        mController.updateState(mPreference);
+
+        verify(mPreference).setValue(mListValues[3]);
+        verify(mPreference).setSummary(mListSummaries[3]);
+    }
+
+    @Test
+    public void updateState_noValueSet_shouldSetDefaultToNotSpecified() {
+        mController.updateState(mPreference);
+
+        verify(mPreference).setValue(mListValues[0]);
+        verify(mPreference).setSummary(mListSummaries[0]);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImplTest.java
index 1a43dbb..c9591a5 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImplTest.java
@@ -68,6 +68,15 @@
     }
 
     @Test
+    public void testIsBatteryTipsEnabled_returnFalse() {
+        assertThat(mPowerFeatureProvider.isBatteryTipsEnabled()).isFalse();
+    }
+
+    @Test
+    public void testIsBatteryTipsFeedbackEnabled_returnTrue() {
+        assertThat(mPowerFeatureProvider.isBatteryTipsFeedbackEnabled()).isTrue();
+    }
+    @Test
     public void testGetBatteryUsageListConsumePowerThreshold_return0() {
         assertThat(mPowerFeatureProvider.getBatteryUsageListConsumePowerThreshold()).isEqualTo(0.0);
     }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceControllerTest.java
index b444309..f6bc297 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceControllerTest.java
@@ -31,6 +31,7 @@
 import android.hardware.usb.UsbManager;
 import android.hardware.usb.UsbPort;
 import android.hardware.usb.UsbPortStatus;
+import android.os.BatteryManager;
 
 import androidx.preference.Preference;
 import androidx.test.core.app.ApplicationProvider;
@@ -146,6 +147,17 @@
     }
 
     @Test
+    public void getDashboardLabel_notChargingState_returnsCorrectLabel() {
+        mController.mPreference = new Preference(mContext);
+        BatteryInfo info = new BatteryInfo();
+        info.batteryStatus = BatteryManager.BATTERY_STATUS_NOT_CHARGING;
+        info.statusLabel = "expected returned label";
+
+        assertThat(mController.getDashboardLabel(mContext, info, true))
+                .isEqualTo(info.statusLabel);
+    }
+
+    @Test
     public void getSummary_batteryNotPresent_shouldShowWarningMessage() {
         mController.mIsBatteryPresent = false;
         assertThat(mController.getSummary())
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/IncompatibleChargerTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/IncompatibleChargerTipTest.java
index a5f1ab3..9f6e4e3 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/IncompatibleChargerTipTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/IncompatibleChargerTipTest.java
@@ -85,7 +85,7 @@
     @Test
     public void getIcon_showIcon() {
         assertThat(mIncompatibleChargerTip.getIconId())
-                .isEqualTo(R.drawable.ic_battery_alert_theme);
+                .isEqualTo(R.drawable.ic_battery_charger);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreferenceTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreferenceTest.java
new file mode 100644
index 0000000..6f9a474
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreferenceTest.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.fuelgauge.batteryusage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+
+import com.android.settings.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public final class BatteryTipsCardPreferenceTest {
+
+    private Context mContext;
+    private BatteryTipsCardPreference mBatteryTipsCardPreference;
+
+    @Before
+    public void setUp() {
+        mContext = spy(RuntimeEnvironment.application);
+        mBatteryTipsCardPreference = new BatteryTipsCardPreference(mContext, /*attrs=*/ null);
+    }
+
+    @Test
+    public void constructor_returnExpectedResult() {
+        assertThat(mBatteryTipsCardPreference.getLayoutResource()).isEqualTo(
+                R.layout.battery_tips_card);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java
index aa1ebd7..566df52 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java
@@ -22,8 +22,10 @@
 
 import android.app.AlarmManager;
 import android.app.Application;
+import android.app.usage.UsageStatsManager;
 import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
 
 import androidx.test.core.app.ApplicationProvider;
 
@@ -40,7 +42,6 @@
 import org.robolectric.shadows.ShadowAlarmManager;
 
 import java.time.Clock;
-import java.time.Duration;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
@@ -65,10 +66,12 @@
         BatteryTestUtils.insertDataToBatteryStateTable(
                 mContext, Clock.systemUTC().millis(), "com.android.systemui");
         mDao = database.batteryStateDao();
+        clearSharedPreferences();
     }
 
     @After
     public void tearDown() {
+        clearSharedPreferences();
         mPeriodicJobManager.reset();
     }
 
@@ -82,8 +85,21 @@
 
     @Test
     public void onReceive_withBootCompletedIntent_refreshesJob() {
+        final SharedPreferences sharedPreferences = DatabaseUtils.getSharedPreferences(mContext);
+        sharedPreferences
+                .edit()
+                .putInt(DatabaseUtils.KEY_LAST_USAGE_SOURCE,
+                        UsageStatsManager.USAGE_SOURCE_CURRENT_ACTIVITY)
+                .apply();
+
         mReceiver.onReceive(mContext, new Intent(Intent.ACTION_BOOT_COMPLETED));
+
         assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNotNull();
+        assertThat(
+                DatabaseUtils
+                        .getSharedPreferences(mContext)
+                        .contains(DatabaseUtils.KEY_LAST_USAGE_SOURCE))
+                .isFalse();
     }
 
     @Test
@@ -133,15 +149,7 @@
                 BootBroadcastReceiver.ACTION_PERIODIC_JOB_RECHECK);
     }
 
-    private void insertExpiredData(int shiftDay) {
-        final long expiredTimeInMs =
-                Clock.systemUTC().millis() - Duration.ofDays(shiftDay).toMillis();
-        BatteryTestUtils.insertDataToBatteryStateTable(
-                mContext, expiredTimeInMs - 1, "com.android.systemui");
-        BatteryTestUtils.insertDataToBatteryStateTable(
-                mContext, expiredTimeInMs, "com.android.systemui");
-        // Ensures the testing environment is correct.
-        assertThat(mDao.getAllAfter(0)).hasSize(3);
+    private void clearSharedPreferences() {
+        DatabaseUtils.getSharedPreferences(mContext).edit().clear().apply();
     }
-
 }
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 6b8073b..3cbe8a4 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
@@ -25,7 +25,6 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
-import android.app.usage.IUsageStatsManager;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageEvents.Event;
 import android.content.ContentValues;
@@ -35,7 +34,6 @@
 import android.os.BatteryManager;
 import android.os.BatteryUsageStats;
 import android.os.LocaleList;
-import android.os.RemoteException;
 import android.os.UserHandle;
 
 import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
@@ -62,14 +60,13 @@
     @Mock
     private BatteryUsageStats mBatteryUsageStats;
     @Mock
-    private IUsageStatsManager mUsageStatsManager;
-    @Mock
     private BatteryEntry mMockBatteryEntry;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mContext = spy(RuntimeEnvironment.application);
+        ConvertUtils.sUsageSource = ConvertUtils.EMPTY_USAGE_SOURCE;
         when(mContext.getPackageManager()).thenReturn(mMockPackageManager);
     }
 
@@ -302,7 +299,7 @@
 
         final long userId = 2;
         final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEvent(
-                mContext, mUsageStatsManager, event, userId);
+                mContext, event, userId);
         assertThat(appUsageEvent.getTimestamp()).isEqualTo(101L);
         assertThat(appUsageEvent.getType()).isEqualTo(AppUsageEventType.ACTIVITY_RESUMED);
         assertThat(appUsageEvent.getPackageName()).isEqualTo("com.android.settings1");
@@ -322,8 +319,8 @@
         when(mMockPackageManager.getPackageUidAsUser(any(), anyInt())).thenReturn(1001);
 
         final long userId = 1;
-        final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEvent(
-                mContext, mUsageStatsManager, event, userId);
+        final AppUsageEvent appUsageEvent =
+                ConvertUtils.convertToAppUsageEvent(mContext, event, userId);
         assertThat(appUsageEvent.getTimestamp()).isEqualTo(101L);
         assertThat(appUsageEvent.getType()).isEqualTo(AppUsageEventType.DEVICE_SHUTDOWN);
         assertThat(appUsageEvent.getPackageName()).isEqualTo("com.android.settings1");
@@ -338,8 +335,8 @@
         final Event event = new Event();
         event.mPackage = null;
 
-        final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEvent(
-                mContext, mUsageStatsManager, event, /*userId=*/ 0);
+        final AppUsageEvent appUsageEvent =
+                ConvertUtils.convertToAppUsageEvent(mContext, event, /*userId=*/ 0);
 
         assertThat(appUsageEvent).isNull();
     }
@@ -354,8 +351,8 @@
                 .thenThrow(new PackageManager.NameNotFoundException());
 
         final long userId = 1;
-        final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEvent(
-                mContext, mUsageStatsManager, event, userId);
+        final AppUsageEvent appUsageEvent =
+                ConvertUtils.convertToAppUsageEvent(mContext, event, userId);
 
         assertThat(appUsageEvent).isNull();
     }
@@ -450,51 +447,47 @@
     }
 
     @Test
-    public void getEffectivePackageName_currentActivity_returnPackageName() throws RemoteException {
-        when(mUsageStatsManager.getUsageSource()).thenReturn(USAGE_SOURCE_CURRENT_ACTIVITY);
+    public void getEffectivePackageName_currentActivity_returnPackageName() {
+        ConvertUtils.sUsageSource = USAGE_SOURCE_CURRENT_ACTIVITY;
         final String packageName = "com.android.settings1";
         final String taskRootPackageName = "com.android.settings2";
 
         assertThat(ConvertUtils.getEffectivePackageName(
-                mUsageStatsManager, packageName, taskRootPackageName))
+                mContext, packageName, taskRootPackageName))
                 .isEqualTo(packageName);
     }
 
     @Test
-    public void getEffectivePackageName_usageSourceThrowException_returnPackageName()
-            throws RemoteException {
-        when(mUsageStatsManager.getUsageSource()).thenThrow(new RemoteException());
+    public void getEffectivePackageName_emptyUsageSource_returnPackageName() {
         final String packageName = "com.android.settings1";
         final String taskRootPackageName = "com.android.settings2";
 
         assertThat(ConvertUtils.getEffectivePackageName(
-                mUsageStatsManager, packageName, taskRootPackageName))
+                mContext, packageName, taskRootPackageName))
                 .isEqualTo(packageName);
     }
 
     @Test
-    public void getEffectivePackageName_rootActivity_returnTaskRootPackageName()
-            throws RemoteException {
-        when(mUsageStatsManager.getUsageSource()).thenReturn(USAGE_SOURCE_TASK_ROOT_ACTIVITY);
+    public void getEffectivePackageName_rootActivity_returnTaskRootPackageName() {
+        ConvertUtils.sUsageSource = USAGE_SOURCE_TASK_ROOT_ACTIVITY;
         final String packageName = "com.android.settings1";
         final String taskRootPackageName = "com.android.settings2";
 
         assertThat(ConvertUtils.getEffectivePackageName(
-                mUsageStatsManager, packageName, taskRootPackageName))
+                mContext, packageName, taskRootPackageName))
                 .isEqualTo(taskRootPackageName);
     }
 
     @Test
-    public void getEffectivePackageName_nullOrEmptyTaskRoot_returnPackageName()
-            throws RemoteException {
-        when(mUsageStatsManager.getUsageSource()).thenReturn(USAGE_SOURCE_TASK_ROOT_ACTIVITY);
+    public void getEffectivePackageName_nullOrEmptyTaskRoot_returnPackageName() {
+        ConvertUtils.sUsageSource = USAGE_SOURCE_TASK_ROOT_ACTIVITY;
         final String packageName = "com.android.settings1";
 
         assertThat(ConvertUtils.getEffectivePackageName(
-                mUsageStatsManager, packageName, /*taskRootPackageName=*/ null))
+                mContext, packageName, /*taskRootPackageName=*/ null))
                 .isEqualTo(packageName);
         assertThat(ConvertUtils.getEffectivePackageName(
-                mUsageStatsManager, packageName, /*taskRootPackageName=*/ ""))
+                mContext, packageName, /*taskRootPackageName=*/ ""))
                 .isEqualTo(packageName);
     }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java
index b610cfb..7f7fe43 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java
@@ -72,7 +72,7 @@
         MockitoAnnotations.initMocks(this);
 
         mContext = spy(RuntimeEnvironment.application);
-        DataProcessor.sUsageStatsManager = mUsageStatsManager;
+        DatabaseUtils.sUsageStatsManager = mUsageStatsManager;
         doReturn(mContext).when(mContext).getApplicationContext();
         doReturn(mUserManager)
                 .when(mContext)
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 e2274e2..8bed054 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java
@@ -93,7 +93,7 @@
         mPowerUsageFeatureProvider = mFeatureFactory.powerUsageFeatureProvider;
 
         DataProcessor.sTestSystemAppsPackageNames = Set.of();
-        DataProcessor.sUsageStatsManager = mUsageStatsManager;
+        DatabaseUtils.sUsageStatsManager = mUsageStatsManager;
         doReturn(mIntent).when(mContext).registerReceiver(
                 isA(BroadcastReceiver.class), isA(IntentFilter.class));
         doReturn(100).when(mIntent).getIntExtra(eq(BatteryManager.EXTRA_SCALE), anyInt());
@@ -249,7 +249,7 @@
 
         final Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>> periodMap =
                 DataProcessor.generateAppUsagePeriodMap(
-                        14400000L, hourlyBatteryLevelsPerDay, appUsageEventList, new ArrayList<>());
+                        mContext, hourlyBatteryLevelsPerDay, appUsageEventList, new ArrayList<>());
 
         assertThat(periodMap).hasSize(3);
         // Day 1
@@ -287,7 +287,8 @@
         hourlyBatteryLevelsPerDay.add(
                 new BatteryLevelData.PeriodBatteryLevelData(new ArrayList<>(), new ArrayList<>()));
         assertThat(DataProcessor.generateAppUsagePeriodMap(
-                0L, hourlyBatteryLevelsPerDay, new ArrayList<>(), new ArrayList<>())).isNull();
+                mContext, hourlyBatteryLevelsPerDay, new ArrayList<>(), new ArrayList<>()))
+                .isNull();
     }
 
     @Test
@@ -1644,7 +1645,7 @@
 
         final Map<Long, Map<String, List<AppUsagePeriod>>> appUsagePeriodMap =
                 DataProcessor.buildAppUsagePeriodList(
-                        appUsageEvents, new ArrayList<>(), 0, 5);
+                        mContext, appUsageEvents, new ArrayList<>(), 0, 5);
 
         assertThat(appUsagePeriodMap).hasSize(2);
         final Map<String, List<AppUsagePeriod>> userMap1 = appUsagePeriodMap.get(1L);
@@ -1668,7 +1669,7 @@
     @Test
     public void buildAppUsagePeriodList_emptyEventList_returnNull() {
         assertThat(DataProcessor.buildAppUsagePeriodList(
-                new ArrayList<>(), new ArrayList<>(), 0, 1)).isNull();
+                mContext, new ArrayList<>(), new ArrayList<>(), 0, 1)).isNull();
     }
 
     @Test
@@ -1680,7 +1681,7 @@
                 AppUsageEventType.DEVICE_SHUTDOWN, /*timestamp=*/ 2));
 
         assertThat(DataProcessor.buildAppUsagePeriodList(
-                appUsageEvents, new ArrayList<>(), 0, 3)).isNull();
+                mContext, appUsageEvents, new ArrayList<>(), 0, 3)).isNull();
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtilsTest.java
index efce44e..24be769 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtilsTest.java
@@ -16,6 +16,9 @@
 
 package com.android.settings.fuelgauge.batteryusage;
 
+import static android.app.usage.UsageStatsManager.USAGE_SOURCE_CURRENT_ACTIVITY;
+import static android.app.usage.UsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVITY;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -23,15 +26,19 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
 
+import android.app.usage.IUsageStatsManager;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.database.MatrixCursor;
 import android.os.BatteryManager;
 import android.os.BatteryUsageStats;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 
@@ -67,6 +74,7 @@
     @Mock private BatteryEntry mMockBatteryEntry2;
     @Mock private BatteryEntry mMockBatteryEntry3;
     @Mock private Context mMockContext;
+    @Mock private IUsageStatsManager mUsageStatsManager;
 
     @Before
     public void setUp() {
@@ -77,6 +85,7 @@
         doReturn(mPackageManager).when(mMockContext).getPackageManager();
         doReturn(mPackageManager).when(mContext).getPackageManager();
         DatabaseUtils.getSharedPreferences(mContext).edit().clear().apply();
+        DatabaseUtils.sUsageStatsManager = mUsageStatsManager;
     }
 
     @Test
@@ -423,6 +432,71 @@
     }
 
     @Test
+    public void removeUsageSource_hasNoData() {
+        DatabaseUtils.removeUsageSource(mContext);
+        assertThat(
+                DatabaseUtils
+                        .getSharedPreferences(mContext)
+                        .contains(DatabaseUtils.KEY_LAST_USAGE_SOURCE))
+                .isFalse();
+    }
+
+    @Test
+    public void removeUsageSource_hasData_deleteUsageSource() {
+        final SharedPreferences sharedPreferences = DatabaseUtils.getSharedPreferences(mContext);
+        sharedPreferences
+                .edit()
+                .putInt(DatabaseUtils.KEY_LAST_USAGE_SOURCE, USAGE_SOURCE_TASK_ROOT_ACTIVITY)
+                .apply();
+
+        DatabaseUtils.removeUsageSource(mContext);
+
+        assertThat(
+                DatabaseUtils
+                        .getSharedPreferences(mContext)
+                        .contains(DatabaseUtils.KEY_LAST_USAGE_SOURCE))
+                .isFalse();
+    }
+
+    @Test
+    public void getUsageSource_hasData() {
+        final SharedPreferences sharedPreferences = DatabaseUtils.getSharedPreferences(mContext);
+        sharedPreferences
+                .edit()
+                .putInt(DatabaseUtils.KEY_LAST_USAGE_SOURCE, USAGE_SOURCE_TASK_ROOT_ACTIVITY)
+                .apply();
+
+        assertThat(DatabaseUtils.getUsageSource(mContext))
+                .isEqualTo(USAGE_SOURCE_TASK_ROOT_ACTIVITY);
+    }
+
+    @Test
+    public void getUsageSource_notHasData_writeLoadedData() throws RemoteException {
+        when(mUsageStatsManager.getUsageSource()).thenReturn(USAGE_SOURCE_TASK_ROOT_ACTIVITY);
+
+        assertThat(DatabaseUtils.getUsageSource(mContext))
+                .isEqualTo(USAGE_SOURCE_TASK_ROOT_ACTIVITY);
+        assertThat(
+                DatabaseUtils
+                        .getSharedPreferences(mContext)
+                        .getInt(DatabaseUtils.KEY_LAST_USAGE_SOURCE, USAGE_SOURCE_CURRENT_ACTIVITY))
+                .isEqualTo(USAGE_SOURCE_TASK_ROOT_ACTIVITY);
+    }
+
+    @Test
+    public void getUsageSource_throwException_writeDefaultData() throws RemoteException {
+        when(mUsageStatsManager.getUsageSource()).thenThrow(new RemoteException());
+
+        assertThat(DatabaseUtils.getUsageSource(mContext))
+                .isEqualTo(USAGE_SOURCE_CURRENT_ACTIVITY);
+        assertThat(
+                DatabaseUtils
+                        .getSharedPreferences(mContext)
+                        .getInt(DatabaseUtils.KEY_LAST_USAGE_SOURCE, USAGE_SOURCE_CURRENT_ACTIVITY))
+                .isEqualTo(USAGE_SOURCE_CURRENT_ACTIVITY);
+    }
+
+    @Test
     public void recordDateTime_writeDataIntoSharedPreferences() {
         final String preferenceKey = "test_preference_key";
         DatabaseUtils.recordDateTime(mContext, preferenceKey);
diff --git a/tests/robotests/src/com/android/settings/media/MediaDeviceUpdateWorkerTest.java b/tests/robotests/src/com/android/settings/media/MediaDeviceUpdateWorkerTest.java
index 73b5ab0..e0d76ce 100644
--- a/tests/robotests/src/com/android/settings/media/MediaDeviceUpdateWorkerTest.java
+++ b/tests/robotests/src/com/android/settings/media/MediaDeviceUpdateWorkerTest.java
@@ -218,16 +218,13 @@
         mMediaDeviceUpdateWorker.mLocalMediaManager = mock(LocalMediaManager.class);
         final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
         final RoutingSessionInfo remoteSessionInfo = mock(RoutingSessionInfo.class);
-        final RoutingSessionInfo localSessionInfo = mock(RoutingSessionInfo.class);
         when(remoteSessionInfo.isSystemSession()).thenReturn(false);
-        when(localSessionInfo.isSystemSession()).thenReturn(true);
         routingSessionInfos.add(remoteSessionInfo);
-        routingSessionInfos.add(localSessionInfo);
-        when(mMediaDeviceUpdateWorker.mLocalMediaManager.getActiveMediaSession()).thenReturn(
-                routingSessionInfos);
+        when(mMediaDeviceUpdateWorker.mLocalMediaManager.getRemoteRoutingSessions())
+                .thenReturn(routingSessionInfos);
 
-        assertThat(mMediaDeviceUpdateWorker.getActiveRemoteMediaDevice()).containsExactly(
-                remoteSessionInfo);
+        assertThat(mMediaDeviceUpdateWorker.getActiveRemoteMediaDevices())
+                .containsExactly(remoteSessionInfo);
     }
 
     @Test
@@ -246,6 +243,7 @@
                 TEST_DEVICE_PACKAGE_NAME1);
 
         mMediaDeviceUpdateWorker = new MediaDeviceUpdateWorker(mContext, URI2);
+        mMediaDeviceUpdateWorker.mManager = mock(MediaRouter2Manager.class);
         mMediaDeviceUpdateWorker.mLocalMediaManager = mock(LocalMediaManager.class);
         when(mMediaDeviceUpdateWorker.mLocalMediaManager.getPackageName())
                 .thenReturn(TEST_DEVICE_PACKAGE_NAME2);
diff --git a/tests/robotests/src/com/android/settings/media/RemoteMediaSliceTest.java b/tests/robotests/src/com/android/settings/media/RemoteMediaSliceTest.java
index d995793..5188ad4 100644
--- a/tests/robotests/src/com/android/settings/media/RemoteMediaSliceTest.java
+++ b/tests/robotests/src/com/android/settings/media/RemoteMediaSliceTest.java
@@ -100,8 +100,8 @@
         when(remoteSessionInfo.getVolume()).thenReturn(10);
         when(remoteSessionInfo.isSystemSession()).thenReturn(false);
         mRoutingSessionInfos.add(remoteSessionInfo);
-        when(sMediaDeviceUpdateWorker.getActiveRemoteMediaDevice()).thenReturn(
-                mRoutingSessionInfos);
+        when(sMediaDeviceUpdateWorker.getActiveRemoteMediaDevices())
+                .thenReturn(mRoutingSessionInfos);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/notification/RemoteVolumeGroupControllerTest.java b/tests/robotests/src/com/android/settings/notification/RemoteVolumeGroupControllerTest.java
index e713963..1e42e18 100644
--- a/tests/robotests/src/com/android/settings/notification/RemoteVolumeGroupControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/RemoteVolumeGroupControllerTest.java
@@ -31,6 +31,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageStats;
+import android.media.MediaRoute2Info;
 import android.media.MediaRouter2Manager;
 import android.media.RoutingSessionInfo;
 import android.media.session.MediaSessionManager;
@@ -83,6 +84,8 @@
     private SharedPreferences mSharedPreferences;
     @Mock
     private MediaSessionManager mMediaSessionManager;
+    @Mock
+    private MediaRouter2Manager mRouterManager;
 
     private final List<RoutingSessionInfo> mRoutingSessionInfos = new ArrayList<>();
 
@@ -102,7 +105,7 @@
                 Context.MEDIA_SESSION_SERVICE);
         mController = new RemoteVolumeGroupController(mContext, KEY_REMOTE_VOLUME_GROUP);
         mController.mLocalMediaManager = mLocalMediaManager;
-        mController.mRouterManager = mock(MediaRouter2Manager.class);
+        mController.mRouterManager = mRouterManager;
         mPreferenceCategory = spy(new PreferenceCategory(mContext));
         mPreferenceCategory.setKey(mController.getPreferenceKey());
 
@@ -118,7 +121,7 @@
         when(remoteSessionInfo.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
         when(remoteSessionInfo.isSystemSession()).thenReturn(false);
         mRoutingSessionInfos.add(remoteSessionInfo);
-        when(mLocalMediaManager.getActiveMediaSession()).thenReturn(mRoutingSessionInfos);
+        when(mLocalMediaManager.getRemoteRoutingSessions()).thenReturn(mRoutingSessionInfos);
     }
 
     @Test
@@ -178,6 +181,10 @@
 
     @Test
     public void displayPreference_withActiveSession_checkSwitcherPreferenceTitle() {
+        // Preference title needs media output to be enabled.
+        when(mRouterManager.getTransferableRoutes(TEST_PACKAGE_NAME)).thenReturn(List.of(mock(
+                MediaRoute2Info.class)));
+
         initPackage();
         mShadowPackageManager.addPackage(mPackageInfo, mPackageStats);
         mController.displayPreference(mScreen);
diff --git a/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceTest.java b/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceTest.java
index ad374cd..6abfe69 100644
--- a/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/notification/VolumeSeekBarPreferenceTest.java
@@ -17,62 +17,81 @@
 package com.android.settings.notification;
 
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.media.AudioManager;
+import android.os.LocaleList;
 import android.preference.SeekBarVolumizer;
 import android.widget.SeekBar;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 
+import java.util.Locale;
+
 @RunWith(RobolectricTestRunner.class)
 public class VolumeSeekBarPreferenceTest {
 
     private static final CharSequence CONTENT_DESCRIPTION = "TEST";
+    private static final int STREAM = 5;
     @Mock
     private AudioManager mAudioManager;
     @Mock
     private VolumeSeekBarPreference mPreference;
     @Mock
     private Context mContext;
+
+    @Mock
+    private Resources mRes;
+    @Mock
+    private Configuration mConfig;
     @Mock
     private SeekBar mSeekBar;
+    @Captor
+    private ArgumentCaptor<SeekBarVolumizer.Callback> mSbvc;
     @Mock
     private SeekBarVolumizer mVolumizer;
+    @Mock
+    private SeekBarVolumizerFactory mSeekBarVolumizerFactory;
     private VolumeSeekBarPreference.Listener mListener;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         when(mContext.getSystemService(Context.AUDIO_SERVICE)).thenReturn(mAudioManager);
+        when(mSeekBarVolumizerFactory.create(eq(STREAM), eq(null), mSbvc.capture()))
+                .thenReturn(mVolumizer);
+        doCallRealMethod().when(mPreference).setStream(anyInt());
         doCallRealMethod().when(mPreference).updateContentDescription(CONTENT_DESCRIPTION);
         mPreference.mSeekBar = mSeekBar;
         mPreference.mAudioManager = mAudioManager;
-        mPreference.mVolumizer = mVolumizer;
+        mPreference.mSeekBarVolumizerFactory = mSeekBarVolumizerFactory;
         mListener = () -> mPreference.updateContentDescription(CONTENT_DESCRIPTION);
     }
 
     @Test
     public void setStream_shouldSetMinMaxAndProgress() {
-        final int stream = 5;
         final int max = 17;
         final int min = 1;
         final int progress = 4;
-        when(mAudioManager.getStreamMaxVolume(stream)).thenReturn(max);
-        when(mAudioManager.getStreamMinVolumeInt(stream)).thenReturn(min);
-        when(mAudioManager.getStreamVolume(stream)).thenReturn(progress);
-        doCallRealMethod().when(mPreference).setStream(anyInt());
+        when(mAudioManager.getStreamMaxVolume(STREAM)).thenReturn(max);
+        when(mAudioManager.getStreamMinVolumeInt(STREAM)).thenReturn(min);
+        when(mAudioManager.getStreamVolume(STREAM)).thenReturn(progress);
 
-        mPreference.setStream(stream);
+        mPreference.setStream(STREAM);
 
         verify(mPreference).setMax(max);
         verify(mPreference).setMin(min);
@@ -85,6 +104,7 @@
         doCallRealMethod().when(mPreference).setListener(mListener);
         doCallRealMethod().when(mPreference).init();
 
+        mPreference.setStream(STREAM);
         mPreference.setListener(mListener);
         mPreference.init();
 
@@ -107,8 +127,69 @@
         doCallRealMethod().when(mPreference).setListener(mListener);
         doCallRealMethod().when(mPreference).init();
 
+        mPreference.setStream(STREAM);
         mPreference.init();
 
         verify(mPreference, never()).updateContentDescription(CONTENT_DESCRIPTION);
     }
+
+    @Test
+    public void init_changeProgress_overrideStateDescriptionCalled() {
+        final int progress = 4;
+        when(mPreference.formatStateDescription(progress)).thenReturn(CONTENT_DESCRIPTION);
+        doCallRealMethod().when(mPreference).init();
+
+        mPreference.setStream(STREAM);
+        mPreference.init();
+
+        verify(mSeekBarVolumizerFactory).create(eq(STREAM), eq(null), mSbvc.capture());
+
+        mSbvc.getValue().onProgressChanged(mSeekBar, 4, true);
+
+        verify(mPreference).overrideSeekBarStateDescription(CONTENT_DESCRIPTION);
+    }
+
+    @Test
+    public void init_changeProgress_stateDescriptionValueUpdated() {
+        final int max = 17;
+        final int min = 1;
+        int progress = 4;
+        when(mAudioManager.getStreamMaxVolume(STREAM)).thenReturn(max);
+        when(mAudioManager.getStreamMinVolumeInt(STREAM)).thenReturn(min);
+        when(mAudioManager.getStreamVolume(STREAM)).thenReturn(progress);
+        when(mPreference.getMin()).thenReturn(min);
+        when(mPreference.getMax()).thenReturn(max);
+        when(mPreference.getContext()).thenReturn(mContext);
+        when(mContext.getResources()).thenReturn(mRes);
+        when(mRes.getConfiguration()).thenReturn(mConfig);
+        when(mConfig.getLocales()).thenReturn(new LocaleList(Locale.US));
+        doCallRealMethod().when(mPreference).init();
+
+        mPreference.setStream(STREAM);
+        mPreference.init();
+
+        // On progress change, Round down the percent to match it with what the talkback says.
+        // (b/285458191)
+        // when progress is 4, the percent is 0.187. The state description should be set to 18%.
+        testFormatStateDescription(progress, "18%");
+
+        progress = 6;
+
+        // when progress is 6, the percent is 0.3125. The state description should be set to 31%.
+        testFormatStateDescription(progress, "31%");
+
+        progress = 7;
+
+        // when progress is 7, the percent is 0.375. The state description should be set to 37%.
+        testFormatStateDescription(progress, "37%");
+    }
+
+    private void testFormatStateDescription(int progress, String expected) {
+        doCallRealMethod().when(mPreference).formatStateDescription(progress);
+        doCallRealMethod().when(mPreference).getPercent(progress);
+
+        mSbvc.getValue().onProgressChanged(mSeekBar, progress, true);
+
+        verify(mPreference).overrideSeekBarStateDescription(expected);
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java
index 12e4b8d..dea936d 100644
--- a/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java
+++ b/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java
@@ -27,10 +27,8 @@
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
-import static android.provider.DeviceConfig.NAMESPACE_AUTO_PIN_CONFIRMATION;
 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
 
-import static com.android.internal.widget.LockPatternUtils.FLAG_ENABLE_AUTO_PIN_CONFIRMATION;
 import static com.android.internal.widget.LockPatternUtils.PASSWORD_TYPE_KEY;
 import static com.android.settings.password.ChooseLockGeneric.CONFIRM_CREDENTIALS;
 
@@ -45,7 +43,6 @@
 import android.app.admin.PasswordPolicy;
 import android.content.Intent;
 import android.os.UserHandle;
-import android.provider.DeviceConfig;
 import android.view.View;
 import android.widget.CheckBox;
 import android.widget.TextView;
@@ -55,7 +52,6 @@
 import com.android.settings.password.ChooseLockPassword.ChooseLockPasswordFragment;
 import com.android.settings.password.ChooseLockPassword.IntentBuilder;
 import com.android.settings.testutils.shadow.SettingsShadowResources;
-import com.android.settings.testutils.shadow.ShadowDeviceConfig;
 import com.android.settings.testutils.shadow.ShadowDevicePolicyManager;
 import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
 import com.android.settings.testutils.shadow.ShadowUtils;
@@ -79,7 +75,6 @@
         ShadowLockPatternUtils.class,
         ShadowUtils.class,
         ShadowDevicePolicyManager.class,
-        ShadowDeviceConfig.class,
 })
 public class ChooseLockPasswordTest {
     @Before
@@ -453,8 +448,6 @@
 
     @Test
     public void autoPinConfirmOption_featureEnabledAndUntouchedByUser_changeStateAsPerRules() {
-        DeviceConfig.setProperty(NAMESPACE_AUTO_PIN_CONFIRMATION, FLAG_ENABLE_AUTO_PIN_CONFIRMATION,
-                /* value= */ "true", /* makeDefault= */ false);
         ChooseLockPassword passwordActivity = setupActivityWithPinTypeAndDefaultPolicy();
 
         ChooseLockPasswordFragment fragment = getChooseLockPasswordFragment(passwordActivity);
@@ -491,8 +484,6 @@
 
     @Test
     public void autoPinConfirmOption_featureEnabledAndModifiedByUser_shouldChangeStateAsPerRules() {
-        DeviceConfig.setProperty(NAMESPACE_AUTO_PIN_CONFIRMATION, FLAG_ENABLE_AUTO_PIN_CONFIRMATION,
-                /* value= */ "true", /* makeDefault= */ false);
         ChooseLockPassword passwordActivity = setupActivityWithPinTypeAndDefaultPolicy();
 
         ChooseLockPasswordFragment fragment = getChooseLockPasswordFragment(passwordActivity);
diff --git a/tests/robotests/src/com/android/settings/security/screenlock/AutoPinConfirmPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/security/screenlock/AutoPinConfirmPreferenceControllerTest.java
index 715913c..86c1244 100644
--- a/tests/robotests/src/com/android/settings/security/screenlock/AutoPinConfirmPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/security/screenlock/AutoPinConfirmPreferenceControllerTest.java
@@ -16,22 +16,16 @@
 
 package com.android.settings.security.screenlock;
 
-import static android.provider.DeviceConfig.NAMESPACE_AUTO_PIN_CONFIRMATION;
-
-import static com.android.internal.widget.LockPatternUtils.FLAG_ENABLE_AUTO_PIN_CONFIRMATION;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
-import android.provider.DeviceConfig;
 
 import androidx.preference.SwitchPreference;
 import androidx.test.core.app.ApplicationProvider;
 
 import com.android.internal.widget.LockPatternUtils;
-import com.android.settings.testutils.shadow.ShadowDeviceConfig;
 import com.android.settingslib.core.lifecycle.ObservablePreferenceFragment;
 
 import org.junit.Before;
@@ -40,10 +34,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
 
 @RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowDeviceConfig.class})
 public class AutoPinConfirmPreferenceControllerTest {
     private static final Integer TEST_USER_ID = 1;
     @Mock
@@ -65,8 +57,6 @@
 
     @Test
     public void isAvailable_featureEnabledAndLockSetToNone_shouldReturnFalse() {
-        DeviceConfig.setProperty(NAMESPACE_AUTO_PIN_CONFIRMATION, FLAG_ENABLE_AUTO_PIN_CONFIRMATION,
-                "true", /* makeDefault */ false);
         when(mLockPatternUtils.isSecure(TEST_USER_ID)).thenReturn(true);
 
         assertThat(mController.isAvailable()).isFalse();
@@ -74,8 +64,6 @@
 
     @Test
     public void isAvailable_featureEnabledAndLockSetToPassword_shouldReturnFalse() {
-        DeviceConfig.setProperty(NAMESPACE_AUTO_PIN_CONFIRMATION, FLAG_ENABLE_AUTO_PIN_CONFIRMATION,
-                "true", /* makeDefault */ false);
         when(mLockPatternUtils.isSecure(TEST_USER_ID)).thenReturn(true);
         when(mLockPatternUtils.getCredentialTypeForUser(TEST_USER_ID))
                 .thenReturn(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD);
@@ -85,8 +73,6 @@
 
     @Test
     public void isAvailable_featureEnabledAndLockSetToPIN_lengthLessThanSix_shouldReturnFalse() {
-        DeviceConfig.setProperty(NAMESPACE_AUTO_PIN_CONFIRMATION, FLAG_ENABLE_AUTO_PIN_CONFIRMATION,
-                "true", /* makeDefault */ false);
         when(mLockPatternUtils.getCredentialTypeForUser(TEST_USER_ID))
                 .thenReturn(LockPatternUtils.CREDENTIAL_TYPE_PIN);
         when(mLockPatternUtils.getPinLength(TEST_USER_ID)).thenReturn(5);
@@ -96,8 +82,6 @@
 
     @Test
     public void isAvailable_featureEnabledAndLockSetToPIN_lengthMoreThanEqSix_shouldReturnTrue() {
-        DeviceConfig.setProperty(NAMESPACE_AUTO_PIN_CONFIRMATION, FLAG_ENABLE_AUTO_PIN_CONFIRMATION,
-                "true", /* makeDefault */ false);
         when(mLockPatternUtils.isSecure(TEST_USER_ID)).thenReturn(true);
         when(mLockPatternUtils.getCredentialTypeForUser(TEST_USER_ID))
                 .thenReturn(LockPatternUtils.CREDENTIAL_TYPE_PIN);
@@ -107,20 +91,7 @@
     }
 
     @Test
-    public void isAvailable_featureDisabledAndLockSetToPIN_shouldReturnFalse() {
-        DeviceConfig.setProperty(NAMESPACE_AUTO_PIN_CONFIRMATION, FLAG_ENABLE_AUTO_PIN_CONFIRMATION,
-                "false", /* makeDefault */ false);
-        when(mLockPatternUtils.isSecure(TEST_USER_ID)).thenReturn(true);
-        when(mLockPatternUtils.getCredentialTypeForUser(TEST_USER_ID))
-                .thenReturn(LockPatternUtils.CREDENTIAL_TYPE_PIN);
-
-        assertThat(mController.isAvailable()).isFalse();
-    }
-
-    @Test
     public void updateState_ChangingSettingState_shouldSetPreferenceToAppropriateCheckedState() {
-        DeviceConfig.setProperty(NAMESPACE_AUTO_PIN_CONFIRMATION, FLAG_ENABLE_AUTO_PIN_CONFIRMATION,
-                "true", /* makeDefault */ false);
         // When auto_pin_confirm setting is disabled, switchPreference is unchecked
         when(mLockPatternUtils.isAutoPinConfirmEnabled(TEST_USER_ID)).thenReturn(false);
         mController.updateState(mPreference);
diff --git a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
index 90985dd..cd9b081 100644
--- a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -171,8 +171,9 @@
         return powerUsageFeatureProvider;
     }
 
+    @NotNull
     @Override
-    public DashboardFeatureProvider getDashboardFeatureProvider(Context context) {
+    public DashboardFeatureProvider getDashboardFeatureProvider() {
         return dashboardFeatureProvider;
     }
 
diff --git a/tests/robotests/src/com/android/settings/wifi/WifiEntryPreferenceTest.java b/tests/robotests/src/com/android/settings/wifi/WifiEntryPreferenceTest.java
index a60b531..316beb3 100644
--- a/tests/robotests/src/com/android/settings/wifi/WifiEntryPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/WifiEntryPreferenceTest.java
@@ -18,11 +18,15 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
+import android.net.wifi.sharedconnectivity.app.NetworkProviderInfo;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.LinearLayout;
@@ -31,6 +35,7 @@
 
 import com.android.settingslib.R;
 import com.android.settingslib.wifi.WifiUtils;
+import com.android.wifitrackerlib.HotspotNetworkEntry;
 import com.android.wifitrackerlib.WifiEntry;
 
 import org.junit.Before;
@@ -52,6 +57,8 @@
     @Mock
     private WifiEntry mMockWifiEntry;
     @Mock
+    private HotspotNetworkEntry mHotspotNetworkEntry;
+    @Mock
     private WifiUtils.InternetIconInjector mMockIconInjector;
 
     @Mock
@@ -256,4 +263,26 @@
     public void getSecondTargetResId_shouldNotReturnZero() {
         assertThat(mPref.getSecondTargetResId()).isNotEqualTo(0);
     }
+
+    @Test
+    public void refresh_itsHotspotNetworkEntry_shouldUpdateHotspotIcon() {
+        int deviceType = NetworkProviderInfo.DEVICE_TYPE_PHONE;
+        when(mHotspotNetworkEntry.getDeviceType()).thenReturn(deviceType);
+        WifiEntryPreference pref = spy(
+                new WifiEntryPreference(mContext, mHotspotNetworkEntry, mMockIconInjector));
+
+        pref.refresh();
+
+        verify(pref).updateHotspotIcon(deviceType);
+    }
+
+    @Test
+    public void refresh_notHotspotNetworkEntry_shouldNotUpdateHotspotIcon() {
+        WifiEntryPreference pref = spy(
+                new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector));
+
+        pref.refresh();
+
+        verify(pref, never()).updateHotspotIcon(anyInt());
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/wifi/slice/WifiScanWorkerTest.java b/tests/robotests/src/com/android/settings/wifi/slice/WifiScanWorkerTest.java
index 9d57d4a..54a546a 100644
--- a/tests/robotests/src/com/android/settings/wifi/slice/WifiScanWorkerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/slice/WifiScanWorkerTest.java
@@ -26,19 +26,25 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.Context;
+import android.net.wifi.WifiManager;
+
 import androidx.lifecycle.Lifecycle;
+import androidx.test.core.app.ApplicationProvider;
 
 import com.android.settings.wifi.WifiPickerTrackerHelper;
 import com.android.wifitrackerlib.WifiEntry;
 import com.android.wifitrackerlib.WifiPickerTracker;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
 
 import java.util.Arrays;
 
@@ -47,17 +53,24 @@
 
     private static final int SUB_ID = 2;
 
-    private WifiScanWorker mWifiScanWorker;
+    @Rule
+    public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+    @Spy
+    Context mContext = ApplicationProvider.getApplicationContext();
+    @Mock
+    WifiManager mWifiManager;
     @Mock
     WifiPickerTracker mWifiPickerTracker;
     @Mock
     WifiPickerTrackerHelper mWifiPickerTrackerHelper;
 
+    private WifiScanWorker mWifiScanWorker;
+
     @Before
     public void setUp() {
-        MockitoAnnotations.initMocks(this);
+        when(mContext.getSystemService(WifiManager.class)).thenReturn(mWifiManager);
 
-        mWifiScanWorker = new WifiScanWorker(RuntimeEnvironment.application, WIFI_SLICE_URI);
+        mWifiScanWorker = new WifiScanWorker(mContext, WIFI_SLICE_URI);
         mWifiScanWorker.mWifiPickerTracker = mWifiPickerTracker;
         mWifiScanWorker.mWifiPickerTrackerHelper = mWifiPickerTrackerHelper;
         when(mWifiPickerTrackerHelper.isCarrierNetworkProvisionEnabled(SUB_ID)).thenReturn(false);
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppPreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppPreferenceTest.kt
new file mode 100644
index 0000000..342405a
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppPreferenceTest.kt
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.appcompat
+
+import android.content.Context
+import android.content.pm.ActivityInfo
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.os.Build
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsNotDisplayed
+import androidx.compose.ui.test.hasTextExactly
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performClick
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import android.provider.DeviceConfig.NAMESPACE_WINDOW_MANAGER
+import com.android.settings.R
+import com.android.settings.applications.appinfo.AppInfoDashboardFragment
+import com.android.settings.applications.appcompat.UserAspectRatioDetails
+import com.android.settings.applications.appcompat.UserAspectRatioManager
+import com.android.settings.spa.app.appinfo.AppInfoSettingsProvider
+import com.android.settings.testutils.TestDeviceConfig
+import com.android.settingslib.spa.testutils.delay
+import com.android.settingslib.spa.testutils.waitUntilExists
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.mock
+import org.mockito.MockitoSession
+import org.mockito.Spy
+import org.mockito.quality.Strictness
+import org.mockito.Mockito.`when` as whenever
+
+/**
+ * To run this test: atest SettingsSpaUnitTests:UserAspectRatioAppPreferenceTest
+ */
+@RunWith(AndroidJUnit4::class)
+class UserAspectRatioAppPreferenceTest {
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    private lateinit var mockSession: MockitoSession
+
+    @Spy
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    @Spy
+    private val resources = context.resources
+
+    private val aspectRatioEnabledConfig =
+        TestDeviceConfig(NAMESPACE_WINDOW_MANAGER, "enable_app_compat_user_aspect_ratio_settings")
+
+    private lateinit var userAspectRatioManager: UserAspectRatioManager
+
+    @Mock
+    private lateinit var packageManager: PackageManager
+
+    @Before
+    fun setUp() {
+        mockSession = ExtendedMockito.mockitoSession()
+            .initMocks(this)
+            .mockStatic(UserAspectRatioDetails::class.java)
+            .mockStatic(AppInfoDashboardFragment::class.java)
+            .strictness(Strictness.LENIENT)
+            .startMocking()
+        whenever(context.resources).thenReturn(resources)
+        whenever(context.packageManager).thenReturn(packageManager)
+        userAspectRatioManager = mock(UserAspectRatioManager::class.java)
+    }
+
+    @After
+    fun tearDown() {
+        aspectRatioEnabledConfig.reset()
+        mockSession.finishMocking()
+    }
+
+    @Test
+    fun whenConfigIsFalse_notDisplayed() {
+        setConfig(false)
+
+        setContent()
+
+        composeTestRule.onRoot().assertIsNotDisplayed()
+    }
+
+    @Test
+    fun whenCannotDisplayAspectRatioUi_notDisplayed() {
+        setContent()
+
+        composeTestRule.onRoot().assertIsNotDisplayed()
+    }
+
+    @Test
+    fun whenCanDisplayAspectRatioUiAndConfigFalse_notDisplayed() {
+        setConfig(false)
+        whenever(packageManager.queryIntentActivities(any(), anyInt()))
+            .thenReturn(listOf(RESOLVE_INFO))
+
+        setContent()
+
+        composeTestRule.onRoot().assertIsNotDisplayed()
+    }
+
+    @Test
+    fun whenCannotDisplayAspectRatioUiAndConfigTrue_notDisplayed() {
+        setConfig(true)
+
+        setContent()
+
+        composeTestRule.onRoot().assertIsNotDisplayed()
+    }
+
+    @Test
+    fun whenCanDisplayAspectRatioUiAndConfigTrue_Displayed() {
+        setConfig(true)
+        whenever(packageManager.queryIntentActivities(any(), anyInt()))
+            .thenReturn(listOf(RESOLVE_INFO))
+
+        setContent()
+
+        composeTestRule.onNode(
+            hasTextExactly(
+                context.getString(R.string.aspect_ratio_title),
+                context.getString(R.string.user_aspect_ratio_app_default)
+            ),
+        ).assertIsDisplayed().assertIsEnabled()
+    }
+
+    @Test
+    fun onClick_startActivity() {
+        setConfig(true)
+        whenever(packageManager.queryIntentActivities(any(), anyInt()))
+            .thenReturn(listOf(RESOLVE_INFO))
+
+        setContent()
+        composeTestRule.onRoot().performClick()
+
+        ExtendedMockito.verify {
+            AppInfoDashboardFragment.startAppInfoFragment(
+                UserAspectRatioDetails::class.java,
+                APP,
+                context,
+                AppInfoSettingsProvider.METRICS_CATEGORY,
+            )
+        }
+    }
+
+    private fun setConfig(enabled: Boolean) {
+        whenever(resources.getBoolean(
+            com.android.internal.R.bool.config_appCompatUserAppAspectRatioSettingsIsEnabled
+        )).thenReturn(enabled)
+        aspectRatioEnabledConfig.override(enabled)
+    }
+
+    private fun setContent() {
+        composeTestRule.setContent {
+            CompositionLocalProvider(LocalContext provides context) {
+                UserAspectRatioAppPreference(APP)
+            }
+        }
+        composeTestRule.delay()
+    }
+
+    private companion object {
+        const val PACKAGE_NAME = "package.name"
+        const val UID = 123
+        val APP = ApplicationInfo().apply {
+            packageName = PACKAGE_NAME
+            uid = UID
+        }
+        private val RESOLVE_INFO = ResolveInfo().apply {
+            activityInfo = ActivityInfo().apply {
+                packageName = PACKAGE_NAME
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProviderTest.kt
new file mode 100644
index 0000000..0d2869c
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProviderTest.kt
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.appcompat
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN
+import android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET
+import android.os.Build
+import androidx.compose.runtime.State
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.testutils.FakeNavControllerWrapper
+import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
+import com.android.settingslib.spaprivileged.template.app.AppListItemModel
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * To run this test: atest SettingsSpaUnitTests:UserAspectRatioAppsPageProviderTest
+ */
+@RunWith(AndroidJUnit4::class)
+class UserAspectRatioAppsPageProviderTest {
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    private val context: Context = ApplicationProvider.getApplicationContext()
+    private val fakeNavControllerWrapper = FakeNavControllerWrapper()
+
+    @Test
+    fun aspectRatioAppsPageProvider_name() {
+        assertThat(UserAspectRatioAppsPageProvider.name).isEqualTo(EXPECTED_PROVIDER_NAME)
+    }
+
+    @Test
+    fun injectEntry_title() {
+        setInjectEntry()
+        composeTestRule.onNodeWithText(context.getString(R.string.aspect_ratio_title))
+            .assertIsDisplayed()
+    }
+
+    @Test
+    fun injectEntry_summary() {
+        setInjectEntry()
+        composeTestRule.onNodeWithText(context.getString(R.string.aspect_ratio_summary, Build.MODEL))
+            .assertIsDisplayed()
+    }
+
+    @Test
+    fun injectEntry_onClick_navigate() {
+        setInjectEntry()
+        composeTestRule.onNodeWithText(context.getString(R.string.aspect_ratio_title)).performClick()
+        assertThat(fakeNavControllerWrapper.navigateCalledWith).isEqualTo("UserAspectRatioAppsPage")
+    }
+
+    private fun setInjectEntry() {
+        composeTestRule.setContent {
+            fakeNavControllerWrapper.Wrapper {
+                UserAspectRatioAppsPageProvider.buildInjectEntry().build().UiLayout()
+            }
+        }
+    }
+
+    @Test
+    fun title_displayed() {
+        composeTestRule.setContent {
+            UserAspectRatioAppList {}
+        }
+
+        composeTestRule.onNodeWithText(context.getString(R.string.aspect_ratio_title))
+            .assertIsDisplayed()
+    }
+
+    @Test
+    fun item_labelDisplayed() {
+        setItemContent()
+
+        composeTestRule.onNodeWithText(LABEL).assertIsDisplayed()
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun aspectRatioAppListModel_transform() = runTest {
+        val listModel = UserAspectRatioAppListModel(context)
+        val recordListFlow = listModel.transform(flowOf(USER_ID), flowOf(listOf(APP)))
+        val recordList = recordListFlow.firstWithTimeoutOrNull()!!
+
+        assertThat(recordList).hasSize(1)
+        assertThat(recordList[0].app).isSameInstanceAs(APP)
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun aspectRatioAppListModel_filter() = runTest {
+        val listModel = UserAspectRatioAppListModel(context)
+
+        val recordListFlow = listModel.filter(flowOf(USER_ID), 0,
+            flowOf(listOf(APP_RECORD_NOT_DISPLAYED, APP_RECORD_SUGGESTED)))
+
+        val recordList = checkNotNull(recordListFlow.firstWithTimeoutOrNull())
+        assertThat(recordList).containsExactly(APP_RECORD_SUGGESTED)
+    }
+
+    private fun setItemContent() {
+        composeTestRule.setContent {
+            fakeNavControllerWrapper.Wrapper {
+                with(UserAspectRatioAppListModel(context)) {
+                    AppListItemModel(
+                        record = APP_RECORD_SUGGESTED,
+                        label = LABEL,
+                        summary = stateOf(SUMMARY)
+                    ).AppItem()
+                }
+            }
+        }
+    }
+
+    @Test
+    fun aspectRatioAppListModel_getSummaryDefault() {
+        val summaryState = setSummaryState(USER_MIN_ASPECT_RATIO_UNSET)
+        assertThat(summaryState.value)
+            .isEqualTo(context.getString(R.string.user_aspect_ratio_app_default))
+    }
+
+    @Test
+    fun aspectRatioAppListModel_getSummaryWhenSplitScreen() {
+        val summaryState = setSummaryState(USER_MIN_ASPECT_RATIO_SPLIT_SCREEN)
+        assertThat(summaryState.value)
+            .isEqualTo(context.getString(R.string.user_aspect_ratio_half_screen))
+    }
+
+    private fun setSummaryState(override: Int): State<String> {
+        val listModel = UserAspectRatioAppListModel(context)
+        lateinit var summaryState: State<String>
+        composeTestRule.setContent {
+            summaryState = listModel.getSummary(option = 0,
+                record = UserAspectRatioAppListItemModel(
+                    app = APP,
+                    override = override,
+                    suggested = false,
+                    canDisplay = true,
+                ))
+        }
+        return summaryState
+    }
+
+
+    private companion object {
+        private const val EXPECTED_PROVIDER_NAME = "UserAspectRatioAppsPage"
+        private const val PACKAGE_NAME = "package.name"
+        private const val USER_ID = 0
+        private const val LABEL = "Label"
+        private const val SUMMARY = "Summary"
+
+        private val APP = ApplicationInfo().apply {
+            packageName = PACKAGE_NAME
+        }
+        private val APP_RECORD_SUGGESTED = UserAspectRatioAppListItemModel(
+            APP,
+            override = USER_MIN_ASPECT_RATIO_UNSET,
+            suggested = true,
+            canDisplay = true
+        )
+        private val APP_RECORD_NOT_DISPLAYED = UserAspectRatioAppListItemModel(
+            APP,
+            override = USER_MIN_ASPECT_RATIO_UNSET,
+            suggested = true,
+            canDisplay = false
+        )
+    }
+}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/storage/StorageAppListTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/storage/StorageAppListTest.kt
new file mode 100644
index 0000000..836bf09
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/app/storage/StorageAppListTest.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.storage
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.icu.text.CollationKey
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
+import com.android.settingslib.spaprivileged.model.app.AppEntry
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class StorageAppListTest {
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    @Test
+    fun storageAppListPageProvider_apps_name() {
+        assertThat(StorageAppListPageProvider.Apps.name).isEqualTo("StorageAppList")
+    }
+
+    @Test
+    fun storageAppListPageProvider_games_name() {
+        assertThat(StorageAppListPageProvider.Games.name).isEqualTo("GameStorageAppList")
+    }
+
+    @Test
+    fun transform_containsSize() = runTest {
+        val listModel = StorageAppListModel(context, StorageType.Apps)
+        val recordListFlow = listModel.transform(flowOf(0), flowOf(listOf(APP)))
+        val recordList = recordListFlow.firstWithTimeoutOrNull()!!
+        assertThat(recordList).hasSize(1)
+        assertThat(recordList.first().app).isSameInstanceAs(APP)
+        assertThat(recordList.first().size).isEqualTo(0L)
+    }
+
+    @Test
+    fun filter_apps_appWithoutGame() = runTest {
+        val listModel = StorageAppListModel(context, StorageType.Apps)
+        val recordListFlow = listModel.filter(
+            flowOf(0),
+            0,
+            flowOf(
+                listOf(
+                    AppRecordWithSize(APP, 1L),
+                    AppRecordWithSize(APP2, 1L),
+                    AppRecordWithSize(GAME, 1L)
+                )
+            )
+        )
+        val recordList = recordListFlow.firstWithTimeoutOrNull()!!
+        assertThat(recordList).hasSize(2)
+        assertThat(recordList.none { it.app === GAME }).isTrue()
+    }
+
+    @Test
+    fun filter_games_gameWithoutApp() = runTest {
+        val listModel = StorageAppListModel(context, StorageType.Games)
+        val recordListFlow = listModel.filter(
+            flowOf(0),
+            0,
+            flowOf(
+                listOf(
+                    AppRecordWithSize(APP, 1L),
+                    AppRecordWithSize(APP2, 1L),
+                    AppRecordWithSize(GAME, 1L)
+                )
+            )
+        )
+        val recordList = recordListFlow.firstWithTimeoutOrNull()!!
+        assertThat(recordList).hasSize(1)
+        assertThat(recordList.all { it.app === GAME }).isTrue()
+    }
+
+    @Test
+    fun getComparator_sortsByDescendingSize() {
+        val listModel = StorageAppListModel(context, StorageType.Apps)
+        val source = listOf(
+            AppEntry(
+                AppRecordWithSize(app = APP, size = 1L),
+                "app1",
+                CollationKey("first", byteArrayOf(0))
+            ),
+            AppEntry(
+                AppRecordWithSize(app = APP2, size = 3L),
+                "app2",
+                CollationKey("second", byteArrayOf(0))
+            ),
+            AppEntry(
+                AppRecordWithSize(app = APP3, size = 3L),
+                "app3",
+                CollationKey("third", byteArrayOf(0))
+            )
+        )
+
+        val result = source.sortedWith(listModel.getComparator(0))
+
+        assertThat(result[0].record.app).isSameInstanceAs(APP2)
+        assertThat(result[1].record.app).isSameInstanceAs(APP3)
+        assertThat(result[2].record.app).isSameInstanceAs(APP)
+    }
+
+    private companion object {
+        const val APP_PACKAGE_NAME = "app.package.name"
+        const val APP_PACKAGE_NAME2 = "app.package.name2"
+        const val APP_PACKAGE_NAME3 = "app.package.name3"
+        const val GAME_PACKAGE_NAME = "game.package.name"
+        val APP = ApplicationInfo().apply {
+            packageName = APP_PACKAGE_NAME
+            flags = ApplicationInfo.FLAG_INSTALLED
+        }
+        val APP2 = ApplicationInfo().apply {
+            packageName = APP_PACKAGE_NAME2
+            flags = ApplicationInfo.FLAG_INSTALLED
+        }
+        val APP3 = ApplicationInfo().apply {
+            packageName = APP_PACKAGE_NAME3
+            flags = ApplicationInfo.FLAG_INSTALLED
+        }
+        val GAME = ApplicationInfo().apply {
+            packageName = GAME_PACKAGE_NAME
+            flags = ApplicationInfo.FLAG_INSTALLED or ApplicationInfo.FLAG_IS_GAME
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/spa/core/instrumentation/SpaLogDataTest.kt b/tests/spa_unit/src/com/android/settings/spa/core/instrumentation/SpaLogDataTest.kt
index 19be10e..162f8ea 100644
--- a/tests/spa_unit/src/com/android/settings/spa/core/instrumentation/SpaLogDataTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/core/instrumentation/SpaLogDataTest.kt
@@ -53,7 +53,7 @@
         bundle.putString(LOG_DATA_SESSION_NAME, SESSION_BROWSE)
         val spaLogData = SpaLogData(TEST_PID, LogEvent.PAGE_ENTER, bundle, dataModel)
 
-        assertThat(spaLogData.getSessionType()).isEqualTo(SettingsEnums.BROWSE)
+        assertThat(spaLogData.getSessionType()).isEqualTo(SettingsEnums.SESSION_BROWSE)
     }
 
     @Test
@@ -61,7 +61,7 @@
         bundle.putString(LOG_DATA_SESSION_NAME, SESSION_SEARCH)
         val spaLogData = SpaLogData(TEST_PID, LogEvent.PAGE_ENTER, bundle, dataModel)
 
-        assertThat(spaLogData.getSessionType()).isEqualTo(SettingsEnums.SEARCH)
+        assertThat(spaLogData.getSessionType()).isEqualTo(SettingsEnums.SESSION_SEARCH)
     }
 
     @Test
diff --git a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
index 54934a7..0fe634a 100644
--- a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
+++ b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
@@ -77,9 +77,8 @@
     override val batterySettingsFeatureProvider: BatterySettingsFeatureProvider
         get() = TODO("Not yet implemented")
 
-    override fun getDashboardFeatureProvider(context: Context): DashboardFeatureProvider {
-        TODO("Not yet implemented")
-    }
+    override val dashboardFeatureProvider: DashboardFeatureProvider
+        get() = TODO("Not yet implemented")
 
     override val dockUpdaterFeatureProvider: DockUpdaterFeatureProvider
         get() = TODO("Not yet implemented")
diff --git a/tests/unit/src/com/android/settings/accessibility/FontWeightAdjustmentPreferenceControllerTest.java b/tests/unit/src/com/android/settings/accessibility/FontWeightAdjustmentPreferenceControllerTest.java
index e3d2408..9a6bf70 100644
--- a/tests/unit/src/com/android/settings/accessibility/FontWeightAdjustmentPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/accessibility/FontWeightAdjustmentPreferenceControllerTest.java
@@ -27,6 +27,7 @@
 
 import com.android.settings.core.BasePreferenceController;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -51,6 +52,11 @@
                 mContext, "font_weight_adjustment");
     }
 
+    @After
+    public void teardown() {
+        Settings.Secure.resetToDefaults(mContext.getContentResolver(), /* tag= */ null);
+    }
+
     @Test
     public void getAvailabilityStatus_byDefault_shouldReturnAvailable() {
         assertThat(mController.getAvailabilityStatus()).isEqualTo(
diff --git a/tests/unit/src/com/android/settings/applications/appcompat/UserAspectRatioManagerTest.java b/tests/unit/src/com/android/settings/applications/appcompat/UserAspectRatioManagerTest.java
new file mode 100644
index 0000000..f4dcaf8
--- /dev/null
+++ b/tests/unit/src/com/android/settings/applications/appcompat/UserAspectRatioManagerTest.java
@@ -0,0 +1,219 @@
+/*
+ * 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.appcompat;
+
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_16_9;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
+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;
+
+import static com.android.settings.applications.appcompat.UserAspectRatioManager.KEY_ENABLE_USER_ASPECT_RATIO_FULLSCREEN;
+import static com.android.settings.applications.appcompat.UserAspectRatioManager.KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.provider.DeviceConfig;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.R;
+import com.android.settings.testutils.ResourcesUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * To run this test: atest SettingsUnitTests:UserAspectRatioManagerTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class UserAspectRatioManagerTest {
+
+    private Context mContext;
+    private Resources mResources;
+    private UserAspectRatioManager mUtils;
+    private String mOriginalSettingsFlag;
+    private String mOriginalFullscreenFlag;
+
+    @Before
+    public void setUp() {
+        mContext = spy(ApplicationProvider.getApplicationContext());
+        mResources = spy(mContext.getResources());
+        mUtils = spy(new UserAspectRatioManager(mContext));
+
+        when(mContext.getResources()).thenReturn(mResources);
+
+        mOriginalSettingsFlag = DeviceConfig.getProperty(
+                DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS);
+        setAspectRatioSettingsBuildTimeFlagEnabled(true);
+        setAspectRatioSettingsDeviceConfigEnabled("true" /* enabled */, false /* makeDefault */);
+
+        mOriginalFullscreenFlag = DeviceConfig.getProperty(
+                DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_USER_ASPECT_RATIO_FULLSCREEN);
+        setAspectRatioFullscreenBuildTimeFlagEnabled(true);
+        setAspectRatioFullscreenDeviceConfigEnabled("true" /* enabled */, false /* makeDefault */);
+    }
+
+    @After
+    public void tearDown() {
+        setAspectRatioSettingsDeviceConfigEnabled(mOriginalSettingsFlag, true /* makeDefault */);
+        setAspectRatioFullscreenDeviceConfigEnabled(mOriginalFullscreenFlag,
+                true /* makeDefault */);
+    }
+
+    @Test
+    public void testCanDisplayAspectRatioUi() {
+        final ApplicationInfo canDisplay = new ApplicationInfo();
+        canDisplay.packageName = "com.app.candisplay";
+        addResolveInfoLauncherEntry(canDisplay.packageName);
+
+        assertTrue(mUtils.canDisplayAspectRatioUi(canDisplay));
+
+        final ApplicationInfo noLauncherEntry = new ApplicationInfo();
+        noLauncherEntry.packageName = "com.app.nolauncherentry";
+
+        assertFalse(mUtils.canDisplayAspectRatioUi(noLauncherEntry));
+    }
+
+    @Test
+    public void testIsFeatureEnabled() {
+        assertTrue(UserAspectRatioManager.isFeatureEnabled(mContext));
+    }
+
+    @Test
+    public void testIsFeatureEnabled_disabledBuildTimeFlag_returnFalse() {
+        setAspectRatioSettingsBuildTimeFlagEnabled(false);
+        assertFalse(UserAspectRatioManager.isFeatureEnabled(mContext));
+    }
+
+    @Test
+    public void testIsFeatureEnabled_disabledRuntimeFlag_returnFalse() {
+        setAspectRatioSettingsDeviceConfigEnabled("false" /* enabled */, false /* makeDefault */);
+        assertFalse(UserAspectRatioManager.isFeatureEnabled(mContext));
+    }
+
+    @Test
+    public void testIsFullscreenOptionEnabled() {
+        assertTrue(mUtils.isFullscreenOptionEnabled());
+    }
+
+    @Test
+    public void testIsFullscreenOptionEnabled_settingsDisabled_returnFalse() {
+        setAspectRatioFullscreenBuildTimeFlagEnabled(false);
+        assertFalse(mUtils.isFullscreenOptionEnabled());
+    }
+
+    @Test
+    public void testIsFullscreenOptionEnabled_disabledBuildTimeFlag_returnFalse() {
+        setAspectRatioFullscreenBuildTimeFlagEnabled(false);
+        assertFalse(mUtils.isFullscreenOptionEnabled());
+    }
+
+    @Test
+    public void testIsFullscreenOptionEnabled_disabledRuntimeFlag_returnFalse() {
+        setAspectRatioFullscreenDeviceConfigEnabled("false" /* enabled */, false /*makeDefault */);
+        assertFalse(mUtils.isFullscreenOptionEnabled());
+    }
+
+    @Test
+    public void containsAspectRatioOption_fullscreen() {
+        assertTrue(mUtils.containsAspectRatioOption(USER_MIN_ASPECT_RATIO_FULLSCREEN));
+
+        when(mUtils.isFullscreenOptionEnabled()).thenReturn(false);
+        assertFalse(mUtils.containsAspectRatioOption(USER_MIN_ASPECT_RATIO_FULLSCREEN));
+    }
+
+    @Test
+    public void testGetUserMinAspectRatioEntry() {
+        // R.string.user_aspect_ratio_app_default
+        final String appDefault = ResourcesUtils.getResourcesString(mContext,
+                "user_aspect_ratio_app_default");
+        assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET))
+                .isEqualTo(appDefault);
+        // should always return default if value does not correspond to anything
+        assertThat(mUtils.getUserMinAspectRatioEntry(-1))
+                .isEqualTo(appDefault);
+        // R.string.user_aspect_ratio_half_screen
+        assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_SPLIT_SCREEN))
+                .isEqualTo(ResourcesUtils.getResourcesString(mContext,
+                        "user_aspect_ratio_half_screen"));
+        // R.string.user_aspect_ratio_3_2
+        assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_3_2))
+                .isEqualTo(ResourcesUtils.getResourcesString(mContext, "user_aspect_ratio_3_2"));
+        // R,string.user_aspect_ratio_4_3
+        assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_4_3))
+                .isEqualTo(ResourcesUtils.getResourcesString(mContext, "user_aspect_ratio_4_3"));
+        // R.string.user_aspect_ratio_16_9
+        assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_16_9))
+                .isEqualTo(ResourcesUtils.getResourcesString(mContext, "user_aspect_ratio_16_9"));
+        // R.string.user_aspect_ratio_fullscreen
+        assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_FULLSCREEN))
+                .isEqualTo(ResourcesUtils.getResourcesString(mContext,
+                        "user_aspect_ratio_fullscreen"));
+    }
+
+    @Test
+    public void testGetUserMinAspectRatioEntry_fullscreenDisabled_shouldReturnDefault() {
+        setAspectRatioFullscreenBuildTimeFlagEnabled(false);
+        assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_FULLSCREEN))
+                .isEqualTo(ResourcesUtils.getResourcesString(mContext,
+                        "user_aspect_ratio_app_default"));
+    }
+
+    private void setAspectRatioSettingsBuildTimeFlagEnabled(boolean enabled) {
+        when(mResources.getBoolean(R.bool.config_appCompatUserAppAspectRatioSettingsIsEnabled))
+                .thenReturn(enabled);
+    }
+
+    private void setAspectRatioSettingsDeviceConfigEnabled(String enabled, boolean makeDefault) {
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+                KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS, enabled, makeDefault);
+    }
+
+    private void setAspectRatioFullscreenBuildTimeFlagEnabled(boolean enabled) {
+        when(mResources.getBoolean(R.bool.config_appCompatUserAppAspectRatioFullscreenIsEnabled))
+                .thenReturn(enabled);
+    }
+
+    private void setAspectRatioFullscreenDeviceConfigEnabled(String enabled, boolean makeDefault) {
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+                KEY_ENABLE_USER_ASPECT_RATIO_FULLSCREEN, enabled, makeDefault);
+    }
+
+    private void addResolveInfoLauncherEntry(String packageName) {
+        final ResolveInfo resolveInfo = mock(ResolveInfo.class);
+        final ActivityInfo activityInfo = mock(ActivityInfo.class);
+        activityInfo.packageName = packageName;
+        resolveInfo.activityInfo = activityInfo;
+        mUtils.addInfoHasLauncherEntry(resolveInfo);
+    }
+}
diff --git a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollErrorDialogViewModelTest.kt b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollErrorDialogViewModelTest.kt
index ae829b9..3679dd2 100644
--- a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollErrorDialogViewModelTest.kt
+++ b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollErrorDialogViewModelTest.kt
@@ -24,6 +24,9 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.toList
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
@@ -56,79 +59,69 @@
     @OptIn(ExperimentalCoroutinesApi::class)
     @Test
     fun testNewDialog() = runTest {
-        backgroundScope.launch {
-            mutableListOf<Any>().let { list ->
-                viewModel.newDialogFlow.toList(list)
-                assertThat(list.size).isEqualTo(0)
-            }
-
-            mutableListOf<FingerprintErrorDialogSetResultAction>().let { list ->
-                val testErrorMsgId = 3456
-                viewModel.newDialog(testErrorMsgId)
-
-                assertThat(viewModel.isDialogShown).isTrue()
-                viewModel.setResultFlow.toList(list)
-                assertThat(list.size).isEqualTo(1)
-                assertThat(list[0]).isEqualTo(testErrorMsgId)
+        val newDialogs: List<Int> = mutableListOf<Int>().also {
+            backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
+                viewModel.newDialogFlow.toList(it)
             }
         }
+
+        runCurrent()
+
+        // Default values
+        assertThat(viewModel.isDialogShown).isFalse()
+        assertThat(newDialogs.size).isEqualTo(0)
+
+        val testErrorMsgId = 3456
+        viewModel.newDialog(testErrorMsgId)
+        runCurrent()
+
+        // verify after emit
+        assertThat(viewModel.isDialogShown).isTrue()
+        assertThat(newDialogs.size).isEqualTo(1)
+        assertThat(newDialogs[0]).isEqualTo(testErrorMsgId)
     }
 
     @OptIn(ExperimentalCoroutinesApi::class)
     @Test
     fun testTriggerRetry() = runTest {
-        backgroundScope.launch {
-            // triggerRetryFlow shall be empty on begin
-            mutableListOf<Any>().let { list ->
-                viewModel.triggerRetryFlow.toList(list)
-                assertThat(list.size).isEqualTo(0)
-            }
-
-            // emit newDialog
-            mutableListOf<FingerprintErrorDialogSetResultAction>().let { list ->
-                viewModel.newDialog(0)
-                viewModel.triggerRetry()
-
-                assertThat(viewModel.isDialogShown).isFalse()
-                viewModel.setResultFlow.toList(list)
-                assertThat(list.size).isEqualTo(1)
+        val triggerRetries: List<Any> = mutableListOf<Any>().also {
+            backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
+                viewModel.triggerRetryFlow.toList(it)
             }
         }
+
+        runCurrent()
+
+        // Default values
+        assertThat(triggerRetries.size).isEqualTo(0)
+
+        viewModel.triggerRetry()
+        runCurrent()
+
+        // verify after emit
+        assertThat(triggerRetries.size).isEqualTo(1)
     }
 
     @OptIn(ExperimentalCoroutinesApi::class)
     @Test
     fun testSetResultFinish() = runTest {
-        backgroundScope.launch {
-            // setResultFlow shall be empty on begin
-            mutableListOf<FingerprintErrorDialogSetResultAction>().let { list ->
-                viewModel.setResultFlow.toList(list)
-                assertThat(list.size).isEqualTo(0)
+        val setResults: List<FingerprintErrorDialogSetResultAction> =
+            mutableListOf<FingerprintErrorDialogSetResultAction>().also {
+                backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
+                    viewModel.setResultFlow.toList(it)
+                }
             }
 
-            // emit FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH
-            viewModel = FingerprintEnrollErrorDialogViewModel(application, false)
-            mutableListOf<FingerprintErrorDialogSetResultAction>().let { list ->
-                viewModel.newDialog(0)
-                viewModel.setResultAndFinish(FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH)
+        runCurrent()
 
-                assertThat(viewModel.isDialogShown).isFalse()
-                viewModel.setResultFlow.toList(list)
-                assertThat(list.size).isEqualTo(1)
-                assertThat(list[0]).isEqualTo(FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH)
-            }
+        // Default values
+        assertThat(setResults.size).isEqualTo(0)
 
-            // emit FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT
-            viewModel = FingerprintEnrollErrorDialogViewModel(application, false)
-            mutableListOf<FingerprintErrorDialogSetResultAction>().let { list ->
-                viewModel.newDialog(0)
-                viewModel.setResultAndFinish(FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_TIMEOUT)
+        viewModel.setResultAndFinish(FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH)
+        runCurrent()
 
-                assertThat(viewModel.isDialogShown).isFalse()
-                viewModel.setResultFlow.toList(list)
-                assertThat(list.size).isEqualTo(1)
-                assertThat(list[0]).isEqualTo(FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH)
-            }
-        }
+        // verify after emit
+        assertThat(setResults.size).isEqualTo(1)
+        assertThat(setResults[0]).isEqualTo(FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH)
     }
 }
diff --git a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
index 79804e3..0c3bc8c 100644
--- a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -170,8 +170,9 @@
         return powerUsageFeatureProvider;
     }
 
+    @NotNull
     @Override
-    public DashboardFeatureProvider getDashboardFeatureProvider(Context context) {
+    public DashboardFeatureProvider getDashboardFeatureProvider() {
         return dashboardFeatureProvider;
     }