Merge "[Contrast] Migrate contrast settings into Display" into main
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index bf7d4a1..fd9f2e5 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -3343,6 +3343,7 @@
                 <action android:name="com.google.android.setupwizard.SETUP_WIZARD_FINISHED"/>
                 <action android:name="com.android.settings.battery.action.PERIODIC_JOB_RECHECK"/>
                 <action android:name="android.intent.action.TIME_SET"/>
+                <action android:name="android.intent.action.TIMEZONE_CHANGED"/>
             </intent-filter>
         </receiver>
 
@@ -5138,8 +5139,6 @@
                 <action android:name="com.android.settings.action.OPEN_PRIVATE_SPACE_SETTINGS" />
                 <category android:name="android.intent.category.DEFAULT"/>
             </intent-filter>
-            <meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
-                       android:value="@string/menu_key_security"/>
         </activity>
 
         <activity android:name=".privatespace.PrivateProfileContextHelperActivity" android:exported="false"/>
@@ -5190,7 +5189,8 @@
             android:name="com.android.settings.network.SatelliteWarningDialogActivity"
             android:configChanges="orientation|keyboard|keyboardHidden|screenSize|screenLayout|smallestScreenSize"
             android:excludeFromRecents="true"
-            android:exported="false"
+            android:exported="true"
+            android:permission="android.permission.NETWORK_SETTINGS"
             android:theme="@style/Theme.SpaLib.Dialog">
         </activity>
 
diff --git a/protos/fuelgauge_log.proto b/protos/fuelgauge_log.proto
index 4bee75c..b16958d 100644
--- a/protos/fuelgauge_log.proto
+++ b/protos/fuelgauge_log.proto
@@ -45,6 +45,7 @@
     FETCH_USAGE_DATA = 4;
     INSERT_USAGE_DATA = 5;
     TIME_UPDATED = 6;
+    TIMEZONE_UPDATED = 7;
   }
 
   optional int64 timestamp = 1;
diff --git a/res/drawable-sw600dp/ic_settings_about_device_filled.xml b/res/drawable-sw600dp/ic_settings_about_device_filled.xml
new file mode 100644
index 0000000..33ec5fe
--- /dev/null
+++ b/res/drawable-sw600dp/ic_settings_about_device_filled.xml
@@ -0,0 +1,25 @@
+<!--
+    Copyright (C) 2024 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"
+    android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M200,920Q167,920 143.5,896.5Q120,873 120,840L120,120Q120,87 143.5,63.5Q167,40 200,40L760,40Q793,40 816.5,63.5Q840,87 840,120L840,840Q840,873 816.5,896.5Q793,920 760,920L200,920ZM400,800L560,800L560,760L400,760L400,800ZM200,640L760,640L760,240L200,240L200,640Z"/>
+</vector>
diff --git a/res/drawable/homepage_highlighted_item_background_v2.xml b/res/drawable/homepage_highlighted_item_background_v2.xml
new file mode 100644
index 0000000..7aa4895
--- /dev/null
+++ b/res/drawable/homepage_highlighted_item_background_v2.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2024 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.
+  -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        android:color="@*android:color/ripple_material_light">
+    <item
+        android:left="?android:attr/listPreferredItemPaddingStart"
+        android:right="?android:attr/listPreferredItemPaddingEnd">
+        <shape android:shape="rectangle">
+            <solid
+                android:color="@color/settingslib_materialColorPrimaryContainer" />
+            <corners
+                android:radius="?android:attr/dialogCornerRadius" />
+        </shape>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/res/drawable/homepage_selectable_item_background_v2.xml b/res/drawable/homepage_selectable_item_background_v2.xml
new file mode 100644
index 0000000..d2f79ff
--- /dev/null
+++ b/res/drawable/homepage_selectable_item_background_v2.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2024 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.
+  -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        android:color="?android:attr/colorControlHighlight">
+    <item android:id="@android:id/mask"
+        android:left="?android:attr/listPreferredItemPaddingStart"
+        android:right="?android:attr/listPreferredItemPaddingEnd">
+        <shape android:shape="rectangle">
+            <solid
+                android:color="@android:color/white" />
+            <corners
+                android:radius="?android:attr/dialogCornerRadius" />
+        </shape>
+    </item>
+</ripple>
diff --git a/res/drawable/ic_apps_filled.xml b/res/drawable/ic_apps_filled.xml
new file mode 100644
index 0000000..5f86a92
--- /dev/null
+++ b/res/drawable/ic_apps_filled.xml
@@ -0,0 +1,25 @@
+<!--
+    Copyright (C) 2024 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"
+    android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M240,800Q207,800 183.5,776.5Q160,753 160,720Q160,687 183.5,663.5Q207,640 240,640Q273,640 296.5,663.5Q320,687 320,720Q320,753 296.5,776.5Q273,800 240,800ZM480,800Q447,800 423.5,776.5Q400,753 400,720Q400,687 423.5,663.5Q447,640 480,640Q513,640 536.5,663.5Q560,687 560,720Q560,753 536.5,776.5Q513,800 480,800ZM720,800Q687,800 663.5,776.5Q640,753 640,720Q640,687 663.5,663.5Q687,640 720,640Q753,640 776.5,663.5Q800,687 800,720Q800,753 776.5,776.5Q753,800 720,800ZM240,560Q207,560 183.5,536.5Q160,513 160,480Q160,447 183.5,423.5Q207,400 240,400Q273,400 296.5,423.5Q320,447 320,480Q320,513 296.5,536.5Q273,560 240,560ZM480,560Q447,560 423.5,536.5Q400,513 400,480Q400,447 423.5,423.5Q447,400 480,400Q513,400 536.5,423.5Q560,447 560,480Q560,513 536.5,536.5Q513,560 480,560ZM720,560Q687,560 663.5,536.5Q640,513 640,480Q640,447 663.5,423.5Q687,400 720,400Q753,400 776.5,423.5Q800,447 800,480Q800,513 776.5,536.5Q753,560 720,560ZM240,320Q207,320 183.5,296.5Q160,273 160,240Q160,207 183.5,183.5Q207,160 240,160Q273,160 296.5,183.5Q320,207 320,240Q320,273 296.5,296.5Q273,320 240,320ZM480,320Q447,320 423.5,296.5Q400,273 400,240Q400,207 423.5,183.5Q447,160 480,160Q513,160 536.5,183.5Q560,207 560,240Q560,273 536.5,296.5Q513,320 480,320ZM720,320Q687,320 663.5,296.5Q640,273 640,240Q640,207 663.5,183.5Q687,160 720,160Q753,160 776.5,183.5Q800,207 800,240Q800,273 776.5,296.5Q753,320 720,320Z"/>
+</vector>
diff --git a/res/drawable/ic_devices_other_filled.xml b/res/drawable/ic_devices_other_filled.xml
new file mode 100644
index 0000000..a2ded48
--- /dev/null
+++ b/res/drawable/ic_devices_other_filled.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2024 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"
+    android:tint="?android:attr/colorControlNormal"
+    android:autoMirrored="true">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M280,800L160,800Q127,800 103.5,776.5Q80,753 80,720L80,240Q80,207 103.5,183.5Q127,160 160,160L800,160L800,240L160,240Q160,240 160,240Q160,240 160,240L160,720Q160,720 160,720Q160,720 160,720L280,720L280,800ZM440,700Q465,700 482.5,682.5Q500,665 500,640Q500,615 482.5,597.5Q465,580 440,580Q415,580 397.5,597.5Q380,615 380,640Q380,665 397.5,682.5Q415,700 440,700ZM360,800L360,729Q341,712 330.5,689Q320,666 320,640Q320,614 330.5,591Q341,568 360,551L360,480L520,480L520,551Q539,568 549.5,591Q560,614 560,640Q560,666 549.5,689Q539,712 520,729L520,800L360,800ZM840,800L640,800Q623,800 611.5,788.5Q600,777 600,760L600,400Q600,383 611.5,371.5Q623,360 640,360L840,360Q857,360 868.5,371.5Q880,383 880,400L880,760Q880,777 868.5,788.5Q857,800 840,800Z"/>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_help_filled.xml b/res/drawable/ic_help_filled.xml
new file mode 100644
index 0000000..79cbb0b
--- /dev/null
+++ b/res/drawable/ic_help_filled.xml
@@ -0,0 +1,26 @@
+<!--
+    Copyright (C) 2024 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"
+    android:tint="?android:attr/colorControlNormal"
+    android:autoMirrored="true">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M478,720Q499,720 513.5,705.5Q528,691 528,670Q528,649 513.5,634.5Q499,620 478,620Q457,620 442.5,634.5Q428,649 428,670Q428,691 442.5,705.5Q457,720 478,720ZM442,566L516,566Q516,533 523.5,514Q531,495 566,462Q592,436 607,412.5Q622,389 622,356Q622,300 581,270Q540,240 484,240Q427,240 391.5,270Q356,300 342,342L408,368Q413,350 430.5,329Q448,308 484,308Q516,308 532,325.5Q548,343 548,364Q548,384 536,401.5Q524,419 506,434Q462,473 452,493Q442,513 442,566ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880Z"/>
+</vector>
diff --git a/res/drawable/ic_notifications_filled.xml b/res/drawable/ic_notifications_filled.xml
new file mode 100644
index 0000000..3f53913
--- /dev/null
+++ b/res/drawable/ic_notifications_filled.xml
@@ -0,0 +1,26 @@
+<!--
+    Copyright (C) 2024 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"
+    android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M160,760L160,680L240,680L240,400Q240,317 290,252.5Q340,188 420,168L420,140Q420,115 437.5,97.5Q455,80 480,80Q505,80 522.5,97.5Q540,115 540,140L540,168Q620,188 670,252.5Q720,317 720,400L720,680L800,680L800,760L160,760ZM480,880Q447,880 423.5,856.5Q400,833 400,800L560,800Q560,833 536.5,856.5Q513,880 480,880Z"/>
+</vector>
+
diff --git a/res/drawable/ic_settings_about_device_filled.xml b/res/drawable/ic_settings_about_device_filled.xml
new file mode 100644
index 0000000..fb6b2be
--- /dev/null
+++ b/res/drawable/ic_settings_about_device_filled.xml
@@ -0,0 +1,25 @@
+<!--
+    Copyright (C) 2024 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"
+    android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M440,660L440,440L520,440L520,660L440,660ZM480,360Q463,360 451.5,348.5Q440,337 440,320Q440,303 451.5,291.5Q463,280 480,280Q497,280 508.5,291.5Q520,303 520,320Q520,337 508.5,348.5Q497,360 480,360ZM280,920Q247,920 223.5,896.5Q200,873 200,840L200,120Q200,87 223.5,63.5Q247,40 280,40L680,40Q713,40 736.5,63.5Q760,87 760,120L760,840Q760,873 736.5,896.5Q713,920 680,920L280,920ZM280,720L680,720L680,240L280,240L280,720Z"/>
+</vector>
diff --git a/res/drawable/ic_settings_accessibility_filled.xml b/res/drawable/ic_settings_accessibility_filled.xml
new file mode 100644
index 0000000..24a5304
--- /dev/null
+++ b/res/drawable/ic_settings_accessibility_filled.xml
@@ -0,0 +1,25 @@
+<!--
+    Copyright (C) 2024 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"
+    android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M480,160Q447,160 423.5,136.5Q400,113 400,80Q400,47 423.5,23.5Q447,0 480,0Q513,0 536.5,23.5Q560,47 560,80Q560,113 536.5,136.5Q513,160 480,160ZM360,760L360,280Q300,275 238,265Q176,255 120,240L140,160Q218,181 306,190.5Q394,200 480,200Q566,200 654,190.5Q742,181 820,160L840,240Q784,255 722,265Q660,275 600,280L600,760L520,760L520,520L440,520L440,760L360,760ZM320,960Q303,960 291.5,948.5Q280,937 280,920Q280,903 291.5,891.5Q303,880 320,880Q337,880 348.5,891.5Q360,903 360,920Q360,937 348.5,948.5Q337,960 320,960ZM480,960Q463,960 451.5,948.5Q440,937 440,920Q440,903 451.5,891.5Q463,880 480,880Q497,880 508.5,891.5Q520,903 520,920Q520,937 508.5,948.5Q497,960 480,960ZM640,960Q623,960 611.5,948.5Q600,937 600,920Q600,903 611.5,891.5Q623,880 640,880Q657,880 668.5,891.5Q680,903 680,920Q680,937 668.5,948.5Q657,960 640,960Z"/>
+</vector>
diff --git a/res/drawable/ic_settings_battery_filled.xml b/res/drawable/ic_settings_battery_filled.xml
new file mode 100644
index 0000000..122fb0a
--- /dev/null
+++ b/res/drawable/ic_settings_battery_filled.xml
@@ -0,0 +1,25 @@
+<!--
+  Copyright (C) 2024 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"
+    android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M320,880Q303,880 291.5,868.5Q280,857 280,840L280,200Q280,183 291.5,171.5Q303,160 320,160L400,160L400,80L560,80L560,160L640,160Q657,160 668.5,171.5Q680,183 680,200L680,840Q680,857 668.5,868.5Q657,880 640,880L320,880Z"/>
+</vector>
diff --git a/res/drawable/ic_settings_display_filled.xml b/res/drawable/ic_settings_display_filled.xml
new file mode 100644
index 0000000..ef61cbb
--- /dev/null
+++ b/res/drawable/ic_settings_display_filled.xml
@@ -0,0 +1,25 @@
+<!--
+  Copyright (C) 2024 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"
+    android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M480,932L346,800L160,800L160,614L28,480L160,346L160,160L346,160L480,28L614,160L800,160L800,346L932,480L800,614L800,800L614,800L480,932ZM480,820L580,720L720,720L720,580L820,480L720,380L720,240L580,240L480,140L380,240L240,240L240,380L140,480L240,580L240,720L380,720L480,820ZM480,680Q563,680 621.5,621.5Q680,563 680,480Q680,397 621.5,338.5Q563,280 480,280L480,680Z"/>
+</vector>
diff --git a/res/drawable/ic_settings_emergency_filled.xml b/res/drawable/ic_settings_emergency_filled.xml
new file mode 100644
index 0000000..af58127
--- /dev/null
+++ b/res/drawable/ic_settings_emergency_filled.xml
@@ -0,0 +1,25 @@
+<!--
+    Copyright (C) 2024 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"
+    android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M410,840L410,602L204,721L134,600L340,480L134,361L204,240L410,359L410,120L550,120L550,359L756,240L826,361L620,480L826,600L756,721L550,602L550,840L410,840Z"/>
+</vector>
diff --git a/res/drawable/ic_settings_location_filled.xml b/res/drawable/ic_settings_location_filled.xml
new file mode 100644
index 0000000..2649521
--- /dev/null
+++ b/res/drawable/ic_settings_location_filled.xml
@@ -0,0 +1,25 @@
+<!--
+    Copyright (C) 2024 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"
+    android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M480,480Q513,480 536.5,456.5Q560,433 560,400Q560,367 536.5,343.5Q513,320 480,320Q447,320 423.5,343.5Q400,367 400,400Q400,433 423.5,456.5Q447,480 480,480ZM480,880Q319,743 239.5,625.5Q160,508 160,408Q160,258 256.5,169Q353,80 480,80Q607,80 703.5,169Q800,258 800,408Q800,508 720.5,625.5Q641,743 480,880Z"/>
+</vector>
diff --git a/res/drawable/ic_settings_passwords_filled.xml b/res/drawable/ic_settings_passwords_filled.xml
new file mode 100644
index 0000000..eee77af
--- /dev/null
+++ b/res/drawable/ic_settings_passwords_filled.xml
@@ -0,0 +1,25 @@
+<!--
+    Copyright (C) 2024 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"
+    android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M280,600Q330,600 365,565Q400,530 400,480Q400,430 365,395Q330,360 280,360Q230,360 195,395Q160,430 160,480Q160,530 195,565Q230,600 280,600ZM280,720Q180,720 110,650Q40,580 40,480Q40,380 110,310Q180,240 280,240Q361,240 421.5,286Q482,332 506,400L841,400L920,479L780,639L680,560L600,640L520,560L506,560Q481,632 419,676Q357,720 280,720Z"/>
+</vector>
diff --git a/res/drawable/ic_settings_privacy_filled.xml b/res/drawable/ic_settings_privacy_filled.xml
new file mode 100644
index 0000000..9512735
--- /dev/null
+++ b/res/drawable/ic_settings_privacy_filled.xml
@@ -0,0 +1,25 @@
+<!--
+  Copyright (C) 2024 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"
+    android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M480,568Q435,568 403.5,536.5Q372,505 372,460Q372,415 403.5,383.5Q435,352 480,352Q525,352 556.5,383.5Q588,415 588,460Q588,505 556.5,536.5Q525,568 480,568ZM480,760Q334,760 214,678.5Q94,597 40,460Q94,323 214,241.5Q334,160 480,160Q621,160 737.5,236Q854,312 912,440L760,440Q732,440 707,447Q682,454 660,467Q660,465 660,463.5Q660,462 660,460Q660,385 607.5,332.5Q555,280 480,280Q405,280 352.5,332.5Q300,385 300,460Q300,535 352.5,587.5Q405,640 480,640Q502,640 522.5,635Q543,630 561,621Q560,626 560,630.5Q560,635 560,640L560,753Q540,756 520,758Q500,760 480,760ZM680,840Q663,840 651.5,828.5Q640,817 640,800L640,680Q640,663 651.5,651.5Q663,640 680,640L680,640L680,600Q680,567 703.5,543.5Q727,520 760,520Q793,520 816.5,543.5Q840,567 840,600L840,640L840,640Q857,640 868.5,651.5Q880,663 880,680L880,800Q880,817 868.5,828.5Q857,840 840,840L680,840ZM720,640L800,640L800,600Q800,583 788.5,571.5Q777,560 760,560Q743,560 731.5,571.5Q720,583 720,600L720,640Z"/>
+</vector>
diff --git a/res/drawable/ic_settings_safety_center_filled.xml b/res/drawable/ic_settings_safety_center_filled.xml
new file mode 100644
index 0000000..8b6bb6c
--- /dev/null
+++ b/res/drawable/ic_settings_safety_center_filled.xml
@@ -0,0 +1,29 @@
+<!--
+  Copyright (C) 2024 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"
+    android:tint="?android:attr/colorControlNormal">
+  <group>
+    <clip-path
+        android:pathData="M4,2h16v20h-16z"/>
+    <path
+        android:pathData="M18.92,4.4L12.56,2.1C12.38,2.03 12.19,2 12,2C11.81,2 11.62,2.03 11.44,2.1L5.08,4.4C4.43,4.63 4,5.25 4,5.94V10.32C4.02,11.07 4.07,11.79 4.17,12.54C4.64,15.72 6.44,19.33 11.37,21.85C11.57,21.95 11.78,22 12,22C12.22,22 12.43,21.95 12.63,21.85C13.08,21.62 13.5,21.37 13.9,21.12C14.04,21.05 14.18,20.96 14.32,20.86C17.98,18.43 19.41,15.32 19.82,12.54C19.92,11.8 19.98,11.07 19.99,10.32V5.94C19.99,5.25 19.56,4.64 18.91,4.4H18.92ZM12.25,19.78C12.1,19.87 11.9,19.87 11.74,19.78C8.5,17.97 6.62,15.43 6.15,12.27C6.06,11.59 6.01,10.94 6,10.32V6.55C6,6.34 6.13,6.15 6.33,6.08L8.26,5.38C8.11,5.89 8.03,6.44 8.03,7.04C8.04,8.91 9.03,10.68 10.7,11.8C11.15,12.08 12.39,12.89 12.78,13.19C13.27,13.57 13.95,14.21 14.26,14.74C15.29,16.52 14.26,18.46 13.1,19.27C12.83,19.45 12.55,19.62 12.25,19.79V19.78ZM17.85,12.24C17.66,13.49 17.26,14.63 16.65,15.68C16.57,15.04 16.37,14.38 15.99,13.74C15.42,12.75 14.33,11.86 14.01,11.61C13.47,11.19 11.94,10.22 11.79,10.12C10.69,9.39 10.04,8.23 10.03,6.99C10.03,5.01 11.25,4.34 11.98,4.12C12.08,4.09 12.19,4.1 12.29,4.13L17.68,6.08C17.88,6.15 18.01,6.34 18.01,6.55V10.28C18,10.94 17.95,11.59 17.86,12.24H17.85Z"
+        android:fillColor="@android:color/white"/>
+  </group>
+</vector>
diff --git a/res/drawable/ic_settings_security_filled.xml b/res/drawable/ic_settings_security_filled.xml
new file mode 100644
index 0000000..fa2a42b
--- /dev/null
+++ b/res/drawable/ic_settings_security_filled.xml
@@ -0,0 +1,25 @@
+<!--
+  Copyright (C) 2024 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"
+    android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M240,880Q207,880 183.5,856.5Q160,833 160,800L160,400Q160,367 183.5,343.5Q207,320 240,320L280,320L280,240Q280,157 338.5,98.5Q397,40 480,40Q563,40 621.5,98.5Q680,157 680,240L680,320L720,320Q753,320 776.5,343.5Q800,367 800,400L800,800Q800,833 776.5,856.5Q753,880 720,880L240,880ZM480,680Q513,680 536.5,656.5Q560,633 560,600Q560,567 536.5,543.5Q513,520 480,520Q447,520 423.5,543.5Q400,567 400,600Q400,633 423.5,656.5Q447,680 480,680ZM360,320L600,320L600,240Q600,190 565,155Q530,120 480,120Q430,120 395,155Q360,190 360,240L360,320Z"/>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_settings_system_dashboard_filled.xml b/res/drawable/ic_settings_system_dashboard_filled.xml
new file mode 100644
index 0000000..aa2756e
--- /dev/null
+++ b/res/drawable/ic_settings_system_dashboard_filled.xml
@@ -0,0 +1,25 @@
+<!--
+  Copyright (C) 2024 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"
+    android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M370,880L354,752Q341,747 329.5,740Q318,733 307,725L188,775L78,585L181,507Q180,500 180,493.5Q180,487 180,480Q180,473 180,466.5Q180,460 181,453L78,375L188,185L307,235Q318,227 330,220Q342,213 354,208L370,80L590,80L606,208Q619,213 630.5,220Q642,227 653,235L772,185L882,375L779,453Q780,460 780,466.5Q780,473 780,480Q780,487 780,493.5Q780,500 778,507L881,585L771,775L653,725Q642,733 630,740Q618,747 606,752L590,880L370,880ZM482,620Q540,620 581,579Q622,538 622,480Q622,422 581,381Q540,340 482,340Q423,340 382.5,381Q342,422 342,480Q342,538 382.5,579Q423,620 482,620Z"/>
+</vector>
diff --git a/res/drawable/ic_settings_wallpaper_filled.xml b/res/drawable/ic_settings_wallpaper_filled.xml
new file mode 100644
index 0000000..cbcc3b2
--- /dev/null
+++ b/res/drawable/ic_settings_wallpaper_filled.xml
@@ -0,0 +1,25 @@
+<!--
+  Copyright (C) 2024 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"
+android:tint="?android:attr/colorControlNormal">
+<path
+    android:fillColor="@android:color/white"
+    android:pathData="M480,880Q398,880 325,848.5Q252,817 197.5,762.5Q143,708 111.5,635Q80,562 80,480Q80,397 112.5,324Q145,251 200.5,197Q256,143 330,111.5Q404,80 488,80Q568,80 639,107.5Q710,135 763.5,183.5Q817,232 848.5,298.5Q880,365 880,442Q880,557 810,618.5Q740,680 640,680L566,680Q557,680 553.5,685Q550,690 550,696Q550,708 565,730.5Q580,753 580,782Q580,832 552.5,856Q525,880 480,880ZM260,520Q286,520 303,503Q320,486 320,460Q320,434 303,417Q286,400 260,400Q234,400 217,417Q200,434 200,460Q200,486 217,503Q234,520 260,520ZM380,360Q406,360 423,343Q440,326 440,300Q440,274 423,257Q406,240 380,240Q354,240 337,257Q320,274 320,300Q320,326 337,343Q354,360 380,360ZM580,360Q606,360 623,343Q640,326 640,300Q640,274 623,257Q606,240 580,240Q554,240 537,257Q520,274 520,300Q520,326 537,343Q554,360 580,360ZM700,520Q726,520 743,503Q760,486 760,460Q760,434 743,417Q726,400 700,400Q674,400 657,417Q640,434 640,460Q640,486 657,503Q674,520 700,520Z"/>
+</vector>
diff --git a/res/drawable/ic_settings_wireless_filled.xml b/res/drawable/ic_settings_wireless_filled.xml
new file mode 100644
index 0000000..ec85a8b
--- /dev/null
+++ b/res/drawable/ic_settings_wireless_filled.xml
@@ -0,0 +1,25 @@
+<!--
+    Copyright (C) 2024 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"
+    android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M480,840Q438,840 409,811Q380,782 380,740Q380,698 409,669Q438,640 480,640Q522,640 551,669Q580,698 580,740Q580,782 551,811Q522,840 480,840ZM254,614L170,528Q229,469 308.5,434.5Q388,400 480,400Q572,400 651.5,435Q731,470 790,530L706,614Q662,570 604,545Q546,520 480,520Q414,520 356,545Q298,570 254,614ZM84,444L0,360Q92,266 215,213Q338,160 480,160Q622,160 745,213Q868,266 960,360L876,444Q799,367 697.5,323.5Q596,280 480,280Q364,280 262.5,323.5Q161,367 84,444Z"/>
+</vector>
diff --git a/res/drawable/ic_storage_filled.xml b/res/drawable/ic_storage_filled.xml
new file mode 100644
index 0000000..2fa3c74
--- /dev/null
+++ b/res/drawable/ic_storage_filled.xml
@@ -0,0 +1,25 @@
+<!--
+  Copyright (C) 2024 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"
+    android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M120,800L120,640L840,640L840,800L120,800ZM200,760L280,760L280,680L200,680L200,760ZM120,320L120,160L840,160L840,320L120,320ZM200,280L280,280L280,200L200,200L200,280ZM120,560L120,400L840,400L840,560L120,560ZM200,520L280,520L280,440L200,440L200,520Z"/>
+</vector>
diff --git a/res/drawable/ic_volume_up_filled.xml b/res/drawable/ic_volume_up_filled.xml
new file mode 100644
index 0000000..da3a867
--- /dev/null
+++ b/res/drawable/ic_volume_up_filled.xml
@@ -0,0 +1,26 @@
+<!--
+  Copyright (C) 2024 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"
+    android:tint="?android:attr/colorControlNormal"
+    android:autoMirrored="true">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M560,829L560,747Q650,721 705,647Q760,573 760,479Q760,385 705,311Q650,237 560,211L560,129Q684,157 762,254.5Q840,352 840,479Q840,606 762,703.5Q684,801 560,829ZM120,600L120,360L280,360L480,160L480,800L280,600L120,600ZM560,640L560,318Q607,340 633.5,384Q660,428 660,480Q660,531 633.5,574.5Q607,618 560,640Z"/>
+</vector>
diff --git a/res/layout/fingerprint_rename_dialog.xml b/res/layout/fingerprint_rename_dialog.xml
index 1e1ef11..070d924 100644
--- a/res/layout/fingerprint_rename_dialog.xml
+++ b/res/layout/fingerprint_rename_dialog.xml
@@ -39,6 +39,7 @@
         android:id="@+id/fingerprint_rename_field"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:inputType="textCapWords"/>
+        android:inputType="textCapWords"
+        android:minHeight = "48dp"/>
 
 </LinearLayout>
diff --git a/res/layout/homepage_preference_v2.xml b/res/layout/homepage_preference_v2.xml
new file mode 100644
index 0000000..4d441d3
--- /dev/null
+++ b/res/layout/homepage_preference_v2.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:minHeight="72dp"
+    android:gravity="center_vertical"
+    android:background="?android:attr/selectableItemBackground"
+    android:clipToPadding="false"
+    android:baselineAligned="false">
+
+    <LinearLayout
+        android:id="@+id/icon_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:minWidth="40dp"
+        android:gravity="end|center_vertical"
+        android:orientation="horizontal"
+        android:paddingStart="24dp"
+        android:paddingEnd="8dp"
+        android:paddingTop="8dp"
+        android:paddingBottom="8dp">
+
+        <androidx.preference.internal.PreferenceImageView
+            android:id="@android:id/icon"
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            app:maxWidth="40dp"
+            app:maxHeight="40dp"/>
+
+    </LinearLayout>
+
+    <RelativeLayout
+        android:id="@+id/text_frame"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp"
+        android:paddingStart="@dimen/homepage_preference_text_padding_start"
+        android:paddingEnd="16dp">
+
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="start"
+            android:textAlignment="viewStart"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceListItem"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            android:ellipsize="marquee"/>
+
+        <TextView
+            android:id="@android:id/summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@android:id/title"
+            android:layout_alignStart="@android:id/title"
+            android:layout_gravity="start"
+            android:textAlignment="viewStart"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
+            android:maxLines="4"
+            android:hyphenationFrequency="normalFast"
+            android:lineBreakWordStyle="phrase"
+            style="@style/PreferenceSummaryTextStyle"/>
+    </RelativeLayout>
+</LinearLayout>
diff --git a/res/layout/search_bar_unified_version.xml b/res/layout/search_bar_unified_version.xml
new file mode 100644
index 0000000..eec8406
--- /dev/null
+++ b/res/layout/search_bar_unified_version.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<com.google.android.material.card.MaterialCardView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    style="@style/SearchBarStyle"
+    android:layout_width="0dp"
+    android:layout_height="wrap_content"
+    android:layout_weight="1">
+    <Toolbar
+        android:id="@+id/search_action_bar_unified"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/search_bar_height"
+        android:paddingStart="8dp"
+        android:paddingEnd="24dp"
+        android:background="@drawable/search_bar_selected_background"
+        android:touchscreenBlocksFocus="false"
+        android:nextFocusForward="@+id/homepage_container"
+        android:contentInsetStartWithNavigation="@dimen/search_bar_content_inset"
+        android:navigationIcon="@drawable/ic_homepage_search">
+        <TextView
+            android:id="@+id/search_bar_title"
+            style="@style/TextAppearance.SearchBar"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingStart="8dp"
+            android:layout_gravity="start"
+            android:text="@string/search_settings"/>
+    </Toolbar>
+</com.google.android.material.card.MaterialCardView>
diff --git a/res/layout/settings_homepage_app_bar_unified_layout.xml b/res/layout/settings_homepage_app_bar_unified_layout.xml
new file mode 100644
index 0000000..3e25418
--- /dev/null
+++ b/res/layout/settings_homepage_app_bar_unified_layout.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/app_bar_content"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_marginHorizontal="16dp"
+    android:padding="6dp"
+    android:orientation="horizontal">
+
+    <include layout="@layout/search_bar_unified_version"/>
+
+</LinearLayout>
diff --git a/res/layout/settings_homepage_container_v2.xml b/res/layout/settings_homepage_container_v2.xml
new file mode 100644
index 0000000..73b8f21
--- /dev/null
+++ b/res/layout/settings_homepage_container_v2.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<androidx.coordinatorlayout.widget.CoordinatorLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/settings_homepage_container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <androidx.core.widget.NestedScrollView
+        android:id="@+id/main_content_scrollable_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        app:layout_behavior="com.android.settings.widget.HomepageAppBarScrollingViewBehavior">
+
+        <LinearLayout
+            android:id="@+id/homepage_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <FrameLayout
+                android:id="@+id/unified_suggestion_content"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="10dp"
+                android:layout_marginStart="?android:attr/listPreferredItemPaddingStart"
+                android:layout_marginEnd="?android:attr/listPreferredItemPaddingEnd" />
+
+            <FrameLayout
+                android:id="@+id/contextual_cards_content"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="@dimen/contextual_card_side_margin"
+                android:layout_marginEnd="@dimen/contextual_card_side_margin"/>
+
+            <FrameLayout
+                android:id="@+id/main_content"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:animateLayoutChanges="true"/>
+
+        </LinearLayout>
+    </androidx.core.widget.NestedScrollView>
+
+    <com.google.android.material.appbar.AppBarLayout
+        android:id="@+id/app_bar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:touchscreenBlocksFocus="false"
+        android:keyboardNavigationCluster="false">
+        <LinearLayout
+            android:id="@+id/app_bar_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:minHeight="76dp"
+            app:layout_scrollFlags="scroll|exitUntilCollapsed">
+
+            <include
+                android:id="@+id/homepage_app_bar_unified_view"
+                layout="@layout/settings_homepage_app_bar_unified_layout"/>
+        </LinearLayout>
+    </com.google.android.material.appbar.AppBarLayout>
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/res/values/config.xml b/res/values/config.xml
index 6d9d784..4d3a233 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -257,7 +257,7 @@
     <bool name="config_show_device_header_in_device_info">true</bool>
 
     <!-- Whether or not TopLevelSettings should force rounded icon for injected tiles -->
-    <bool name="config_force_rounded_icon_TopLevelSettings">true</bool>
+    <bool name="config_force_rounded_icon_TopLevelSettings">false</bool>
 
     <!-- Whether dismissal timestamp should be kept before deletion -->
     <bool name="config_keep_contextual_card_dismissal_timestamp">false</bool>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a1428a6..855866d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -672,8 +672,10 @@
     <string name="location_settings_footer_learn_more_content_description">
         Learn more about Location settings
     </string>
-    <!-- Tooltip for switchbar on Chrome devices. [CHAR LIMIT=90]-->
-    <string name="location_settings_tooltip_text_for_chrome">To change location access, go to Settings > Security and Privacy > Privacy controls</string>
+    <!-- Tooltip for switchbar on ChromeOS devices. [CHAR LIMIT=NONE]-->
+    <string name="location_settings_tooltip_text_for_chrome">
+        To change go to ChromeOS Settings > Privacy and security > Privacy controls > Location access
+    </string>
 
     <!-- Main Settings screen setting option title for the item to take you to the accounts screen [CHAR LIMIT=22] -->
     <string name="account_settings_title">Accounts</string>
@@ -12577,11 +12579,17 @@
     <!-- Title for Thread network preference [CHAR_LIMIT=60] -->
     <string name="thread_network_settings_title">Thread</string>
 
-    <!-- Summary for Thread network preference. [CHAR_LIMIT=NONE]-->
-    <string name="thread_network_settings_summary">Connect to compatible devices using Thread for a seamless smart home experience</string>
+    <!-- Title for Thread network settings main switch [CHAR_LIMIT=60] -->
+    <string name="thread_network_settings_main_switch_title">Use Thread</string>
 
-    <!-- Summary for Thread network preference when airplane mode is enabled. [CHAR_LIMIT=NONE]-->
-    <string name="thread_network_settings_summary_airplane_mode">Turn off airplane mode to use Thread</string>
+    <!-- Title for Thread network settings footer [CHAR_LIMIT=NONE] -->
+    <string name="thread_network_settings_footer_title">Thread helps connect your smart home devices, boosting efficiency, and performance.\n\nWhen enabled, this device is eligible to join a Thread network, allowing control of Matter supported devices through this phone.</string>
+
+    <!-- Text for Thread network settings learn more link [CHAR_LIMIT=NONE] -->
+    <string name="thread_network_settings_learn_more">Learn more about Thread</string>
+
+    <!-- URL for Thread network settings learn more link [CHAR_LIMIT=NONE] -->
+    <string name="thread_network_settings_learn_more_link" translatable="false">https://developers.home.google.com</string>
 
     <!-- Label for the camera use toggle [CHAR LIMIT=40] -->
     <string name="camera_toggle_title">Camera access</string>
diff --git a/res/xml/connected_devices_advanced.xml b/res/xml/connected_devices_advanced.xml
index 87db619..68b4c04 100644
--- a/res/xml/connected_devices_advanced.xml
+++ b/res/xml/connected_devices_advanced.xml
@@ -47,6 +47,17 @@
         settings:keywords="@string/keywords_wifi_display_settings" />
 
     <com.android.settingslib.RestrictedPreference
+        android:fragment="com.android.settings.connecteddevice.threadnetwork.ThreadNetworkFragment"
+        android:key="thread_network_settings"
+        android:title="@string/thread_network_settings_title"
+        android:icon="@*android:drawable/ic_thread_network"
+        android:order="-5"
+        settings:searchable="false"
+        settings:controller="com.android.settings.connecteddevice.threadnetwork.ThreadNetworkFragmentController"
+        settings:userRestriction="no_thread_network"
+        settings:useAdminDisabledSummary="true"/>
+
+    <com.android.settingslib.RestrictedPreference
         android:fragment="com.android.settings.print.PrintSettingsFragment"
         android:icon="@*android:drawable/ic_settings_print"
         android:key="connected_device_printing"
@@ -63,15 +74,6 @@
         settings:useAdminDisabledSummary="true"
         settings:userRestriction="no_ultra_wideband_radio" />
 
-    <com.android.settingslib.RestrictedSwitchPreference
-        android:key="thread_network_settings"
-        android:title="@string/thread_network_settings_title"
-        android:order="110"
-        android:summary="@string/summary_placeholder"
-        settings:controller="com.android.settings.connecteddevice.threadnetwork.ThreadNetworkPreferenceController"
-        settings:userRestriction="no_thread_network"
-        settings:useAdminDisabledSummary="true"/>
-
     <PreferenceCategory
         android:key="dashboard_tile_placeholder"
         android:order="-8" />
diff --git a/res/xml/thread_network_settings.xml b/res/xml/thread_network_settings.xml
new file mode 100644
index 0000000..549d650
--- /dev/null
+++ b/res/xml/thread_network_settings.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2024 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/thread_network_settings_title">
+
+    <com.android.settingslib.widget.MainSwitchPreference
+        android:key="toggle_thread_network"
+        android:title="@string/thread_network_settings_main_switch_title"
+        settings:controller="com.android.settings.connecteddevice.threadnetwork.ThreadNetworkToggleController"/>
+
+    <com.android.settingslib.widget.FooterPreference
+        android:key="thread_network_settings_footer"
+        android:title="@string/thread_network_settings_footer_title"
+        android:selectable="false"
+        settings:searchable="false"
+        settings:controller="com.android.settings.connecteddevice.threadnetwork.ThreadNetworkFooterController"/>
+</PreferenceScreen>
diff --git a/res/xml/top_level_settings_v2.xml b/res/xml/top_level_settings_v2.xml
new file mode 100644
index 0000000..9cd8dbe
--- /dev/null
+++ b/res/xml/top_level_settings_v2.xml
@@ -0,0 +1,241 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:key="top_level_settings">
+
+    <PreferenceCategory
+        android:order="-140"
+        android:key="top_level_account_category">
+    </PreferenceCategory>
+
+    <PreferenceCategory
+        android:order="-130"
+        android:key="top_level_connectivity_category">
+        <com.android.settings.widget.HomepagePreference
+            android:fragment="com.android.settings.network.NetworkDashboardFragment"
+            android:icon="@drawable/ic_settings_wireless_filled"
+            android:key="top_level_network"
+            android:order="-20"
+            android:title="@string/network_dashboard_title"
+            android:summary="@string/summary_placeholder"
+            settings:highlightableMenuKey="@string/menu_key_network"
+            settings:controller="com.android.settings.network.TopLevelNetworkEntryPreferenceController"/>
+
+        <com.android.settings.widget.HomepagePreference
+            android:fragment="com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment"
+            android:icon="@drawable/ic_devices_other_filled"
+            android:key="top_level_connected_devices"
+            android:order="-10"
+            android:title="@string/connected_devices_dashboard_title"
+            android:summary="@string/connected_devices_dashboard_default_summary"
+            settings:highlightableMenuKey="@string/menu_key_connected_devices"
+            settings:controller="com.android.settings.connecteddevice.TopLevelConnectedDevicesPreferenceController"/>
+    </PreferenceCategory>
+
+    <PreferenceCategory
+        android:order="-120"
+        android:key="top_level_personalize_category">
+        <com.android.settings.widget.HomepagePreference
+            android:fragment="com.android.settings.applications.AppDashboardFragment"
+            android:icon="@drawable/ic_apps_filled"
+            android:key="top_level_apps"
+            android:order="-60"
+            android:title="@string/apps_dashboard_title"
+            android:summary="@string/app_and_notification_dashboard_summary"
+            settings:highlightableMenuKey="@string/menu_key_apps"/>
+
+        <com.android.settings.widget.HomepagePreference
+            android:fragment="com.android.settings.notification.ConfigureNotificationSettings"
+            android:icon="@drawable/ic_notifications_filled"
+            android:key="top_level_notifications"
+            android:order="-50"
+            android:title="@string/configure_notification_settings"
+            android:summary="@string/notification_dashboard_summary"
+            settings:highlightableMenuKey="@string/menu_key_notifications"/>
+
+        <com.android.settings.widget.HomepagePreference
+            android:fragment="com.android.settings.notification.SoundSettings"
+            android:icon="@drawable/ic_volume_up_filled"
+            android:key="top_level_sound"
+            android:order="-40"
+            android:title="@string/sound_settings"
+            android:summary="@string/sound_dashboard_summary"
+            settings:highlightableMenuKey="@string/menu_key_sound"/>
+
+        <com.android.settings.widget.HomepagePreference
+            android:fragment="com.android.settings.communal.CommunalDashboardFragment"
+            android:icon="@drawable/ia_settings_communal"
+            android:key="top_level_communal"
+            android:order="-30"
+            android:title="@string/communal_settings_title"
+            android:summary="@string/communal_settings_summary"
+            settings:highlightableMenuKey="@string/menu_key_communal"
+            settings:controller="com.android.settings.communal.CommunalPreferenceController"/>
+
+        <com.android.settings.widget.HomepagePreference
+            android:fragment="com.android.settings.DisplaySettings"
+            android:icon="@drawable/ic_settings_display_filled"
+            android:key="top_level_display"
+            android:order="-20"
+            android:title="@string/display_settings"
+            android:summary="@string/display_dashboard_summary"
+            settings:highlightableMenuKey="@string/menu_key_display"
+            settings:controller="com.android.settings.display.TopLevelDisplayPreferenceController"/>
+
+        <com.android.settings.widget.RestrictedHomepagePreference
+            android:icon="@drawable/ic_settings_wallpaper_filled"
+            android:key="top_level_wallpaper"
+            android:order="-10"
+            android:title="@string/wallpaper_settings_title"
+            android:summary="@string/wallpaper_dashboard_summary"
+            settings:highlightableMenuKey="@string/menu_key_wallpaper"
+            settings:controller="com.android.settings.display.TopLevelWallpaperPreferenceController"/>
+    </PreferenceCategory>
+
+    <PreferenceCategory
+        android:order="-110"
+        android:key="top_level_system_info_category">
+        <com.android.settings.widget.HomepagePreference
+            android:fragment="com.android.settings.deviceinfo.StorageDashboardFragment"
+            android:icon="@drawable/ic_storage_filled"
+            android:key="top_level_storage"
+            android:order="-50"
+            android:title="@string/storage_settings"
+            android:summary="@string/summary_placeholder"
+            settings:highlightableMenuKey="@string/menu_key_storage"
+            settings:controller="com.android.settings.deviceinfo.TopLevelStoragePreferenceController"/>
+
+        <com.android.settings.widget.HomepagePreference
+            android:fragment="com.android.settings.fuelgauge.batteryusage.PowerUsageSummary"
+            android:icon="@drawable/ic_settings_battery_filled"
+            android:key="top_level_battery"
+            android:order="-30"
+            android:title="@string/power_usage_summary_title"
+            android:summary="@string/summary_placeholder"
+            settings:highlightableMenuKey="@string/menu_key_battery"
+            settings:controller="com.android.settings.fuelgauge.TopLevelBatteryPreferenceController"/>
+
+        <com.android.settings.widget.HomepagePreference
+            android:fragment="com.android.settings.system.SystemDashboardFragment"
+            android:icon="@drawable/ic_settings_system_dashboard_filled"
+            android:key="top_level_system"
+            android:order="-20"
+            android:title="@string/header_category_system"
+            android:summary="@string/system_dashboard_summary"
+            settings:highlightableMenuKey="@string/menu_key_system"/>
+
+        <com.android.settings.widget.HomepagePreference
+            android:fragment="com.android.settings.deviceinfo.aboutphone.MyDeviceInfoFragment"
+            android:icon="@drawable/ic_settings_about_device_filled"
+            android:key="top_level_about_device"
+            android:order="-10"
+            android:title="@string/about_settings"
+            android:summary="@string/summary_placeholder"
+            settings:highlightableMenuKey="@string/menu_key_about_device"
+            settings:controller="com.android.settings.deviceinfo.aboutphone.TopLevelAboutDevicePreferenceController"/>
+    </PreferenceCategory>
+
+    <PreferenceCategory
+        android:order="-100"
+        android:key="top_level_security_privacy_category">
+        <com.android.settings.widget.HomepagePreference
+            android:icon="@drawable/ic_settings_safety_center_filled"
+            android:key="top_level_safety_center"
+            android:order="-50"
+            android:title="@string/safety_center_title"
+            android:summary="@string/safety_center_summary"
+            settings:highlightableMenuKey="@string/menu_key_safety_center"
+            settings:controller="com.android.settings.safetycenter.TopLevelSafetyCenterEntryPreferenceController"/>
+
+        <com.android.settings.widget.HomepagePreference
+            android:fragment="com.android.settings.security.SecuritySettings"
+            android:icon="@drawable/ic_settings_security_filled"
+            android:key="top_level_security"
+            android:order="-40"
+            android:title="@string/security_settings_title"
+            android:summary="@string/security_dashboard_summary"
+            settings:highlightableMenuKey="@string/menu_key_security"
+            settings:controller="com.android.settings.security.TopLevelSecurityEntryPreferenceController"/>
+
+        <com.android.settings.widget.HomepagePreference
+            android:fragment="com.android.settings.privacy.PrivacyDashboardFragment"
+            android:icon="@drawable/ic_settings_privacy_filled"
+            android:key="top_level_privacy"
+            android:order="-30"
+            android:title="@string/privacy_dashboard_title"
+            android:summary="@string/privacy_dashboard_summary"
+            settings:highlightableMenuKey="@string/menu_key_privacy"
+            settings:controller="com.android.settings.privacy.TopLevelPrivacyEntryPreferenceController"/>
+
+        <com.android.settings.widget.HomepagePreference
+            android:fragment="com.android.settings.location.LocationSettings"
+            android:icon="@drawable/ic_settings_location_filled"
+            android:key="top_level_location"
+            android:order="-20"
+            android:title="@string/location_settings_title"
+            android:summary="@string/location_settings_loading_app_permission_stats"
+            settings:highlightableMenuKey="@string/menu_key_location"
+            settings:controller="com.android.settings.location.TopLevelLocationPreferenceController"/>
+
+        <com.android.settings.widget.HomepagePreference
+            android:fragment="com.android.settings.accounts.AccountDashboardFragment"
+            android:icon="@drawable/ic_settings_passwords_filled"
+            android:key="top_level_accounts"
+            android:order="-10"
+            android:title="@string/account_dashboard_title_with_passkeys"
+            android:summary="@string/summary_placeholder"
+            settings:highlightableMenuKey="@string/menu_key_accounts"
+            settings:controller="com.android.settings.accounts.TopLevelAccountEntryPreferenceController"/>
+    </PreferenceCategory>
+
+    <PreferenceCategory
+        android:order="100"
+        android:key="top_level_support_category">
+        <com.android.settings.widget.HomepagePreference
+            android:key="top_level_emergency"
+            android:title="@string/emergency_settings_preference_title"
+            android:summary="@string/emergency_dashboard_summary"
+            android:icon="@drawable/ic_settings_emergency_filled"
+            android:order="-30"
+            android:fragment="com.android.settings.emergency.EmergencyDashboardFragment"
+            settings:isPreferenceVisible="@bool/config_show_emergency_settings"
+            settings:highlightableMenuKey="@string/menu_key_emergency"/>
+
+        <com.android.settings.widget.HomepagePreference
+            android:fragment="com.android.settings.accessibility.AccessibilitySettings"
+            android:icon="@drawable/ic_settings_accessibility_filled"
+            android:key="top_level_accessibility"
+            android:order="-20"
+            android:title="@string/accessibility_settings"
+            android:summary="@string/accessibility_settings_summary"
+            settings:highlightableMenuKey="@string/menu_key_accessibility"
+            settings:controller="com.android.settings.accessibility.TopLevelAccessibilityPreferenceController"/>
+
+        <com.android.settings.widget.HomepagePreference
+            android:icon="@drawable/ic_help_filled"
+            android:key="top_level_support"
+            android:order="-10"
+            android:title="@string/page_tab_title_support"
+            android:summary="@string/support_summary"
+            settings:highlightableMenuKey="@string/menu_key_support"
+            settings:controller="com.android.settings.support.SupportPreferenceController"/>
+    </PreferenceCategory>
+
+</PreferenceScreen>
diff --git a/src/com/android/settings/bluetooth/BluetoothEnabler.java b/src/com/android/settings/bluetooth/BluetoothEnabler.java
index eaa0b96..df5cc72 100644
--- a/src/com/android/settings/bluetooth/BluetoothEnabler.java
+++ b/src/com/android/settings/bluetooth/BluetoothEnabler.java
@@ -16,6 +16,9 @@
 
 package com.android.settings.bluetooth;
 
+import static com.android.settings.network.SatelliteWarningDialogActivity.EXTRA_TYPE_OF_SATELLITE_WARNING_DIALOG;
+import static com.android.settings.network.SatelliteWarningDialogActivity.TYPE_IS_BLUETOOTH;
+
 import android.bluetooth.BluetoothAdapter;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -23,22 +26,32 @@
 import android.content.IntentFilter;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.util.Log;
 import android.widget.Toast;
 
 import androidx.annotation.VisibleForTesting;
 
 import com.android.settings.R;
+import com.android.settings.network.SatelliteRepository;
+import com.android.settings.network.SatelliteWarningDialogActivity;
 import com.android.settings.widget.SwitchWidgetController;
 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 import com.android.settingslib.WirelessUtils;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
 /**
  * BluetoothEnabler is a helper to manage the Bluetooth on/off checkbox
  * preference. It turns on/off Bluetooth and ensures the summary of the
  * preference reflects the current state.
  */
 public final class BluetoothEnabler implements SwitchWidgetController.OnSwitchChangeListener {
+    private static final String TAG = BluetoothEnabler.class.getSimpleName();
     private final SwitchWidgetController mSwitchController;
     private final MetricsFeatureProvider mMetricsFeatureProvider;
     private Context mContext;
@@ -51,6 +64,9 @@
     private static final String EVENT_DATA_IS_BT_ON = "is_bluetooth_on";
     private static final int EVENT_UPDATE_INDEX = 0;
     private final int mMetricsEvent;
+    private SatelliteRepository mSatelliteRepository;
+    @VisibleForTesting
+    AtomicBoolean mIsSatelliteOn = new AtomicBoolean(false);
 
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
@@ -81,6 +97,7 @@
         }
         mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
         mRestrictionUtils = restrictionUtils;
+        mSatelliteRepository = new SatelliteRepository(context);
     }
 
     public void setupSwitchController() {
@@ -112,6 +129,15 @@
         mContext.registerReceiver(mReceiver, mIntentFilter,
                 Context.RECEIVER_EXPORTED_UNAUDITED);
         mValidListener = true;
+
+        new Thread(() -> {
+            try {
+                mIsSatelliteOn.set(mSatelliteRepository.requestIsEnabled(
+                        Executors.newSingleThreadExecutor()).get(3000, TimeUnit.MILLISECONDS));
+            } catch (InterruptedException | ExecutionException | TimeoutException e) {
+                Log.e(TAG, "Error to get satellite status : " + e);
+            }
+        }).start();
     }
 
     public void pause() {
@@ -168,6 +194,17 @@
             return true;
         }
 
+        if (mIsSatelliteOn.get()) {
+            mContext.startActivity(
+                    new Intent(mContext, SatelliteWarningDialogActivity.class)
+                            .putExtra(
+                                    EXTRA_TYPE_OF_SATELLITE_WARNING_DIALOG,
+                                    TYPE_IS_BLUETOOTH)
+            );
+            mSwitchController.setChecked(!isChecked);
+            return false;
+        }
+
         // Show toast message if Bluetooth is not allowed in airplane mode
         if (isChecked &&
                 !WirelessUtils.isRadioAllowed(mContext, Settings.Global.RADIO_BLUETOOTH)) {
diff --git a/src/com/android/settings/bluetooth/BluetoothPairingDetail.java b/src/com/android/settings/bluetooth/BluetoothPairingDetail.java
index 234d6d2..ca53854 100644
--- a/src/com/android/settings/bluetooth/BluetoothPairingDetail.java
+++ b/src/com/android/settings/bluetooth/BluetoothPairingDetail.java
@@ -16,10 +16,15 @@
 
 package com.android.settings.bluetooth;
 
+import static com.android.settings.network.SatelliteWarningDialogActivity.EXTRA_TYPE_OF_SATELLITE_WARNING_DIALOG;
+import static com.android.settings.network.SatelliteWarningDialogActivity.TYPE_IS_BLUETOOTH;
+
 import android.app.settings.SettingsEnums;
 import android.bluetooth.BluetoothAdapter;
 import android.content.Context;
+import android.content.Intent;
 import android.os.Bundle;
+import android.util.Log;
 import android.view.View;
 
 import androidx.annotation.NonNull;
@@ -27,10 +32,17 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.settings.R;
+import com.android.settings.network.SatelliteRepository;
+import com.android.settings.network.SatelliteWarningDialogActivity;
 import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
 import com.android.settingslib.search.Indexable;
 import com.android.settingslib.widget.FooterPreference;
 
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
 /**
  * BluetoothPairingDetail is a page to scan bluetooth devices and pair them.
  */
@@ -55,9 +67,35 @@
     @Override
     public void onAttach(Context context) {
         super.onAttach(context);
+        if (mayStartSatelliteWarningDialog()) {
+            finish();
+            return;
+        }
         use(BluetoothDeviceRenamePreferenceController.class).setFragment(this);
     }
 
+    private boolean mayStartSatelliteWarningDialog() {
+        SatelliteRepository satelliteRepository = new SatelliteRepository(this.getContext());
+        boolean isSatelliteOn = true;
+        try {
+            isSatelliteOn =
+                    satelliteRepository.requestIsEnabled(
+                            Executors.newSingleThreadExecutor()).get(3000, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException | ExecutionException | TimeoutException e) {
+            Log.e(TAG, "Error to get satellite status : " + e);
+        }
+        if (!isSatelliteOn) {
+            return false;
+        }
+        startActivity(
+                new Intent(getContext(), SatelliteWarningDialogActivity.class)
+                        .putExtra(
+                                EXTRA_TYPE_OF_SATELLITE_WARNING_DIALOG,
+                                TYPE_IS_BLUETOOTH)
+        );
+        return true;
+    }
+
     @Override
     public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
diff --git a/src/com/android/settings/connecteddevice/threadnetwork/BaseThreadNetworkController.kt b/src/com/android/settings/connecteddevice/threadnetwork/BaseThreadNetworkController.kt
new file mode 100644
index 0000000..583706a
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/threadnetwork/BaseThreadNetworkController.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 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.connecteddevice.threadnetwork
+
+import android.net.thread.ThreadNetworkController
+import android.net.thread.ThreadNetworkController.StateCallback
+import android.net.thread.ThreadNetworkException
+import android.os.OutcomeReceiver
+import androidx.annotation.VisibleForTesting
+import java.util.concurrent.Executor
+
+/**
+ * A testable interface for [ThreadNetworkController] which is `final`.
+ *
+ * We are in a awkward situation that Android API guideline suggest `final` for API classes
+ * while Robolectric test is being deprecated for platform testing (See
+ * tests/robotests/new_tests_hook.sh). This force us to use "mockito-target-extended" but it's
+ * conflicting with the default "mockito-target" which is somehow indirectly depended by the
+ * `SettingsUnitTests` target.
+ */
+@VisibleForTesting
+interface BaseThreadNetworkController {
+    fun setEnabled(
+        enabled: Boolean,
+        executor: Executor,
+        receiver: OutcomeReceiver<Void?, ThreadNetworkException>
+    )
+
+    fun registerStateCallback(executor: Executor, callback: StateCallback)
+
+    fun unregisterStateCallback(callback: StateCallback)
+}
\ No newline at end of file
diff --git a/src/com/android/settings/connecteddevice/threadnetwork/ThreadNetworkFooterController.kt b/src/com/android/settings/connecteddevice/threadnetwork/ThreadNetworkFooterController.kt
new file mode 100644
index 0000000..1e3b624
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/threadnetwork/ThreadNetworkFooterController.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 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.connecteddevice.threadnetwork
+
+import android.content.Context
+import android.util.Log
+import androidx.preference.PreferenceScreen
+import com.android.settings.R
+import com.android.settings.core.BasePreferenceController
+import com.android.settingslib.HelpUtils
+import com.android.settingslib.widget.FooterPreference
+
+/**
+ * The footer preference controller for Thread settings in
+ * "Connected devices > Connection preferences > Thread".
+ */
+class ThreadNetworkFooterController(
+    context: Context,
+    preferenceKey: String
+) : BasePreferenceController(context, preferenceKey) {
+    override fun getAvailabilityStatus(): Int {
+        // The thread_network_settings screen won't be displayed and it doesn't matter if this
+        // controller always return AVAILABLE
+        return AVAILABLE
+    }
+
+    override fun displayPreference(screen: PreferenceScreen) {
+        val footer: FooterPreference? = screen.findPreference(KEY_PREFERENCE_FOOTER)
+        if (footer != null) {
+            footer.setLearnMoreAction { _ -> openLocaleLearnMoreLink() }
+            footer.setLearnMoreText(mContext.getString(R.string.thread_network_settings_learn_more))
+        }
+    }
+
+    private fun openLocaleLearnMoreLink() {
+        val intent = HelpUtils.getHelpIntent(
+            mContext,
+            mContext.getString(R.string.thread_network_settings_learn_more_link),
+            mContext::class.java.name
+        )
+        if (intent != null) {
+            mContext.startActivity(intent)
+        } else {
+            Log.w(TAG, "HelpIntent is null")
+        }
+    }
+
+    companion object {
+        private const val TAG = "ThreadNetworkSettings"
+        private const val KEY_PREFERENCE_FOOTER = "thread_network_settings_footer"
+    }
+}
diff --git a/src/com/android/settings/connecteddevice/threadnetwork/ThreadNetworkFragment.kt b/src/com/android/settings/connecteddevice/threadnetwork/ThreadNetworkFragment.kt
new file mode 100644
index 0000000..fd385d7
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/threadnetwork/ThreadNetworkFragment.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 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.connecteddevice.threadnetwork
+
+import android.app.settings.SettingsEnums
+import com.android.settings.R
+import com.android.settings.dashboard.DashboardFragment
+import com.android.settings.search.BaseSearchIndexProvider
+import com.android.settingslib.search.SearchIndexable
+
+/** The fragment for Thread settings in "Connected devices > Connection preferences > Thread". */
+@SearchIndexable(forTarget = SearchIndexable.ALL and SearchIndexable.ARC.inv())
+class ThreadNetworkFragment : DashboardFragment() {
+    override fun getPreferenceScreenResId() = R.xml.thread_network_settings
+
+    override fun getLogTag() = "ThreadNetworkFragment"
+
+    override fun getMetricsCategory() = SettingsEnums.CONNECTED_DEVICE_PREFERENCES_THREAD
+
+    companion object {
+        /** For Search. */
+        @JvmField
+        val SEARCH_INDEX_DATA_PROVIDER = BaseSearchIndexProvider(R.xml.thread_network_settings)
+    }
+}
diff --git a/src/com/android/settings/connecteddevice/threadnetwork/ThreadNetworkFragmentController.kt b/src/com/android/settings/connecteddevice/threadnetwork/ThreadNetworkFragmentController.kt
new file mode 100644
index 0000000..beb824a
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/threadnetwork/ThreadNetworkFragmentController.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2024 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.connecteddevice.threadnetwork
+
+import android.content.Context
+import android.net.thread.ThreadNetworkController
+import android.net.thread.ThreadNetworkController.StateCallback
+import androidx.annotation.VisibleForTesting
+import androidx.core.content.ContextCompat
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleEventObserver
+import androidx.lifecycle.LifecycleOwner
+import androidx.preference.Preference
+import androidx.preference.PreferenceScreen
+import com.android.settings.R
+import com.android.settings.core.BasePreferenceController
+import com.android.settings.flags.Flags
+import java.util.concurrent.Executor
+
+/**
+ * The fragment controller for Thread settings in
+ * "Connected devices > Connection preferences > Thread".
+ */
+class ThreadNetworkFragmentController @VisibleForTesting constructor(
+    context: Context,
+    preferenceKey: String,
+    private val executor: Executor,
+    private val threadController: BaseThreadNetworkController?
+) : BasePreferenceController(context, preferenceKey), LifecycleEventObserver {
+    private val stateCallback: StateCallback
+    private var threadEnabled = false
+    private var preference: Preference? = null
+
+    constructor(context: Context, preferenceKey: String) : this(
+        context,
+        preferenceKey,
+        ContextCompat.getMainExecutor(context),
+        ThreadNetworkUtils.getThreadNetworkController(context)
+    )
+
+    init {
+        stateCallback = newStateCallback()
+    }
+
+    override fun getAvailabilityStatus(): Int {
+        return if (!Flags.threadSettingsEnabled()) {
+            CONDITIONALLY_UNAVAILABLE
+        } else if (threadController == null) {
+            UNSUPPORTED_ON_DEVICE
+        } else {
+            AVAILABLE
+        }
+    }
+
+    override fun getSummary(): CharSequence {
+        return if (threadEnabled) {
+            mContext.getText(R.string.switch_on_text)
+        } else {
+            mContext.getText(R.string.switch_off_text)
+        }
+    }
+
+    override fun displayPreference(screen: PreferenceScreen) {
+        super.displayPreference(screen)
+        preference = screen.findPreference(preferenceKey)
+    }
+
+    override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
+        if (threadController == null) {
+            return
+        }
+
+        when (event) {
+            Lifecycle.Event.ON_START ->
+                threadController.registerStateCallback(executor, stateCallback)
+
+            Lifecycle.Event.ON_STOP ->
+                threadController.unregisterStateCallback(stateCallback)
+
+            else -> {}
+        }
+    }
+
+    private fun newStateCallback(): StateCallback {
+        return object : StateCallback {
+            override fun onThreadEnableStateChanged(enabledState: Int) {
+                threadEnabled = enabledState == ThreadNetworkController.STATE_ENABLED
+                preference?.let { preference -> refreshSummary(preference) }
+            }
+
+            override fun onDeviceRoleChanged(role: Int) {}
+        }
+    }
+}
diff --git a/src/com/android/settings/connecteddevice/threadnetwork/ThreadNetworkPreferenceController.kt b/src/com/android/settings/connecteddevice/threadnetwork/ThreadNetworkPreferenceController.kt
deleted file mode 100644
index 1c01750..0000000
--- a/src/com/android/settings/connecteddevice/threadnetwork/ThreadNetworkPreferenceController.kt
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright (C) 2024 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.connecteddevice.threadnetwork
-
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
-import android.content.pm.PackageManager
-import android.net.thread.ThreadNetworkController
-import android.net.thread.ThreadNetworkController.StateCallback
-import android.net.thread.ThreadNetworkException
-import android.net.thread.ThreadNetworkManager
-import android.os.OutcomeReceiver
-import android.provider.Settings
-import android.util.Log
-import androidx.annotation.VisibleForTesting
-import androidx.core.content.ContextCompat
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleEventObserver
-import androidx.lifecycle.LifecycleOwner
-import androidx.preference.Preference
-import androidx.preference.PreferenceScreen
-import com.android.settings.R
-import com.android.settings.core.TogglePreferenceController
-import com.android.settings.flags.Flags
-import java.util.concurrent.Executor
-
-/** Controller for the "Thread" toggle in "Connected devices > Connection preferences".  */
-class ThreadNetworkPreferenceController @VisibleForTesting constructor(
-    context: Context,
-    key: String,
-    private val executor: Executor,
-    private val threadController: BaseThreadNetworkController?
-) : TogglePreferenceController(context, key), LifecycleEventObserver {
-    private val stateCallback: StateCallback
-    private val airplaneModeReceiver: BroadcastReceiver
-    private var threadEnabled = false
-    private var airplaneModeOn = false
-    private var preference: Preference? = null
-
-    /**
-     * A testable interface for [ThreadNetworkController] which is `final`.
-     *
-     * We are in a awkward situation that Android API guideline suggest `final` for API classes
-     * while Robolectric test is being deprecated for platform testing (See
-     * tests/robotests/new_tests_hook.sh). This force us to use "mockito-target-extended" but it's
-     * conflicting with the default "mockito-target" which is somehow indirectly depended by the
-     * `SettingsUnitTests` target.
-     */
-    @VisibleForTesting
-    interface BaseThreadNetworkController {
-        fun setEnabled(
-            enabled: Boolean,
-            executor: Executor,
-            receiver: OutcomeReceiver<Void?, ThreadNetworkException>
-        )
-
-        fun registerStateCallback(executor: Executor, callback: StateCallback)
-
-        fun unregisterStateCallback(callback: StateCallback)
-    }
-
-    constructor(context: Context, key: String) : this(
-        context,
-        key,
-        ContextCompat.getMainExecutor(context),
-        getThreadNetworkController(context)
-    )
-
-    init {
-        stateCallback = newStateCallback()
-        airplaneModeReceiver = newAirPlaneModeReceiver()
-    }
-
-    val isThreadSupportedOnDevice: Boolean
-        get() = threadController != null
-
-    private fun newStateCallback(): StateCallback {
-        return object : StateCallback {
-            override fun onThreadEnableStateChanged(enabledState: Int) {
-                threadEnabled = enabledState == ThreadNetworkController.STATE_ENABLED
-            }
-
-            override fun onDeviceRoleChanged(role: Int) {}
-        }
-    }
-
-    private fun newAirPlaneModeReceiver(): BroadcastReceiver {
-        return object : BroadcastReceiver() {
-            override fun onReceive(context: Context, intent: Intent) {
-                airplaneModeOn = isAirplaneModeOn(context)
-                Log.i(TAG, "Airplane mode is " + if (airplaneModeOn) "ON" else "OFF")
-                preference?.let { preference -> updateState(preference) }
-            }
-        }
-    }
-
-    override fun getAvailabilityStatus(): Int {
-        return if (!Flags.threadSettingsEnabled()) {
-            CONDITIONALLY_UNAVAILABLE
-        } else if (!isThreadSupportedOnDevice) {
-            UNSUPPORTED_ON_DEVICE
-        } else if (airplaneModeOn) {
-            DISABLED_DEPENDENT_SETTING
-        } else {
-            AVAILABLE
-        }
-    }
-
-    override fun displayPreference(screen: PreferenceScreen) {
-        super.displayPreference(screen)
-        preference = screen.findPreference(preferenceKey)
-    }
-
-    override fun isChecked(): Boolean {
-        // TODO (b/322742298):
-        // Check airplane mode here because it's planned to disable Thread state in airplane mode
-        // (code in the mainline module). But it's currently not implemented yet (b/322742298).
-        // By design, the toggle should be unchecked in airplane mode, so explicitly check the
-        // airplane mode here to acchieve the same UX.
-        return !airplaneModeOn && threadEnabled
-    }
-
-    override fun setChecked(isChecked: Boolean): Boolean {
-        if (threadController == null) {
-            return false
-        }
-        val action = if (isChecked) "enable" else "disable"
-        threadController.setEnabled(
-            isChecked,
-            executor,
-            object : OutcomeReceiver<Void?, ThreadNetworkException> {
-                override fun onError(e: ThreadNetworkException) {
-                    // TODO(b/327549838): gracefully handle the failure by resetting the UI state
-                    Log.e(TAG, "Failed to $action Thread", e)
-                }
-
-                override fun onResult(unused: Void?) {
-                    Log.d(TAG, "Successfully $action Thread")
-                }
-            })
-        return true
-    }
-
-    override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
-        if (threadController == null) {
-            return
-        }
-
-        when (event) {
-            Lifecycle.Event.ON_START -> {
-                threadController.registerStateCallback(executor, stateCallback)
-                airplaneModeOn = isAirplaneModeOn(mContext)
-                mContext.registerReceiver(
-                    airplaneModeReceiver,
-                    IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)
-                )
-                preference?.let { preference -> updateState(preference) }
-            }
-            Lifecycle.Event.ON_STOP -> {
-                threadController.unregisterStateCallback(stateCallback)
-                mContext.unregisterReceiver(airplaneModeReceiver)
-            }
-            else -> {}
-        }
-    }
-
-    override fun updateState(preference: Preference) {
-        super.updateState(preference)
-        preference.isEnabled = !airplaneModeOn
-        refreshSummary(preference)
-    }
-
-    override fun getSummary(): CharSequence {
-        val resId: Int = if (airplaneModeOn) {
-            R.string.thread_network_settings_summary_airplane_mode
-        } else {
-            R.string.thread_network_settings_summary
-        }
-        return mContext.getResources().getString(resId)
-    }
-
-    override fun getSliceHighlightMenuRes(): Int {
-        return R.string.menu_key_connected_devices
-    }
-
-    companion object {
-        private const val TAG = "ThreadNetworkSettings"
-        private fun getThreadNetworkController(context: Context): BaseThreadNetworkController? {
-            if (!context.packageManager.hasSystemFeature(PackageManager.FEATURE_THREAD_NETWORK)) {
-                return null
-            }
-            val manager = context.getSystemService(ThreadNetworkManager::class.java) ?: return null
-            val controller = manager.allThreadNetworkControllers[0]
-            return object : BaseThreadNetworkController {
-                override fun setEnabled(
-                    enabled: Boolean,
-                    executor: Executor,
-                    receiver: OutcomeReceiver<Void?, ThreadNetworkException>
-                ) {
-                    controller.setEnabled(enabled, executor, receiver)
-                }
-
-                override fun registerStateCallback(executor: Executor, callback: StateCallback) {
-                    controller.registerStateCallback(executor, callback)
-                }
-
-                override fun unregisterStateCallback(callback: StateCallback) {
-                    controller.unregisterStateCallback(callback)
-                }
-            }
-        }
-
-        private fun isAirplaneModeOn(context: Context): Boolean {
-            return Settings.Global.getInt(
-                context.contentResolver,
-                Settings.Global.AIRPLANE_MODE_ON,
-                0
-            ) == 1
-        }
-    }
-}
diff --git a/src/com/android/settings/connecteddevice/threadnetwork/ThreadNetworkToggleController.kt b/src/com/android/settings/connecteddevice/threadnetwork/ThreadNetworkToggleController.kt
new file mode 100644
index 0000000..2af4675
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/threadnetwork/ThreadNetworkToggleController.kt
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2024 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.connecteddevice.threadnetwork
+
+import android.content.Context
+import android.net.thread.ThreadNetworkController
+import android.net.thread.ThreadNetworkController.StateCallback
+import android.net.thread.ThreadNetworkException
+import android.os.OutcomeReceiver
+import android.util.Log
+import androidx.annotation.VisibleForTesting
+import androidx.core.content.ContextCompat
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleEventObserver
+import androidx.lifecycle.LifecycleOwner
+import androidx.preference.Preference
+import androidx.preference.PreferenceScreen
+import com.android.settings.R
+import com.android.settings.core.TogglePreferenceController
+import com.android.settings.flags.Flags
+import java.util.concurrent.Executor
+
+/**
+ * Controller for the "Use Thread" toggle in "Connected devices > Connection preferences > Thread".
+ */
+class ThreadNetworkToggleController @VisibleForTesting constructor(
+    context: Context,
+    key: String,
+    private val executor: Executor,
+    private val threadController: BaseThreadNetworkController?
+) : TogglePreferenceController(context, key), LifecycleEventObserver {
+    private val stateCallback: StateCallback
+    private var threadEnabled = false
+    private var preference: Preference? = null
+
+    constructor(context: Context, key: String) : this(
+        context,
+        key,
+        ContextCompat.getMainExecutor(context),
+        ThreadNetworkUtils.getThreadNetworkController(context)
+    )
+
+    init {
+        stateCallback = newStateCallback()
+    }
+
+    val isThreadSupportedOnDevice: Boolean
+        get() = threadController != null
+
+    private fun newStateCallback(): StateCallback {
+        return object : StateCallback {
+            override fun onThreadEnableStateChanged(enabledState: Int) {
+                threadEnabled = enabledState == ThreadNetworkController.STATE_ENABLED
+                preference?.let { preference -> updateState(preference) }
+            }
+
+            override fun onDeviceRoleChanged(role: Int) {}
+        }
+    }
+
+    override fun getAvailabilityStatus(): Int {
+        return if (!Flags.threadSettingsEnabled()) {
+            CONDITIONALLY_UNAVAILABLE
+        } else if (!isThreadSupportedOnDevice) {
+            UNSUPPORTED_ON_DEVICE
+        } else {
+            AVAILABLE
+        }
+    }
+
+    override fun displayPreference(screen: PreferenceScreen) {
+        super.displayPreference(screen)
+        preference = screen.findPreference(preferenceKey)
+    }
+
+    override fun isChecked(): Boolean {
+        return threadEnabled
+    }
+
+    override fun setChecked(isChecked: Boolean): Boolean {
+        if (threadController == null) {
+            return false
+        }
+
+        // Avoids dead loop of setChecked -> threadController.setEnabled() ->
+        // StateCallback.onThreadEnableStateChanged -> updateState -> setChecked
+        if (isChecked == isChecked()) {
+            return true
+        }
+
+        val action = if (isChecked) "enable" else "disable"
+        threadController.setEnabled(
+            isChecked,
+            executor,
+            object : OutcomeReceiver<Void?, ThreadNetworkException> {
+                override fun onError(e: ThreadNetworkException) {
+                    // TODO(b/327549838): gracefully handle the failure by resetting the UI state
+                    Log.e(TAG, "Failed to $action Thread", e)
+                }
+
+                override fun onResult(unused: Void?) {
+                    Log.d(TAG, "Successfully $action Thread")
+                }
+            })
+        return true
+    }
+
+    override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
+        if (threadController == null) {
+            return
+        }
+
+        when (event) {
+            Lifecycle.Event.ON_START -> {
+                threadController.registerStateCallback(executor, stateCallback)
+            }
+
+            Lifecycle.Event.ON_STOP -> {
+                threadController.unregisterStateCallback(stateCallback)
+            }
+
+            else -> {}
+        }
+    }
+
+    override fun getSliceHighlightMenuRes(): Int {
+        return R.string.menu_key_connected_devices
+    }
+
+    companion object {
+        private const val TAG = "ThreadNetworkSettings"
+    }
+}
diff --git a/src/com/android/settings/connecteddevice/threadnetwork/ThreadNetworkUtils.kt b/src/com/android/settings/connecteddevice/threadnetwork/ThreadNetworkUtils.kt
new file mode 100644
index 0000000..70830ed
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/threadnetwork/ThreadNetworkUtils.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 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.connecteddevice.threadnetwork
+
+import android.content.Context
+import android.content.pm.PackageManager
+import android.net.thread.ThreadNetworkController
+import android.net.thread.ThreadNetworkController.StateCallback
+import android.net.thread.ThreadNetworkException
+import android.net.thread.ThreadNetworkManager
+import android.os.OutcomeReceiver
+import androidx.annotation.VisibleForTesting
+import java.util.concurrent.Executor
+
+/** Common utilities for Thread settings classes. */
+object ThreadNetworkUtils {
+    /**
+     * Retrieves the [BaseThreadNetworkController] instance that is backed by the Android
+     * [ThreadNetworkController].
+     */
+    fun getThreadNetworkController(context: Context): BaseThreadNetworkController? {
+        if (!context.packageManager.hasSystemFeature(PackageManager.FEATURE_THREAD_NETWORK)) {
+            return null
+        }
+        val manager = context.getSystemService(ThreadNetworkManager::class.java) ?: return null
+        val controller = manager.allThreadNetworkControllers[0]
+        return object : BaseThreadNetworkController {
+            override fun setEnabled(
+                enabled: Boolean,
+                executor: Executor,
+                receiver: OutcomeReceiver<Void?, ThreadNetworkException>
+            ) {
+                controller.setEnabled(enabled, executor, receiver)
+            }
+
+            override fun registerStateCallback(executor: Executor, callback: StateCallback) {
+                controller.registerStateCallback(executor, callback)
+            }
+
+            override fun unregisterStateCallback(callback: StateCallback) {
+                controller.unregisterStateCallback(callback)
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/core/RoundCornerPreferenceAdapter.java b/src/com/android/settings/core/RoundCornerPreferenceAdapter.java
new file mode 100644
index 0000000..e5f3763
--- /dev/null
+++ b/src/com/android/settings/core/RoundCornerPreferenceAdapter.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2024 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.core;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceGroup;
+import androidx.preference.PreferenceGroupAdapter;
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.settingslib.widget.theme.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class RoundCornerPreferenceAdapter extends PreferenceGroupAdapter {
+
+    private static final int ROUND_CORNER_CENTER = 1;
+    private static final int ROUND_CORNER_TOP = 1 << 1;
+    private static final int ROUND_CORNER_BOTTOM = 1 << 2;
+
+    private final PreferenceGroup mPreferenceGroup;
+
+    private List<Integer> mRoundCornerMappingList;
+
+    private final Handler mHandler;
+
+    private final Runnable mSyncRunnable = new Runnable() {
+        @Override
+        public void run() {
+            updatePreferences();
+        }
+    };
+
+    public RoundCornerPreferenceAdapter(@NonNull PreferenceGroup preferenceGroup) {
+        super(preferenceGroup);
+        mPreferenceGroup = preferenceGroup;
+        mHandler = new Handler(Looper.getMainLooper());
+        updatePreferences();
+    }
+
+    @Override
+    public void onPreferenceHierarchyChange(@NonNull Preference preference) {
+        super.onPreferenceHierarchyChange(preference);
+        mHandler.removeCallbacks(mSyncRunnable);
+        mHandler.post(mSyncRunnable);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull PreferenceViewHolder holder, int position) {
+        super.onBindViewHolder(holder, position);
+        updateBackground(holder, position);
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    private void updatePreferences() {
+        mRoundCornerMappingList = new ArrayList<>();
+        mappingPreferenceGroup(mRoundCornerMappingList, mPreferenceGroup);
+    }
+    private void mappingPreferenceGroup(List<Integer> visibleList, PreferenceGroup group) {
+        int groupSize = group.getPreferenceCount();
+        int firstVisible = 0;
+        int lastVisible = 0;
+        for (int i = 0; i < groupSize; i++) {
+            Preference pref = group.getPreference(i);
+            if (!pref.isVisible()) {
+                continue;
+            }
+
+            //the first visible preference.
+            Preference firstVisiblePref = group.getPreference(firstVisible);
+            if (!firstVisiblePref.isVisible()) {
+                firstVisible = i;
+            }
+
+            int value = 0;
+            if (group instanceof PreferenceCategory) {
+                if (pref instanceof PreferenceCategory) {
+                    visibleList.add(value);
+                    mappingPreferenceGroup(visibleList, (PreferenceCategory) pref);
+                } else {
+                    if (i == firstVisible) {
+                        value |= ROUND_CORNER_TOP;
+                    }
+
+                    value |= ROUND_CORNER_BOTTOM;
+                    if (i > lastVisible) {
+                        // the last
+                        int lastIndex = visibleList.size() - 1;
+                        int newValue = visibleList.get(lastIndex) & ~ROUND_CORNER_BOTTOM;
+                        visibleList.set(lastIndex, newValue);
+                        lastVisible = i;
+                    }
+
+                    value |= ROUND_CORNER_CENTER;
+                    visibleList.add(value);
+                }
+            } else {
+                visibleList.add(value);
+                if (pref instanceof PreferenceCategory) {
+                    mappingPreferenceGroup(visibleList, (PreferenceCategory) pref);
+                }
+            }
+        }
+    }
+
+    /** handle roundCorner background */
+    private void updateBackground(PreferenceViewHolder holder, int position) {
+        int CornerType = mRoundCornerMappingList.get(position);
+
+        if ((CornerType & ROUND_CORNER_CENTER) == 0) {
+            return;
+        }
+
+        View v = holder.itemView;
+        if (((CornerType & ROUND_CORNER_TOP) != 0) && ((CornerType & ROUND_CORNER_BOTTOM) == 0)) {
+            // the first
+            v.setBackgroundResource(R.drawable.settingslib_round_background_top);
+        } else if (((CornerType & ROUND_CORNER_BOTTOM) != 0)
+                && ((CornerType & ROUND_CORNER_TOP) == 0)) {
+            // the last
+            v.setBackgroundResource(R.drawable.settingslib_round_background_bottom);
+        } else if (((CornerType & ROUND_CORNER_TOP) != 0)
+                && ((CornerType & ROUND_CORNER_BOTTOM) != 0)) {
+            // the only one preference
+            v.setBackgroundResource(R.drawable.settingslib_round_background);
+        } else {
+            // in the center
+            v.setBackgroundResource(R.drawable.settingslib_round_background_center);
+        }
+    }
+}
diff --git a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
index b95d927..ffc97dc 100644
--- a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
+++ b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
@@ -444,7 +444,9 @@
         }
         if (TextUtils.equals(tile.getCategory(), CategoryKey.CATEGORY_HOMEPAGE)) {
             iconDrawable.setTint(Utils.getHomepageIconColor(preference.getContext()));
-        } else if (forceRoundedIcon && !TextUtils.equals(mContext.getPackageName(), iconPackage)) {
+        }
+
+        if (forceRoundedIcon && !TextUtils.equals(mContext.getPackageName(), iconPackage)) {
             iconDrawable = new AdaptiveIcon(mContext, iconDrawable,
                     R.dimen.dashboard_tile_foreground_image_inset);
             ((AdaptiveIcon) iconDrawable).setBackgroundColor(mContext, tile);
diff --git a/src/com/android/settings/datausage/BillingCyclePreference.kt b/src/com/android/settings/datausage/BillingCyclePreference.kt
index a6904bc..8dd7d0f 100644
--- a/src/com/android/settings/datausage/BillingCyclePreference.kt
+++ b/src/com/android/settings/datausage/BillingCyclePreference.kt
@@ -26,11 +26,9 @@
 import com.android.settings.R
 import com.android.settings.core.SubSettingLauncher
 import com.android.settings.datausage.lib.BillingCycleRepository
-import com.android.settings.network.mobileDataEnabledFlow
 import com.android.settings.spa.preference.ComposePreference
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
-import kotlinx.coroutines.flow.map
 
 /**
  * Preference which displays billing cycle of subscription
@@ -46,8 +44,8 @@
 
     override fun setTemplate(template: NetworkTemplate, subId: Int) {
         setContent {
-            val isModifiable by remember {
-                context.mobileDataEnabledFlow(subId).map { repository.isModifiable(subId) }
+            val isModifiable by remember(subId) {
+                repository.isModifiableFlow(subId)
             }.collectAsStateWithLifecycle(initialValue = false)
 
             Preference(object : PreferenceModel {
diff --git a/src/com/android/settings/datausage/DataUsageList.kt b/src/com/android/settings/datausage/DataUsageList.kt
index 1995097..a8f5460 100644
--- a/src/com/android/settings/datausage/DataUsageList.kt
+++ b/src/com/android/settings/datausage/DataUsageList.kt
@@ -35,7 +35,7 @@
 import com.android.settings.datausage.lib.NetworkUsageData
 import com.android.settings.network.MobileNetworkRepository
 import com.android.settings.network.SubscriptionUtil
-import com.android.settings.network.mobileDataEnabledFlow
+import com.android.settings.network.telephony.requireSubscriptionManager
 import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity
 import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
 import com.android.settingslib.spaprivileged.framework.common.userManager
@@ -113,8 +113,8 @@
     override fun onViewCreated(v: View, savedInstanceState: Bundle?) {
         super.onViewCreated(v, savedInstanceState)
 
-        requireContext().mobileDataEnabledFlow(subId)
-            .collectLatestWithLifecycle(viewLifecycleOwner) { updatePolicy() }
+        billingCycleRepository.isModifiableFlow(subId)
+            .collectLatestWithLifecycle(viewLifecycleOwner, action = ::updatePolicy)
 
         val template = template ?: return
         viewModel.templateFlow.value = template
@@ -163,16 +163,14 @@
     }
 
     /** Update chart sweeps and cycle list to reflect [NetworkPolicy] for current [template]. */
-    private fun updatePolicy() {
-        val isBillingCycleModifiable = isBillingCycleModifiable()
+    private fun updatePolicy(isModifiable: Boolean) {
+        val isBillingCycleModifiable = isModifiable && isActiveSubscription()
         dataUsageListHeaderController?.setConfigButtonVisible(isBillingCycleModifiable)
         chartDataUsagePreferenceController?.setBillingCycleModifiable(isBillingCycleModifiable)
     }
 
-    private fun isBillingCycleModifiable(): Boolean =
-        billingCycleRepository.isModifiable(subId) &&
-            requireContext().getSystemService(SubscriptionManager::class.java)!!
-                .getActiveSubscriptionInfo(subId) != null
+    private fun isActiveSubscription(): Boolean =
+            requireContext().requireSubscriptionManager().getActiveSubscriptionInfo(subId) != null
 
     /**
      * Updates the chart and detail data when initial loaded or selected cycle changed.
diff --git a/src/com/android/settings/datausage/lib/BillingCycleRepository.kt b/src/com/android/settings/datausage/lib/BillingCycleRepository.kt
index bd6aa27..d324c75 100644
--- a/src/com/android/settings/datausage/lib/BillingCycleRepository.kt
+++ b/src/com/android/settings/datausage/lib/BillingCycleRepository.kt
@@ -19,10 +19,15 @@
 import android.content.Context
 import android.os.INetworkManagementService
 import android.os.ServiceManager
-import android.telephony.TelephonyManager
 import android.util.Log
 import androidx.annotation.OpenForTesting
+import com.android.settings.network.telephony.TelephonyRepository
 import com.android.settingslib.spaprivileged.framework.common.userManager
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
 
 @OpenForTesting
 open class BillingCycleRepository @JvmOverloads constructor(
@@ -31,12 +36,14 @@
         INetworkManagementService.Stub.asInterface(
             ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)
         ),
+    private val telephonyRepository: TelephonyRepository = TelephonyRepository(context),
 ) {
     private val userManager = context.userManager
-    private val telephonyManager = context.getSystemService(TelephonyManager::class.java)!!
 
-    fun isModifiable(subId: Int): Boolean =
-        isBandwidthControlEnabled() && userManager.isAdminUser && isDataEnabled(subId)
+    fun isModifiableFlow(subId: Int): Flow<Boolean> =
+        telephonyRepository.isDataEnabledFlow(subId).map { isDataEnabled ->
+            isDataEnabled && isBandwidthControlEnabled() && userManager.isAdminUser
+        }.conflate().flowOn(Dispatchers.Default)
 
     open fun isBandwidthControlEnabled(): Boolean = try {
         networkService.isBandwidthControlEnabled
@@ -45,10 +52,6 @@
         false
     }
 
-    private fun isDataEnabled(subId: Int): Boolean =
-        telephonyManager.createForSubscriptionId(subId)
-            .isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER)
-
     companion object {
         private const val TAG = "BillingCycleRepository"
     }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
index b938c72..5e17f4b 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
@@ -649,9 +649,7 @@
 
     private final class HourlyChartLabelTextGenerator extends BaseLabelTextGenerator
             implements BatteryChartViewModel.LabelTextGenerator {
-        private static final int FULL_CHARGE_BATTERY_LEVEL = 100;
-
-        private boolean mIsFromFullCharge;
+        private boolean mIsStartTimestamp;
         private long mFistTimestamp;
         private long mLatestTimestamp;
 
@@ -664,7 +662,7 @@
             long timestamp = timestamps.get(index);
             boolean showMinute = false;
             if (Objects.equal(timestamp, mFistTimestamp)) {
-                if (mIsFromFullCharge) {
+                if (mIsStartTimestamp) {
                     showMinute = true;
                 } else {
                     // starts from 7 days ago
@@ -699,8 +697,7 @@
                 @NonNull final BatteryLevelData batteryLevelData) {
             BatteryLevelData.PeriodBatteryLevelData firstDayLevelData =
                     batteryLevelData.getHourlyBatteryLevelsPerDay().get(0);
-            this.mIsFromFullCharge =
-                    firstDayLevelData.getLevels().get(0) == FULL_CHARGE_BATTERY_LEVEL;
+            this.mIsStartTimestamp = firstDayLevelData.isStartTimestamp();
             this.mFistTimestamp = firstDayLevelData.getTimestamps().get(0);
             this.mLatestTimestamp =
                     getLast(
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryLevelData.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryLevelData.java
index 231c730..d1bf49b 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryLevelData.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryLevelData.java
@@ -28,6 +28,7 @@
 import androidx.core.util.Preconditions;
 
 import java.util.ArrayList;
+import java.util.Calendar;
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
@@ -39,17 +40,24 @@
     private static final long MIN_SIZE = 2;
     private static final long TIME_SLOT = DateUtils.HOUR_IN_MILLIS * 2;
 
+    // For testing only.
+    @VisibleForTesting @Nullable static Calendar sTestCalendar;
+
     /** A container for the battery timestamp and level data. */
     public static final class PeriodBatteryLevelData {
         // The length of mTimestamps and mLevels must be the same. mLevels[index] might be null when
         // there is no level data for the corresponding timestamp.
         private final List<Long> mTimestamps;
         private final List<Integer> mLevels;
+        private final boolean mIsStartTimestamp;
 
         public PeriodBatteryLevelData(
-                @NonNull Map<Long, Integer> batteryLevelMap, @NonNull List<Long> timestamps) {
+                @NonNull Map<Long, Integer> batteryLevelMap,
+                @NonNull List<Long> timestamps,
+                boolean isStartTimestamp) {
             mTimestamps = timestamps;
             mLevels = new ArrayList<>(timestamps.size());
+            mIsStartTimestamp = isStartTimestamp;
             for (Long timestamp : timestamps) {
                 mLevels.add(
                         batteryLevelMap.containsKey(timestamp)
@@ -66,6 +74,10 @@
             return mLevels;
         }
 
+        public boolean isStartTimestamp() {
+            return mIsStartTimestamp;
+        }
+
         @Override
         public String toString() {
             return String.format(
@@ -105,14 +117,21 @@
 
         final List<Long> timestampList = new ArrayList<>(batteryLevelMap.keySet());
         Collections.sort(timestampList);
+        final long minTimestamp = timestampList.get(0);
+        final long sixDaysAgoTimestamp =
+                DatabaseUtils.getTimestampSixDaysAgo(sTestCalendar != null ? sTestCalendar : null);
+        final boolean isStartTimestamp = minTimestamp > sixDaysAgoTimestamp;
         final List<Long> dailyTimestamps = getDailyTimestamps(timestampList);
         final List<List<Long>> hourlyTimestamps = getHourlyTimestamps(dailyTimestamps);
 
-        mDailyBatteryLevels = new PeriodBatteryLevelData(batteryLevelMap, dailyTimestamps);
+        mDailyBatteryLevels =
+                new PeriodBatteryLevelData(batteryLevelMap, dailyTimestamps, isStartTimestamp);
         mHourlyBatteryLevelsPerDay = new ArrayList<>(hourlyTimestamps.size());
-        for (List<Long> hourlyTimestampsPerDay : hourlyTimestamps) {
+        for (int i = 0; i < hourlyTimestamps.size(); i++) {
+            final List<Long> hourlyTimestampsPerDay = hourlyTimestamps.get(i);
             mHourlyBatteryLevelsPerDay.add(
-                    new PeriodBatteryLevelData(batteryLevelMap, hourlyTimestampsPerDay));
+                    new PeriodBatteryLevelData(
+                            batteryLevelMap, hourlyTimestampsPerDay, isStartTimestamp && i == 0));
         }
     }
 
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java b/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java
index e407c63..b758df4 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java
@@ -67,9 +67,13 @@
                 refreshJobs(context);
                 break;
             case Intent.ACTION_TIME_CHANGED:
-                Log.d(TAG, "refresh job and clear all data from action=" + action);
+                Log.d(TAG, "refresh job and clear data from action=" + action);
                 DatabaseUtils.clearDataAfterTimeChangedIfNeeded(context, intent);
                 break;
+            case Intent.ACTION_TIMEZONE_CHANGED:
+                Log.d(TAG, "refresh job and clear all data from action=" + action);
+                DatabaseUtils.clearDataAfterTimeZoneChangedIfNeeded(context);
+                break;
             default:
                 Log.w(TAG, "receive unsupported action=" + action);
         }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
index a41e9bd..5b28abb 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
@@ -16,8 +16,6 @@
 
 package com.android.settings.fuelgauge.batteryusage;
 
-import static android.content.Intent.FLAG_RECEIVER_REPLACE_PENDING;
-
 import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.utcToLocalTimeForLogging;
 
 import android.app.usage.IUsageStatsManager;
@@ -59,6 +57,7 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
+import java.util.TimeZone;
 import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
@@ -436,6 +435,23 @@
                 });
     }
 
+    /** Clears data after a specific startTimestamp in the battery usage database. */
+    public static void clearAllAfter(Context context, long startTimestamp) {
+        AsyncTask.execute(
+                () -> {
+                    try {
+                        final BatteryStateDatabase database =
+                                BatteryStateDatabase.getInstance(context.getApplicationContext());
+                        database.appUsageEventDao().clearAllAfter(startTimestamp);
+                        database.batteryEventDao().clearAllAfter(startTimestamp);
+                        database.batteryStateDao().clearAllAfter(startTimestamp);
+                        database.batteryUsageSlotDao().clearAllAfter(startTimestamp);
+                    } catch (RuntimeException e) {
+                        Log.e(TAG, "clearAllAfter() failed", e);
+                    }
+                });
+    }
+
     /** Clears all out-of-date data in the battery usage database. */
     public static void clearExpiredDataIfNeeded(Context context) {
         AsyncTask.execute(
@@ -456,14 +472,14 @@
                 });
     }
 
-    /** Clears all data and jobs if current timestamp is out of the range of last recorded job. */
+    /** Clears data after new updated time and refresh periodic job. */
     public static void clearDataAfterTimeChangedIfNeeded(Context context, Intent intent) {
-        if ((intent.getFlags() & FLAG_RECEIVER_REPLACE_PENDING) != 0) {
+        if ((intent.hasExtra(Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT))) {
             BatteryUsageLogUtils.writeLog(
                     context,
                     Action.TIME_UPDATED,
-                    "Database is not cleared because the time change intent is only"
-                            + " for the existing pending receiver.");
+                    "Database is not cleared because the time change intent is"
+                            + " for time format change");
             return;
         }
         AsyncTask.execute(
@@ -480,6 +496,22 @@
                 });
     }
 
+    /** Clears all data and reset jobs if timezone changed. */
+    public static void clearDataAfterTimeZoneChangedIfNeeded(Context context) {
+        AsyncTask.execute(
+                () -> {
+                    try {
+                        clearDataAfterTimeZoneChangedIfNeededInternal(context);
+                    } catch (RuntimeException e) {
+                        Log.e(TAG, "clearDataAfterTimeZoneChangedIfNeeded() failed", e);
+                        BatteryUsageLogUtils.writeLog(
+                                context,
+                                Action.TIMEZONE_UPDATED,
+                                "clearDataAfterTimeZoneChangedIfNeeded() failed" + e);
+                    }
+                });
+    }
+
     /** Returns the timestamp for 00:00 6 days before the calendar date. */
     public static long getTimestampSixDaysAgo(Calendar calendar) {
         Calendar startCalendar =
@@ -861,37 +893,38 @@
     }
 
     private static void clearDataAfterTimeChangedIfNeededInternal(Context context) {
+        final long currentTime = System.currentTimeMillis();
+        final String logInfo =
+                String.format(Locale.ENGLISH, "clear data after current time = %d", currentTime);
+        Log.d(TAG, logInfo);
+        BatteryUsageLogUtils.writeLog(context, Action.TIME_UPDATED, logInfo);
+        DatabaseUtils.clearAllAfter(context, currentTime);
+        PeriodicJobManager.getInstance(context).refreshJob(/* fromBoot= */ false);
+
         final List<BatteryEvent> batteryLevelRecordEvents =
                 DatabaseUtils.getBatteryEvents(
                         context,
                         Calendar.getInstance(),
                         getLastFullChargeTime(context),
                         BATTERY_LEVEL_RECORD_EVENTS);
-        final long lastRecordTimestamp =
-                batteryLevelRecordEvents.isEmpty()
-                        ? INVALID_TIMESTAMP
-                        : batteryLevelRecordEvents.get(0).getTimestamp();
-        final long nextRecordTimestamp =
-                TimestampUtils.getNextEvenHourTimestamp(lastRecordTimestamp);
-        final long currentTime = System.currentTimeMillis();
-        final boolean isOutOfTimeRange =
-                lastRecordTimestamp == INVALID_TIMESTAMP
-                        || currentTime < lastRecordTimestamp
-                        || currentTime > nextRecordTimestamp;
+        if (batteryLevelRecordEvents.isEmpty()) {
+            // Take a snapshot of battery usage data immediately if there's no battery events.
+            BatteryUsageDataLoader.enqueueWork(context, /* isFullChargeStart= */ true);
+        }
+    }
+
+    private static void clearDataAfterTimeZoneChangedIfNeededInternal(Context context) {
         final String logInfo =
                 String.format(
                         Locale.ENGLISH,
-                        "clear database = %b, current time = %d, last record time = %d",
-                        isOutOfTimeRange,
-                        currentTime,
-                        lastRecordTimestamp);
+                        "clear database for new time zone = %s",
+                        TimeZone.getDefault().toString());
+        BatteryUsageLogUtils.writeLog(context, Action.TIMEZONE_UPDATED, logInfo);
         Log.d(TAG, logInfo);
-        BatteryUsageLogUtils.writeLog(context, Action.TIME_UPDATED, logInfo);
-        if (isOutOfTimeRange) {
-            DatabaseUtils.clearAll(context);
-            PeriodicJobManager.getInstance(context)
-                    .refreshJob(/* fromBoot= */ false);
-        }
+        DatabaseUtils.clearAll(context);
+        PeriodicJobManager.getInstance(context).refreshJob(/* fromBoot= */ false);
+        // Take a snapshot of battery usage data immediately
+        BatteryUsageDataLoader.enqueueWork(context, /* isFullChargeStart= */ true);
     }
 
     private static long loadLongFromContentProvider(
diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventDao.java b/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventDao.java
index d220b15..2497801 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventDao.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventDao.java
@@ -55,6 +55,10 @@
     @Query("DELETE FROM AppUsageEventEntity WHERE timestamp <= :timestamp")
     void clearAllBefore(long timestamp);
 
+    /** Deletes all recorded data after a specific timestamp. */
+    @Query("DELETE FROM AppUsageEventEntity WHERE timestamp >= :timestamp")
+    void clearAllAfter(long timestamp);
+
     /** Clears all recorded data in the database. */
     @Query("DELETE FROM AppUsageEventEntity")
     void clearAll();
diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDao.java b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDao.java
index 8b696fe..19d2043 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDao.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryEventDao.java
@@ -65,6 +65,10 @@
     @Query("DELETE FROM BatteryEventEntity WHERE timestamp <= :timestamp")
     void clearAllBefore(long timestamp);
 
+    /** Deletes all recorded data after a specific timestamp. */
+    @Query("DELETE FROM BatteryEventEntity WHERE timestamp >= :timestamp")
+    void clearAllAfter(long timestamp);
+
     /** Clears all recorded data in the database. */
     @Query("DELETE FROM BatteryEventEntity")
     void clearAll();
diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDao.java b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDao.java
index 520c6be..049251e 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDao.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDao.java
@@ -61,6 +61,10 @@
     @Query("DELETE FROM BatteryState WHERE timestamp <= :timestamp")
     void clearAllBefore(long timestamp);
 
+    /** Deletes all recorded data after a specific timestamp. */
+    @Query("DELETE FROM BatteryState WHERE timestamp >= :timestamp")
+    void clearAllAfter(long timestamp);
+
     /** Clears all recorded data in the database. */
     @Query("DELETE FROM BatteryState")
     void clearAll();
diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryUsageSlotDao.java b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryUsageSlotDao.java
index d8cf41d..d53b0cf 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryUsageSlotDao.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryUsageSlotDao.java
@@ -52,6 +52,10 @@
     @Query("DELETE FROM BatteryUsageSlotEntity WHERE timestamp <= :timestamp")
     void clearAllBefore(long timestamp);
 
+    /** Deletes all recorded data after a specific timestamp. */
+    @Query("DELETE FROM BatteryUsageSlotEntity WHERE timestamp >= :timestamp")
+    void clearAllAfter(long timestamp);
+
     /** Clears all recorded data in the database. */
     @Query("DELETE FROM BatteryUsageSlotEntity")
     void clearAll();
diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java
index 566136d..e2557ed 100644
--- a/src/com/android/settings/homepage/SettingsHomepageActivity.java
+++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java
@@ -73,6 +73,7 @@
 import com.android.settings.activityembedding.EmbeddedDeepLinkUtils;
 import com.android.settings.core.CategoryMixin;
 import com.android.settings.core.FeatureFlags;
+import com.android.settings.flags.Flags;
 import com.android.settings.homepage.contextualcards.ContextualCardsFragment;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
@@ -160,8 +161,12 @@
         if (mAllowUpdateSuggestion) {
             Log.i(TAG, "showHomepageWithSuggestion: " + showSuggestion);
             mAllowUpdateSuggestion = false;
-            mSuggestionView.setVisibility(showSuggestion ? View.VISIBLE : View.GONE);
-            mTwoPaneSuggestionView.setVisibility(showSuggestion ? View.VISIBLE : View.GONE);
+            if (Flags.homepageRevamp()) {
+                mSuggestionView.setVisibility(showSuggestion ? View.VISIBLE : View.GONE);
+            } else {
+                mSuggestionView.setVisibility(showSuggestion ? View.VISIBLE : View.GONE);
+                mTwoPaneSuggestionView.setVisibility(showSuggestion ? View.VISIBLE : View.GONE);
+            }
         }
 
         if (mHomepageView == null) {
@@ -245,7 +250,10 @@
         }
 
         setupEdgeToEdge();
-        setContentView(R.layout.settings_homepage_container);
+        setContentView(
+                Flags.homepageRevamp()
+                        ? R.layout.settings_homepage_container_v2
+                        : R.layout.settings_homepage_container);
 
         mIsTwoPane = ActivityEmbeddingUtils.isAlreadyEmbedded(this);
 
@@ -397,19 +405,31 @@
     }
 
     private void initSearchBarView() {
-        final Toolbar toolbar = findViewById(R.id.search_action_bar);
-        FeatureFactory.getFeatureFactory().getSearchFeatureProvider()
-                .initSearchToolbar(this /* activity */, toolbar, SettingsEnums.SETTINGS_HOMEPAGE);
-
-        if (mIsEmbeddingActivityEnabled) {
-            final Toolbar toolbarTwoPaneVersion = findViewById(R.id.search_action_bar_two_pane);
+        if (Flags.homepageRevamp()) {
+            Toolbar toolbar = findViewById(R.id.search_action_bar_unified);
             FeatureFactory.getFeatureFactory().getSearchFeatureProvider()
-                    .initSearchToolbar(this /* activity */, toolbarTwoPaneVersion,
+                    .initSearchToolbar(this /* activity */, toolbar,
                             SettingsEnums.SETTINGS_HOMEPAGE);
+        } else {
+            final Toolbar toolbar = findViewById(R.id.search_action_bar);
+            FeatureFactory.getFeatureFactory().getSearchFeatureProvider()
+                    .initSearchToolbar(this /* activity */, toolbar,
+                            SettingsEnums.SETTINGS_HOMEPAGE);
+
+            if (mIsEmbeddingActivityEnabled) {
+                final Toolbar toolbarTwoPaneVersion = findViewById(R.id.search_action_bar_two_pane);
+                FeatureFactory.getFeatureFactory().getSearchFeatureProvider()
+                        .initSearchToolbar(this /* activity */, toolbarTwoPaneVersion,
+                                SettingsEnums.SETTINGS_HOMEPAGE);
+            }
         }
     }
 
     private void initAvatarView() {
+        if (Flags.homepageRevamp()) {
+            return;
+        }
+
         final ImageView avatarView = findViewById(R.id.account_avatar);
         final ImageView avatarTwoPaneView = findViewById(R.id.account_avatar_two_pane_version);
         if (AvatarViewMixin.isAvatarSupported(this)) {
@@ -458,8 +478,12 @@
             return;
         }
 
-        mSuggestionView = findViewById(R.id.suggestion_content);
-        mTwoPaneSuggestionView = findViewById(R.id.two_pane_suggestion_content);
+        if (Flags.homepageRevamp()) {
+            mSuggestionView = findViewById(R.id.unified_suggestion_content);
+        } else {
+            mSuggestionView = findViewById(R.id.suggestion_content);
+            mTwoPaneSuggestionView = findViewById(R.id.two_pane_suggestion_content);
+        }
         mHomepageView = findViewById(R.id.settings_homepage_container);
         // Hide the homepage for preparing the suggestion. If scrolling is needed, the list views
         // should be initialized in the invisible homepage view to prevent a scroll flicker.
@@ -467,11 +491,16 @@
         // Schedule a timer to show the homepage and hide the suggestion on timeout.
         mHomepageView.postDelayed(() -> showHomepageWithSuggestion(false),
                 HOMEPAGE_LOADING_TIMEOUT_MS);
-        showFragment(new SuggestionFragCreator(fragmentClass, /* isTwoPaneLayout= */ false),
-                R.id.suggestion_content);
-        if (mIsEmbeddingActivityEnabled) {
-            showFragment(new SuggestionFragCreator(fragmentClass, /* isTwoPaneLayout= */ true),
-                    R.id.two_pane_suggestion_content);
+        if (Flags.homepageRevamp()) {
+            showFragment(new SuggestionFragCreator(fragmentClass, true),
+                    R.id.unified_suggestion_content);
+        } else {
+            showFragment(new SuggestionFragCreator(fragmentClass, /* isTwoPaneLayout= */ false),
+                    R.id.suggestion_content);
+            if (mIsEmbeddingActivityEnabled) {
+                showFragment(new SuggestionFragCreator(fragmentClass, /* isTwoPaneLayout= */ true),
+                        R.id.two_pane_suggestion_content);
+            }
         }
     }
 
@@ -736,7 +765,7 @@
     }
 
     private void updateHomepageAppBar() {
-        if (!mIsEmbeddingActivityEnabled) {
+        if (Flags.homepageRevamp() || !mIsEmbeddingActivityEnabled) {
             return;
         }
         updateAppBarMinHeight();
@@ -752,7 +781,7 @@
     }
 
     private void updateHomepagePaddings() {
-        if (!mIsEmbeddingActivityEnabled) {
+        if (Flags.homepageRevamp() || !mIsEmbeddingActivityEnabled) {
             return;
         }
         if (mIsTwoPane) {
@@ -766,6 +795,9 @@
     }
 
     private void updateAppBarMinHeight() {
+        if (Flags.homepageRevamp()) {
+            return;
+        }
         final int searchBarHeight = getResources().getDimensionPixelSize(R.dimen.search_bar_height);
         final int margin = getResources().getDimensionPixelSize(
                 mIsEmbeddingActivityEnabled && mIsTwoPane
diff --git a/src/com/android/settings/homepage/TopLevelSettings.java b/src/com/android/settings/homepage/TopLevelSettings.java
index d1fa760..6642861 100644
--- a/src/com/android/settings/homepage/TopLevelSettings.java
+++ b/src/com/android/settings/homepage/TopLevelSettings.java
@@ -42,8 +42,10 @@
 import com.android.settings.Utils;
 import com.android.settings.activityembedding.ActivityEmbeddingRulesController;
 import com.android.settings.activityembedding.ActivityEmbeddingUtils;
+import com.android.settings.core.RoundCornerPreferenceAdapter;
 import com.android.settings.core.SubSettingLauncher;
 import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.flags.Flags;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.support.SupportPreferenceController;
@@ -84,7 +86,7 @@
 
     @Override
     protected int getPreferenceScreenResId() {
-        return R.xml.top_level_settings;
+        return Flags.homepageRevamp() ? R.xml.top_level_settings_v2 : R.xml.top_level_settings;
     }
 
     @Override
@@ -331,10 +333,14 @@
 
     @Override
     protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) {
-        if (!mIsEmbeddingActivityEnabled || !(getActivity() instanceof SettingsHomepageActivity)) {
-            return super.onCreateAdapter(preferenceScreen);
+        if (mIsEmbeddingActivityEnabled && (getActivity() instanceof SettingsHomepageActivity)) {
+            return mHighlightMixin.onCreateAdapter(this, preferenceScreen, mScrollNeeded);
         }
-        return mHighlightMixin.onCreateAdapter(this, preferenceScreen, mScrollNeeded);
+
+        if (Flags.homepageRevamp()) {
+            return new RoundCornerPreferenceAdapter(preferenceScreen);
+        }
+        return super.onCreateAdapter(preferenceScreen);
     }
 
     @Override
@@ -376,7 +382,10 @@
     }
 
     public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
-            new BaseSearchIndexProvider(R.xml.top_level_settings) {
+            new BaseSearchIndexProvider(
+                    Flags.homepageRevamp()
+                            ? R.xml.top_level_settings_v2
+                            : R.xml.top_level_settings) {
 
                 @Override
                 protected boolean isPageSearchEnabled(Context context) {
diff --git a/src/com/android/settings/network/SatelliteWarningDialogActivity.kt b/src/com/android/settings/network/SatelliteWarningDialogActivity.kt
index 0702e4f..3f1d416 100644
--- a/src/com/android/settings/network/SatelliteWarningDialogActivity.kt
+++ b/src/com/android/settings/network/SatelliteWarningDialogActivity.kt
@@ -27,6 +27,7 @@
 import com.android.settingslib.spa.SpaDialogWindowTypeActivity
 import com.android.settingslib.spa.widget.dialog.AlertDialogButton
 import com.android.settingslib.spa.widget.dialog.SettingsAlertDialogContent
+import com.android.settingslib.wifi.WifiUtils
 
 /** A dialog to show the warning message when device is under satellite mode. */
 class SatelliteWarningDialogActivity : SpaDialogWindowTypeActivity() {
@@ -41,7 +42,10 @@
     }
 
     override fun getDialogWindowType(): Int {
-        return WindowManager.LayoutParams.LAST_APPLICATION_WINDOW
+        return intent.getIntExtra(
+            WifiUtils.DIALOG_WINDOW_TYPE,
+            WindowManager.LayoutParams.LAST_APPLICATION_WINDOW
+        )
     }
 
     @Composable
diff --git a/src/com/android/settings/network/telephony/DisableSimFooterPreferenceController.java b/src/com/android/settings/network/telephony/DisableSimFooterPreferenceController.java
deleted file mode 100644
index d14c8d0..0000000
--- a/src/com/android/settings/network/telephony/DisableSimFooterPreferenceController.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.network.telephony;
-
-import android.content.Context;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-
-import com.android.settings.network.SubscriptionUtil;
-
-/**
- * Shows information about disable a physical SIM.
- */
-public class DisableSimFooterPreferenceController extends TelephonyBasePreferenceController {
-
-    /**
-     * Constructor
-     */
-    public DisableSimFooterPreferenceController(Context context, String preferenceKey) {
-        super(context, preferenceKey);
-    }
-
-    /**
-     * re-init for SIM based on given subscription ID.
-     * @param subId is the given subscription ID
-     */
-    public void init(int subId) {
-        mSubId = subId;
-    }
-
-    @Override
-    public int getAvailabilityStatus(int subId) {
-        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-            return CONDITIONALLY_UNAVAILABLE;
-        }
-
-        SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class);
-        for (SubscriptionInfo info : SubscriptionUtil.getAvailableSubscriptions(mContext)) {
-            if (info.getSubscriptionId() == subId) {
-                if (info.isEmbedded() || SubscriptionUtil.showToggleForPhysicalSim(subManager)) {
-                    return CONDITIONALLY_UNAVAILABLE;
-                }
-                break;
-            }
-        }
-        return AVAILABLE;
-    }
-}
diff --git a/src/com/android/settings/network/telephony/DisableSimFooterPreferenceController.kt b/src/com/android/settings/network/telephony/DisableSimFooterPreferenceController.kt
new file mode 100644
index 0000000..8e3e398
--- /dev/null
+++ b/src/com/android/settings/network/telephony/DisableSimFooterPreferenceController.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.telephony
+
+import android.content.Context
+import android.telephony.SubscriptionManager
+
+/**
+ * Shows information about disable a physical SIM.
+ */
+class DisableSimFooterPreferenceController @JvmOverloads constructor(
+    context: Context,
+    preferenceKey: String,
+    private val subscriptionRepository: SubscriptionRepository = SubscriptionRepository(context),
+) : TelephonyBasePreferenceController(context, preferenceKey) {
+
+    /**
+     * Re-init for SIM based on given subscription ID.
+     *
+     * @param subId is the given subscription ID
+     */
+    fun init(subId: Int) {
+        mSubId = subId
+    }
+
+    override fun getAvailabilityStatus(subId: Int): Int {
+        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID ||
+            subscriptionRepository.canDisablePhysicalSubscription()
+        ) {
+            return CONDITIONALLY_UNAVAILABLE
+        }
+
+        val isAvailable =
+            subscriptionRepository.getSelectableSubscriptionInfoList().any { subInfo ->
+                subInfo.subscriptionId == subId && !subInfo.isEmbedded
+            }
+
+        return if (isAvailable) AVAILABLE else CONDITIONALLY_UNAVAILABLE
+    }
+}
diff --git a/src/com/android/settings/network/telephony/NetworkSelectSettings.java b/src/com/android/settings/network/telephony/NetworkSelectSettings.java
index f29a5ef..9455b70 100644
--- a/src/com/android/settings/network/telephony/NetworkSelectSettings.java
+++ b/src/com/android/settings/network/telephony/NetworkSelectSettings.java
@@ -48,13 +48,12 @@
 import com.android.settings.R;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.network.telephony.scan.NetworkScanRepository;
-import com.android.settings.network.telephony.scan.NetworkScanRepository.NetworkScanCellInfos;
-import com.android.settings.network.telephony.scan.NetworkScanRepository.NetworkScanComplete;
-import com.android.settings.network.telephony.scan.NetworkScanRepository.NetworkScanError;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.utils.ThreadUtils;
 
+import com.google.common.collect.ImmutableList;
+
 import kotlin.Unit;
 
 import java.util.ArrayList;
@@ -83,7 +82,8 @@
     private View mProgressHeader;
     private Preference mStatusMessagePreference;
     @VisibleForTesting
-    List<CellInfo> mCellInfoList;
+    @NonNull
+    List<CellInfo> mCellInfoList = ImmutableList.of();
     private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     private TelephonyManager mTelephonyManager;
     private SatelliteManager mSatelliteManager;
@@ -96,7 +96,6 @@
     private AtomicBoolean mShouldFilterOutSatellitePlmn = new AtomicBoolean();
 
     private NetworkScanRepository mNetworkScanRepository;
-    private boolean mUpdateScanResult = false;
 
     private NetworkSelectRepository mNetworkSelectRepository;
 
@@ -213,38 +212,16 @@
     }
 
     private void launchNetworkScan() {
+        setProgressBarVisible(true);
         mNetworkScanRepository.launchNetworkScan(getViewLifecycleOwner(), (networkScanResult) -> {
-            if (!mUpdateScanResult) {
-                // Not update UI if not in scan mode.
-                return Unit.INSTANCE;
-            }
-            if (networkScanResult instanceof NetworkScanCellInfos networkScanCellInfos) {
-                scanResultHandler(networkScanCellInfos.getCellInfos());
-                return Unit.INSTANCE;
-            }
-            if (!isPreferenceScreenEnabled()) {
-                clearPreferenceSummary();
-                enablePreferenceScreen(true);
-            } else if (networkScanResult instanceof NetworkScanComplete
-                    && mCellInfoList == null) {
-                // In case the scan timeout before getting any results
-                addMessagePreference(R.string.empty_networks_list);
-            } else if (networkScanResult instanceof NetworkScanError) {
-                addMessagePreference(R.string.network_query_error);
+            if (isPreferenceScreenEnabled()) {
+                scanResultHandler(networkScanResult);
             }
 
             return Unit.INSTANCE;
         });
     }
 
-    @Override
-    public void onStart() {
-        super.onStart();
-
-        setProgressBarVisible(true);
-        mUpdateScanResult = true;
-    }
-
     /**
      * Update forbidden PLMNs from the USIM App
      */
@@ -268,8 +245,6 @@
             return false;
         }
 
-        mUpdateScanResult = false;
-
         // Refresh the last selected item in case users reselect network.
         clearPreferenceSummary();
         if (mSelectedPreference != null) {
@@ -380,27 +355,19 @@
         }
     }
 
-    @Keep
     @VisibleForTesting
-    protected void scanResultHandler(List<CellInfo> results) {
-        mCellInfoList = filterOutSatellitePlmn(results);
+    protected void scanResultHandler(NetworkScanRepository.NetworkScanResult results) {
+        mCellInfoList = filterOutSatellitePlmn(results.getCellInfos());
         Log.d(TAG, "CellInfoList: " + CellInfoUtil.cellInfoListToString(mCellInfoList));
-        if (mCellInfoList != null && mCellInfoList.size() != 0) {
-            final NetworkOperatorPreference connectedPref = updateAllPreferenceCategory();
-            if (connectedPref != null) {
-                // update selected preference instance into connected preference
-                if (mSelectedPreference != null) {
-                    mSelectedPreference = connectedPref;
-                }
-            } else if (!isPreferenceScreenEnabled()) {
-                mSelectedPreference.setSummary(R.string.network_connecting);
-            }
-            enablePreferenceScreen(true);
-        } else if (isPreferenceScreenEnabled()) {
+        updateAllPreferenceCategory();
+        NetworkScanRepository.NetworkScanState state = results.getState();
+        if (state == NetworkScanRepository.NetworkScanState.ERROR) {
+            addMessagePreference(R.string.network_query_error);
+        } else if (mCellInfoList.isEmpty()) {
             addMessagePreference(R.string.empty_networks_list);
-            // keep showing progress bar, it will be stopped when error or completed
-            setProgressBarVisible(true);
         }
+        // keep showing progress bar, it will be stopped when error or completed
+        setProgressBarVisible(state == NetworkScanRepository.NetworkScanState.ACTIVE);
     }
 
     @Keep
@@ -417,11 +384,8 @@
 
     /**
      * Update the content of network operators list.
-     *
-     * @return preference which shows connected
      */
-    @Nullable
-    private NetworkOperatorPreference updateAllPreferenceCategory() {
+    private void updateAllPreferenceCategory() {
         int numberOfPreferences = mPreferenceCategory.getPreferenceCount();
 
         // remove unused preferences
@@ -432,7 +396,6 @@
         }
 
         // update the content of preference
-        NetworkOperatorPreference connectedPref = null;
         for (int index = 0; index < mCellInfoList.size(); index++) {
             final CellInfo cellInfo = mCellInfoList.get(index);
 
@@ -457,23 +420,10 @@
 
             if (mCellInfoList.get(index).isRegistered()) {
                 pref.setSummary(R.string.network_connected);
-                connectedPref = pref;
             } else {
                 pref.setSummary(null);
             }
         }
-
-        // update selected preference instance by index
-        for (int index = 0; index < mCellInfoList.size(); index++) {
-            final CellInfo cellInfo = mCellInfoList.get(index);
-
-            if ((mSelectedPreference != null) && mSelectedPreference.isSameCell(cellInfo)) {
-                mSelectedPreference = (NetworkOperatorPreference)
-                        (mPreferenceCategory.getPreference(index));
-            }
-        }
-
-        return connectedPref;
     }
 
     /**
@@ -524,13 +474,6 @@
         }
     }
 
-    private boolean isProgressBarVisible() {
-        if (mProgressHeader == null) {
-            return false;
-        }
-        return (mProgressHeader.getVisibility() == View.VISIBLE);
-    }
-
     protected void setProgressBarVisible(boolean visible) {
         if (mProgressHeader != null) {
             mProgressHeader.setVisibility(visible ? View.VISIBLE : View.GONE);
@@ -538,7 +481,6 @@
     }
 
     private void addMessagePreference(int messageId) {
-        setProgressBarVisible(false);
         mStatusMessagePreference.setTitle(messageId);
         mPreferenceCategory.removeAll();
         mPreferenceCategory.addPreference(mStatusMessagePreference);
diff --git a/src/com/android/settings/network/telephony/SubscriptionRepository.kt b/src/com/android/settings/network/telephony/SubscriptionRepository.kt
index f4bbc76..05cfad8 100644
--- a/src/com/android/settings/network/telephony/SubscriptionRepository.kt
+++ b/src/com/android/settings/network/telephony/SubscriptionRepository.kt
@@ -36,6 +36,8 @@
 private const val TAG = "SubscriptionRepository"
 
 class SubscriptionRepository(private val context: Context) {
+    private val subscriptionManager = context.requireSubscriptionManager()
+
     /**
      * Return a list of subscriptions that are available and visible to the user.
      *
@@ -55,6 +57,7 @@
         isSubscriptionEnabledFlow(subId).collectLatestWithLifecycle(lifecycleOwner, action = action)
     }
 
+    fun canDisablePhysicalSubscription() = subscriptionManager.canDisablePhysicalSubscription()
 }
 
 val Context.subscriptionManager: SubscriptionManager?
diff --git a/src/com/android/settings/network/telephony/TelephonyRepository.kt b/src/com/android/settings/network/telephony/TelephonyRepository.kt
index cc9b53d..d0d53b7 100644
--- a/src/com/android/settings/network/telephony/TelephonyRepository.kt
+++ b/src/com/android/settings/network/telephony/TelephonyRepository.kt
@@ -29,10 +29,12 @@
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.catch
 import kotlinx.coroutines.flow.conflate
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
 
 class TelephonyRepository(
     private val context: Context,
@@ -64,19 +66,21 @@
         telephonyManager.setMobileDataPolicyEnabled(policy, enabled)
     }
 
-    fun isDataEnabled(
-        subId: Int,
-    ): Flow<Boolean> {
+    fun isDataEnabledFlow(subId: Int): Flow<Boolean> {
         if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false)
 
-        Log.d(TAG, "register mobileDataEnabledFlow: [$subId]")
         return context.mobileDataEnabledFlow(subId)
             .map {
-                Log.d(TAG, "mobileDataEnabledFlow: receive mobile data [$subId] start")
                 val telephonyManager = context.telephonyManager(subId)
                 telephonyManager.isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER)
-                    .also { Log.d(TAG, "mobileDataEnabledFlow: [$subId] isDataEnabled(): $it") }
             }
+            .catch {
+                Log.w(TAG, "[$subId] isDataEnabledFlow: exception", it)
+                emit(false)
+            }
+            .onEach { Log.d(TAG, "[$subId] isDataEnabledFlow: isDataEnabled() = $it") }
+            .conflate()
+            .flowOn(Dispatchers.Default)
     }
 
     fun setMobileData(
@@ -100,6 +104,7 @@
             wifiPickerTrackerHelper.setCarrierNetworkEnabled(enabled)
         }
     }
+
     private companion object {
         private const val TAG = "TelephonyRepository"
     }
diff --git a/src/com/android/settings/network/telephony/scan/NetworkScanRepository.kt b/src/com/android/settings/network/telephony/scan/NetworkScanRepository.kt
index dfa79cb..4ae5842 100644
--- a/src/com/android/settings/network/telephony/scan/NetworkScanRepository.kt
+++ b/src/com/android/settings/network/telephony/scan/NetworkScanRepository.kt
@@ -27,25 +27,29 @@
 import android.util.Log
 import androidx.lifecycle.LifecycleOwner
 import com.android.settings.R
-import com.android.settings.network.telephony.CellInfoUtil
 import com.android.settings.network.telephony.CellInfoUtil.getNetworkTitle
+import com.android.settings.network.telephony.telephonyManager
 import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.asExecutor
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.conflate
 import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.onEach
 
 class NetworkScanRepository(private val context: Context, subId: Int) {
-    sealed interface NetworkScanResult
+    enum class NetworkScanState {
+        ACTIVE, COMPLETE, ERROR
+    }
 
-    data class NetworkScanCellInfos(val cellInfos: List<CellInfo>) : NetworkScanResult
-    data object NetworkScanComplete : NetworkScanResult
-    data class NetworkScanError(val error: Int) : NetworkScanResult
+    data class NetworkScanResult(
+        val state: NetworkScanState,
+        val cellInfos: List<CellInfo>,
+    )
 
-    private val telephonyManager =
-        context.getSystemService(TelephonyManager::class.java)!!.createForSubscriptionId(subId)
+    private val telephonyManager = context.telephonyManager(subId)
 
     /** TODO: Move this to UI layer, when UI layer migrated to Kotlin. */
     fun launchNetworkScan(lifecycleOwner: LifecycleOwner, onResult: (NetworkScanResult) -> Unit) {
@@ -65,23 +69,29 @@
     }
 
     fun networkScanFlow(): Flow<NetworkScanResult> = callbackFlow {
+        var state = NetworkScanState.ACTIVE
+        var cellInfos: List<CellInfo> = emptyList()
+
         val callback = object : TelephonyScanManager.NetworkScanCallback() {
             override fun onResults(results: List<CellInfo>) {
-                val cellInfos = results.distinctBy { CellInfoScanKey(it) }
-                trySend(NetworkScanCellInfos(cellInfos))
-                Log.d(TAG, "CellInfoList: ${CellInfoUtil.cellInfoListToString(cellInfos)}")
+                cellInfos = results.distinctBy { CellInfoScanKey(it) }
+                sendResult()
             }
 
             override fun onComplete() {
-                trySend(NetworkScanComplete)
-                close()
-                Log.d(TAG, "onComplete")
+                state = NetworkScanState.COMPLETE
+                sendResult()
+                // Don't call close() here since onComplete() could happens before onResults()
             }
 
             override fun onError(error: Int) {
-                trySend(NetworkScanError(error))
+                state = NetworkScanState.ERROR
+                sendResult()
                 close()
-                Log.d(TAG, "onError: $error")
+            }
+
+            private fun sendResult() {
+                trySend(NetworkScanResult(state, cellInfos))
             }
         }
 
@@ -92,7 +102,7 @@
         )
 
         awaitClose { networkScan.stopScan() }
-    }.flowOn(Dispatchers.Default)
+    }.conflate().onEach { Log.d(TAG, "networkScanFlow: $it") }.flowOn(Dispatchers.Default)
 
     /** Create network scan for allowed network types. */
     private fun createNetworkScan(): NetworkScanRequest {
diff --git a/src/com/android/settings/panel/PanelContent.java b/src/com/android/settings/panel/PanelContent.java
index 6b58228..1bbe2db 100644
--- a/src/com/android/settings/panel/PanelContent.java
+++ b/src/com/android/settings/panel/PanelContent.java
@@ -28,7 +28,10 @@
 
 /**
  * Represents the data class needed to create a Settings Panel. See {@link PanelFragment}.
+ *
+ * @deprecated this is no longer used after V and will be removed.
  */
+@Deprecated(forRemoval = true)
 public interface PanelContent extends Instrumentable {
 
     int VIEW_TYPE_SLIDER = 1;
diff --git a/src/com/android/settings/panel/PanelContentCallback.java b/src/com/android/settings/panel/PanelContentCallback.java
index e59d699..cceecd1 100644
--- a/src/com/android/settings/panel/PanelContentCallback.java
+++ b/src/com/android/settings/panel/PanelContentCallback.java
@@ -18,7 +18,10 @@
 /**
  * PanelContentCallback provides a callback interface for {@link PanelFragment} to receive
  * events from {@link PanelContent}.
+ *
+ * @deprecated this is no longer used after V and will be removed.
  */
+@Deprecated(forRemoval = true)
 public interface PanelContentCallback {
 
     /**
diff --git a/src/com/android/settings/panel/PanelFeatureProvider.java b/src/com/android/settings/panel/PanelFeatureProvider.java
index 402a562..943c37d 100644
--- a/src/com/android/settings/panel/PanelFeatureProvider.java
+++ b/src/com/android/settings/panel/PanelFeatureProvider.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.os.Bundle;
 
+@Deprecated(forRemoval = true)
 public interface PanelFeatureProvider {
 
     /**
diff --git a/src/com/android/settings/panel/PanelFeatureProviderImpl.java b/src/com/android/settings/panel/PanelFeatureProviderImpl.java
index ddfce65..a0aeec6 100644
--- a/src/com/android/settings/panel/PanelFeatureProviderImpl.java
+++ b/src/com/android/settings/panel/PanelFeatureProviderImpl.java
@@ -27,6 +27,7 @@
 import com.android.settings.Utils;
 import com.android.settings.flags.Flags;
 
+@Deprecated(forRemoval = true)
 public class PanelFeatureProviderImpl implements PanelFeatureProvider {
 
     @Override
diff --git a/src/com/android/settings/panel/PanelFragment.java b/src/com/android/settings/panel/PanelFragment.java
index 1590283..b3a2884 100644
--- a/src/com/android/settings/panel/PanelFragment.java
+++ b/src/com/android/settings/panel/PanelFragment.java
@@ -66,6 +66,7 @@
 import java.util.List;
 import java.util.Map;
 
+@Deprecated(forRemoval = true)
 public class PanelFragment extends Fragment {
 
     private static final String TAG = "PanelFragment";
@@ -519,6 +520,7 @@
         return mPanel.getViewType();
     }
 
+    @Deprecated(forRemoval = true)
     class LocalPanelCallback implements PanelContentCallback {
 
         @Override
diff --git a/src/com/android/settings/panel/PanelLoggingContract.java b/src/com/android/settings/panel/PanelLoggingContract.java
index e6e3012..fd145f8 100644
--- a/src/com/android/settings/panel/PanelLoggingContract.java
+++ b/src/com/android/settings/panel/PanelLoggingContract.java
@@ -21,7 +21,10 @@
  * <p>
  *    Constants should only be removed if underlying panel, or use case is removed.
  * </p>
+ *
+ * @deprecated this is no longer used after V and will be removed.
  */
+@Deprecated(forRemoval = true)
 public class PanelLoggingContract {
 
     /**
diff --git a/src/com/android/settings/panel/PanelSlicesAdapter.java b/src/com/android/settings/panel/PanelSlicesAdapter.java
index a2360d8..2223cbb 100644
--- a/src/com/android/settings/panel/PanelSlicesAdapter.java
+++ b/src/com/android/settings/panel/PanelSlicesAdapter.java
@@ -48,7 +48,10 @@
 
 /**
  * RecyclerView adapter for Slices in Settings Panels.
+ *
+ * @deprecated this is no longer used after V and will be removed.
  */
+@Deprecated(forRemoval = true)
 public class PanelSlicesAdapter
         extends RecyclerView.Adapter<PanelSlicesAdapter.SliceRowViewHolder> {
 
@@ -112,7 +115,10 @@
 
     /**
      * ViewHolder for binding Slices to SliceViews.
+     *
+     * @deprecated this is no longer used after V and will be removed.
      */
+    @Deprecated(forRemoval = true)
     public class SliceRowViewHolder extends RecyclerView.ViewHolder
             implements DividerItemDecoration.DividedViewHolder {
 
diff --git a/src/com/android/settings/panel/PanelSlicesLoaderCountdownLatch.java b/src/com/android/settings/panel/PanelSlicesLoaderCountdownLatch.java
index 6137d6c..49fd861 100644
--- a/src/com/android/settings/panel/PanelSlicesLoaderCountdownLatch.java
+++ b/src/com/android/settings/panel/PanelSlicesLoaderCountdownLatch.java
@@ -36,7 +36,10 @@
  *     {@link Uri}. Then check if all of the Slices have loaded with
  *     {@link #isPanelReadyToLoad()}, which will return {@code true} the first time after all
  *     Slices have loaded.
+ *
+ * @deprecated this is no longer used after V and will be removed.
  */
+@Deprecated(forRemoval = true)
 public class PanelSlicesLoaderCountdownLatch {
     private final Set<Uri> mLoadedSlices;
     private final CountDownLatch mCountDownLatch;
diff --git a/src/com/android/settings/panel/SettingsPanelActivity.java b/src/com/android/settings/panel/SettingsPanelActivity.java
index 60b8f88..d539c43 100644
--- a/src/com/android/settings/panel/SettingsPanelActivity.java
+++ b/src/com/android/settings/panel/SettingsPanelActivity.java
@@ -42,7 +42,10 @@
 
 /**
  * Dialog Activity to host Settings Slices.
+ *
+ * @deprecated this is no longer used after V and will be removed.
  */
+@Deprecated(forRemoval = true)
 public class SettingsPanelActivity extends FragmentActivity {
 
     private static final String TAG = "SettingsPanelActivity";
diff --git a/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java b/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java
index 623816a..0b35bd3 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java
@@ -19,6 +19,7 @@
 import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
 
 import static com.android.internal.app.SetScreenLockDialogActivity.LAUNCH_REASON_PRIVATE_SPACE_SETTINGS_ACCESS;
+import static com.android.settings.activityembedding.EmbeddedDeepLinkUtils.tryStartMultiPaneDeepLink;
 
 import android.app.ActivityOptions;
 import android.app.AlertDialog;
@@ -36,11 +37,12 @@
 import androidx.activity.result.ActivityResultLauncher;
 import androidx.activity.result.contract.ActivityResultContracts;
 import androidx.annotation.Nullable;
+import androidx.fragment.app.FragmentActivity;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.SetScreenLockDialogActivity;
 import com.android.settings.R;
-import com.android.settings.SettingsActivity;
+import com.android.settings.activityembedding.ActivityEmbeddingUtils;
 import com.android.settings.core.SubSettingLauncher;
 import com.android.settingslib.transition.SettingsTransitionHelper;
 
@@ -52,7 +54,7 @@
  * user to set a device lock if not set with an alert dialog. This can be launched using the intent
  * com.android.settings.action.OPEN_PRIVATE_SPACE_SETTINGS.
  */
-public class PrivateSpaceAuthenticationActivity extends SettingsActivity {
+public class PrivateSpaceAuthenticationActivity extends FragmentActivity {
     private static final String TAG = "PrivateSpaceAuthCheck";
     public static final String EXTRA_SHOW_PRIVATE_SPACE_UNLOCKED =
             "extra_show_private_space_unlocked";
@@ -76,31 +78,55 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        if (isFinishing()) {
+        if (!(Flags.allowPrivateProfile()
+                && android.multiuser.Flags.enablePrivateSpaceFeatures())) {
+            finish();
             return;
         }
 
-        if (Flags.allowPrivateProfile()
-                && android.multiuser.Flags.enablePrivateSpaceFeatures()) {
-            ThemeHelper.trySetDynamicColor(this);
-            mPrivateSpaceMaintainer =
-                    new Injector().injectPrivateSpaceMaintainer(getApplicationContext());
-            if (getKeyguardManager().isDeviceSecure()) {
-                if (savedInstanceState == null) {
-                    if (mPrivateSpaceMaintainer.doesPrivateSpaceExist()) {
-                        unlockAndLaunchPrivateSpaceSettings(this);
-                    } else {
-                        authenticatePrivateSpaceEntry();
-                    }
+        Intent intent = getIntent();
+        String highlightMenuKey = getString(R.string.menu_key_security);
+        if (shouldShowMultiPaneDeepLink(intent)
+                && tryStartMultiPaneDeepLink(this, intent, highlightMenuKey)) {
+            finish();
+            return;
+        }
+
+        ThemeHelper.trySetDynamicColor(this);
+        mPrivateSpaceMaintainer =
+                new Injector().injectPrivateSpaceMaintainer(getApplicationContext());
+        if (getKeyguardManager().isDeviceSecure()) {
+            if (savedInstanceState == null) {
+                if (mPrivateSpaceMaintainer.doesPrivateSpaceExist()) {
+                    unlockAndLaunchPrivateSpaceSettings(this);
+                } else {
+                    authenticatePrivateSpaceEntry();
                 }
-            } else {
-                promptToSetDeviceLock();
             }
         } else {
-            finish();
+            promptToSetDeviceLock();
         }
     }
 
+    private boolean shouldShowMultiPaneDeepLink(Intent intent) {
+        if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this)) {
+            return false;
+        }
+
+        // If the activity is task root, starting trampoline is needed in order to show two-pane UI.
+        // If FLAG_ACTIVITY_NEW_TASK is set, the activity will become the start of a new task on
+        // this history stack, so starting trampoline is needed in order to notify the homepage that
+        // the highlight key is changed.
+        if (!isTaskRoot() && (intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
+            return false;
+        }
+
+        // Only starts trampoline for deep links. Should return false for all the cases that
+        // Settings app starts SettingsActivity or SubSetting by itself.
+        // Other apps should send deep link intent which matches intent filter of the Activity.
+        return intent.getAction() != null;
+    }
+
     /** Starts private space setup flow or the PS settings page on device lock authentication */
     @VisibleForTesting
     public void onLockAuthentication(Context context) {
diff --git a/src/com/android/settings/sound/AudioSwitchPreferenceController.java b/src/com/android/settings/sound/AudioSwitchPreferenceController.java
index b785210..5938362 100644
--- a/src/com/android/settings/sound/AudioSwitchPreferenceController.java
+++ b/src/com/android/settings/sound/AudioSwitchPreferenceController.java
@@ -242,6 +242,10 @@
             return connectedDevices;
         }
         final List<BluetoothDevice> devices = leAudioProfile.getConnectedDevices();
+        if (devices == null) {
+          Log.d(TAG, "No connected LeAudioProfile devices");
+          return connectedDevices;
+        }
         for (BluetoothDevice device : devices) {
             if (device.isConnected() && isDeviceInCachedList(device)) {
                 connectedDevices.add(device);
diff --git a/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt b/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt
index 7b7c85d..98d8340 100644
--- a/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt
+++ b/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt
@@ -36,11 +36,11 @@
 import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalLifecycleOwner
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.res.vectorResource
 import androidx.lifecycle.LifecycleOwner
 import androidx.lifecycle.LifecycleRegistry
+import androidx.lifecycle.compose.LocalLifecycleOwner
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import androidx.lifecycle.viewmodel.compose.viewModel
 import com.android.settings.R
@@ -62,7 +62,6 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.conflate
 import kotlinx.coroutines.flow.flowOf
@@ -207,7 +206,7 @@
         }.collectAsStateWithLifecycle(initialValue = null)
 
         val mobileDataStateChanged by remember(mobileDataSelectedId.intValue) {
-            TelephonyRepository(context).isDataEnabled(mobileDataSelectedId.intValue)
+            TelephonyRepository(context).isDataEnabledFlow(mobileDataSelectedId.intValue)
         }.collectAsStateWithLifecycle(initialValue = false)
         val coroutineScope = rememberCoroutineScope()
 
@@ -410,11 +409,22 @@
     enabled: Boolean,
 ): Unit =
     withContext(Dispatchers.Default) {
-        Log.d(NetworkCellularGroupProvider.fileName, "setMobileData: $enabled")
+        Log.d(NetworkCellularGroupProvider.fileName, "setMobileData[$subId]: $enabled")
+
+        var targetSubId = subId
+        val activeSubIdList = subscriptionManager?.activeSubscriptionIdList
+        if (activeSubIdList?.size == 1) {
+            targetSubId = activeSubIdList[0]
+            Log.d(
+                NetworkCellularGroupProvider.fileName,
+                "There is only one sim in the device, correct dds as $targetSubId"
+            )
+        }
+
         if (enabled) {
-            Log.d(NetworkCellularGroupProvider.fileName, "setDefaultData: [$subId]")
-            subscriptionManager?.setDefaultDataSubId(subId)
+            Log.d(NetworkCellularGroupProvider.fileName, "setDefaultData: [$targetSubId]")
+            subscriptionManager?.setDefaultDataSubId(targetSubId)
         }
         TelephonyRepository(context)
-            .setMobileData(subId, enabled, wifiPickerTrackerHelper)
+            .setMobileData(targetSubId, enabled, wifiPickerTrackerHelper)
     }
\ No newline at end of file
diff --git a/src/com/android/settings/webview/WebViewAppPicker.java b/src/com/android/settings/webview/WebViewAppPicker.java
index 0060fa0..b1dfd14 100644
--- a/src/com/android/settings/webview/WebViewAppPicker.java
+++ b/src/com/android/settings/webview/WebViewAppPicker.java
@@ -33,14 +33,12 @@
 
 import com.android.settings.R;
 import com.android.settings.applications.defaultapps.DefaultAppPickerFragment;
-import com.android.settings.development.DeveloperOptionAwareMixin;
 import com.android.settingslib.applications.DefaultAppInfo;
 
 import java.util.ArrayList;
 import java.util.List;
 
-public class WebViewAppPicker extends DefaultAppPickerFragment implements
-        DeveloperOptionAwareMixin {
+public class WebViewAppPicker extends DefaultAppPickerFragment {
     private WebViewUpdateServiceWrapper mWebViewUpdateServiceWrapper;
 
     private WebViewUpdateServiceWrapper getWebViewUpdateServiceWrapper() {
diff --git a/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java b/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java
index 8084a48..4ba1205 100644
--- a/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java
+++ b/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java
@@ -34,6 +34,7 @@
 
 import com.android.settings.R;
 import com.android.settings.Utils;
+import com.android.settings.flags.Flags;
 import com.android.settings.homepage.SettingsHomepageActivity;
 
 /**
@@ -46,9 +47,13 @@
 
     static final long DELAY_HIGHLIGHT_DURATION_MILLIS = 100L;
     private static final int RES_NORMAL_BACKGROUND =
-            R.drawable.homepage_selectable_item_background;
+            Flags.homepageRevamp()
+                    ? R.drawable.homepage_selectable_item_background_v2
+                    : R.drawable.homepage_selectable_item_background;
     private static final int RES_HIGHLIGHTED_BACKGROUND =
-            R.drawable.homepage_highlighted_item_background;
+            Flags.homepageRevamp()
+                    ? R.drawable.homepage_highlighted_item_background_v2
+                    : R.drawable.homepage_highlighted_item_background;
 
     private final int mTitleColorNormal;
     private final int mTitleColorHighlight;
diff --git a/src/com/android/settings/widget/HomepagePreferenceLayoutHelper.java b/src/com/android/settings/widget/HomepagePreferenceLayoutHelper.java
index 6242e23..2251180 100644
--- a/src/com/android/settings/widget/HomepagePreferenceLayoutHelper.java
+++ b/src/com/android/settings/widget/HomepagePreferenceLayoutHelper.java
@@ -22,6 +22,7 @@
 import androidx.preference.PreferenceViewHolder;
 
 import com.android.settings.R;
+import com.android.settings.flags.Flags;
 
 /** Helper for homepage preference to manage layout. */
 public class HomepagePreferenceLayoutHelper {
@@ -39,7 +40,10 @@
     }
 
     public HomepagePreferenceLayoutHelper(Preference preference) {
-        preference.setLayoutResource(R.layout.homepage_preference);
+        preference.setLayoutResource(
+                Flags.homepageRevamp()
+                        ? R.layout.homepage_preference_v2
+                        : R.layout.homepage_preference);
     }
 
     /** Sets whether the icon should be visible */
diff --git a/src/com/android/settings/wifi/WifiConfigController2.java b/src/com/android/settings/wifi/WifiConfigController2.java
index 1606469..1e40568 100644
--- a/src/com/android/settings/wifi/WifiConfigController2.java
+++ b/src/com/android/settings/wifi/WifiConfigController2.java
@@ -514,12 +514,6 @@
         submit.setEnabled(isSubmittable());
     }
 
-    boolean isValidWepPassword(String password) {
-        // Checks for WEP40 and WEP104 password lengths (5 and 13 bytes)
-        int passwordLen = (password != null) ? password.length() : -1;
-        return passwordLen == 5 || passwordLen == 13;
-    }
-
     boolean isValidPsk(String password) {
         if (password.length() == 64 && password.matches("[0-9A-Fa-f]{64}")) {
             return true;
@@ -541,7 +535,7 @@
         boolean passwordInvalid = false;
         if (mPasswordView != null
                 && ((mWifiEntrySecurity == WifiEntry.SECURITY_WEP
-                        && !isValidWepPassword(mPasswordView.getText().toString()))
+                        && mPasswordView.length() == 0)
                     || (mWifiEntrySecurity == WifiEntry.SECURITY_PSK
                            && !isValidPsk(mPasswordView.getText().toString()))
                     || (mWifiEntrySecurity == WifiEntry.SECURITY_SAE
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothEnablerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothEnablerTest.java
index 4b82782..9052268 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothEnablerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothEnablerTest.java
@@ -34,6 +34,7 @@
 import android.content.IntentFilter;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.util.AndroidRuntimeException;
 import android.view.View;
 
 import androidx.preference.PreferenceViewHolder;
@@ -102,6 +103,19 @@
     }
 
     @Test
+    public void onSwitchToggled_satelliteOn_showWarningDialog() {
+        mBluetoothEnabler.mIsSatelliteOn.set(true);
+
+        try {
+            mBluetoothEnabler.onSwitchToggled(true);
+        } catch (AndroidRuntimeException e) {
+            // Catch exception of starting activity .
+        }
+
+        verify(mContext).startActivity(any());
+    }
+
+    @Test
     public void onSwitchToggled_shouldLogActionWithSuppliedEvent() {
         // WHEN the switch is toggled...
         mBluetoothEnabler.onSwitchToggled(false);
diff --git a/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java
index ee00068..b2449da 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java
@@ -30,7 +30,7 @@
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
-import android.content.pm.PackageInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
 import android.media.AudioManager;
@@ -44,7 +44,6 @@
 import com.android.settings.testutils.shadow.ShadowAudioManager;
 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
 import com.android.settings.testutils.shadow.ShadowCachedBluetoothDeviceManager;
-import com.android.settingslib.bluetooth.BluetoothUtils;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.flags.Flags;
 
@@ -68,7 +67,7 @@
 public class ConnectedBluetoothDeviceUpdaterTest {
 
     private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C";
-    private static final String FAKE_EXCLUSIVE_MANAGER_NAME = "com.fake.name";
+    private static final String TEST_EXCLUSIVE_MANAGER = "com.test.manager";
 
     @Rule
     public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@@ -355,13 +354,16 @@
 
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
-    public void update_notAllowedExclusiveManagedDevice_addDevice() {
+    public void update_exclusivelyManagedDevice_packageNotInstalled_addDevice()
+            throws Exception {
         mAudioManager.setMode(AudioManager.MODE_NORMAL);
         when(mBluetoothDeviceUpdater
                 .isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
         when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
         when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
-                FAKE_EXCLUSIVE_MANAGER_NAME.getBytes());
+                TEST_EXCLUSIVE_MANAGER.getBytes());
+        doThrow(new PackageManager.NameNotFoundException()).when(mPackageManager)
+                .getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0);
 
         mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
 
@@ -370,64 +372,39 @@
 
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
-    public void update_existingExclusivelyManagedDeviceWithPackageInstalled_removePreference()
+    public void update_exclusivelyManagedDevice_packageNotEnabled_addDevice()
             throws Exception {
-        final String exclusiveManagerName =
-                BluetoothUtils.getExclusiveManagers().stream().findAny().orElse(
-                        FAKE_EXCLUSIVE_MANAGER_NAME);
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.enabled = false;
         mAudioManager.setMode(AudioManager.MODE_NORMAL);
         when(mBluetoothDeviceUpdater
-                .isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
+            .isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
         when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
         when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
-                exclusiveManagerName.getBytes());
-        doReturn(new PackageInfo()).when(mPackageManager).getPackageInfo(exclusiveManagerName, 0);
-
-        mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
-
-        verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
-        verify(mBluetoothDeviceUpdater, never()).addPreference(mCachedBluetoothDevice);
-    }
-
-    @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
-    public void update_newExclusivelyManagedDeviceWithPackageInstalled_doNotAddPreference()
-            throws Exception {
-        final String exclusiveManagerName =
-                BluetoothUtils.getExclusiveManagers().stream().findAny().orElse(
-                        FAKE_EXCLUSIVE_MANAGER_NAME);
-        mAudioManager.setMode(AudioManager.MODE_NORMAL);
-        when(mBluetoothDeviceUpdater
-                .isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
-        when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
-        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
-                exclusiveManagerName.getBytes());
-        doReturn(new PackageInfo()).when(mPackageManager).getPackageInfo(exclusiveManagerName, 0);
-
-        mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
-
-        verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
-        verify(mBluetoothDeviceUpdater, never()).addPreference(mCachedBluetoothDevice);
-    }
-
-    @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
-    public void update_exclusivelyManagedDeviceWithoutPackageInstalled_addDevice()
-            throws Exception {
-        final String exclusiveManagerName =
-                BluetoothUtils.getExclusiveManagers().stream().findAny().orElse(
-                        FAKE_EXCLUSIVE_MANAGER_NAME);
-        mAudioManager.setMode(AudioManager.MODE_NORMAL);
-        when(mBluetoothDeviceUpdater
-                .isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
-        when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
-        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
-                exclusiveManagerName.getBytes());
-        doThrow(new PackageManager.NameNotFoundException()).when(mPackageManager).getPackageInfo(
-                exclusiveManagerName, 0);
+                TEST_EXCLUSIVE_MANAGER.getBytes());
+        doReturn(appInfo).when(mPackageManager).getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0);
 
         mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
 
         verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice);
     }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
+    public void update_exclusivelyManagedDevice_packageInstalledAndEnabled_removePreference()
+            throws Exception {
+        mAudioManager.setMode(AudioManager.MODE_NORMAL);
+        when(mBluetoothDeviceUpdater
+            .isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
+        when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
+                TEST_EXCLUSIVE_MANAGER.getBytes());
+        doReturn(new ApplicationInfo()).when(mPackageManager).getApplicationInfo(
+                TEST_EXCLUSIVE_MANAGER, 0);
+
+        mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
+
+        verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
+        verify(mBluetoothDeviceUpdater, never()).addPreference(mCachedBluetoothDevice);
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java
index 796120d..e2cf148 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java
@@ -29,7 +29,7 @@
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
-import android.content.pm.PackageInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
 import android.platform.test.annotations.RequiresFlagsDisabled;
@@ -41,7 +41,6 @@
 import com.android.settings.connecteddevice.DevicePreferenceCallback;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
-import com.android.settingslib.bluetooth.BluetoothUtils;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -66,7 +65,7 @@
 public class SavedBluetoothDeviceUpdaterTest {
 
     private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C";
-    private static final String FAKE_EXCLUSIVE_MANAGER_NAME = "com.fake.name";
+    private static final String TEST_EXCLUSIVE_MANAGER = "com.test.manager";
 
     @Rule
     public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@@ -339,42 +338,18 @@
 
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
-    public void update_notAllowedExclusivelyManagedDevice_addDevice() {
-        final Collection<CachedBluetoothDevice> cachedDevices = new ArrayList<>();
-        cachedDevices.add(mCachedBluetoothDevice);
-
-        when(mBluetoothAdapter.isEnabled()).thenReturn(true);
-        when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
-        when(mDeviceManager.getCachedDevicesCopy()).thenReturn(cachedDevices);
-        when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
-        when(mBluetoothDevice.isConnected()).thenReturn(false);
-        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
-                FAKE_EXCLUSIVE_MANAGER_NAME.getBytes());
-
-        mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
-
-        verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice,
-                BluetoothDevicePreference.SortType.TYPE_NO_SORT);
-    }
-
-    @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
-    public void update_existingExclusivelyManagedDeviceWithPackageInstalled_removePreference()
+    public void update_existingExclusivelyManagedDevice_packageEnabled_removePreference()
             throws Exception {
         final Collection<CachedBluetoothDevice> cachedDevices = new ArrayList<>();
-        final String exclusiveManagerName =
-                BluetoothUtils.getExclusiveManagers().stream().findAny().orElse(
-                        FAKE_EXCLUSIVE_MANAGER_NAME);
-
         when(mBluetoothAdapter.isEnabled()).thenReturn(true);
         when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
         when(mDeviceManager.getCachedDevicesCopy()).thenReturn(cachedDevices);
         when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
         when(mBluetoothDevice.isConnected()).thenReturn(false);
         when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
-                exclusiveManagerName.getBytes());
-
-        doReturn(new PackageInfo()).when(mPackageManager).getPackageInfo(exclusiveManagerName, 0);
+                TEST_EXCLUSIVE_MANAGER.getBytes());
+        doReturn(new ApplicationInfo()).when(mPackageManager).getApplicationInfo(
+                TEST_EXCLUSIVE_MANAGER, 0);
         mBluetoothDeviceUpdater.mPreferenceMap.put(mBluetoothDevice, mPreference);
 
         mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
@@ -386,23 +361,19 @@
 
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
-    public void update_newExclusivelyManagedDeviceWithPackageInstalled_doNotAddPreference()
+    public void update_newExclusivelyManagedDevice_packageEnabled_doNotAddPreference()
             throws Exception {
         final Collection<CachedBluetoothDevice> cachedDevices = new ArrayList<>();
-        final String exclusiveManagerName =
-                BluetoothUtils.getExclusiveManagers().stream().findAny().orElse(
-                        FAKE_EXCLUSIVE_MANAGER_NAME);
         cachedDevices.add(mCachedBluetoothDevice);
-
         when(mBluetoothAdapter.isEnabled()).thenReturn(true);
         when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
         when(mDeviceManager.getCachedDevicesCopy()).thenReturn(cachedDevices);
         when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
         when(mBluetoothDevice.isConnected()).thenReturn(false);
         when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
-                exclusiveManagerName.getBytes());
-
-        doReturn(new PackageInfo()).when(mPackageManager).getPackageInfo(exclusiveManagerName, 0);
+                TEST_EXCLUSIVE_MANAGER.getBytes());
+        doReturn(new ApplicationInfo()).when(mPackageManager).getApplicationInfo(
+                TEST_EXCLUSIVE_MANAGER, 0);
 
         mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
 
@@ -413,24 +384,42 @@
 
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
-    public void update_exclusivelyManagedDeviceWithoutPackageInstalled_addDevice()
+    public void update_exclusivelyManagedDevice_packageNotInstalled_addDevice()
             throws Exception {
         final Collection<CachedBluetoothDevice> cachedDevices = new ArrayList<>();
-        final String exclusiveManagerName =
-                BluetoothUtils.getExclusiveManagers().stream().findAny().orElse(
-                        FAKE_EXCLUSIVE_MANAGER_NAME);
         cachedDevices.add(mCachedBluetoothDevice);
-
         when(mBluetoothAdapter.isEnabled()).thenReturn(true);
         when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
         when(mDeviceManager.getCachedDevicesCopy()).thenReturn(cachedDevices);
         when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
         when(mBluetoothDevice.isConnected()).thenReturn(false);
         when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
-                exclusiveManagerName.getBytes());
+                TEST_EXCLUSIVE_MANAGER.getBytes());
+        doThrow(new PackageManager.NameNotFoundException()).when(mPackageManager)
+                .getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0);
 
-        doThrow(new PackageManager.NameNotFoundException()).when(mPackageManager).getPackageInfo(
-                exclusiveManagerName, 0);
+        mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
+
+        verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice,
+                BluetoothDevicePreference.SortType.TYPE_NO_SORT);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
+    public void update_exclusivelyManagedDevice_packageNotEnabled_addDevice()
+            throws Exception {
+        final Collection<CachedBluetoothDevice> cachedDevices = new ArrayList<>();
+        cachedDevices.add(mCachedBluetoothDevice);
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.enabled = false;
+        when(mBluetoothAdapter.isEnabled()).thenReturn(true);
+        when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
+        when(mDeviceManager.getCachedDevicesCopy()).thenReturn(cachedDevices);
+        when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        when(mBluetoothDevice.isConnected()).thenReturn(false);
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
+                TEST_EXCLUSIVE_MANAGER.getBytes());
+        doReturn(appInfo).when(mPackageManager).getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0);
 
         mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
 
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java
index f62fdb8..44a16f1 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java
@@ -50,6 +50,7 @@
 import com.android.settings.SettingsActivity;
 import com.android.settings.testutils.FakeFeatureFactory;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -58,6 +59,7 @@
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
+import java.util.Calendar;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -84,10 +86,13 @@
         MockitoAnnotations.initMocks(this);
         Locale.setDefault(new Locale("en_US"));
         org.robolectric.shadows.ShadowSettings.set24HourTimeFormat(false);
-        TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+        final TimeZone timeZone = TimeZone.getTimeZone("UTC");
+        TimeZone.setDefault(timeZone);
         DataProcessor.sTestSystemAppsPackageNames = Set.of();
         mFeatureFactory = FakeFeatureFactory.setupForTest();
         mContext = spy(RuntimeEnvironment.application);
+        BatteryLevelData.sTestCalendar = Calendar.getInstance();
+        BatteryLevelData.sTestCalendar.setTimeZone(timeZone);
         doReturn(mContext).when(mContext).getApplicationContext();
         doReturn(mUserManager).when(mContext).getSystemService(UserManager.class);
         doReturn(true).when(mUserManager).isUserUnlocked(anyInt());
@@ -115,6 +120,11 @@
                 new BatteryEntry.NameAndIcon("fakeName", /* icon= */ null, /* iconId= */ 1));
     }
 
+    @After
+    public void tearDown() {
+        BatteryLevelData.sTestCalendar = null;
+    }
+
     @Test
     public void onDestroy_activityIsChanging_clearBatteryEntryCache() {
         doReturn(true).when(mSettingsActivity).isChangingConfigurations();
@@ -141,7 +151,8 @@
         reset(mHourlyChartView);
         setupHourlyChartViewAnimationMock();
 
-        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(6));
+        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
+                createBatteryLevelData(/* numOfHours= */ 6, /* levelOffset= */ 0));
 
         verify(mDailyChartView, atLeastOnce()).setVisibility(View.GONE);
         // Ignore fast refresh ui from the data processor callback.
@@ -178,7 +189,8 @@
                         BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS,
                         mBatteryChartPreferenceController.mDailyChartLabelTextGenerator);
 
-        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60));
+        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
+                createBatteryLevelData(/* numOfHours= */ 60, /* levelOffset= */ 0));
 
         verify(mDailyChartView, atLeastOnce()).setVisibility(View.VISIBLE);
         verify(mViewPropertyAnimator, atLeastOnce()).alpha(0f);
@@ -283,7 +295,8 @@
     public void onBatteryLevelDataUpdate_oneDay_showHourlyChartOnly() {
         doReturn(View.GONE).when(mHourlyChartView).getVisibility();
 
-        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(6));
+        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
+                createBatteryLevelData(/* numOfHours= */ 6, /* levelOffset= */ 0));
 
         verify(mChartSummaryTextView).setVisibility(View.VISIBLE);
         verify(mDailyChartView).setVisibility(View.GONE);
@@ -295,7 +308,8 @@
         doReturn(View.GONE).when(mHourlyChartView).getVisibility();
 
         mBatteryChartPreferenceController.mDailyChartIndex = SELECTED_INDEX_ALL;
-        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60));
+        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
+                createBatteryLevelData(/* numOfHours= */ 60, /* levelOffset= */ 0));
 
         verify(mChartSummaryTextView).setVisibility(View.VISIBLE);
         verify(mDailyChartView).setVisibility(View.VISIBLE);
@@ -307,7 +321,8 @@
         doReturn(View.GONE).when(mHourlyChartView).getVisibility();
 
         mBatteryChartPreferenceController.mDailyChartIndex = 0;
-        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60));
+        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
+                createBatteryLevelData(/* numOfHours= */ 60, /* levelOffset= */ 0));
 
         verify(mChartSummaryTextView).setVisibility(View.VISIBLE);
         verify(mDailyChartView).setVisibility(View.VISIBLE);
@@ -379,7 +394,8 @@
 
     @Test
     public void selectedSlotText_selectAllDaysAllHours_returnNull() {
-        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60));
+        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
+                createBatteryLevelData(/* numOfHours= */ 60, /* levelOffset= */ 0));
         mBatteryChartPreferenceController.mDailyChartIndex = SELECTED_INDEX_ALL;
         mBatteryChartPreferenceController.mHourlyChartIndex = SELECTED_INDEX_ALL;
 
@@ -390,7 +406,8 @@
 
     @Test
     public void selectedSlotText_onlyOneDayDataSelectAllHours_returnNull() {
-        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(6));
+        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
+                createBatteryLevelData(/* numOfHours= */ 6, /* levelOffset= */ 0));
         mBatteryChartPreferenceController.mDailyChartIndex = 0;
         mBatteryChartPreferenceController.mHourlyChartIndex = SELECTED_INDEX_ALL;
 
@@ -401,7 +418,8 @@
 
     @Test
     public void selectedSlotText_selectADayAllHours_onlyDayText() {
-        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60));
+        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
+                createBatteryLevelData(/* numOfHours= */ 60, /* levelOffset= */ 0));
         mBatteryChartPreferenceController.mDailyChartIndex = 1;
         mBatteryChartPreferenceController.mHourlyChartIndex = SELECTED_INDEX_ALL;
 
@@ -412,7 +430,8 @@
 
     @Test
     public void selectedSlotText_onlyOneDayDataSelectAnHour_onlyHourText() {
-        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(6));
+        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
+                createBatteryLevelData(/* numOfHours= */ 6, /* levelOffset= */ 0));
         mBatteryChartPreferenceController.mDailyChartIndex = 0;
         mBatteryChartPreferenceController.mHourlyChartIndex = 2;
 
@@ -426,7 +445,8 @@
 
     @Test
     public void selectedSlotText_SelectADayAnHour_dayAndHourText() {
-        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60));
+        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
+                createBatteryLevelData(/* numOfHours= */ 60, /* levelOffset= */ 0));
         mBatteryChartPreferenceController.mDailyChartIndex = 1;
         mBatteryChartPreferenceController.mHourlyChartIndex = 8;
 
@@ -439,8 +459,9 @@
     }
 
     @Test
-    public void selectedSlotText_selectFirstSlot_withMinuteText() {
-        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(6));
+    public void selectedSlotText_selectFirstSlotAfterFullCharged_withMinuteText() {
+        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
+                createBatteryLevelData(/* numOfHours= */ 6, /* levelOffset= */ 0));
         mBatteryChartPreferenceController.mDailyChartIndex = 0;
         mBatteryChartPreferenceController.mHourlyChartIndex = 0;
 
@@ -453,8 +474,28 @@
     }
 
     @Test
+    public void selectedSlotText_selectFirstSlotAfterTimeUpdated_withMinuteText() {
+        BatteryLevelData batteryLevelData =
+                createBatteryLevelData(/* numOfHours= */ 6, /* levelOffset= */ 10);
+        assertThat(batteryLevelData.getHourlyBatteryLevelsPerDay().get(0).isStartTimestamp())
+                .isTrue();
+        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
+                createBatteryLevelData(/* numOfHours= */ 6, /* levelOffset= */ 10));
+        mBatteryChartPreferenceController.mDailyChartIndex = 0;
+        mBatteryChartPreferenceController.mHourlyChartIndex = 0;
+
+        assertThat(mBatteryChartPreferenceController.getSlotInformation(false))
+                .isEqualTo("7:01 AM - 8 AM");
+        assertThat(mBatteryChartPreferenceController.getSlotInformation(true))
+                .isEqualTo("7:01 AM to 8 AM");
+        assertThat(mBatteryChartPreferenceController.getBatteryLevelPercentageInfo())
+                .isEqualTo("Battery level percentage from 90% to 89%");
+    }
+
+    @Test
     public void selectedSlotText_selectLastSlot_withNowText() {
-        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(6));
+        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
+                createBatteryLevelData(/* numOfHours= */ 6, /* levelOffset= */ 0));
         mBatteryChartPreferenceController.mDailyChartIndex = 0;
         mBatteryChartPreferenceController.mHourlyChartIndex = 3;
 
@@ -468,7 +509,8 @@
 
     @Test
     public void selectedSlotText_selectOnlySlot_withMinuteAndNowText() {
-        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(1));
+        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
+                createBatteryLevelData(/* numOfHours= */ 1, /* levelOffset= */ 0));
         mBatteryChartPreferenceController.mDailyChartIndex = 0;
         mBatteryChartPreferenceController.mHourlyChartIndex = 0;
 
@@ -493,7 +535,8 @@
         mBatteryChartPreferenceController.mHourlyChartIndex = -1;
 
         mBatteryChartPreferenceController.onCreate(bundle);
-        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(25));
+        mBatteryChartPreferenceController.onBatteryLevelDataUpdate(
+                createBatteryLevelData(/* numOfHours= */ 25, /* levelOffset= */ 0));
 
         assertThat(mBatteryChartPreferenceController.mDailyChartIndex)
                 .isEqualTo(expectedDailyIndex);
@@ -503,7 +546,8 @@
 
     @Test
     public void getTotalHours_getExpectedResult() {
-        BatteryLevelData batteryLevelData = createBatteryLevelData(60);
+        BatteryLevelData batteryLevelData =
+                createBatteryLevelData(/* numOfHours= */ 60, /* levelOffset= */ 0);
 
         final int totalHour = BatteryChartPreferenceController.getTotalHours(batteryLevelData);
 
@@ -516,10 +560,10 @@
         return 1619247600000L + index * DateUtils.HOUR_IN_MILLIS;
     }
 
-    private static BatteryLevelData createBatteryLevelData(int numOfHours) {
+    private static BatteryLevelData createBatteryLevelData(int numOfHours, int levelOffset) {
         Map<Long, Integer> batteryLevelMap = new ArrayMap<>();
         for (int index = 0; index < numOfHours; index += 2) {
-            final Integer level = 100 - index;
+            final Integer level = 100 - index - levelOffset;
             Long timestamp = generateTimestamp(index);
             if (index == 0) {
                 timestamp += DateUtils.MINUTE_IN_MILLIS;
@@ -529,6 +573,8 @@
         }
         long current = generateTimestamp(numOfHours - 1) + DateUtils.MINUTE_IN_MILLIS * 2;
         batteryLevelMap.put(current, 66);
+
+        BatteryLevelData.sTestCalendar.setTimeInMillis(current);
         DataProcessor.sTestCurrentTimeMillis = current;
         return new BatteryLevelData(batteryLevelMap);
     }
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 df330a3..545f773 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java
@@ -35,7 +35,6 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
@@ -64,9 +63,8 @@
 
         // Inserts fake data into database for testing.
         final BatteryStateDatabase database = BatteryTestUtils.setUpBatteryStateDatabase(mContext);
-        BatteryTestUtils.insertDataToBatteryStateTable(
-                mContext, Clock.systemUTC().millis(), "com.android.systemui");
         mDao = database.batteryStateDao();
+        mDao.clearAll();
         clearSharedPreferences();
     }
 
@@ -129,10 +127,13 @@
         assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNull();
     }
 
-    @Ignore("b/314921894")
     @Test
-    public void onReceive_withTimeChangedIntent_clearsAllDataAndRefreshesJob()
+    public void onReceive_withTimeChangedIntentSetEarlierTime_refreshesJob()
             throws InterruptedException {
+        BatteryTestUtils.insertDataToBatteryStateTable(
+                mContext, Clock.systemUTC().millis() + 60000, "com.android.systemui");
+        assertThat(mDao.getAllAfter(0).size()).isEqualTo(1);
+
         mReceiver.onReceive(mContext, new Intent(Intent.ACTION_TIME_CHANGED));
 
         TimeUnit.MILLISECONDS.sleep(100);
@@ -141,6 +142,52 @@
     }
 
     @Test
+    public void onReceive_withTimeChangedIntentSetLaterTime_clearNoDataAndRefreshesJob()
+            throws InterruptedException {
+        BatteryTestUtils.insertDataToBatteryStateTable(
+                mContext, Clock.systemUTC().millis() - 60000, "com.android.systemui");
+        assertThat(mDao.getAllAfter(0).size()).isEqualTo(1);
+
+        mReceiver.onReceive(mContext, new Intent(Intent.ACTION_TIME_CHANGED));
+
+        TimeUnit.MILLISECONDS.sleep(100);
+        assertThat(mDao.getAllAfter(0).size()).isEqualTo(1);
+        assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNotNull();
+    }
+
+    @Test
+    public void onReceive_withTimeFormatChangedIntent_skipRefreshJob() throws InterruptedException {
+        BatteryTestUtils.insertDataToBatteryStateTable(
+                mContext, Clock.systemUTC().millis() + 60000, "com.android.systemui");
+        assertThat(mDao.getAllAfter(0).size()).isEqualTo(1);
+
+        mReceiver.onReceive(
+                mContext,
+                new Intent(Intent.EXTRA_INTENT)
+                        .putExtra(
+                                Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT,
+                                Intent.EXTRA_TIME_PREF_VALUE_USE_12_HOUR));
+
+        TimeUnit.MILLISECONDS.sleep(100);
+        assertThat(mDao.getAllAfter(0).size()).isEqualTo(1);
+        assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNull();
+    }
+
+    @Test
+    public void onReceive_withTimeZoneChangedIntent_clearAllDataAndRefreshesJob()
+            throws InterruptedException {
+        BatteryTestUtils.insertDataToBatteryStateTable(
+                mContext, Clock.systemUTC().millis(), "com.android.systemui");
+        assertThat(mDao.getAllAfter(0).size()).isEqualTo(1);
+
+        mReceiver.onReceive(mContext, new Intent(Intent.ACTION_TIMEZONE_CHANGED));
+
+        TimeUnit.MILLISECONDS.sleep(100);
+        assertThat(mDao.getAllAfter(0)).isEmpty();
+        assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNotNull();
+    }
+
+    @Test
     public void invokeJobRecheck_broadcastsIntent() {
         BootBroadcastReceiver.invokeJobRecheck(mContext);
 
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 7faca0d..6042801 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java
@@ -170,7 +170,8 @@
         final Map<Long, Integer> batteryLevelMap1 =
                 Map.of(timestamps1.get(0), 100, timestamps1.get(1), 100, timestamps1.get(2), 100);
         hourlyBatteryLevelsPerDay.add(
-                new BatteryLevelData.PeriodBatteryLevelData(batteryLevelMap1, timestamps1));
+                new BatteryLevelData.PeriodBatteryLevelData(
+                        batteryLevelMap1, timestamps1, /* isStartTimestamp= */ false));
         // Adds the day 2 data.
         hourlyBatteryLevelsPerDay.add(null);
         // Adds the day 3 data.
@@ -178,7 +179,8 @@
         final Map<Long, Integer> batteryLevelMap2 =
                 Map.of(timestamps2.get(0), 100, timestamps2.get(1), 100);
         hourlyBatteryLevelsPerDay.add(
-                new BatteryLevelData.PeriodBatteryLevelData(batteryLevelMap2, timestamps2));
+                new BatteryLevelData.PeriodBatteryLevelData(
+                        batteryLevelMap2, timestamps2, /* isStartTimestamp= */ false));
         // Fake current usage data.
         final UsageEvents.Event event1 =
                 getUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, /* timestamp= */ 1, packageName);
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 2897343..ae4c56d 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java
@@ -209,7 +209,8 @@
         final Map<Long, Integer> batteryLevelMap1 =
                 Map.of(timestamps1.get(0), 100, timestamps1.get(1), 100, timestamps1.get(2), 100);
         hourlyBatteryLevelsPerDay.add(
-                new BatteryLevelData.PeriodBatteryLevelData(batteryLevelMap1, timestamps1));
+                new BatteryLevelData.PeriodBatteryLevelData(
+                        batteryLevelMap1, timestamps1, /* isStartTimestamp= */ false));
         // Adds the day 2 data.
         hourlyBatteryLevelsPerDay.add(null);
         // Adds the day 3 data.
@@ -217,7 +218,8 @@
         final Map<Long, Integer> batteryLevelMap2 =
                 Map.of(timestamps2.get(0), 100, timestamps2.get(1), 100);
         hourlyBatteryLevelsPerDay.add(
-                new BatteryLevelData.PeriodBatteryLevelData(batteryLevelMap2, timestamps2));
+                new BatteryLevelData.PeriodBatteryLevelData(
+                        batteryLevelMap2, timestamps2, /* isStartTimestamp= */ false));
         final List<AppUsageEvent> appUsageEventList = new ArrayList<>();
         // Adds some events before the start timestamp.
         appUsageEventList.add(
@@ -365,7 +367,8 @@
         final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
                 new ArrayList<>();
         hourlyBatteryLevelsPerDay.add(
-                new BatteryLevelData.PeriodBatteryLevelData(new ArrayMap<>(), new ArrayList<>()));
+                new BatteryLevelData.PeriodBatteryLevelData(
+                        new ArrayMap<>(), new ArrayList<>(), /* isStartTimestamp= */ false));
         assertThat(
                         DataProcessor.generateAppUsagePeriodMap(
                                 mContext,
@@ -858,7 +861,8 @@
                 new ArrayList<>();
 
         hourlyBatteryLevelsPerDay.add(
-                new BatteryLevelData.PeriodBatteryLevelData(new ArrayMap<>(), new ArrayList<>()));
+                new BatteryLevelData.PeriodBatteryLevelData(
+                        new ArrayMap<>(), new ArrayList<>(), /* isStartTimestamp= */ false));
 
         assertThat(
                         DataProcessor.getBatteryDiffDataMap(
diff --git a/tests/robotests/src/com/android/settings/homepage/TopLevelSettingsTest.java b/tests/robotests/src/com/android/settings/homepage/TopLevelSettingsTest.java
index 44f44aa..36c48e3 100644
--- a/tests/robotests/src/com/android/settings/homepage/TopLevelSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/TopLevelSettingsTest.java
@@ -16,8 +16,6 @@
 
 package com.android.settings.homepage;
 
-import static com.google.common.truth.Truth.assertThat;
-
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.doReturn;
@@ -60,11 +58,6 @@
     }
 
     @Test
-    public void shouldForceRoundedIcon_true() {
-        assertThat(mSettings.shouldForceRoundedIcon()).isTrue();
-    }
-
-    @Test
     public void onCreatePreferences_shouldTintPreferenceIcon() {
         final Preference preference = new Preference(mContext);
         preference.setTitle(R.string.network_dashboard_title);
diff --git a/tests/robotests/src/com/android/settings/panel/FakePanelContent.java b/tests/robotests/src/com/android/settings/panel/FakePanelContent.java
index 06beb3e..17787cd 100644
--- a/tests/robotests/src/com/android/settings/panel/FakePanelContent.java
+++ b/tests/robotests/src/com/android/settings/panel/FakePanelContent.java
@@ -29,7 +29,10 @@
 
 /**
  * Fake PanelContent for testing.
+ *
+ * @deprecated this is no longer used after V and will be removed.
  */
+@Deprecated(forRemoval = true)
 public class FakePanelContent implements PanelContent {
 
     public static final String FAKE_ACTION = "fake_action";
diff --git a/tests/robotests/src/com/android/settings/panel/FakeSettingsPanelActivity.java b/tests/robotests/src/com/android/settings/panel/FakeSettingsPanelActivity.java
index ba763ce..fe19f28 100644
--- a/tests/robotests/src/com/android/settings/panel/FakeSettingsPanelActivity.java
+++ b/tests/robotests/src/com/android/settings/panel/FakeSettingsPanelActivity.java
@@ -19,6 +19,7 @@
 import android.content.ComponentName;
 import android.content.Intent;
 
+@Deprecated(forRemoval = true)
 public class FakeSettingsPanelActivity extends SettingsPanelActivity {
     @Override
     public ComponentName getCallingActivity() {
diff --git a/tests/robotests/src/com/android/settings/panel/PanelFragmentTest.java b/tests/robotests/src/com/android/settings/panel/PanelFragmentTest.java
index 42f3977..e77eeab 100644
--- a/tests/robotests/src/com/android/settings/panel/PanelFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/panel/PanelFragmentTest.java
@@ -54,6 +54,7 @@
 
 import java.util.Objects;
 
+@Deprecated(forRemoval = true)
 @Ignore("b/313576125")
 @RunWith(RobolectricTestRunner.class)
 @Config(shadows = {
diff --git a/tests/robotests/src/com/android/settings/panel/PanelSlicesAdapterTest.java b/tests/robotests/src/com/android/settings/panel/PanelSlicesAdapterTest.java
index 87a798a..e778cb8 100644
--- a/tests/robotests/src/com/android/settings/panel/PanelSlicesAdapterTest.java
+++ b/tests/robotests/src/com/android/settings/panel/PanelSlicesAdapterTest.java
@@ -67,6 +67,7 @@
 import java.util.LinkedHashMap;
 import java.util.Map;
 
+@Deprecated(forRemoval = true)
 @RunWith(RobolectricTestRunner.class)
 @Config(shadows = PanelSlicesAdapterTest.ShadowLayoutInflater.class)
 public class PanelSlicesAdapterTest {
diff --git a/tests/robotests/src/com/android/settings/panel/SettingsPanelActivityTest.java b/tests/robotests/src/com/android/settings/panel/SettingsPanelActivityTest.java
index e550284..4f03abb 100644
--- a/tests/robotests/src/com/android/settings/panel/SettingsPanelActivityTest.java
+++ b/tests/robotests/src/com/android/settings/panel/SettingsPanelActivityTest.java
@@ -59,6 +59,7 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.util.ReflectionHelpers;
 
+@Deprecated(forRemoval = true)
 @RunWith(RobolectricTestRunner.class)
 @Config(shadows = {
         com.android.settings.testutils.shadow.ShadowFragment.class,
diff --git a/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java
index a272d9c..91fd33d 100644
--- a/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java
@@ -517,6 +517,15 @@
     }
 
     @Test
+    public void getConnectedLeAudioDevices_nullConnectedDevices_returnEmptyList() {
+      when(mLeAudioProfile.getConnectedDevices()).thenReturn(null);
+
+      List<BluetoothDevice> connectedDevices = mController.getConnectedLeAudioDevices();
+
+      assertThat(connectedDevices.isEmpty()).isTrue();
+    }
+
+    @Test
     public void findActiveLeAudioDevice_noActiveDevice_returnNull() {
         mLeAudioActiveDevices.clear();
         when(mLeAudioProfile.getActiveDevices()).thenReturn(mLeAudioActiveDevices);
diff --git a/tests/robotests/src/com/android/settings/wifi/WifiConfigController2Test.java b/tests/robotests/src/com/android/settings/wifi/WifiConfigController2Test.java
index a531e3f..7d96496 100644
--- a/tests/robotests/src/com/android/settings/wifi/WifiConfigController2Test.java
+++ b/tests/robotests/src/com/android/settings/wifi/WifiConfigController2Test.java
@@ -203,39 +203,6 @@
     }
 
     @Test
-    public void isSubmittable_wepPasswordLength5_returnTrue() {
-        when(mWifiEntry.getSecurity()).thenReturn(WifiEntry.SECURITY_WEP);
-        createController(mWifiEntry, WifiConfigUiBase2.MODE_CONNECT, false);
-        final TextView password = mView.findViewById(R.id.password);
-        assertThat(password).isNotNull();
-        password.setText("12345");
-
-        assertThat(mController.isSubmittable()).isTrue();
-    }
-
-    @Test
-    public void isSubmittable_wepPasswordLength13_returnTrue() {
-        when(mWifiEntry.getSecurity()).thenReturn(WifiEntry.SECURITY_WEP);
-        createController(mWifiEntry, WifiConfigUiBase2.MODE_CONNECT, false);
-        final TextView password = mView.findViewById(R.id.password);
-        assertThat(password).isNotNull();
-        password.setText("1234567890123");
-
-        assertThat(mController.isSubmittable()).isTrue();
-    }
-
-    @Test
-    public void isSubmittable_wepPasswordLength1_returnFalse() {
-        when(mWifiEntry.getSecurity()).thenReturn(WifiEntry.SECURITY_WEP);
-        createController(mWifiEntry, WifiConfigUiBase2.MODE_CONNECT, false);
-        final TextView password = mView.findViewById(R.id.password);
-        assertThat(password).isNotNull();
-        password.setText("1");
-
-        assertThat(mController.isSubmittable()).isFalse();
-    }
-
-    @Test
     public void isSubmittable_longPsk_shouldReturnFalse() {
         createController(mWifiEntry, WifiConfigUiBase2.MODE_CONNECT, false);
         final TextView password = mView.findViewById(R.id.password);
diff --git a/tests/spa_unit/src/com/android/settings/datausage/BillingCyclePreferenceTest.kt b/tests/spa_unit/src/com/android/settings/datausage/BillingCyclePreferenceTest.kt
index 4bf3851..1db0d48 100644
--- a/tests/spa_unit/src/com/android/settings/datausage/BillingCyclePreferenceTest.kt
+++ b/tests/spa_unit/src/com/android/settings/datausage/BillingCyclePreferenceTest.kt
@@ -27,6 +27,8 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.settings.R
 import com.android.settings.datausage.lib.BillingCycleRepository
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flowOf
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -39,7 +41,9 @@
     @get:Rule
     val composeTestRule = createComposeRule()
 
-    private val mockBillingCycleRepository = mock<BillingCycleRepository>()
+    private val mockBillingCycleRepository = mock<BillingCycleRepository> {
+        on { isModifiableFlow(SUB_ID) } doReturn emptyFlow()
+    }
 
     private val context: Context = ApplicationProvider.getApplicationContext()
 
@@ -56,7 +60,7 @@
     @Test
     fun setTemplate_modifiable_enabled() {
         mockBillingCycleRepository.stub {
-            on { isModifiable(SUB_ID) } doReturn true
+            on { isModifiableFlow(SUB_ID) } doReturn flowOf(true)
         }
 
         setTemplate()
@@ -67,7 +71,7 @@
     @Test
     fun setTemplate_notModifiable_notEnabled() {
         mockBillingCycleRepository.stub {
-            on { isModifiable(SUB_ID) } doReturn false
+            on { isModifiableFlow(SUB_ID) } doReturn flowOf(false)
         }
 
         setTemplate()
diff --git a/tests/spa_unit/src/com/android/settings/datausage/lib/BillingCycleRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/datausage/lib/BillingCycleRepositoryTest.kt
index deaaf2d..22e5dfe 100644
--- a/tests/spa_unit/src/com/android/settings/datausage/lib/BillingCycleRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/datausage/lib/BillingCycleRepositoryTest.kt
@@ -22,8 +22,10 @@
 import android.telephony.TelephonyManager
 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.framework.common.userManager
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.runBlocking
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.kotlin.doReturn
@@ -55,43 +57,43 @@
     private val repository = BillingCycleRepository(context, mockNetworkManagementService)
 
     @Test
-    fun isModifiable_bandwidthControlDisabled_returnFalse() {
+    fun isModifiable_bandwidthControlDisabled_returnFalse() = runBlocking {
         whenever(mockNetworkManagementService.isBandwidthControlEnabled).thenReturn(false)
 
-        val modifiable = repository.isModifiable(SUB_ID)
+        val modifiable = repository.isModifiableFlow(SUB_ID).firstWithTimeoutOrNull()
 
         assertThat(modifiable).isFalse()
     }
 
     @Test
-    fun isModifiable_notAdminUser_returnFalse() {
+    fun isModifiable_notAdminUser_returnFalse() = runBlocking {
         whenever(mockUserManager.isAdminUser).thenReturn(false)
 
-        val modifiable = repository.isModifiable(SUB_ID)
+        val modifiable = repository.isModifiableFlow(SUB_ID).firstWithTimeoutOrNull()
 
         assertThat(modifiable).isFalse()
     }
 
     @Test
-    fun isModifiable_dataDisabled_returnFalse() {
+    fun isModifiable_dataDisabled_returnFalse() = runBlocking {
         whenever(
             mockTelephonyManager.isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER)
         ).thenReturn(false)
 
-        val modifiable = repository.isModifiable(SUB_ID)
+        val modifiable = repository.isModifiableFlow(SUB_ID).firstWithTimeoutOrNull()
 
         assertThat(modifiable).isFalse()
     }
 
     @Test
-    fun isModifiable_meetAllRequirements_returnTrue() {
+    fun isModifiable_meetAllRequirements_returnTrue() = runBlocking {
         whenever(mockNetworkManagementService.isBandwidthControlEnabled).thenReturn(true)
         whenever(mockUserManager.isAdminUser).thenReturn(true)
         whenever(
             mockTelephonyManager.isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER)
         ).thenReturn(true)
 
-        val modifiable = repository.isModifiable(SUB_ID)
+        val modifiable = repository.isModifiableFlow(SUB_ID).firstWithTimeoutOrNull()
 
         assertThat(modifiable).isTrue()
     }
diff --git a/tests/spa_unit/src/com/android/settings/network/MobileDataEnabledFlowTest.kt b/tests/spa_unit/src/com/android/settings/network/MobileDataEnabledFlowTest.kt
index 4862309..c4611ac 100644
--- a/tests/spa_unit/src/com/android/settings/network/MobileDataEnabledFlowTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/MobileDataEnabledFlowTest.kt
@@ -82,7 +82,7 @@
         delay(100)
         mobileDataEnabledForSubId = true
 
-        assertThat(listDeferred.await()).hasSize(2)
+        assertThat(listDeferred.await().size).isAtLeast(2)
     }
 
     private companion object {
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/DisableSimFooterPreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/DisableSimFooterPreferenceControllerTest.kt
new file mode 100644
index 0000000..0ddaa52
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/DisableSimFooterPreferenceControllerTest.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.telephony
+
+import android.content.Context
+import android.telephony.SubscriptionInfo
+import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.core.BasePreferenceController.AVAILABLE
+import com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
+
+@RunWith(AndroidJUnit4::class)
+class DisableSimFooterPreferenceControllerTest {
+
+    private val subscriptionInfo = mock<SubscriptionInfo> {
+        on { subscriptionId } doReturn SUB_ID
+    }
+
+    private var context: Context = ApplicationProvider.getApplicationContext()
+
+    private val mockSubscriptionRepository = mock<SubscriptionRepository> {
+        on { getSelectableSubscriptionInfoList() } doReturn listOf(subscriptionInfo)
+    }
+
+    private var controller = DisableSimFooterPreferenceController(
+        context = context,
+        preferenceKey = PREFERENCE_KEY,
+        subscriptionRepository = mockSubscriptionRepository,
+    ).apply { init(SUB_ID) }
+
+    @Test
+    fun getAvailabilityStatus_invalidId_notAvailable() {
+        val availabilityStatus = controller.getAvailabilityStatus(INVALID_SUBSCRIPTION_ID)
+
+        assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE)
+    }
+
+    @Test
+    fun getAvailabilityStatus_eSim_notAvailable() {
+        subscriptionInfo.stub {
+            on { isEmbedded } doReturn true
+        }
+
+        val availabilityStatus = controller.getAvailabilityStatus(SUB_ID)
+
+        assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE)
+    }
+
+    @Test
+    fun getAvailabilityStatus_pSimAndCannotDisable_available() {
+        mockSubscriptionRepository.stub {
+            on { canDisablePhysicalSubscription() } doReturn false
+        }
+        subscriptionInfo.stub {
+            on { isEmbedded } doReturn false
+        }
+
+        val availabilityStatus = controller.getAvailabilityStatus(SUB_ID)
+
+        assertThat(availabilityStatus).isEqualTo(AVAILABLE)
+    }
+
+    @Test
+    fun getAvailabilityStatus_pSimAndCanDisable_notAvailable() {
+        mockSubscriptionRepository.stub {
+            on { canDisablePhysicalSubscription() } doReturn true
+        }
+        subscriptionInfo.stub {
+            on { isEmbedded } doReturn false
+        }
+
+        val availabilityStatus = controller.getAvailabilityStatus(SUB_ID)
+
+        assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE)
+    }
+
+    private companion object {
+        const val PREFERENCE_KEY = "preference_key"
+        const val SUB_ID = 111
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyRepositoryTest.kt
index 6058935..65e8c47 100644
--- a/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyRepositoryTest.kt
@@ -93,7 +93,7 @@
 
     @Test
     fun isDataEnabled_invalidSub_returnFalse() = runBlocking {
-        val state = repository.isDataEnabled(
+        val state = repository.isDataEnabledFlow(
             subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID,
         )
 
@@ -108,9 +108,7 @@
             } doReturn true
         }
 
-        val state = repository.isDataEnabled(
-            subId = SUB_ID,
-        )
+        val state = repository.isDataEnabledFlow(subId = SUB_ID)
 
         assertThat(state.firstWithTimeoutOrNull()).isTrue()
     }
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/scan/NetworkScanRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/scan/NetworkScanRepositoryTest.kt
index 070c779..c0b918f 100644
--- a/tests/spa_unit/src/com/android/settings/network/telephony/scan/NetworkScanRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/scan/NetworkScanRepositoryTest.kt
@@ -32,9 +32,6 @@
 import android.telephony.TelephonyScanManager
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.settings.network.telephony.scan.NetworkScanRepository.NetworkScanCellInfos
-import com.android.settings.network.telephony.scan.NetworkScanRepository.NetworkScanComplete
-import com.android.settings.network.telephony.scan.NetworkScanRepository.NetworkScanError
 import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
 import com.android.settingslib.spa.testutils.toListWithTimeout
 import com.google.common.truth.Truth.assertThat
@@ -88,7 +85,12 @@
 
         callback?.onResults(cellInfos)
 
-        assertThat(listDeferred.await()).containsExactly(NetworkScanCellInfos(cellInfos))
+        assertThat(listDeferred.await()).containsExactly(
+            NetworkScanRepository.NetworkScanResult(
+                state = NetworkScanRepository.NetworkScanState.ACTIVE,
+                cellInfos = cellInfos,
+            )
+        )
     }
 
     @Test
@@ -100,7 +102,12 @@
 
         callback?.onComplete()
 
-        assertThat(listDeferred.await()).containsExactly(NetworkScanComplete)
+        assertThat(listDeferred.await()).containsExactly(
+            NetworkScanRepository.NetworkScanResult(
+                state = NetworkScanRepository.NetworkScanState.COMPLETE,
+                cellInfos = emptyList(),
+            )
+        )
     }
 
     @Test
@@ -112,7 +119,12 @@
 
         callback?.onError(1)
 
-        assertThat(listDeferred.await()).containsExactly(NetworkScanError(1))
+        assertThat(listDeferred.await()).containsExactly(
+            NetworkScanRepository.NetworkScanResult(
+                state = NetworkScanRepository.NetworkScanState.ERROR,
+                cellInfos = emptyList(),
+            )
+        )
     }
 
     @Test
@@ -133,12 +145,13 @@
         callback?.onResults(cellInfos)
 
         assertThat(listDeferred.await()).containsExactly(
-            NetworkScanCellInfos(
-                listOf(
+            NetworkScanRepository.NetworkScanResult(
+                state = NetworkScanRepository.NetworkScanState.ACTIVE,
+                cellInfos = listOf(
                     createCellInfoLte("123", false),
                     createCellInfoLte("124", true),
                     createCellInfoGsm("123", false),
-                )
+                ),
             )
         )
     }
@@ -162,8 +175,9 @@
         callback?.onResults(cellInfos)
 
         assertThat(listDeferred.await()).containsExactly(
-            NetworkScanCellInfos(
-                listOf(
+            NetworkScanRepository.NetworkScanResult(
+                state = NetworkScanRepository.NetworkScanState.ACTIVE,
+                cellInfos = listOf(
                     createCellInfoLte("123", false),
                     createCellInfoLte("123", true),
                     createCellInfoLte("124", false),
diff --git a/tests/unit/src/com/android/settings/conecteddevice/threadnetwork/ThreadNetworkPreferenceControllerTest.kt b/tests/unit/src/com/android/settings/conecteddevice/threadnetwork/ThreadNetworkPreferenceControllerTest.kt
deleted file mode 100644
index 976096c..0000000
--- a/tests/unit/src/com/android/settings/conecteddevice/threadnetwork/ThreadNetworkPreferenceControllerTest.kt
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- * Copyright (C) 2024 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.connecteddevice.threadnetwork
-
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import android.net.thread.ThreadNetworkController.STATE_DISABLED
-import android.net.thread.ThreadNetworkController.STATE_DISABLING
-import android.net.thread.ThreadNetworkController.STATE_ENABLED
-import android.net.thread.ThreadNetworkController.StateCallback
-import android.net.thread.ThreadNetworkException
-import android.os.OutcomeReceiver
-import android.platform.test.flag.junit.SetFlagsRule
-import android.provider.Settings
-import androidx.core.content.ContextCompat
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleOwner
-import androidx.preference.PreferenceManager
-import androidx.preference.SwitchPreference
-import androidx.test.core.app.ApplicationProvider
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.settings.R
-import com.android.settings.core.BasePreferenceController.AVAILABLE
-import com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE
-import com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING
-import com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE
-import com.android.settings.connecteddevice.threadnetwork.ThreadNetworkPreferenceController.BaseThreadNetworkController
-import com.android.settings.flags.Flags
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.any
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.spy
-import org.mockito.Mockito.verify
-import java.util.concurrent.Executor
-
-/** Unit tests for [ThreadNetworkPreferenceController].  */
-@RunWith(AndroidJUnit4::class)
-class ThreadNetworkPreferenceControllerTest {
-    @get:Rule
-    val mSetFlagsRule = SetFlagsRule()
-    private lateinit var context: Context
-    private lateinit var executor: Executor
-    private lateinit var controller: ThreadNetworkPreferenceController
-    private lateinit var fakeThreadNetworkController: FakeThreadNetworkController
-    private lateinit var preference: SwitchPreference
-    private val broadcastReceiverArgumentCaptor = ArgumentCaptor.forClass(
-        BroadcastReceiver::class.java
-    )
-
-    @Before
-    fun setUp() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_THREAD_SETTINGS_ENABLED)
-        context = spy(ApplicationProvider.getApplicationContext<Context>())
-        executor = ContextCompat.getMainExecutor(context)
-        fakeThreadNetworkController = FakeThreadNetworkController(executor)
-        controller = newControllerWithThreadFeatureSupported(true)
-        val preferenceManager = PreferenceManager(context)
-        val preferenceScreen = preferenceManager.createPreferenceScreen(context)
-        preference = SwitchPreference(context)
-        preference.key = "thread_network_settings"
-        preferenceScreen.addPreference(preference)
-        controller.displayPreference(preferenceScreen)
-
-        Settings.Global.putInt(context.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0)
-    }
-
-    private fun newControllerWithThreadFeatureSupported(
-        present: Boolean
-    ): ThreadNetworkPreferenceController {
-        return ThreadNetworkPreferenceController(
-            context,
-            "thread_network_settings" /* key */,
-            executor,
-            if (present) fakeThreadNetworkController else null
-        )
-    }
-
-    @Test
-    fun availabilityStatus_flagDisabled_returnsConditionallyUnavailable() {
-        mSetFlagsRule.disableFlags(Flags.FLAG_THREAD_SETTINGS_ENABLED)
-        assertThat(controller.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE)
-    }
-
-    @Test
-    fun availabilityStatus_airPlaneModeOn_returnsDisabledDependentSetting() {
-        Settings.Global.putInt(context.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 1)
-        controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
-
-        assertThat(controller.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING)
-    }
-
-    @Test
-    fun availabilityStatus_airPlaneModeOff_returnsAvailable() {
-        Settings.Global.putInt(context.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0)
-        controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
-
-        assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE)
-    }
-
-    @Test
-    fun availabilityStatus_threadFeatureNotSupported_returnsUnsupported() {
-        controller = newControllerWithThreadFeatureSupported(false)
-        controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
-
-        assertThat(fakeThreadNetworkController.registeredStateCallback).isNull()
-        assertThat(controller.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE)
-    }
-
-    @Test
-    fun isChecked_threadSetEnabled_returnsTrue() {
-        fakeThreadNetworkController.setEnabled(true, executor) { }
-        controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
-
-        assertThat(controller.isChecked).isTrue()
-    }
-
-    @Test
-    fun isChecked_threadSetDisabled_returnsFalse() {
-        fakeThreadNetworkController.setEnabled(false, executor) { }
-        controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
-
-        assertThat(controller.isChecked).isFalse()
-    }
-
-    @Test
-    fun setChecked_setChecked_threadIsEnabled() {
-        controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
-
-        controller.setChecked(true)
-
-        assertThat(fakeThreadNetworkController.isEnabled).isTrue()
-    }
-
-    @Test
-    fun setChecked_setUnchecked_threadIsDisabled() {
-        controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
-
-        controller.setChecked(false)
-
-        assertThat(fakeThreadNetworkController.isEnabled).isFalse()
-    }
-
-    @Test
-    fun updatePreference_airPlaneModeOff_preferenceEnabled() {
-        Settings.Global.putInt(context.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0)
-        controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
-
-        assertThat(preference.isEnabled).isTrue()
-        assertThat(preference.summary).isEqualTo(
-            context.resources.getString(R.string.thread_network_settings_summary)
-        )
-    }
-
-    @Test
-    fun updatePreference_airPlaneModeOn_preferenceDisabled() {
-        Settings.Global.putInt(context.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 1)
-        controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
-
-        assertThat(preference.isEnabled).isFalse()
-        assertThat(preference.summary).isEqualTo(
-            context.resources.getString(R.string.thread_network_settings_summary_airplane_mode)
-        )
-    }
-
-    @Test
-    fun updatePreference_airPlaneModeTurnedOn_preferenceDisabled() {
-        Settings.Global.putInt(context.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0)
-        startControllerAndCaptureCallbacks()
-
-        Settings.Global.putInt(context.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 1)
-        broadcastReceiverArgumentCaptor.value.onReceive(context, Intent())
-
-        assertThat(preference.isEnabled).isFalse()
-        assertThat(preference.summary).isEqualTo(
-            context.resources.getString(R.string.thread_network_settings_summary_airplane_mode)
-        )
-    }
-
-    private fun startControllerAndCaptureCallbacks() {
-        controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
-        verify(context)!!.registerReceiver(broadcastReceiverArgumentCaptor.capture(), any())
-    }
-
-    private class FakeThreadNetworkController(private val executor: Executor) :
-        BaseThreadNetworkController {
-        var isEnabled = true
-            private set
-        var registeredStateCallback: StateCallback? = null
-            private set
-
-        override fun setEnabled(
-            enabled: Boolean,
-            executor: Executor,
-            receiver: OutcomeReceiver<Void?, ThreadNetworkException>
-        ) {
-            isEnabled = enabled
-            if (registeredStateCallback != null) {
-                if (!isEnabled) {
-                    executor.execute {
-                        registeredStateCallback!!.onThreadEnableStateChanged(
-                            STATE_DISABLING
-                        )
-                    }
-                    executor.execute {
-                        registeredStateCallback!!.onThreadEnableStateChanged(
-                            STATE_DISABLED
-                        )
-                    }
-                } else {
-                    executor.execute {
-                        registeredStateCallback!!.onThreadEnableStateChanged(
-                            STATE_ENABLED
-                        )
-                    }
-                }
-            }
-            executor.execute { receiver.onResult(null) }
-        }
-
-        override fun registerStateCallback(
-            executor: Executor,
-            callback: StateCallback
-        ) {
-            require(callback !== registeredStateCallback) { "callback is already registered" }
-            registeredStateCallback = callback
-            val enabledState =
-                if (isEnabled) STATE_ENABLED else STATE_DISABLED
-            executor.execute { registeredStateCallback!!.onThreadEnableStateChanged(enabledState) }
-        }
-
-        override fun unregisterStateCallback(callback: StateCallback) {
-            requireNotNull(registeredStateCallback) { "callback is already unregistered" }
-            registeredStateCallback = null
-        }
-    }
-}
diff --git a/tests/unit/src/com/android/settings/connecteddevice/threadnetwork/FakeThreadNetworkController.kt b/tests/unit/src/com/android/settings/connecteddevice/threadnetwork/FakeThreadNetworkController.kt
new file mode 100644
index 0000000..e30226e
--- /dev/null
+++ b/tests/unit/src/com/android/settings/connecteddevice/threadnetwork/FakeThreadNetworkController.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2024 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.connecteddevice.threadnetwork
+
+import android.net.thread.ThreadNetworkController
+import android.net.thread.ThreadNetworkException
+import android.os.OutcomeReceiver
+import java.util.concurrent.Executor
+
+/** A fake implementation of [BaseThreadNetworkController] for unit tests. */
+class FakeThreadNetworkController : BaseThreadNetworkController {
+    var isEnabled = false
+        private set
+    var registeredStateCallback: ThreadNetworkController.StateCallback? = null
+        private set
+
+    override fun setEnabled(
+        enabled: Boolean,
+        executor: Executor,
+        receiver: OutcomeReceiver<Void?, ThreadNetworkException>
+    ) {
+        isEnabled = enabled
+        if (registeredStateCallback != null) {
+            if (!isEnabled) {
+                executor.execute {
+                    registeredStateCallback!!.onThreadEnableStateChanged(
+                        ThreadNetworkController.STATE_DISABLING
+                    )
+                }
+                executor.execute {
+                    registeredStateCallback!!.onThreadEnableStateChanged(
+                        ThreadNetworkController.STATE_DISABLED
+                    )
+                }
+            } else {
+                executor.execute {
+                    registeredStateCallback!!.onThreadEnableStateChanged(
+                        ThreadNetworkController.STATE_ENABLED
+                    )
+                }
+            }
+        }
+        executor.execute { receiver.onResult(null) }
+    }
+
+    override fun registerStateCallback(
+        executor: Executor,
+        callback: ThreadNetworkController.StateCallback
+    ) {
+        require(callback !== registeredStateCallback) { "callback is already registered" }
+        registeredStateCallback = callback
+        val enabledState =
+            if (isEnabled) ThreadNetworkController.STATE_ENABLED else ThreadNetworkController.STATE_DISABLED
+        executor.execute { registeredStateCallback!!.onThreadEnableStateChanged(enabledState) }
+    }
+
+    override fun unregisterStateCallback(callback: ThreadNetworkController.StateCallback) {
+        requireNotNull(registeredStateCallback) { "callback is already unregistered" }
+        registeredStateCallback = null
+    }
+}
diff --git a/tests/unit/src/com/android/settings/conecteddevice/threadnetwork/OWNERS b/tests/unit/src/com/android/settings/connecteddevice/threadnetwork/OWNERS
similarity index 100%
rename from tests/unit/src/com/android/settings/conecteddevice/threadnetwork/OWNERS
rename to tests/unit/src/com/android/settings/connecteddevice/threadnetwork/OWNERS
diff --git a/tests/unit/src/com/android/settings/connecteddevice/threadnetwork/ThreadNetworkFragmentControllerTest.kt b/tests/unit/src/com/android/settings/connecteddevice/threadnetwork/ThreadNetworkFragmentControllerTest.kt
new file mode 100644
index 0000000..0d57daf
--- /dev/null
+++ b/tests/unit/src/com/android/settings/connecteddevice/threadnetwork/ThreadNetworkFragmentControllerTest.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2024 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.connecteddevice.threadnetwork
+
+import android.content.Context
+import android.platform.test.flag.junit.SetFlagsRule
+import androidx.core.content.ContextCompat
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.core.BasePreferenceController.AVAILABLE
+import com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE
+import com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE
+import com.android.settings.flags.Flags
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.spy
+import java.util.concurrent.Executor
+
+/** Unit tests for [ThreadNetworkFragmentController].  */
+@RunWith(AndroidJUnit4::class)
+class ThreadNetworkFragmentControllerTest {
+    @get:Rule
+    val mSetFlagsRule = SetFlagsRule()
+    private lateinit var context: Context
+    private lateinit var executor: Executor
+    private lateinit var controller: ThreadNetworkFragmentController
+    private lateinit var fakeThreadNetworkController: FakeThreadNetworkController
+
+    @Before
+    fun setUp() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_THREAD_SETTINGS_ENABLED)
+        context = spy(ApplicationProvider.getApplicationContext<Context>())
+        executor = ContextCompat.getMainExecutor(context)
+        fakeThreadNetworkController = FakeThreadNetworkController()
+        controller = newControllerWithThreadFeatureSupported(true)
+    }
+
+    private fun newControllerWithThreadFeatureSupported(
+        present: Boolean
+    ): ThreadNetworkFragmentController {
+        return ThreadNetworkFragmentController(
+            context,
+            "thread_network_settings" /* key */,
+            executor,
+            if (present) fakeThreadNetworkController else null
+        )
+    }
+
+    @Test
+    fun availabilityStatus_flagDisabled_returnsConditionallyUnavailable() {
+        mSetFlagsRule.disableFlags(Flags.FLAG_THREAD_SETTINGS_ENABLED)
+        startController(controller)
+
+        assertThat(controller.availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE)
+    }
+
+    @Test
+    fun availabilityStatus_threadFeatureNotSupported_returnsUnsupported() {
+        controller = newControllerWithThreadFeatureSupported(false)
+        startController(controller)
+
+        assertThat(fakeThreadNetworkController.registeredStateCallback).isNull()
+        assertThat(controller.availabilityStatus).isEqualTo(UNSUPPORTED_ON_DEVICE)
+    }
+
+    @Test
+    fun availabilityStatus_threadFeatureSupported_returnsAvailable() {
+        controller = newControllerWithThreadFeatureSupported(true)
+        startController(controller)
+
+        assertThat(controller.availabilityStatus).isEqualTo(AVAILABLE)
+    }
+
+    @Test
+    fun getSummary_ThreadIsEnabled_returnsOn() {
+        startController(controller)
+        fakeThreadNetworkController.setEnabled(true, executor) {}
+
+        assertThat(controller.summary).isEqualTo("On")
+    }
+
+    @Test
+    fun getSummary_ThreadIsDisabled_returnsOff() {
+        startController(controller)
+        fakeThreadNetworkController.setEnabled(false, executor) {}
+
+        assertThat(controller.summary).isEqualTo("Off")
+    }
+
+    private fun startController(controller: ThreadNetworkFragmentController) {
+        controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
+    }
+}
diff --git a/tests/unit/src/com/android/settings/connecteddevice/threadnetwork/ThreadNetworkToggleControllerTest.kt b/tests/unit/src/com/android/settings/connecteddevice/threadnetwork/ThreadNetworkToggleControllerTest.kt
new file mode 100644
index 0000000..04ebc92
--- /dev/null
+++ b/tests/unit/src/com/android/settings/connecteddevice/threadnetwork/ThreadNetworkToggleControllerTest.kt
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2024 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.connecteddevice.threadnetwork
+
+import android.content.Context
+import android.platform.test.flag.junit.SetFlagsRule
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.preference.PreferenceManager
+import androidx.preference.SwitchPreference
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE
+import com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE
+import com.android.settings.flags.Flags
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.spy
+import java.util.concurrent.Executor
+
+/** Unit tests for [ThreadNetworkToggleController].  */
+@RunWith(AndroidJUnit4::class)
+class ThreadNetworkToggleControllerTest {
+    @get:Rule
+    val mSetFlagsRule = SetFlagsRule()
+    private lateinit var context: Context
+    private lateinit var executor: Executor
+    private lateinit var controller: ThreadNetworkToggleController
+    private lateinit var fakeThreadNetworkController: FakeThreadNetworkController
+    private lateinit var preference: SwitchPreference
+
+    @Before
+    fun setUp() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_THREAD_SETTINGS_ENABLED)
+        context = spy(ApplicationProvider.getApplicationContext<Context>())
+        executor =  Executor { runnable: Runnable -> runnable.run() }
+        fakeThreadNetworkController = FakeThreadNetworkController()
+        controller = newControllerWithThreadFeatureSupported(true)
+        val preferenceManager = PreferenceManager(context)
+        val preferenceScreen = preferenceManager.createPreferenceScreen(context)
+        preference = SwitchPreference(context)
+        preference.key = "toggle_thread_network"
+        preferenceScreen.addPreference(preference)
+        controller.displayPreference(preferenceScreen)
+    }
+
+    private fun newControllerWithThreadFeatureSupported(
+        present: Boolean
+    ): ThreadNetworkToggleController {
+        return ThreadNetworkToggleController(
+            context,
+            "toggle_thread_network" /* key */,
+            executor,
+            if (present) fakeThreadNetworkController else null
+        )
+    }
+
+    @Test
+    fun availabilityStatus_flagDisabled_returnsConditionallyUnavailable() {
+        mSetFlagsRule.disableFlags(Flags.FLAG_THREAD_SETTINGS_ENABLED)
+        assertThat(controller.availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE)
+    }
+
+    @Test
+    fun availabilityStatus_threadFeatureNotSupported_returnsUnsupported() {
+        controller = newControllerWithThreadFeatureSupported(false)
+        startController(controller)
+
+        assertThat(fakeThreadNetworkController.registeredStateCallback).isNull()
+        assertThat(controller.availabilityStatus).isEqualTo(UNSUPPORTED_ON_DEVICE)
+    }
+
+    @Test
+    fun isChecked_threadSetEnabled_returnsTrue() {
+        fakeThreadNetworkController.setEnabled(true, executor) { }
+        startController(controller)
+
+        assertThat(controller.isChecked).isTrue()
+    }
+
+    @Test
+    fun isChecked_threadSetDisabled_returnsFalse() {
+        fakeThreadNetworkController.setEnabled(false, executor) { }
+        startController(controller)
+
+        assertThat(controller.isChecked).isFalse()
+    }
+
+    @Test
+    fun setChecked_setChecked_threadIsEnabled() {
+        startController(controller)
+
+        controller.setChecked(true)
+
+        assertThat(fakeThreadNetworkController.isEnabled).isTrue()
+    }
+
+    @Test
+    fun setChecked_setUnchecked_threadIsDisabled() {
+        startController(controller)
+
+        controller.setChecked(false)
+
+        assertThat(fakeThreadNetworkController.isEnabled).isFalse()
+    }
+
+    private fun startController(controller: ThreadNetworkToggleController) {
+        controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
+    }
+}
diff --git a/tests/unit/src/com/android/settings/network/telephony/DisableSimFooterPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/DisableSimFooterPreferenceControllerTest.java
deleted file mode 100644
index bbbee21..0000000
--- a/tests/unit/src/com/android/settings/network/telephony/DisableSimFooterPreferenceControllerTest.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.network.telephony;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.android.settings.network.SubscriptionUtil;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Arrays;
-
-@RunWith(AndroidJUnit4.class)
-public class DisableSimFooterPreferenceControllerTest {
-    private static final String PREF_KEY = "pref_key";
-    private static final int SUB_ID = 111;
-
-    @Mock
-    private SubscriptionInfo mInfo;
-
-    private Context mContext;
-    @Mock
-    private SubscriptionManager mSubscriptionManager;
-    private DisableSimFooterPreferenceController mController;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mContext = spy(ApplicationProvider.getApplicationContext());
-        when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
-        when(mSubscriptionManager.createForAllUserProfiles()).thenReturn(mSubscriptionManager);
-
-        when(mInfo.getSubscriptionId()).thenReturn(SUB_ID);
-        SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(mInfo));
-        mController = new DisableSimFooterPreferenceController(mContext, PREF_KEY);
-    }
-
-    @Test
-    public void isAvailable_noInit_notAvailable() {
-        assertThat(mController.isAvailable()).isFalse();
-    }
-
-    @Test
-    public void isAvailable_eSIM_notAvailable() {
-        when(mInfo.isEmbedded()).thenReturn(true);
-        mController.init(SUB_ID);
-        assertThat(mController.isAvailable()).isFalse();
-    }
-
-    @Test
-    public void isAvailable_pSIM_available_cannot_disable_pSIM() {
-        when(mInfo.isEmbedded()).thenReturn(false);
-        mController.init(SUB_ID);
-        doReturn(false).when(mSubscriptionManager).canDisablePhysicalSubscription();
-        assertThat(mController.isAvailable()).isTrue();
-    }
-
-    @Test
-    public void isAvailable_pSIM_available_can_disable_pSIM() {
-        when(mInfo.isEmbedded()).thenReturn(false);
-        mController.init(SUB_ID);
-        doReturn(true).when(mSubscriptionManager).canDisablePhysicalSubscription();
-        assertThat(mController.isAvailable()).isFalse();
-    }
-}
diff --git a/tests/unit/src/com/android/settings/network/telephony/NetworkSelectSettingsTest.java b/tests/unit/src/com/android/settings/network/telephony/NetworkSelectSettingsTest.java
index a4657ce..d71af84 100644
--- a/tests/unit/src/com/android/settings/network/telephony/NetworkSelectSettingsTest.java
+++ b/tests/unit/src/com/android/settings/network/telephony/NetworkSelectSettingsTest.java
@@ -44,8 +44,12 @@
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.core.app.ApplicationProvider;
 
+import com.android.settings.network.telephony.scan.NetworkScanRepository;
+import com.android.settings.network.telephony.scan.NetworkScanRepository.NetworkScanResult;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
+import com.google.common.collect.ImmutableList;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -163,8 +167,7 @@
         }
 
         @Override
-        protected NetworkOperatorPreference
-                createNetworkOperatorPreference(CellInfo cellInfo) {
+        protected NetworkOperatorPreference createNetworkOperatorPreference(CellInfo cellInfo) {
             NetworkOperatorPreference pref = super.createNetworkOperatorPreference(cellInfo);
             if (cellInfo == mTestEnv.mCellInfo1) {
                 pref.updateCell(cellInfo, mTestEnv.mCellId1);
@@ -183,9 +186,14 @@
     @Test
     @UiThreadTest
     public void updateAllPreferenceCategory_correctOrderingPreference() {
+        NetworkScanResult result = new NetworkScanResult(
+                NetworkScanRepository.NetworkScanState.COMPLETE,
+                ImmutableList.of(mCellInfo1, mCellInfo2));
         mNetworkSelectSettings.onCreateInitialization();
         mNetworkSelectSettings.enablePreferenceScreen(true);
-        mNetworkSelectSettings.scanResultHandler(Arrays.asList(mCellInfo1, mCellInfo2));
+
+        mNetworkSelectSettings.scanResultHandler(result);
+
         assertThat(mPreferenceCategory.getPreferenceCount()).isEqualTo(2);
         final NetworkOperatorPreference preference =
                 (NetworkOperatorPreference) mPreferenceCategory.getPreference(1);
diff --git a/tests/unit/src/com/android/settings/panel/PanelSlicesLoaderCountdownLatchTest.java b/tests/unit/src/com/android/settings/panel/PanelSlicesLoaderCountdownLatchTest.java
index 3794e00..e201f42 100644
--- a/tests/unit/src/com/android/settings/panel/PanelSlicesLoaderCountdownLatchTest.java
+++ b/tests/unit/src/com/android/settings/panel/PanelSlicesLoaderCountdownLatchTest.java
@@ -29,6 +29,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+@Deprecated(forRemoval = true)
 @RunWith(AndroidJUnit4.class)
 public class PanelSlicesLoaderCountdownLatchTest {
 
diff --git a/tests/unit/src/com/android/settings/privatespace/delete/PrivateSpaceDeletionProgressFragmentTest.java b/tests/unit/src/com/android/settings/privatespace/delete/PrivateSpaceDeletionProgressFragmentTest.java
index 9806540..6250540 100644
--- a/tests/unit/src/com/android/settings/privatespace/delete/PrivateSpaceDeletionProgressFragmentTest.java
+++ b/tests/unit/src/com/android/settings/privatespace/delete/PrivateSpaceDeletionProgressFragmentTest.java
@@ -64,6 +64,11 @@
     public void setup() {
         MockitoAnnotations.initMocks(this);
         mContext = ApplicationProvider.getApplicationContext();
+        final FakeFeatureFactory featureFactory = FakeFeatureFactory.setupForTest();
+        when(featureFactory.securityFeatureProvider.getLockPatternUtils(mContext))
+                .thenReturn(mLockPatternUtils);
+        doReturn(true).when(mLockPatternUtils).isSecure(anyInt());
+
         mFragment = new PrivateSpaceDeletionProgressFragment();
         PrivateSpaceDeletionProgressFragment.Injector injector =
                 new PrivateSpaceDeletionProgressFragment.Injector() {
@@ -74,10 +79,6 @@
                 };
         mPrivateSpaceMaintainer = PrivateSpaceMaintainer.getInstance(mContext);
         mFragment.setPrivateSpaceMaintainer(injector);
-        final FakeFeatureFactory featureFactory = FakeFeatureFactory.setupForTest();
-        when(featureFactory.securityFeatureProvider.getLockPatternUtils(mContext))
-                .thenReturn(mLockPatternUtils);
-        doReturn(true).when(mLockPatternUtils).isSecure(anyInt());
     }
 
     @After