Merge "Fix settings page flicker in two ways: 1. On create use UiBlocker as recommended by settings team 2. On resume only update the preferences list if the system setting has    changed." into tm-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 214d32a..9986e34 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1846,7 +1846,7 @@
                   android:exported="true"
                   android:clearTaskOnLaunch="true"
                   android:theme="@style/Theme.Settings.NoActionBar">
-            <intent-filter>
+            <intent-filter android:priority="1000">
                 <action android:name="android.app.action.ADD_DEVICE_ADMIN" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
diff --git a/res/layout/apps_filter_spinner.xml b/res/layout/apps_filter_spinner.xml
index fcdcb5e..dfd41d4 100644
--- a/res/layout/apps_filter_spinner.xml
+++ b/res/layout/apps_filter_spinner.xml
@@ -33,8 +33,8 @@
         style="?android:attr/borderlessButtonStyle"
         android:layout_width="56dp"
         android:layout_height="56dp"
-        android:layout_marginTop="12dp"
-        android:layout_toEndOf="@id/filter_spinner"
+        android:layout_centerHorizontal="true"
+        android:layout_toRightOf="@+id/filter_spinner"
         android:contentDescription="@string/configure"
         android:scaleType="center"
         android:src="@drawable/ic_apps_filter_settings_24dp"
diff --git a/res/layout/confirm_convert_fbe.xml b/res/layout/confirm_convert_fbe.xml
deleted file mode 100644
index 537c368..0000000
--- a/res/layout/confirm_convert_fbe.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:orientation="vertical"
-            android:layout_marginBottom="12dp" >
-
-        <TextView
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginStart="12dp"
-                android:layout_marginEnd="12dp"
-                android:layout_marginTop="12dp"
-                android:textSize="18sp"
-                android:text="@string/confirm_convert_to_fbe_warning" />
-
-        <LinearLayout
-                android:layout_width="wrap_content"
-                android:layout_height="fill_parent"
-                android:layout_gravity="center"
-                android:orientation="horizontal">
-            <Button
-                    android:id="@+id/button_confirm_convert_fbe"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_gravity="bottom"
-                    android:layout_marginBottom="12dp"
-                    android:text="@string/button_confirm_convert_fbe" />
-        </LinearLayout>
-
-</LinearLayout>
diff --git a/res/layout/convert_fbe.xml b/res/layout/convert_fbe.xml
deleted file mode 100644
index d1e0cea..0000000
--- a/res/layout/convert_fbe.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 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:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:orientation="vertical"
-            android:layout_marginBottom="12dp" >
-
-        <TextView
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginStart="@dimen/preference_no_icon_padding_start"
-                android:layout_marginEnd="12dp"
-                android:layout_marginTop="12dp"
-                android:textSize="18sp"
-                android:text="@string/convert_to_fbe_warning" />
-
-        <LinearLayout
-                android:layout_width="wrap_content"
-                android:layout_height="fill_parent"
-                android:layout_gravity="center"
-                android:orientation="horizontal">
-            <Button
-                    android:id="@+id/button_convert_fbe"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_gravity="bottom"
-                    android:layout_marginBottom="12dp"
-                    android:text="@string/button_convert_fbe" />
-        </LinearLayout>
-
-</LinearLayout>
diff --git a/res/layout/le_audio_bt_entity_header.xml b/res/layout/le_audio_bt_entity_header.xml
new file mode 100644
index 0000000..6e2a1e8
--- /dev/null
+++ b/res/layout/le_audio_bt_entity_header.xml
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2022 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/entity_header"
+    style="@style/EntityHeader"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_centerHorizontal="true"
+    android:gravity="center_horizontal"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/entity_header_title"
+        style="@style/TextAppearance.EntityHeaderTitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_horizontal"
+        android:singleLine="false"
+        android:ellipsize="marquee"
+        android:textDirection="locale"/>
+
+    <TextView
+        android:id="@+id/entity_header_summary"
+        style="@style/TextAppearance.EntityHeaderSummary"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_horizontal"
+        android:layout_marginTop="4dp"
+        android:singleLine="false"
+        android:ellipsize="marquee"
+        android:textDirection="locale"/>
+
+    <ImageView
+        android:id="@+id/entity_header_icon"
+        android:layout_width="72dp"
+        android:layout_height="72dp"
+        android:layout_marginTop="24dp"
+        android:scaleType="fitCenter"
+        android:antialias="true"/>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_horizontal"
+        android:gravity="center_vertical"
+        android:orientation="horizontal">
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="@dimen/le_bluetooth_battery_start_margin"
+            android:orientation="vertical">
+            <TextView
+                android:id="@+id/bt_battery_case_title"
+                style="@style/TextAppearance.EntityHeaderTitle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/le_bluetooth_battery_top_margin"
+                android:gravity="start|center_vertical"
+                android:ellipsize="end"
+                android:textDirection="locale"
+                android:text="@string/bluetooth_middle_name"
+                android:textSize="@dimen/advanced_bluetooth_header_title_text_size"
+                android:visibility="gone"/>
+            <TextView
+                android:id="@+id/bt_battery_left_title"
+                style="@style/TextAppearance.EntityHeaderTitle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/le_bluetooth_battery_top_margin"
+                android:gravity="start|center_vertical"
+                android:ellipsize="end"
+                android:textDirection="locale"
+                android:text="@string/bluetooth_left_name"
+                android:textSize="@dimen/advanced_bluetooth_header_title_text_size"/>
+            <TextView
+                android:id="@+id/bt_battery_right_title"
+                style="@style/TextAppearance.EntityHeaderTitle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/le_bluetooth_battery_top_margin"
+                android:gravity="start|center_vertical"
+                android:ellipsize="end"
+                android:textDirection="locale"
+                android:text="@string/bluetooth_right_name"
+                android:textSize="@dimen/advanced_bluetooth_header_title_text_size"/>
+        </LinearLayout>
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="@dimen/le_bluetooth_summary_start_margin"
+            android:orientation="vertical">
+            <TextView
+                android:id="@+id/bt_battery_case_summary"
+                style="@style/TextAppearance.EntityHeaderSummary"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/le_bluetooth_battery_top_margin"
+                android:padding="@dimen/le_bluetooth_summary_padding"
+                android:drawablePadding="@dimen/le_bluetooth_summary_drawable_padding"
+                android:visibility="gone"/>
+            <TextView
+                android:id="@+id/bt_battery_left_summary"
+                style="@style/TextAppearance.EntityHeaderSummary"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/le_bluetooth_battery_top_margin"
+                android:padding="@dimen/le_bluetooth_summary_padding"
+                android:drawablePadding="@dimen/le_bluetooth_summary_drawable_padding"/>
+            <TextView
+                android:id="@+id/bt_battery_right_summary"
+                style="@style/TextAppearance.EntityHeaderSummary"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/le_bluetooth_battery_top_margin"
+                android:padding="@dimen/le_bluetooth_summary_padding"
+                android:drawablePadding="@dimen/le_bluetooth_summary_drawable_padding"/>
+        </LinearLayout>
+    </LinearLayout>
+</LinearLayout>
diff --git a/res/raw/udfps_edge_hint_lottie.json b/res/raw/udfps_left_edge_hint_lottie.json
similarity index 100%
rename from res/raw/udfps_edge_hint_lottie.json
rename to res/raw/udfps_left_edge_hint_lottie.json
diff --git a/res/raw/udfps_edge_hint_lottie.json b/res/raw/udfps_right_edge_hint_lottie.json
similarity index 100%
copy from res/raw/udfps_edge_hint_lottie.json
copy to res/raw/udfps_right_edge_hint_lottie.json
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 469f201..867fecb 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -1626,4 +1626,24 @@
         <item>300000</item>
     </string-array>
 
+    <!-- Developer settings: ingress rate limit entries. [DO NOT TRANSLATE] -->
+    <string-array name="ingress_rate_limit_entries">
+        <item>@string/ingress_rate_limit_no_limit_entry</item>
+        <item>128kbps</item>
+        <item>256kbps</item>
+        <item>1Mbps</item>
+        <item>5Mbps</item>
+        <item>15Mbps</item>
+    </string-array>
+
+    <!-- Developer settings: ingress rate limit values. [DO NOT TRANSLATE] -->
+    <string-array name="ingress_rate_limit_values">
+        <item>-1</item> <!-- -1 codes for disabled -->
+        <item>16000</item> <!-- 128kbps == 16000B/s -->
+        <item>32000</item> <!-- 256kbps == 32000B/s -->
+        <item>125000</item> <!-- 1Mbps == 125000B/s -->
+        <item>625000</item> <!-- 5Mbps == 625000/s -->
+        <item>1875000</item> <!-- 15Mbps == 1875000/s -->
+    </string-array>
+
 </resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index 8933305..0d67de7 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -618,4 +618,6 @@
     <!-- Whether the dream setup activity should be enabled as part of setupwizard -->
     <bool name="dream_setup_supported">false</bool>
 
+    <!-- Whether to put the apps with system UID into system component bucket or not -->
+    <bool name="config_battery_combine_system_components">false</bool>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index c44a1cb..8a59da2 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -399,6 +399,13 @@
     <dimen name="advanced_bluetooth_battery_height">27.5dp</dimen>
     <dimen name="advanced_bluetooth_battery_right_margin">-4dp</dimen>
 
+    <!-- Header layout of LE audio bluetooth device at bluretooth device detalis -->
+    <dimen name="le_bluetooth_battery_top_margin">5dp</dimen>
+    <dimen name="le_bluetooth_battery_start_margin">10dp</dimen>
+    <dimen name="le_bluetooth_summary_drawable_padding">6dp</dimen>
+    <dimen name="le_bluetooth_summary_start_margin">20dp</dimen>
+    <dimen name="le_bluetooth_summary_padding">1.5dp</dimen>
+
     <!-- Developer option bluetooth settings dialog -->
     <dimen name="developer_option_dialog_margin_start">8dp</dimen>
     <dimen name="developer_option_dialog_margin_top">8dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index bd9cbbd..cd5f71a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -329,14 +329,16 @@
 
     <!-- UI debug setting: Disable Bluetooth A2DP hardware offload [CHAR LIMIT=none] -->
     <string name="bluetooth_disable_a2dp_hw_offload">Disable Bluetooth A2DP hardware offload</string>
-    <!-- UI debug setting: Disable Bluetooth A2DP hardware offload [CHAR LIMIT=none] -->
-    <string name="bluetooth_disable_a2dp_hw_offload_dialog_title">Restart Device?</string>
-    <!-- UI debug setting: Disable Bluetooth A2DP hardware offload [CHAR LIMIT=none] -->
-    <string name="bluetooth_disable_a2dp_hw_offload_dialog_message">You need to restart your device to change this setting.</string>
-    <!-- UI debug setting: Disable Bluetooth A2DP hardware offload [CHAR LIMIT=none] -->
-    <string name="bluetooth_disable_a2dp_hw_offload_dialog_confirm">Restart</string>
-    <!-- UI debug setting: Disable Bluetooth A2DP hardware offload [CHAR LIMIT=none] -->
-    <string name="bluetooth_disable_a2dp_hw_offload_dialog_cancel">Cancel</string>
+    <!-- UI debug setting: Disable Bluetooth LE AUDIO hardware offload [CHAR LIMIT=none] -->
+    <string name="bluetooth_disable_le_audio_hw_offload">Disable Bluetooth LE AUDIO hardware offload</string>
+    <!-- UI debug setting: Disable Bluetooth hardware offload [CHAR LIMIT=none] -->
+    <string name="bluetooth_disable_hw_offload_dialog_title">Restart Device?</string>
+    <!-- UI debug setting: Disable Bluetooth hardware offload [CHAR LIMIT=none] -->
+    <string name="bluetooth_disable_hw_offload_dialog_message">You need to restart your device to change this setting.</string>
+    <!-- UI debug setting: Disable Bluetooth hardware offload [CHAR LIMIT=none] -->
+    <string name="bluetooth_disable_hw_offload_dialog_confirm">Restart</string>
+    <!-- UI debug setting: Disable Bluetooth hardware offload [CHAR LIMIT=none] -->
+    <string name="bluetooth_disable_hw_offload_dialog_cancel">Cancel</string>
 
     <!-- Title for Bluetooth device group with media capability group [CHAR LIMIT=none]-->
     <string name="connected_device_media_device_title">Media devices</string>
@@ -5251,6 +5253,14 @@
     <string name="accessibility_magnification_switch_shortcut_positive_button">Switch to accessibility button</string>
     <!-- Title for the cancel button in accessibility switch shortcut dialog to keep the config shortcut value. [CHAR LIMIT=54] -->
     <string name="accessibility_magnification_switch_shortcut_negative_button">Use triple-tap</string>
+    <!-- Title for the accessibility magnification triple tap warning dialog. [CHAR LIMIT=48] -->
+    <string name="accessibility_magnification_triple_tap_warning_title">This may slow down your keyboard</string>
+    <!-- Message for the accessibility magnification triple tap warning dialog. [CHAR LIMIT=none] -->
+    <string name="accessibility_magnification_triple_tap_warning_message">When using triple-tap to magnify part of your screen, you may notice issues over the keyboard.\n\nTo avoid this, you can change your magnification shortcut from triple-tap to another option.\n<annotation id="link">Change setting</annotation></string>
+    <!-- Title for the continue button in accessibility triple tap warning dialog to confirm the action. [CHAR LIMIT=30] -->
+    <string name="accessibility_magnification_triple_tap_warning_positive_button">Continue anyway</string>
+    <!-- Title for the cancel button in accessibility triple tap warning dialog to go back to edit. [CHAR LIMIT=12] -->
+    <string name="accessibility_magnification_triple_tap_warning_negative_button">Cancel</string>
     <!-- Title for the accessibility preference screen to enable screen magnification settings. [CHAR LIMIT=35] -->
     <string name="accessibility_magnification_service_settings_title">Magnification settings</string>
     <!-- Title for the accessibility preference screen to enable triple-tap gesture screen magnification. [CHAR LIMIT=35] -->
@@ -5403,7 +5413,7 @@
     <!-- Description for the accessibility button in gesture navigation. Explain how this page works. [CHAR LIMIT=NONE] -->
     <string name="accessibility_button_gesture_description"><b>To get started</b>\n1. Go to accessibility settings\n2. Select a feature and tap the shortcut\n3. Choose whether you want to use a button or gesture to access the feature</string>
     <!-- Description for the accessibility button page. Explain how this page works. [CHAR LIMIT=NONE] -->
-    <string name="accessibility_button_description"><b>To get started</b>\n1. Go to accessibility settings\n2. Select a feature and tap the shortcut\n3. Choose the button to use to access the feature</string>
+    <string name="accessibility_button_description"><b>To get started</b>\n1. Go to accessibility settings\n2. Select a feature and tap the shortcut\n3. Choose the button to access the feature</string>
     <!-- Title for the button or gesture of the accessibility button. [CHAR LIMIT=35] -->
     <string name="accessibility_button_or_gesture_title">Use button or gesture</string>
     <!-- Title for the location of the accessibility button. [CHAR LIMIT=35] -->
@@ -7772,6 +7782,8 @@
     <string name="nfc_payment_btn_text_set_deault">Set default</string>
     <!-- Label of the Update button of the Update default payment app dialog [CHAR LIMIT=40] -->
     <string name="nfc_payment_btn_text_update">Update</string>
+    <!-- Summary text of the work apps in the default payment selection list [CHAR LIMIT=20]-->
+    <string name="nfc_work_text">Work</string>
     <!-- Restrictions settings --><skip/>
 
     <!-- Restriction settings title [CHAR LIMIT=35] -->
@@ -8292,7 +8304,7 @@
     <string name="keywords_sounds_and_notifications_interruptions">dont don\u2019t disturb, interrupt, interruption, break</string>
     <string name="keywords_app">RAM</string>
     <string name="keywords_location">nearby, location, history, reporting, GPS</string>
-    <string name="keywords_accounts">account, add an account, work profile, add account</string>
+    <string name="keywords_accounts">account, add an account, work profile, add account, remove, delete</string>
     <string name="keywords_users">restriction, restrict, restricted</string>
     <string name="keywords_keyboard_and_ime">text correction, correct, sound, vibrate, auto, language, gesture, suggest, suggestion, theme, offensive, word, type, emoji, international</string>
     <string name="keywords_reset_apps">reset, preferences, default</string>
@@ -9848,7 +9860,7 @@
     <string name="zen_mode_people_calls_messages_section_title">Who can interrupt</string>
 
     <!-- [CHAR LIMIT=NONE] Zen mode settings: people screen footer -->
-    <string name="zen_mode_people_footer" translatable="false">Even if messaging or calling apps can\u0027t notify you, people you choose here can still reach you through those apps</string>
+    <string name="zen_mode_people_footer">Even if messaging or calling apps can\u0027t notify you, people you choose here can still reach you through those apps</string>
 
     <!-- [CHAR LIMIT=40] Zen mode settings: Allow calls toggle title -->
     <string name="zen_mode_calls_title">Calls</string>
@@ -11592,11 +11604,6 @@
     <!-- Title for the See more preference item in Special app access settings [CHAR LIMIT=30] -->
     <string name="special_access_more">See more</string>
 
-    <!-- Developer option to convert to file encryption - final warning -->
-    <string name="confirm_convert_to_fbe_warning">Really wipe user data and convert to file encryption?</string>
-    <!-- Developer option to convert to file encryption - final button -->
-    <string name="button_confirm_convert_fbe">Wipe and convert</string>
-
     <!-- Reset rate-limiting in the system service ShortcutManager.  "ShortcutManager" is the name of a system service and not translatable.
     If the word "rate-limit" is hard to translate, use "Reset ShortcutManager API call limit" as the source text, which means
     the same thing in this context.
@@ -14035,4 +14042,13 @@
     <string name="bluetooth_details_head_tracking_title">Make audio more realistic</string>
     <!-- The summary of the head tracking [CHAR LIMIT=none] -->
     <string name="bluetooth_details_head_tracking_summary">Shift positioning of audio so it sounds more natural.</string>
+
+    <!-- Developer Settings: Title for network bandwidth ingress rate limit [CHAR LIMIT=none] -->
+    <string name="ingress_rate_limit_title">Network download rate limit</string>
+    <!-- Developer Settings: Summary for network bandwidth ingress rate limit [CHAR LIMIT=none] -->
+    <string name="ingress_rate_limit_summary">Configure the network bandwidth ingress rate limit which is applied to all networks that provide internet connectivity.</string>
+    <!-- Developer Settings: Dialog for network bandwidth ingress rate limit [CHAR LIMIT=none] -->
+    <string name="ingress_rate_limit_dialog_title">Configure network download rate limit</string>
+    <!-- Developer Settings: Dialog ListPreference option to disable network bandwidth ingress rate limit [CHAR LIMIT=none] -->
+    <string name="ingress_rate_limit_no_limit_entry">No limit</string>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index c3ed5b2..bd0aea1 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -432,12 +432,12 @@
     </style>
 
     <style name="LockPatternContainerStyle">
-        <item name="android:maxHeight">400dp</item>
-        <item name="android:maxWidth">420dp</item>
+        <item name="android:maxHeight">620dp</item>
+        <item name="android:maxWidth">620dp</item>
         <item name="android:minHeight">0dp</item>
         <item name="android:minWidth">0dp</item>
         <item name="android:paddingBottom">0dp</item>
-        <item name="android:paddingHorizontal">44dp</item>
+        <item name="android:paddingHorizontal">0dp</item>
         <item name="android:paddingTop">0dp</item>
     </style>
 
diff --git a/res/xml/bluetooth_device_details_fragment.xml b/res/xml/bluetooth_device_details_fragment.xml
index ef5a990..f6c0af6 100644
--- a/res/xml/bluetooth_device_details_fragment.xml
+++ b/res/xml/bluetooth_device_details_fragment.xml
@@ -34,6 +34,14 @@
         settings:searchable="false"
         settings:controller="com.android.settings.bluetooth.AdvancedBluetoothDetailsHeaderController"/>
 
+    <com.android.settingslib.widget.LayoutPreference
+        android:key="le_audio_bluetooth_device_header"
+        android:layout="@layout/le_audio_bt_entity_header"
+        android:selectable="false"
+        settings:allowDividerBelow="true"
+        settings:searchable="false"
+        settings:controller="com.android.settings.bluetooth.LeAudioBluetoothDetailsHeaderController"/>
+
     <com.android.settingslib.widget.ActionButtonsPreference
         android:key="action_buttons"
         settings:allowDividerBelow="true"/>
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index ed1b4d2..9867419 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -92,12 +92,6 @@
             android:summary="@string/runningservices_settings_summary"
             android:fragment="com.android.settings.applications.RunningServices" />
 
-        <Preference
-            android:key="convert_to_file_encryption"
-            android:title="@string/convert_to_file_encryption"
-            android:summary="@string/convert_to_file_encryption_enabled"
-            android:fragment="com.android.settings.applications.ConvertToFbe" />
-
         <com.android.settings.development.ColorModePreference
             android:key="picture_color_mode"
             android:title="@string/picture_color_mode"
@@ -297,6 +291,14 @@
             android:title="@string/tethering_hardware_offload"
             android:summary="@string/tethering_hardware_offload_summary" />
 
+        <ListPreference
+            android:key="ingress_rate_limit"
+            android:title="@string/ingress_rate_limit_title"
+            android:summary="@string/ingress_rate_limit_summary"
+            android:dialogTitle="@string/ingress_rate_limit_dialog_title"
+            android:entries="@array/ingress_rate_limit_entries"
+            android:entryValues="@array/ingress_rate_limit_values" />
+
         <com.android.settingslib.RestrictedPreference
             android:key="default_usb_configuration"
             android:fragment="com.android.settings.connecteddevice.usb.UsbDefaultFragment"
@@ -313,14 +315,13 @@
             android:summary="@string/bluetooth_disable_absolute_volume_summary" />
 
         <SwitchPreference
-            android:key="bluetooth_gabeldorsche_enable"
-            android:title="@string/bluetooth_enable_gabeldorsche"
-            android:summary="@string/bluetooth_enable_gabeldorsche_summary" />
-
-        <SwitchPreference
             android:key="bluetooth_disable_a2dp_hw_offload"
             android:title="@string/bluetooth_disable_a2dp_hw_offload" />
 
+        <SwitchPreference
+            android:key="bluetooth_disable_le_audio_hw_offload"
+            android:title="@string/bluetooth_disable_le_audio_hw_offload" />
+
         <ListPreference
             android:key="bluetooth_select_avrcp_version"
             android:title="@string/bluetooth_select_avrcp_version_string"
diff --git a/res/xml/zen_mode_sound_vibration_settings.xml b/res/xml/zen_mode_sound_vibration_settings.xml
index 7410716..2db42ac 100644
--- a/res/xml/zen_mode_sound_vibration_settings.xml
+++ b/res/xml/zen_mode_sound_vibration_settings.xml
@@ -19,36 +19,31 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:title="@string/zen_category_exceptions" >
 
-    <PreferenceCategory
-        android:key="zen_mode_settings_category_sound_vibration">
+    <!-- Alarms -->
+    <SwitchPreference
+        android:key="zen_mode_alarms"
+        android:title="@string/zen_mode_alarms"/>
 
-        <!-- Alarms -->
-        <SwitchPreference
-            android:key="zen_mode_alarms"
-            android:title="@string/zen_mode_alarms"/>
+    <!-- Media -->
+    <SwitchPreference
+        android:key="zen_mode_media"
+        android:title="@string/zen_mode_media"
+        android:summary="@string/zen_mode_media_summary"/>
 
-        <!-- Media -->
-        <SwitchPreference
-            android:key="zen_mode_media"
-            android:title="@string/zen_mode_media"
-            android:summary="@string/zen_mode_media_summary"/>
+    <!-- System -->
+    <SwitchPreference
+        android:key="zen_mode_system"
+        android:title="@string/zen_mode_system"
+        android:summary="@string/zen_mode_system_summary"/>
 
-        <!-- System -->
-        <SwitchPreference
-            android:key="zen_mode_system"
-            android:title="@string/zen_mode_system"
-            android:summary="@string/zen_mode_system_summary"/>
+    <!-- Reminders -->
+    <SwitchPreference
+        android:key="zen_mode_reminders"
+        android:title="@string/zen_mode_reminders"/>
 
-        <!-- Reminders -->
-        <SwitchPreference
-            android:key="zen_mode_reminders"
-            android:title="@string/zen_mode_reminders"/>
-
-        <!-- Events -->
-        <SwitchPreference
-            android:key="zen_mode_events"
-            android:title="@string/zen_mode_events"/>
-
-    </PreferenceCategory>
+    <!-- Events -->
+    <SwitchPreference
+        android:key="zen_mode_events"
+        android:title="@string/zen_mode_events"/>
 
 </PreferenceScreen>
diff --git a/src/com/android/settings/accessibility/AccessibilityFooterPreference.java b/src/com/android/settings/accessibility/AccessibilityFooterPreference.java
index 67b7827..fd9b6f8 100644
--- a/src/com/android/settings/accessibility/AccessibilityFooterPreference.java
+++ b/src/com/android/settings/accessibility/AccessibilityFooterPreference.java
@@ -25,9 +25,7 @@
 
 import com.android.settingslib.widget.FooterPreference;
 
-/**
- * A custom preference acting as footer of a page. Disables the movement method by default.
- */
+/** A custom preference acting as footer of a page. Disables the movement method by default. */
 public final class AccessibilityFooterPreference extends FooterPreference {
 
     private boolean mLinkEnabled;
@@ -46,12 +44,16 @@
 
         final TextView title = holder.itemView.findViewById(android.R.id.title);
         if (mLinkEnabled) {
-            // When a TextView has a movement method, it will set the view to clickable. This makes
-            // View.onTouchEvent always return true and consumes the touch event, essentially
-            // nullifying any return values of MovementMethod.onTouchEvent.
+            // When a TextView has a movement method, it will set the view to focusable and
+            // clickable. This makes View.onTouchEvent always return true and consumes the touch
+            // event, essentially nullifying any return values of MovementMethod.onTouchEvent.
             // To still allow propagating touch events to the parent when this view doesn't have
             // links, we only set the movement method here if the text contains links.
             title.setMovementMethod(LinkMovementMethod.getInstance());
+
+            // Groups of related title and link content by making the container focusable,
+            // then make all the children inside not focusable.
+            title.setFocusable(false);
         } else {
             title.setMovementMethod(/* movement= */ null);
         }
diff --git a/src/com/android/settings/accessibility/AccessibilityFooterPreferenceController.java b/src/com/android/settings/accessibility/AccessibilityFooterPreferenceController.java
index c22b11e..a422eb8 100644
--- a/src/com/android/settings/accessibility/AccessibilityFooterPreferenceController.java
+++ b/src/com/android/settings/accessibility/AccessibilityFooterPreferenceController.java
@@ -122,5 +122,8 @@
         } else {
             footerPreference.setLinkEnabled(false);
         }
+
+        // Grouping subcomponents to make more accessible.
+        footerPreference.setSelectable(false);
     }
 }
diff --git a/src/com/android/settings/accessibility/HighTextContrastPreferenceController.java b/src/com/android/settings/accessibility/HighTextContrastPreferenceController.java
index aad69b9..8c9d234 100644
--- a/src/com/android/settings/accessibility/HighTextContrastPreferenceController.java
+++ b/src/com/android/settings/accessibility/HighTextContrastPreferenceController.java
@@ -19,6 +19,9 @@
 import android.content.Context;
 import android.provider.Settings;
 
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+
 import com.android.settings.R;
 import com.android.settings.core.TogglePreferenceController;
 
@@ -27,6 +30,7 @@
  */
 public class HighTextContrastPreferenceController extends TogglePreferenceController implements
         TextReadingResetController.ResetStateListener {
+    private SwitchPreference mSwitchPreference;
 
     public HighTextContrastPreferenceController(Context context, String preferenceKey) {
         super(context, preferenceKey);
@@ -55,7 +59,14 @@
     }
 
     @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mSwitchPreference = screen.findPreference(getPreferenceKey());
+    }
+
+    @Override
     public void resetState() {
         setChecked(false);
+        updateState(mSwitchPreference);
     }
 }
diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
index ccdc78b..fd19376 100644
--- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
@@ -564,13 +564,6 @@
             createFooterPreference(getPreferenceScreen(), mDescription,
                     getString(R.string.accessibility_introduction_title, mPackageName));
         }
-
-        if (TextUtils.isEmpty(mHtmlDescription) && TextUtils.isEmpty(mDescription)) {
-            final CharSequence defaultDescription =
-                    getText(R.string.accessibility_service_default_description);
-            createFooterPreference(getPreferenceScreen(), defaultDescription,
-                    getString(R.string.accessibility_introduction_title, mPackageName));
-        }
     }
 
 
diff --git a/src/com/android/settings/applications/ConfirmConvertToFbe.java b/src/com/android/settings/applications/ConfirmConvertToFbe.java
deleted file mode 100644
index 35ddc6b..0000000
--- a/src/com/android/settings/applications/ConfirmConvertToFbe.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settings.applications;
-
-import android.app.settings.SettingsEnums;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-
-import com.android.settings.R;
-import com.android.settings.SettingsPreferenceFragment;
-
-public class ConfirmConvertToFbe extends SettingsPreferenceFragment {
-    static final String TAG = "ConfirmConvertToFBE";
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-                Bundle savedInstanceState) {
-        View rootView = inflater.inflate(R.layout.confirm_convert_fbe, null);
-
-        final Button button = (Button) rootView.findViewById(R.id.button_confirm_convert_fbe);
-        button.setOnClickListener(new View.OnClickListener() {
-            public void onClick(View v) {
-                Intent intent = new Intent(Intent.ACTION_FACTORY_RESET);
-                intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-                intent.setPackage("android");
-                intent.putExtra(Intent.EXTRA_REASON, "convert_fbe");
-                getActivity().sendBroadcast(intent);
-            }
-        });
-
-        return rootView;
-    }
-
-    @Override
-    public int getMetricsCategory() {
-        return SettingsEnums.CONVERT_FBE_CONFIRM;
-    }
-}
diff --git a/src/com/android/settings/applications/ConvertToFbe.java b/src/com/android/settings/applications/ConvertToFbe.java
deleted file mode 100644
index d470011..0000000
--- a/src/com/android/settings/applications/ConvertToFbe.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settings.applications;
-
-import android.annotation.Nullable;
-import android.app.Activity;
-import android.app.settings.SettingsEnums;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-
-import com.android.settings.R;
-import com.android.settings.core.InstrumentedFragment;
-import com.android.settings.core.SubSettingLauncher;
-import com.android.settings.password.ChooseLockSettingsHelper;
-
-/* Class to prompt for conversion of userdata to file based encryption
- */
-public class ConvertToFbe extends InstrumentedFragment {
-    static final String TAG = "ConvertToFBE";
-    private static final int KEYGUARD_REQUEST = 55;
-
-    private boolean runKeyguardConfirmation(int request) {
-        Resources res = getActivity().getResources();
-        final ChooseLockSettingsHelper.Builder builder =
-                new ChooseLockSettingsHelper.Builder(getActivity(), this);
-        return builder.setRequestCode(request)
-                .setTitle(res.getText(R.string.convert_to_file_encryption))
-                .show();
-    }
-
-    @Override
-    public void onCreate(@Nullable Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        getActivity().setTitle(R.string.convert_to_file_encryption);
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-        View rootView = inflater.inflate(R.layout.convert_fbe, null);
-
-        final Button button = rootView.findViewById(R.id.button_convert_fbe);
-        button.setOnClickListener(v -> {
-            if (!runKeyguardConfirmation(KEYGUARD_REQUEST)) {
-                convert();
-            }
-        });
-
-        return rootView;
-    }
-
-    @Override
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        super.onActivityResult(requestCode, resultCode, data);
-
-        if (requestCode != KEYGUARD_REQUEST) {
-            return;
-        }
-
-        // If the user entered a valid keyguard credential, start the conversion
-        // process
-        if (resultCode == Activity.RESULT_OK) {
-            convert();
-        }
-    }
-
-    private void convert() {
-        new SubSettingLauncher(getContext())
-                .setDestination(ConfirmConvertToFbe.class.getName())
-                .setTitleRes(R.string.convert_to_file_encryption)
-                .setSourceMetricsCategory(getMetricsCategory())
-                .launch();
-    }
-
-    @Override
-    public int getMetricsCategory() {
-        return SettingsEnums.CONVERT_FBE;
-    }
-}
diff --git a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java
index 8d29301..3cb22e0 100644
--- a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java
@@ -43,7 +43,6 @@
 import com.android.settings.fuelgauge.BatteryEntry;
 import com.android.settings.fuelgauge.BatteryUsageStatsLoader;
 import com.android.settings.fuelgauge.BatteryUtils;
-import com.android.settings.fuelgauge.ConvertUtils;
 import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.core.lifecycle.Lifecycle;
@@ -118,11 +117,7 @@
         }
 
         if (mBatteryDiffEntry != null) {
-            Log.i(TAG, "BatteryDiffEntry not null, launch : "
-                    + mBatteryDiffEntry.getPackageName()
-                    + " | uid : "
-                    + mBatteryDiffEntry.mBatteryHistEntry.mUid
-                    + " with DiffEntry data");
+            Log.i(TAG, "handlePreferenceTreeClick():\n" + mBatteryDiffEntry);
             AdvancedPowerUsageDetail.startBatteryDetailPage(
                     mParent.getActivity(),
                     mParent,
@@ -176,30 +171,11 @@
                 if (mPackageName == null) {
                     return null;
                 }
-                final List<BatteryDiffEntry> batteryDiffEntries =
-                        BatteryChartPreferenceController.getBatteryLast24HrUsageData(mContext);
-                if (batteryDiffEntries == null) {
-                    return null;
-                }
-                // Filter entry with consumer type to avoid system app,
-                // then use user id to divide normal app and work profile app,
-                // return target application from filter list by package name.
-                return batteryDiffEntries.stream()
-                        .filter(entry -> entry.mBatteryHistEntry.mConsumerType
-                                == ConvertUtils.CONSUMER_TYPE_UID_BATTERY)
-                        .filter(entry -> entry.mBatteryHistEntry.mUserId == mUserId)
-                        .filter(entry -> {
-                            if (mPackageName.equals(entry.getPackageName())) {
-                                Log.i(TAG, "Return target application: "
-                                        + entry.mBatteryHistEntry.mPackageName
-                                        + " | uid: " + entry.mBatteryHistEntry.mUid
-                                        + " | userId: " + entry.mBatteryHistEntry.mUserId);
-                                return true;
-                            }
-                            return false;
-                        })
-                        .findFirst()
-                        .orElse(/* other */null);
+                final BatteryDiffEntry entry =
+                        BatteryChartPreferenceController.getBatteryLast24HrUsageData(
+                                mContext, mPackageName, mUserId);
+                Log.d(TAG, "loadBatteryDiffEntries():\n" + entry);
+                return entry;
             }
 
             @Override
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
index d42e8f1..70140c4 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
@@ -81,9 +81,11 @@
     private static final int STAGE_CENTER = 0;
     private static final int STAGE_GUIDED = 1;
     private static final int STAGE_FINGERTIP = 2;
-    private static final int STAGE_EDGES = 3;
+    private static final int STAGE_LEFT_EDGE = 3;
+    private static final int STAGE_RIGHT_EDGE = 4;
 
-    @IntDef({STAGE_UNKNOWN, STAGE_CENTER, STAGE_GUIDED, STAGE_FINGERTIP, STAGE_EDGES})
+    @IntDef({STAGE_UNKNOWN, STAGE_CENTER, STAGE_GUIDED, STAGE_FINGERTIP, STAGE_LEFT_EDGE,
+            STAGE_RIGHT_EDGE})
     @Retention(RetentionPolicy.SOURCE)
     private @interface EnrollStage {}
 
@@ -132,7 +134,8 @@
     private boolean mIsAccessibilityEnabled;
     private LottieAnimationView mIllustrationLottie;
     private boolean mHaveShownUdfpsTipLottie;
-    private boolean mHaveShownUdfpsSideLottie;
+    private boolean mHaveShownUdfpsLeftEdgeLottie;
+    private boolean mHaveShownUdfpsRightEdgeLottie;
     private boolean mShouldShowLottie;
 
     private OrientationEventListener mOrientationEventListener;
@@ -372,12 +375,31 @@
                 }
                 break;
 
-            case STAGE_EDGES:
+            case STAGE_LEFT_EDGE:
                 setHeaderText(R.string.security_settings_udfps_enroll_edge_title);
-                if (!mHaveShownUdfpsSideLottie && mIllustrationLottie != null) {
-                    mHaveShownUdfpsSideLottie = true;
+                if (!mHaveShownUdfpsLeftEdgeLottie && mIllustrationLottie != null) {
+                    mHaveShownUdfpsLeftEdgeLottie = true;
                     setDescriptionText("");
-                    mIllustrationLottie.setAnimation(R.raw.udfps_edge_hint_lottie);
+                    mIllustrationLottie.setAnimation(R.raw.udfps_left_edge_hint_lottie);
+                    mIllustrationLottie.setVisibility(View.VISIBLE);
+                    mIllustrationLottie.playAnimation();
+                    mIllustrationLottie.setContentDescription(
+                            getString(R.string.security_settings_udfps_side_fingerprint_help));
+                } else if (mIllustrationLottie == null) {
+                    if (isStageHalfCompleted()) {
+                        setDescriptionText(
+                                R.string.security_settings_fingerprint_enroll_repeat_message);
+                    } else {
+                        setDescriptionText(R.string.security_settings_udfps_enroll_edge_message);
+                    }
+                }
+                break;
+            case STAGE_RIGHT_EDGE:
+                setHeaderText(R.string.security_settings_udfps_enroll_edge_title);
+                if (!mHaveShownUdfpsRightEdgeLottie && mIllustrationLottie != null) {
+                    mHaveShownUdfpsRightEdgeLottie = true;
+                    setDescriptionText("");
+                    mIllustrationLottie.setAnimation(R.raw.udfps_right_edge_hint_lottie);
                     mIllustrationLottie.setVisibility(View.VISIBLE);
                     mIllustrationLottie.playAnimation();
                     mIllustrationLottie.setContentDescription(
@@ -422,8 +444,10 @@
             return STAGE_GUIDED;
         } else if (progressSteps < getStageThresholdSteps(2)) {
             return STAGE_FINGERTIP;
+        } else if (progressSteps < getStageThresholdSteps(3)) {
+            return STAGE_LEFT_EDGE;
         } else {
-            return STAGE_EDGES;
+            return STAGE_RIGHT_EDGE;
         }
     }
 
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
index 405c090..66059e7 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
@@ -312,9 +312,10 @@
                 default:
                     FingerprintManager fpm = Utils.getFingerprintManagerOrNull(this);
                     int enrolled = fpm.getEnrolledFingerprints().size();
-                    int max = getResources().getInteger(
-                            com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
-                    if (enrolled >= max) {
+                    final List<FingerprintSensorPropertiesInternal> props =
+                            fpm.getSensorPropertiesInternal();
+                    final int maxEnrollments = props.get(0).maxEnrollmentsPerUser;
+                    if (enrolled >= maxEnrollments) {
                         finish();
                     } else {
                         // We came back from enrolling but it wasn't completed, start again.
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java
index 80a6f05..74e844a 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.View;
@@ -36,6 +37,7 @@
 import com.google.android.setupcompat.template.FooterButton;
 import com.google.android.setupcompat.util.WizardManagerHelper;
 
+import java.util.List;
 /**
  * Activity which concludes fingerprint enrollment.
  */
@@ -92,10 +94,11 @@
         final FingerprintManager fpm = Utils.getFingerprintManagerOrNull(this);
         boolean hideAddAnother = false;
         if (fpm != null) {
+            final List<FingerprintSensorPropertiesInternal> props =
+                    fpm.getSensorPropertiesInternal();
+            int maxEnrollments = props.get(0).maxEnrollmentsPerUser;
             int enrolled = fpm.getEnrolledFingerprints(mUserId).size();
-            int max = getResources().getInteger(
-                    com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
-            hideAddAnother = enrolled >= max;
+            hideAddAnother = enrolled >= maxEnrollments;
         }
         if (hideAddAnother) {
             // Don't show "Add" button if too many fingerprints already added
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
index abc6d53..a5832ea 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
@@ -457,8 +457,10 @@
             final Preference addPreference = findPreference(KEY_FINGERPRINT_ADD);
 
             /* Disable preference if too many fingerprints added */
-            final int max = getContext().getResources().getInteger(
-                    com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
+            final List<FingerprintSensorPropertiesInternal> props =
+                    mFingerprintManager.getSensorPropertiesInternal();
+            // This will need to be updated for devices with multiple fingerprint sensors
+            final int max = props.get(0).maxEnrollmentsPerUser;
             boolean tooMany = mFingerprintManager.getEnrolledFingerprints(mUserId).size() >= max;
             // retryFingerprint() will be called when remove finishes
             // need to disable enroll or have a way to determine if enroll is in progress
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java
index 9f5e78e..9c7aa58 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.bluetooth;
 
+import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
@@ -53,7 +54,10 @@
 
     @Override
     public boolean isAvailable() {
-        return !Utils.isAdvancedDetailsHeader(mCachedDevice.getDevice());
+        boolean hasLeAudio = mCachedDevice.getConnectableProfiles()
+                .stream()
+                .anyMatch(profile -> profile.getProfileId() == BluetoothProfile.LE_AUDIO);
+        return !Utils.isAdvancedDetailsHeader(mCachedDevice.getDevice()) && !hasLeAudio;
     }
 
     @Override
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
index aacf41f..b57ea92 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
@@ -16,10 +16,12 @@
 
 package com.android.settings.bluetooth;
 
+import android.bluetooth.BluetoothCsipSetCoordinator;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.text.TextUtils;
+import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
@@ -31,6 +33,7 @@
 import com.android.settings.R;
 import com.android.settingslib.bluetooth.A2dpProfile;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LeAudioProfile;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.LocalBluetoothProfile;
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
@@ -39,7 +42,10 @@
 import com.android.settingslib.bluetooth.PbapServerProfile;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * This class adds switches for toggling the individual profiles that a Bluetooth device
@@ -48,8 +54,11 @@
 public class BluetoothDetailsProfilesController extends BluetoothDetailsController
         implements Preference.OnPreferenceClickListener,
         LocalBluetoothProfileManager.ServiceListener {
+    private static final String TAG = "BtDetailsProfilesCtrl";
+
     private static final String KEY_PROFILES_GROUP = "bluetooth_profiles";
     private static final String KEY_BOTTOM_PREFERENCE = "bottom_preference";
+    private static final String HEADSET_CLIENT = "HEADSET_CLIENT";
     private static final int ORDINAL = 99;
 
     @VisibleForTesting
@@ -58,6 +67,9 @@
     private LocalBluetoothManager mManager;
     private LocalBluetoothProfileManager mProfileManager;
     private CachedBluetoothDevice mCachedDevice;
+    private List<CachedBluetoothDevice> mAllOfCachedDevices;
+    private Map<String, List<CachedBluetoothDevice>> mProfileDeviceMap =
+            new HashMap<String, List<CachedBluetoothDevice>>();
 
     @VisibleForTesting
     PreferenceCategory mProfilesContainer;
@@ -68,6 +80,7 @@
         mManager = manager;
         mProfileManager = mManager.getProfileManager();
         mCachedDevice = device;
+        mAllOfCachedDevices = getAllOfCachedBluetoothDevices();
         lifecycle.addObserver(this);
     }
 
@@ -100,11 +113,66 @@
 
     /**
      * Refreshes the state for an existing SwitchPreference for a profile.
+     * If the LeAudio profile is enabled on the LeAudio devices, then the SwitchPreferences of
+     * A2dp profile and Hfp profile are graied out.
      */
     private void refreshProfilePreference(SwitchPreference profilePref,
             LocalBluetoothProfile profile) {
         BluetoothDevice device = mCachedDevice.getDevice();
-        profilePref.setEnabled(!mCachedDevice.isBusy());
+        boolean isLeAudioEnabled = false;
+        if (profile instanceof A2dpProfile || HEADSET_CLIENT.equals(profile.toString())) {
+            LocalBluetoothProfile leAudio = mProfileManager.getLeAudioProfile();
+            if (leAudio != null) {
+                List<CachedBluetoothDevice> leAudioDeviceList = mProfileDeviceMap.get(
+                        leAudio.toString());
+                if (leAudioDeviceList != null
+                        && leAudioDeviceList.stream()
+                        .anyMatch(item -> leAudio.isEnabled(item.getDevice()))) {
+                    isLeAudioEnabled = true;
+                }
+            }
+            if (isLeAudioEnabled) {
+                // If the LeAudio profile is enabled on the LeAudio devices, then the
+                // SwitchPreferences of A2dp profile and Hfp profile are graied out.
+                profilePref.setEnabled(false);
+            } else {
+                List<CachedBluetoothDevice> deviceList = mProfileDeviceMap.get(
+                        profile.toString());
+                boolean isBusy = deviceList != null
+                        && deviceList.stream().anyMatch(item -> item.isBusy());
+                profilePref.setEnabled(!isBusy);
+            }
+        } else if (profile instanceof LeAudioProfile) {
+            List<CachedBluetoothDevice> leAudioDeviceList = mProfileDeviceMap.get(
+                    profile.toString());
+            boolean isLeAudioProfileEnable =
+                    leAudioDeviceList != null && leAudioDeviceList.stream().anyMatch(
+                            item -> profile.isEnabled(item.getDevice()));
+            boolean isBusy = leAudioDeviceList != null
+                    && leAudioDeviceList.stream().anyMatch(item -> item.isBusy());
+            if (isLeAudioProfileEnable && !isBusy) {
+                LocalBluetoothProfile a2dp = mProfileManager.getA2dpProfile();
+                LocalBluetoothProfile hfp = mProfileManager.getHfpClientProfile();
+                // If the LeAudio profile is enabled on the LeAudio devices, then the
+                // SwitchPreferences of A2dp profile and Hfp profile are graied out.
+                if (a2dp != null) {
+                    SwitchPreference pref = mProfilesContainer.findPreference(a2dp.toString());
+                    if (pref != null) {
+                        pref.setEnabled(false);
+                    }
+                }
+                if (hfp != null) {
+                    SwitchPreference pref = mProfilesContainer.findPreference(hfp.toString());
+                    if (pref != null) {
+                        pref.setEnabled(false);
+                    }
+                }
+            }
+            profilePref.setEnabled(!isBusy);
+        } else {
+            profilePref.setEnabled(!mCachedDevice.isBusy());
+        }
+
         if (profile instanceof MapProfile) {
             profilePref.setChecked(device.getMessageAccessPermission()
                     == BluetoothDevice.ACCESS_ALLOWED);
@@ -127,7 +195,7 @@
                     highQualityPref.setVisible(true);
                     highQualityPref.setTitle(a2dp.getHighQualityAudioOptionLabel(device));
                     highQualityPref.setChecked(a2dp.isHighQualityAudioEnabled(device));
-                    highQualityPref.setEnabled(!mCachedDevice.isBusy());
+                    highQualityPref.setEnabled(!mCachedDevice.isBusy() && !isLeAudioEnabled);
                 } else {
                     highQualityPref.setVisible(false);
                 }
@@ -148,6 +216,12 @@
         if (profile instanceof MapProfile) {
             bluetoothDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
         }
+
+        if (profile instanceof LeAudioProfile) {
+            enableLeAudioProfile(profile);
+            return;
+        }
+
         profile.setEnabled(bluetoothDevice, true);
     }
 
@@ -155,8 +229,14 @@
      * Helper method to disable a profile for a device
      */
     private void disableProfile(LocalBluetoothProfile profile) {
+        if (profile instanceof LeAudioProfile) {
+            disableLeAudioProfile(profile);
+            return;
+        }
+
         final BluetoothDevice bluetoothDevice = mCachedDevice.getDevice();
         profile.setEnabled(bluetoothDevice, false);
+
         if (profile instanceof MapProfile) {
             bluetoothDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_REJECTED);
         } else if (profile instanceof PbapServerProfile) {
@@ -190,14 +270,33 @@
         return true;
     }
 
-
     /**
      * Helper to get the list of connectable and special profiles.
      */
     private List<LocalBluetoothProfile> getProfiles() {
-        List<LocalBluetoothProfile> result = mCachedDevice.getConnectableProfiles();
-        final BluetoothDevice device = mCachedDevice.getDevice();
+        List<LocalBluetoothProfile> result = new ArrayList<LocalBluetoothProfile>();
+        mProfileDeviceMap.clear();
+        if (mAllOfCachedDevices == null || mAllOfCachedDevices.isEmpty()) {
+            return result;
+        }
+        for (CachedBluetoothDevice cachedItem : mAllOfCachedDevices) {
+            List<LocalBluetoothProfile> tmpResult = cachedItem.getConnectableProfiles();
+            for (LocalBluetoothProfile profile : tmpResult) {
+                if (mProfileDeviceMap.containsKey(profile.toString())) {
+                    mProfileDeviceMap.get(profile.toString()).add(cachedItem);
+                    Log.d(TAG, "getProfiles: " + profile.toString() + " add device "
+                            + cachedItem.getDevice().getAnonymizedAddress());
+                } else {
+                    List<CachedBluetoothDevice> tmpCachedDeviceList =
+                            new ArrayList<CachedBluetoothDevice>();
+                    tmpCachedDeviceList.add(cachedItem);
+                    mProfileDeviceMap.put(profile.toString(), tmpCachedDeviceList);
+                    result.add(profile);
+                }
+            }
+        }
 
+        final BluetoothDevice device = mCachedDevice.getDevice();
         final int pbapPermission = device.getPhonebookAccessPermission();
         // Only provide PBAP cabability if the client device has requested PBAP.
         if (pbapPermission != BluetoothDevice.ACCESS_UNKNOWN) {
@@ -210,10 +309,93 @@
         if (mapPermission != BluetoothDevice.ACCESS_UNKNOWN) {
             result.add(mapProfile);
         }
-
+        Log.d(TAG, "getProfiles:result:" + result);
         return result;
     }
 
+    private List<CachedBluetoothDevice> getAllOfCachedBluetoothDevices() {
+        List<CachedBluetoothDevice> cachedBluetoothDevices = new ArrayList<>();
+        if (mCachedDevice == null) {
+            return cachedBluetoothDevices;
+        }
+        cachedBluetoothDevices.add(mCachedDevice);
+        if (mCachedDevice.getGroupId() != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
+            for (CachedBluetoothDevice member : mCachedDevice.getMemberDevice()) {
+                cachedBluetoothDevices.add(member);
+            }
+        }
+        return cachedBluetoothDevices;
+    }
+
+    /**
+     * When user disable the Le Audio profile, the system needs to do two things.
+     * 1) Disable the Le Audio profile for each of the Le Audio devices.
+     * 2) Enable the A2dp profile and Hfp profile for the associated device. The system can't
+     * enable the A2dp profile and Hfp profile if the Le Audio profile is enabled.
+     *
+     * @param profile the LeAudio profile
+     */
+    private void disableLeAudioProfile(LocalBluetoothProfile profile) {
+        if (profile == null || mProfileDeviceMap.get(profile.toString()) == null) {
+            Log.e(TAG, "There is no the LE profile or no device in mProfileDeviceMap. Do nothing.");
+            return;
+        }
+        for (CachedBluetoothDevice leAudioDevice : mProfileDeviceMap.get(profile.toString())) {
+            profile.setEnabled(leAudioDevice.getDevice(), false);
+        }
+
+        LocalBluetoothProfile a2dp = mProfileManager.getA2dpProfile();
+        LocalBluetoothProfile hfp = mProfileManager.getHfpClientProfile();
+        if (a2dp != null && mProfileDeviceMap.get(a2dp.toString()) != null) {
+            for (CachedBluetoothDevice a2dpDevice : mProfileDeviceMap.get(a2dp.toString())) {
+                if (!a2dp.isEnabled(a2dpDevice.getDevice())) {
+                    a2dp.setEnabled(a2dpDevice.getDevice(), true);
+                }
+            }
+        }
+        if (hfp != null && mProfileDeviceMap.get(hfp.toString()) != null) {
+            for (CachedBluetoothDevice hfpDevice : mProfileDeviceMap.get(hfp.toString())) {
+                if (!hfp.isEnabled(hfpDevice.getDevice())) {
+                    hfp.setEnabled(hfpDevice.getDevice(), true);
+                }
+            }
+        }
+    }
+
+    /**
+     * When user enable the Le Audio profile, the system needs to do two things.
+     * 1) Disable the A2dp profile and Hfp profile for the associated device. The system can't
+     * enable the Le Audio if the A2dp profile and Hfp profile are enabled.
+     * 2) Enable the Le Audio profile for each of the Le Audio devices.
+     *
+     * @param profile the LeAudio profile
+     */
+    private void enableLeAudioProfile(LocalBluetoothProfile profile) {
+        if (profile == null || mProfileDeviceMap.get(profile.toString()) == null) {
+            Log.e(TAG, "There is no the LE profile or no device in mProfileDeviceMap. Do nothing.");
+            return;
+        }
+        LocalBluetoothProfile a2dp = mProfileManager.getA2dpProfile();
+        LocalBluetoothProfile hfp = mProfileManager.getHfpClientProfile();
+        if (a2dp != null && mProfileDeviceMap.get(a2dp.toString()) != null) {
+            for (CachedBluetoothDevice a2dpDevice : mProfileDeviceMap.get(a2dp.toString())) {
+                if (a2dp.isEnabled(a2dpDevice.getDevice())) {
+                    a2dp.setEnabled(a2dpDevice.getDevice(), false);
+                }
+            }
+        }
+        if (hfp != null && mProfileDeviceMap.get(hfp.toString()) != null) {
+            for (CachedBluetoothDevice hfpDevice : mProfileDeviceMap.get(hfp.toString())) {
+                if (hfp.isEnabled(hfpDevice.getDevice())) {
+                    hfp.setEnabled(hfpDevice.getDevice(), false);
+                }
+            }
+        }
+        for (CachedBluetoothDevice leAudioDevice : mProfileDeviceMap.get(profile.toString())) {
+            profile.setEnabled(leAudioDevice.getDevice(), true);
+        }
+    }
+
     /**
      * This is a helper method to be called after adding a Preference for a profile. If that
      * profile happened to be A2dp and the device supports high quality audio, it will add a
@@ -243,17 +425,34 @@
 
     @Override
     public void onPause() {
-        super.onPause();
+        for (CachedBluetoothDevice item : mAllOfCachedDevices) {
+            item.unregisterCallback(this);
+        }
         mProfileManager.removeServiceListener(this);
     }
 
     @Override
     public void onResume() {
-        super.onResume();
+        for (CachedBluetoothDevice item : mAllOfCachedDevices) {
+            item.registerCallback(this);
+        }
         mProfileManager.addServiceListener(this);
     }
 
     @Override
+    public void onDeviceAttributesChanged() {
+        for (CachedBluetoothDevice item : mAllOfCachedDevices) {
+            item.unregisterCallback(this);
+        }
+        mAllOfCachedDevices = getAllOfCachedBluetoothDevices();
+        for (CachedBluetoothDevice item : mAllOfCachedDevices) {
+            item.registerCallback(this);
+        }
+
+        super.onDeviceAttributesChanged();
+    }
+
+    @Override
     public void onServiceConnected() {
         refresh();
     }
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
index 6532482..6d443ee 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
@@ -117,6 +117,7 @@
             return;
         }
         use(AdvancedBluetoothDetailsHeaderController.class).init(mCachedDevice);
+        use(LeAudioBluetoothDetailsHeaderController.class).init(mCachedDevice, mManager);
 
         final BluetoothFeatureProvider featureProvider = FeatureFactory.getFactory(
                 context).getBluetoothFeatureProvider(context);
diff --git a/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java
new file mode 100644
index 0000000..06cee85
--- /dev/null
+++ b/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.bluetooth;
+
+import android.bluetooth.BluetoothCsipSetCoordinator;
+import android.bluetooth.BluetoothLeAudio;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.util.Pair;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.fuelgauge.BatteryMeterView;
+import com.android.settingslib.bluetooth.BluetoothUtils;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LeAudioProfile;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnDestroy;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+import com.android.settingslib.widget.LayoutPreference;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class adds a header with device name and status (connected/disconnected, etc.).
+ */
+public class LeAudioBluetoothDetailsHeaderController extends BasePreferenceController implements
+        LifecycleObserver, OnStart, OnStop, OnDestroy, CachedBluetoothDevice.Callback {
+    private static final String TAG = "LeAudioBtHeaderCtrl";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    @VisibleForTesting
+    static final int LEFT_DEVICE_ID =
+            BluetoothLeAudio.AUDIO_LOCATION_FRONT_LEFT
+                    | BluetoothLeAudio.AUDIO_LOCATION_BACK_LEFT
+                    | BluetoothLeAudio.AUDIO_LOCATION_FRONT_LEFT_OF_CENTER
+                    | BluetoothLeAudio.AUDIO_LOCATION_SIDE_LEFT
+                    | BluetoothLeAudio.AUDIO_LOCATION_TOP_FRONT_LEFT
+                    | BluetoothLeAudio.AUDIO_LOCATION_TOP_BACK_LEFT
+                    | BluetoothLeAudio.AUDIO_LOCATION_TOP_SIDE_LEFT
+                    | BluetoothLeAudio.AUDIO_LOCATION_BOTTOM_FRONT_LEFT
+                    | BluetoothLeAudio.AUDIO_LOCATION_FRONT_LEFT_WIDE
+                    | BluetoothLeAudio.AUDIO_LOCATION_LEFT_SURROUND;
+
+    @VisibleForTesting
+    static final int RIGHT_DEVICE_ID =
+            BluetoothLeAudio.AUDIO_LOCATION_FRONT_RIGHT
+                    | BluetoothLeAudio.AUDIO_LOCATION_BACK_RIGHT
+                    | BluetoothLeAudio.AUDIO_LOCATION_FRONT_RIGHT_OF_CENTER
+                    | BluetoothLeAudio.AUDIO_LOCATION_SIDE_RIGHT
+                    | BluetoothLeAudio.AUDIO_LOCATION_TOP_FRONT_RIGHT
+                    | BluetoothLeAudio.AUDIO_LOCATION_TOP_BACK_RIGHT
+                    | BluetoothLeAudio.AUDIO_LOCATION_TOP_SIDE_RIGHT
+                    | BluetoothLeAudio.AUDIO_LOCATION_BOTTOM_FRONT_RIGHT
+                    | BluetoothLeAudio.AUDIO_LOCATION_FRONT_RIGHT_WIDE
+                    | BluetoothLeAudio.AUDIO_LOCATION_RIGHT_SURROUND;
+
+    @VisibleForTesting
+    static final int INVALID_RESOURCE_ID = -1;
+
+    @VisibleForTesting
+    LayoutPreference mLayoutPreference;
+    private CachedBluetoothDevice mCachedDevice;
+    @VisibleForTesting
+    Handler mHandler = new Handler(Looper.getMainLooper());
+    @VisibleForTesting
+    boolean mIsRegisterCallback = false;
+
+    private LocalBluetoothProfileManager mProfileManager;
+
+    public LeAudioBluetoothDetailsHeaderController(Context context, String prefKey) {
+        super(context, prefKey);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        if (mCachedDevice == null || mProfileManager == null) {
+            return CONDITIONALLY_UNAVAILABLE;
+        }
+        boolean hasLeAudio = mCachedDevice.getConnectableProfiles()
+                .stream()
+                .anyMatch(profile -> profile.getProfileId() == BluetoothProfile.LE_AUDIO);
+
+        return !Utils.isAdvancedDetailsHeader(mCachedDevice.getDevice()) && hasLeAudio
+                ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mLayoutPreference = screen.findPreference(getPreferenceKey());
+        mLayoutPreference.setVisible(isAvailable());
+    }
+
+    @Override
+    public void onStart() {
+        if (!isAvailable()) {
+            return;
+        }
+        mIsRegisterCallback = true;
+        mCachedDevice.registerCallback(this);
+        refresh();
+    }
+
+    @Override
+    public void onStop() {
+        if (!mIsRegisterCallback) {
+            return;
+        }
+        mCachedDevice.unregisterCallback(this);
+        mIsRegisterCallback = false;
+    }
+
+    @Override
+    public void onDestroy() {
+    }
+
+    public void init(CachedBluetoothDevice cachedBluetoothDevice,
+            LocalBluetoothManager bluetoothManager) {
+        mCachedDevice = cachedBluetoothDevice;
+        mProfileManager = bluetoothManager.getProfileManager();
+    }
+
+    @VisibleForTesting
+    void refresh() {
+        if (mLayoutPreference == null || mCachedDevice == null) {
+            return;
+        }
+        final ImageView imageView = mLayoutPreference.findViewById(R.id.entity_header_icon);
+        if (imageView != null) {
+            final Pair<Drawable, String> pair =
+                    BluetoothUtils.getBtRainbowDrawableWithDescription(mContext, mCachedDevice);
+            imageView.setImageDrawable(pair.first);
+            imageView.setContentDescription(pair.second);
+        }
+
+        final TextView title = mLayoutPreference.findViewById(R.id.entity_header_title);
+        if (title != null) {
+            title.setText(mCachedDevice.getName());
+        }
+        final TextView summary = mLayoutPreference.findViewById(R.id.entity_header_summary);
+        if (summary != null) {
+            summary.setText(mCachedDevice.getConnectionSummary(true /* shortSummary */));
+        }
+
+        if (!mCachedDevice.isConnected() || mCachedDevice.isBusy()) {
+            hideAllOfBatteryLayouts();
+            return;
+        }
+
+        updateBatteryLayout();
+    }
+
+    @VisibleForTesting
+    Drawable createBtBatteryIcon(Context context, int level) {
+        final BatteryMeterView.BatteryMeterDrawable drawable =
+                new BatteryMeterView.BatteryMeterDrawable(context,
+                        context.getColor(R.color.meter_background_color),
+                        context.getResources().getDimensionPixelSize(
+                                R.dimen.advanced_bluetooth_battery_meter_width),
+                        context.getResources().getDimensionPixelSize(
+                                R.dimen.advanced_bluetooth_battery_meter_height));
+        drawable.setBatteryLevel(level);
+        drawable.setColorFilter(new PorterDuffColorFilter(
+                com.android.settings.Utils.getColorAttrDefaultColor(context,
+                        android.R.attr.colorControlNormal),
+                PorterDuff.Mode.SRC));
+        return drawable;
+    }
+
+    private int getBatteryTitleResource(int deviceId) {
+        if (deviceId == LEFT_DEVICE_ID) {
+            return R.id.bt_battery_left_title;
+        }
+        if (deviceId == RIGHT_DEVICE_ID) {
+            return R.id.bt_battery_right_title;
+        }
+        Log.d(TAG, "No resource id. The deviceId is " + deviceId);
+        return INVALID_RESOURCE_ID;
+    }
+
+    private int getBatterySummaryResource(int deviceId) {
+        if (deviceId == LEFT_DEVICE_ID) {
+            return R.id.bt_battery_left_summary;
+        }
+        if (deviceId == RIGHT_DEVICE_ID) {
+            return R.id.bt_battery_right_summary;
+        }
+        Log.d(TAG, "No resource id. The deviceId is " + deviceId);
+        return INVALID_RESOURCE_ID;
+    }
+
+    private void hideAllOfBatteryLayouts() {
+        // hide the case
+        updateBatteryLayout(R.id.bt_battery_case_title, R.id.bt_battery_case_summary,
+                BluetoothUtils.META_INT_ERROR);
+        // hide the left
+        updateBatteryLayout(R.id.bt_battery_left_title, R.id.bt_battery_left_summary,
+                BluetoothUtils.META_INT_ERROR);
+        // hide the right
+        updateBatteryLayout(R.id.bt_battery_right_title, R.id.bt_battery_right_summary,
+                BluetoothUtils.META_INT_ERROR);
+    }
+
+    private List<CachedBluetoothDevice> getAllOfLeAudioDevices() {
+        if (mCachedDevice == null) {
+            return null;
+        }
+        List<CachedBluetoothDevice> leAudioDevices = new ArrayList<>();
+        leAudioDevices.add(mCachedDevice);
+        if (mCachedDevice.getGroupId() != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
+            for (CachedBluetoothDevice member : mCachedDevice.getMemberDevice()) {
+                leAudioDevices.add(member);
+            }
+        }
+        return leAudioDevices;
+    }
+
+    private void updateBatteryLayout() {
+        // Init the battery layouts.
+        hideAllOfBatteryLayouts();
+        final List<CachedBluetoothDevice> leAudioDevices = getAllOfLeAudioDevices();
+        LeAudioProfile leAudioProfile = mProfileManager.getLeAudioProfile();
+        if (leAudioDevices == null || leAudioDevices.isEmpty()) {
+            Log.e(TAG, "There is no LeAudioProfile.");
+            return;
+        }
+
+        if (!leAudioProfile.isEnabled(mCachedDevice.getDevice())) {
+            Log.d(TAG, "Show the legacy battery style if the LeAudio is not enabled.");
+            final TextView summary = mLayoutPreference.findViewById(R.id.entity_header_summary);
+            if (summary != null) {
+                summary.setText(mCachedDevice.getConnectionSummary());
+            }
+            return;
+        }
+
+        for (CachedBluetoothDevice cachedDevice : leAudioDevices) {
+            int deviceId = leAudioProfile.getAudioLocation(cachedDevice.getDevice());
+            Log.d(TAG, "LeAudioDevices:" + cachedDevice.getDevice().getAnonymizedAddress()
+                    + ", deviceId:" + deviceId);
+
+            if (deviceId == BluetoothLeAudio.AUDIO_LOCATION_INVALID) {
+                Log.d(TAG, "The device does not support the AUDIO_LOCATION.");
+                return;
+            }
+            boolean isLeft = (deviceId & LEFT_DEVICE_ID) != 0;
+            boolean isRight = (deviceId & LEFT_DEVICE_ID) != 0;
+            boolean isLeftRight = isLeft && isRight;
+            // The LE device updates the BatteryLayout
+            if (isLeftRight) {
+                Log.d(TAG, "The device id is left+right. Do nothing.");
+            } else if (isLeft) {
+                updateBatteryLayout(getBatteryTitleResource(LEFT_DEVICE_ID),
+                        getBatterySummaryResource(LEFT_DEVICE_ID), cachedDevice.getBatteryLevel());
+            } else if (isRight) {
+                updateBatteryLayout(getBatteryTitleResource(RIGHT_DEVICE_ID),
+                        getBatterySummaryResource(RIGHT_DEVICE_ID), cachedDevice.getBatteryLevel());
+            } else {
+                Log.d(TAG, "The device id is other Audio Location. Do nothing.");
+            }
+        }
+    }
+
+    private void updateBatteryLayout(int titleResId, int summaryResId, int batteryLevel) {
+        final TextView batteryTitleView = mLayoutPreference.findViewById(titleResId);
+        final TextView batterySummaryView = mLayoutPreference.findViewById(summaryResId);
+        if (batteryTitleView == null || batterySummaryView == null) {
+            Log.e(TAG, "updateBatteryLayout: No TextView");
+            return;
+        }
+        if (batteryLevel != BluetoothUtils.META_INT_ERROR) {
+            batteryTitleView.setVisibility(View.VISIBLE);
+            batterySummaryView.setVisibility(View.VISIBLE);
+            batterySummaryView.setText(
+                    com.android.settings.Utils.formatPercentage(batteryLevel));
+            batterySummaryView.setCompoundDrawablesRelativeWithIntrinsicBounds(
+                    createBtBatteryIcon(mContext, batteryLevel), /* top */ null,
+                    /* end */ null, /* bottom */ null);
+        } else {
+            Log.d(TAG, "updateBatteryLayout: Hide it if it doesn't have battery information.");
+            batteryTitleView.setVisibility(View.GONE);
+            batterySummaryView.setVisibility(View.GONE);
+        }
+    }
+
+    @Override
+    public void onDeviceAttributesChanged() {
+        if (mCachedDevice != null) {
+            refresh();
+        }
+    }
+}
diff --git a/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceController.java b/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceController.java
index 95e663b..2ffa11b 100644
--- a/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceController.java
+++ b/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceController.java
@@ -16,9 +16,12 @@
 
 package com.android.settings.development;
 
+import static com.android.settings.development.BluetoothLeAudioHwOffloadPreferenceController.LE_AUDIO_OFFLOAD_DISABLED_PROPERTY;
+
 import android.content.Context;
 import android.os.SystemProperties;
 
+import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
 import androidx.preference.SwitchPreference;
 
@@ -34,6 +37,9 @@
     static final String A2DP_OFFLOAD_DISABLED_PROPERTY = "persist.bluetooth.a2dp_offload.disabled";
     static final String A2DP_OFFLOAD_SUPPORTED_PROPERTY = "ro.bluetooth.a2dp_offload.supported";
 
+    @VisibleForTesting
+    boolean mChanged = false;
+
     public BluetoothA2dpHwOffloadPreferenceController(Context context,
             DevelopmentSettingsDashboardFragment fragment) {
         super(context);
@@ -47,7 +53,8 @@
 
     @Override
     public boolean onPreferenceChange(Preference preference, Object newValue) {
-        BluetoothA2dpHwOffloadRebootDialog.show(mFragment, this);
+        BluetoothHwOffloadRebootDialog.show(mFragment);
+        mChanged = true;
         return false;
     }
 
@@ -85,10 +92,26 @@
         return offloadSupported ? !offloadDisabled : true;
     }
 
-    public void onA2dpHwDialogConfirmed() {
+    /**
+     * Called when the HwOffloadDialog confirm is clicked.
+     */
+    public void onHwOffloadDialogConfirmed() {
+        if (!mChanged) {
+            return;
+        }
         final boolean offloadDisabled =
                 SystemProperties.getBoolean(A2DP_OFFLOAD_DISABLED_PROPERTY, false);
         SystemProperties.set(A2DP_OFFLOAD_DISABLED_PROPERTY, Boolean.toString(!offloadDisabled));
+        if (offloadDisabled) {
+            SystemProperties.set(LE_AUDIO_OFFLOAD_DISABLED_PROPERTY,
+                    Boolean.toString(!offloadDisabled));
+        }
     }
 
+    /**
+     * Called when the HwOffloadDialog cancel is clicked.
+     */
+    public void onHwOffloadDialogCanceled() {
+        mChanged = false;
+    }
 }
diff --git a/src/com/android/settings/development/BluetoothGabeldorschePreferenceController.java b/src/com/android/settings/development/BluetoothGabeldorschePreferenceController.java
deleted file mode 100644
index f5c30f5..0000000
--- a/src/com/android/settings/development/BluetoothGabeldorschePreferenceController.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.development;
-
-import android.content.Context;
-import android.provider.DeviceConfig;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
-import androidx.preference.SwitchPreference;
-
-import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settingslib.development.DeveloperOptionsPreferenceController;
-
-/**
- * Preference controller for Bluetooth Gabeldorche feature
- */
-public class BluetoothGabeldorschePreferenceController extends
-        DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener,
-        PreferenceControllerMixin {
-
-    private static final String BLUETOOTH_GABELDORSCHE_KEY =
-            "bluetooth_gabeldorsche_enable";
-
-    @VisibleForTesting
-    static final String CURRENT_GD_FLAG = "INIT_gd_scanning";
-
-    public BluetoothGabeldorschePreferenceController(Context context) {
-        super(context);
-    }
-
-    @Override
-    public String getPreferenceKey() {
-        return BLUETOOTH_GABELDORSCHE_KEY;
-    }
-
-    @Override
-    public boolean onPreferenceChange(Preference preference, Object newValue) {
-        final boolean isEnabled = (Boolean) newValue;
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BLUETOOTH,
-                CURRENT_GD_FLAG, isEnabled ? "true" : "false", false /* makeDefault */);
-        return true;
-    }
-
-    @Override
-    public void updateState(Preference preference) {
-        final boolean isEnabled = DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_BLUETOOTH, CURRENT_GD_FLAG, false /* default */);
-        ((SwitchPreference) mPreference).setChecked(isEnabled);
-    }
-
-    @Override
-    protected void onDeveloperOptionsSwitchDisabled() {
-        super.onDeveloperOptionsSwitchDisabled();
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BLUETOOTH,
-                CURRENT_GD_FLAG, null, false /* makeDefault */);
-        ((SwitchPreference) mPreference).setChecked(false);
-    }
-}
diff --git a/src/com/android/settings/development/BluetoothA2dpHwOffloadRebootDialog.java b/src/com/android/settings/development/BluetoothHwOffloadRebootDialog.java
similarity index 60%
rename from src/com/android/settings/development/BluetoothA2dpHwOffloadRebootDialog.java
rename to src/com/android/settings/development/BluetoothHwOffloadRebootDialog.java
index 95ef019..389103e 100644
--- a/src/com/android/settings/development/BluetoothA2dpHwOffloadRebootDialog.java
+++ b/src/com/android/settings/development/BluetoothHwOffloadRebootDialog.java
@@ -28,17 +28,23 @@
 import com.android.settings.R;
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
 
-public class BluetoothA2dpHwOffloadRebootDialog extends InstrumentedDialogFragment
+/**
+ * The a2dp and LE audio offload switch should reboot the device to take effect, the dialog is
+ * to ask the user to reboot the device after a2dp or LE audio offload user preference changed
+ */
+public class BluetoothHwOffloadRebootDialog extends InstrumentedDialogFragment
         implements DialogInterface.OnClickListener {
 
-    public static final String TAG = "BluetoothA2dpHwOffloadReboot";
+    public static final String TAG = "BluetoothHwOffloadReboot";
 
-    public static void show(DevelopmentSettingsDashboardFragment host,
-            BluetoothA2dpHwOffloadPreferenceController controller) {
+    /**
+     * The function to show the HwOffloadReboot Dialog.
+     */
+    public static void show(DevelopmentSettingsDashboardFragment host) {
         final FragmentManager manager = host.getActivity().getSupportFragmentManager();
         if (manager.findFragmentByTag(TAG) == null) {
-            final BluetoothA2dpHwOffloadRebootDialog dialog =
-                    new BluetoothA2dpHwOffloadRebootDialog();
+            final BluetoothHwOffloadRebootDialog dialog =
+                    new BluetoothHwOffloadRebootDialog();
             dialog.setTargetFragment(host, 0 /* requestCode */);
             dialog.show(manager, TAG);
         }
@@ -52,33 +58,44 @@
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
         return new AlertDialog.Builder(getActivity())
-                .setMessage(R.string.bluetooth_disable_a2dp_hw_offload_dialog_message)
-                .setTitle(R.string.bluetooth_disable_a2dp_hw_offload_dialog_title)
+                .setMessage(R.string.bluetooth_disable_hw_offload_dialog_message)
+                .setTitle(R.string.bluetooth_disable_hw_offload_dialog_title)
                 .setPositiveButton(
-                        R.string.bluetooth_disable_a2dp_hw_offload_dialog_confirm, this)
+                        R.string.bluetooth_disable_hw_offload_dialog_confirm, this)
                 .setNegativeButton(
-                        R.string.bluetooth_disable_a2dp_hw_offload_dialog_cancel, this)
+                        R.string.bluetooth_disable_hw_offload_dialog_cancel, this)
                 .create();
     }
 
     @Override
     public void onClick(DialogInterface dialog, int which) {
-        final OnA2dpHwDialogConfirmedListener host =
-                (OnA2dpHwDialogConfirmedListener) getTargetFragment();
+        final OnHwOffloadDialogListener host =
+                (OnHwOffloadDialogListener) getTargetFragment();
         if (host == null) {
             return;
         }
         if (which == DialogInterface.BUTTON_POSITIVE) {
-            host.onA2dpHwDialogConfirmed();
+            host.onHwOffloadDialogConfirmed();
             PowerManager pm = getContext().getSystemService(PowerManager.class);
             pm.reboot(null);
+        } else {
+            host.onHwOffloadDialogCanceled();
         }
     }
 
-    public interface OnA2dpHwDialogConfirmedListener {
+    /**
+     * The interface for the HsOffloadDialogListener to provide the action as the
+     * confirmed or canceled clicked.
+     */
+    public interface OnHwOffloadDialogListener {
         /**
          * Called when the user presses reboot on the warning dialog.
          */
-        void onA2dpHwDialogConfirmed();
+        void onHwOffloadDialogConfirmed();
+
+        /**
+         * Called when the user presses cancel on the warning dialog.
+         */
+        void onHwOffloadDialogCanceled();
     }
 }
diff --git a/src/com/android/settings/development/BluetoothLeAudioHwOffloadPreferenceController.java b/src/com/android/settings/development/BluetoothLeAudioHwOffloadPreferenceController.java
new file mode 100644
index 0000000..911b62d
--- /dev/null
+++ b/src/com/android/settings/development/BluetoothLeAudioHwOffloadPreferenceController.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.development;
+
+import static com.android.settings.development.BluetoothA2dpHwOffloadPreferenceController.A2DP_OFFLOAD_SUPPORTED_PROPERTY;
+
+import android.content.Context;
+import android.os.SystemProperties;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.SwitchPreference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.development.DeveloperOptionsPreferenceController;
+
+/**
+ * Preference controller to control Bluetooth LE audio offload
+ */
+public class BluetoothLeAudioHwOffloadPreferenceController
+        extends DeveloperOptionsPreferenceController
+        implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
+
+    private static final String PREFERENCE_KEY = "bluetooth_disable_le_audio_hw_offload";
+    private final DevelopmentSettingsDashboardFragment mFragment;
+
+    static final String LE_AUDIO_OFFLOAD_DISABLED_PROPERTY =
+            "persist.bluetooth.leaudio_offload.disabled";
+    static final String LE_AUDIO_OFFLOAD_SUPPORTED_PROPERTY =
+            "ro.bluetooth.leaudio_offload.supported";
+
+    @VisibleForTesting
+    boolean mChanged = false;
+
+    public BluetoothLeAudioHwOffloadPreferenceController(Context context,
+            DevelopmentSettingsDashboardFragment fragment) {
+        super(context);
+        mFragment = fragment;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return PREFERENCE_KEY;
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        BluetoothHwOffloadRebootDialog.show(mFragment);
+        mChanged = true;
+        return false;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        final boolean offloadSupported =
+                SystemProperties.getBoolean(A2DP_OFFLOAD_SUPPORTED_PROPERTY, false)
+                && SystemProperties.getBoolean(LE_AUDIO_OFFLOAD_SUPPORTED_PROPERTY, false);
+        if (offloadSupported) {
+            final boolean offloadDisabled =
+                    SystemProperties.getBoolean(LE_AUDIO_OFFLOAD_DISABLED_PROPERTY, true);
+            ((SwitchPreference) mPreference).setChecked(offloadDisabled);
+        } else {
+            mPreference.setEnabled(false);
+            ((SwitchPreference) mPreference).setChecked(true);
+        }
+    }
+
+    @Override
+    protected void onDeveloperOptionsSwitchDisabled() {
+        super.onDeveloperOptionsSwitchDisabled();
+        final boolean offloadSupported =
+                SystemProperties.getBoolean(A2DP_OFFLOAD_SUPPORTED_PROPERTY, false)
+                && SystemProperties.getBoolean(LE_AUDIO_OFFLOAD_SUPPORTED_PROPERTY, false);
+        if (offloadSupported) {
+            ((SwitchPreference) mPreference).setChecked(true);
+            SystemProperties.set(LE_AUDIO_OFFLOAD_DISABLED_PROPERTY, "true");
+        }
+    }
+
+    /**
+     * Check if the le audio offload setting is default value.
+     */
+    public boolean isDefaultValue() {
+        final boolean offloadSupported =
+                SystemProperties.getBoolean(A2DP_OFFLOAD_SUPPORTED_PROPERTY, false)
+                && SystemProperties.getBoolean(LE_AUDIO_OFFLOAD_SUPPORTED_PROPERTY, false);
+        final boolean offloadDisabled =
+                    SystemProperties.getBoolean(LE_AUDIO_OFFLOAD_DISABLED_PROPERTY, false);
+        return offloadSupported ? offloadDisabled : true;
+    }
+
+    /**
+     * Called when the HwOffloadDialog confirm is clicked.
+     */
+    public void onHwOffloadDialogConfirmed() {
+        if (!mChanged) {
+            return;
+        }
+
+        final boolean offloadDisabled =
+                SystemProperties.getBoolean(LE_AUDIO_OFFLOAD_DISABLED_PROPERTY,
+                false);
+        SystemProperties.set(LE_AUDIO_OFFLOAD_DISABLED_PROPERTY,
+                Boolean.toString(!offloadDisabled));
+    }
+
+    /**
+     * Called when the HwOffloadDialog cancel is clicked.
+     */
+    public void onHwOffloadDialogCanceled() {
+        mChanged = false;
+    }
+}
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index 57114e2..085a372 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -80,7 +80,7 @@
 public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFragment
         implements OnMainSwitchChangeListener, OemUnlockDialogHost, AdbDialogHost,
         AdbClearKeysDialogHost, LogPersistDialogHost,
-        BluetoothA2dpHwOffloadRebootDialog.OnA2dpHwDialogConfirmedListener,
+        BluetoothHwOffloadRebootDialog.OnHwOffloadDialogListener,
         AbstractBluetoothPreferenceController.Callback {
 
     private static final String TAG = "DevSettingsDashboard";
@@ -293,12 +293,16 @@
             if (isChecked) {
                 EnableDevelopmentSettingWarningDialog.show(this /* host */);
             } else {
-                final BluetoothA2dpHwOffloadPreferenceController controller =
+                final BluetoothA2dpHwOffloadPreferenceController a2dpController =
                         getDevelopmentOptionsController(
                                 BluetoothA2dpHwOffloadPreferenceController.class);
-                // If A2DP hardware offload isn't default value, we must reboot after disable
+                final BluetoothLeAudioHwOffloadPreferenceController leAudioController =
+                        getDevelopmentOptionsController(
+                                BluetoothLeAudioHwOffloadPreferenceController.class);
+                // If hardware offload isn't default value, we must reboot after disable
                 // developer options. Show a dialog for the user to confirm.
-                if (controller == null || controller.isDefaultValue()) {
+                if ((a2dpController == null || a2dpController.isDefaultValue())
+                        && (leAudioController == null || leAudioController.isDefaultValue())) {
                     disableDeveloperOptions();
                 } else {
                     DisableDevSettingsDialogFragment.show(this /* host */);
@@ -358,10 +362,27 @@
     }
 
     @Override
-    public void onA2dpHwDialogConfirmed() {
-        final BluetoothA2dpHwOffloadPreferenceController controller =
+    public void onHwOffloadDialogConfirmed() {
+        final BluetoothA2dpHwOffloadPreferenceController a2dpController =
                 getDevelopmentOptionsController(BluetoothA2dpHwOffloadPreferenceController.class);
-        controller.onA2dpHwDialogConfirmed();
+        a2dpController.onHwOffloadDialogConfirmed();
+
+        final BluetoothLeAudioHwOffloadPreferenceController leAudioController =
+                getDevelopmentOptionsController(
+                    BluetoothLeAudioHwOffloadPreferenceController.class);
+        leAudioController.onHwOffloadDialogConfirmed();
+    }
+
+    @Override
+    public void onHwOffloadDialogCanceled() {
+        final BluetoothA2dpHwOffloadPreferenceController a2dpController =
+                getDevelopmentOptionsController(BluetoothA2dpHwOffloadPreferenceController.class);
+        a2dpController.onHwOffloadDialogCanceled();
+
+        final BluetoothLeAudioHwOffloadPreferenceController leAudioController =
+                getDevelopmentOptionsController(
+                    BluetoothLeAudioHwOffloadPreferenceController.class);
+        leAudioController.onHwOffloadDialogCanceled();
     }
 
     @Override
@@ -484,7 +505,6 @@
         controllers.add(new HdcpCheckingPreferenceController(context));
         controllers.add(new BluetoothSnoopLogPreferenceController(context));
         controllers.add(new OemUnlockPreferenceController(context, activity, fragment));
-        controllers.add(new FileEncryptionPreferenceController(context));
         controllers.add(new PictureColorModePreferenceController(context, lifecycle));
         controllers.add(new WebViewAppPreferenceController(context));
         controllers.add(new CoolColorTemperaturePreferenceController(context));
@@ -517,10 +537,10 @@
         controllers.add(new TetheringHardwareAccelPreferenceController(context));
         controllers.add(new BluetoothDeviceNoNamePreferenceController(context));
         controllers.add(new BluetoothAbsoluteVolumePreferenceController(context));
-        controllers.add(new BluetoothGabeldorschePreferenceController(context));
         controllers.add(new BluetoothAvrcpVersionPreferenceController(context));
         controllers.add(new BluetoothMapVersionPreferenceController(context));
         controllers.add(new BluetoothA2dpHwOffloadPreferenceController(context, fragment));
+        controllers.add(new BluetoothLeAudioHwOffloadPreferenceController(context, fragment));
         controllers.add(new BluetoothMaxConnectedAudioDevicesPreferenceController(context));
         controllers.add(new NfcStackDebugLogPreferenceController(context));
         controllers.add(new ShowTapsPreferenceController(context));
@@ -585,6 +605,7 @@
         controllers.add(new SharedDataPreferenceController(context));
         controllers.add(new OverlaySettingsPreferenceController(context));
         controllers.add(new StylusHandwritingPreferenceController(context));
+        controllers.add(new IngressRateLimitPreferenceController((context)));
 
         return controllers;
     }
diff --git a/src/com/android/settings/development/DisableDevSettingsDialogFragment.java b/src/com/android/settings/development/DisableDevSettingsDialogFragment.java
index 803030e..5db2ed4 100644
--- a/src/com/android/settings/development/DisableDevSettingsDialogFragment.java
+++ b/src/com/android/settings/development/DisableDevSettingsDialogFragment.java
@@ -56,15 +56,15 @@
 
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
-        // Reuse the same text of disable_a2dp_hw_offload_dialog.
+        // Reuse the same text of disable_hw_offload_dialog.
         // The text is generic enough to be used for turning off Dev options.
         return new AlertDialog.Builder(getActivity())
-                .setMessage(R.string.bluetooth_disable_a2dp_hw_offload_dialog_message)
-                .setTitle(R.string.bluetooth_disable_a2dp_hw_offload_dialog_title)
+                .setMessage(R.string.bluetooth_disable_hw_offload_dialog_message)
+                .setTitle(R.string.bluetooth_disable_hw_offload_dialog_title)
                 .setPositiveButton(
-                        R.string.bluetooth_disable_a2dp_hw_offload_dialog_confirm, this)
+                        R.string.bluetooth_disable_hw_offload_dialog_confirm, this)
                 .setNegativeButton(
-                        R.string.bluetooth_disable_a2dp_hw_offload_dialog_cancel, this)
+                        R.string.bluetooth_disable_hw_offload_dialog_cancel, this)
                 .create();
     }
 
diff --git a/src/com/android/settings/development/FileEncryptionPreferenceController.java b/src/com/android/settings/development/FileEncryptionPreferenceController.java
deleted file mode 100644
index 82a58ba..0000000
--- a/src/com/android/settings/development/FileEncryptionPreferenceController.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.development;
-
-import android.content.Context;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.storage.IStorageManager;
-import android.text.TextUtils;
-import android.sysprop.CryptoProperties;
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
-
-import com.android.settings.R;
-import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settingslib.development.DeveloperOptionsPreferenceController;
-
-public class FileEncryptionPreferenceController extends DeveloperOptionsPreferenceController
-        implements PreferenceControllerMixin {
-
-    private static final String KEY_CONVERT_FBE = "convert_to_file_encryption";
-    private static final String KEY_STORAGE_MANAGER = "mount";
-
-    private final IStorageManager mStorageManager;
-
-    public FileEncryptionPreferenceController(Context context) {
-        super(context);
-
-        mStorageManager = getStorageManager();
-    }
-
-    @Override
-    public boolean isAvailable() {
-        if (mStorageManager == null) {
-            return false;
-        }
-
-        try {
-            return mStorageManager.isConvertibleToFBE();
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    @Override
-    public String getPreferenceKey() {
-        return KEY_CONVERT_FBE;
-    }
-
-    @Override
-    public void updateState(Preference preference) {
-        if (CryptoProperties.type().orElse(CryptoProperties.type_values.NONE) !=
-            CryptoProperties.type_values.FILE) {
-            return;
-        }
-
-        mPreference.setEnabled(false);
-        mPreference.setSummary(
-                mContext.getResources().getString(R.string.convert_to_file_encryption_done));
-    }
-
-    private IStorageManager getStorageManager() {
-        try {
-            return IStorageManager.Stub.asInterface(
-                    ServiceManager.getService(KEY_STORAGE_MANAGER));
-        } catch (VerifyError e) {
-            // Used for tests since Robolectric cannot initialize this class.
-            return null;
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/com/android/settings/development/IngressRateLimitPreferenceController.java b/src/com/android/settings/development/IngressRateLimitPreferenceController.java
new file mode 100644
index 0000000..2e84aba
--- /dev/null
+++ b/src/com/android/settings/development/IngressRateLimitPreferenceController.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.development;
+
+import android.content.Context;
+import android.net.ConnectivitySettingsManager;
+import android.util.Log;
+
+import androidx.preference.ListPreference;
+import androidx.preference.Preference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.development.DeveloperOptionsPreferenceController;
+
+/**
+ * Controller for ingress rate limit developer setting.
+ */
+public class IngressRateLimitPreferenceController extends DeveloperOptionsPreferenceController
+        implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
+    private static final String TAG = "IngressRateLimitPreferenceController";
+    private static final String INGRESS_RATE_LIMIT_KEY = "ingress_rate_limit";
+    private static final int RATE_LIMIT_DISABLED = -1;
+
+    public IngressRateLimitPreferenceController(Context context) {
+        super(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return INGRESS_RATE_LIMIT_KEY;
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        final long value = Long.parseLong(newValue.toString());
+        try {
+            ConnectivitySettingsManager.setIngressRateLimitInBytesPerSecond(mContext, value);
+            return true;
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "invalid rate limit", e);
+            return false;
+        }
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        final String ingressRateLimit = String.valueOf(
+                ConnectivitySettingsManager.getIngressRateLimitInBytesPerSecond(mContext));
+
+        // verify ingressRateLimit is valid / present in ListPreference; else do nothing.
+        final CharSequence[] entryValues = ((ListPreference) preference).getEntryValues();
+        for (int i = 0; i < entryValues.length; i++) {
+            if (ingressRateLimit.contentEquals(entryValues[i])) {
+                ((ListPreference) preference).setValue(ingressRateLimit);
+                return;
+            }
+        }
+    }
+
+    @Override
+    protected void onDeveloperOptionsSwitchDisabled() {
+        super.onDeveloperOptionsSwitchDisabled();
+        // disable rate limiting when developer options are disabled
+        ConnectivitySettingsManager.setIngressRateLimitInBytesPerSecond(mContext,
+                RATE_LIMIT_DISABLED);
+        ((ListPreference) mPreference).setValue(String.valueOf(RATE_LIMIT_DISABLED));
+    }
+}
diff --git a/src/com/android/settings/deviceinfo/StorageItemPreference.java b/src/com/android/settings/deviceinfo/StorageItemPreference.java
index d3549d4..91102a0 100644
--- a/src/com/android/settings/deviceinfo/StorageItemPreference.java
+++ b/src/com/android/settings/deviceinfo/StorageItemPreference.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.deviceinfo;
 
+import android.animation.TypeEvaluator;
+import android.animation.ValueAnimator;
 import android.content.Context;
 import android.util.AttributeSet;
 import android.widget.ProgressBar;
@@ -30,6 +32,7 @@
     public int userHandle;
 
     private static final int UNINITIALIZED = -1;
+    private static final int ANIMATE_DURATION_IN_MILLIS = 1000;
 
     private ProgressBar mProgressBar;
     private static final int PROGRESS_MAX = 100;
@@ -46,15 +49,33 @@
     }
 
     public void setStorageSize(long size, long total) {
-        mStorageSize = size;
-        setSummary(StorageUtils.getStorageSizeLabel(getContext(), size));
+        setStorageSize(size, total, false /* animate */);
+    }
 
-        if (total == 0) {
-            mProgressPercent = 0;
+    /**
+     * Set the storage size info with/without animation
+     */
+    public void setStorageSize(long size, long total, boolean animate) {
+        if (animate) {
+            TypeEvaluator<Long> longEvaluator =
+                    (fraction, startValue, endValue) -> {
+                        // Directly returns end value if fraction is 1.0 and the end value is 0.
+                        if (fraction >= 1.0f && endValue == 0) {
+                            return endValue;
+                        }
+                        return startValue + (long) (fraction * (endValue - startValue));
+                    };
+            ValueAnimator valueAnimator = ValueAnimator.ofObject(longEvaluator, mStorageSize, size);
+            valueAnimator.setDuration(ANIMATE_DURATION_IN_MILLIS);
+            valueAnimator.addUpdateListener(
+                    animation -> {
+                        updateProgressBarAndSizeInfo((long) animation.getAnimatedValue(), total);
+                    });
+            valueAnimator.start();
         } else {
-            mProgressPercent = (int)(size * PROGRESS_MAX / total);
+            updateProgressBarAndSizeInfo(size, total);
         }
-        updateProgressBar();
+        mStorageSize = size;
     }
 
     public long getStorageSize() {
@@ -62,11 +83,18 @@
     }
 
     protected void updateProgressBar() {
-        if (mProgressBar == null || mProgressPercent == UNINITIALIZED)
+        if (mProgressBar == null || mProgressPercent == UNINITIALIZED) {
             return;
+        }
 
         mProgressBar.setMax(PROGRESS_MAX);
-        mProgressBar.setProgress(mProgressPercent, true /* animate */);
+        mProgressBar.setProgress(mProgressPercent);
+    }
+
+    private void updateProgressBarAndSizeInfo(long size, long total) {
+        setSummary(StorageUtils.getStorageSizeLabel(getContext(), size));
+        mProgressPercent = total == 0 ? 0 : (int) (size * PROGRESS_MAX / total);
+        updateProgressBar();
     }
 
     @Override
diff --git a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
index 9813439..7e27414 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
@@ -378,18 +378,22 @@
      */
     public void onLoadFinished(@Nullable SparseArray<StorageAsyncLoader.StorageResult> result,
             int userId) {
+        // Enable animation when the storage size info is from StorageAsyncLoader whereas disable
+        // animation when the cached storage size info is used instead.
+        boolean animate = result != null && mIsPreferenceOrderedBySize;
         // Calculate the size info for each category
         StorageCacheHelper.StorageCache storageCache = getSizeInfo(result, userId);
         // Set size info to each preference
-        mImagesPreference.setStorageSize(storageCache.imagesSize, mTotalSize);
-        mVideosPreference.setStorageSize(storageCache.videosSize, mTotalSize);
-        mAudioPreference.setStorageSize(storageCache.audioSize, mTotalSize);
-        mAppsPreference.setStorageSize(storageCache.allAppsExceptGamesSize, mTotalSize);
-        mGamesPreference.setStorageSize(storageCache.gamesSize, mTotalSize);
-        mDocumentsAndOtherPreference.setStorageSize(storageCache.documentsAndOtherSize, mTotalSize);
-        mTrashPreference.setStorageSize(storageCache.trashSize, mTotalSize);
+        mImagesPreference.setStorageSize(storageCache.imagesSize, mTotalSize, animate);
+        mVideosPreference.setStorageSize(storageCache.videosSize, mTotalSize, animate);
+        mAudioPreference.setStorageSize(storageCache.audioSize, mTotalSize, animate);
+        mAppsPreference.setStorageSize(storageCache.allAppsExceptGamesSize, mTotalSize, animate);
+        mGamesPreference.setStorageSize(storageCache.gamesSize, mTotalSize, animate);
+        mDocumentsAndOtherPreference.setStorageSize(storageCache.documentsAndOtherSize, mTotalSize,
+                animate);
+        mTrashPreference.setStorageSize(storageCache.trashSize, mTotalSize, animate);
         if (mSystemPreference != null) {
-            mSystemPreference.setStorageSize(storageCache.systemSize, mTotalSize);
+            mSystemPreference.setStorageSize(storageCache.systemSize, mTotalSize, animate);
         }
         // Cache the size info
         if (result != null) {
@@ -519,7 +523,7 @@
         if (mTrashPreference == null) {
             return;
         }
-        mTrashPreference.setStorageSize(0, mTotalSize);
+        mTrashPreference.setStorageSize(0, mTotalSize, true /* animate */);
         updatePrivateStorageCategoryPreferencesOrder();
     }
 
diff --git a/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java
index 90a86f1..249ee49 100644
--- a/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java
@@ -617,6 +617,7 @@
         return true;
     }
 
+    /** Used for {@link AppBatteryPreferenceController}. */
     public static List<BatteryDiffEntry> getBatteryLast24HrUsageData(Context context) {
         final long start = System.currentTimeMillis();
         final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
@@ -638,6 +639,28 @@
         return batteryIndexedMap.get(BatteryChartView.SELECTED_INDEX_ALL);
     }
 
+    /** Used for {@link AppBatteryPreferenceController}. */
+    public static BatteryDiffEntry getBatteryLast24HrUsageData(
+            Context context, String packageName, int userId) {
+        if (packageName == null) {
+            return null;
+        }
+        final List<BatteryDiffEntry> entries = getBatteryLast24HrUsageData(context);
+        if (entries == null) {
+            return null;
+        }
+        for (BatteryDiffEntry entry : entries) {
+            final BatteryHistEntry batteryHistEntry = entry.mBatteryHistEntry;
+            if (batteryHistEntry != null
+                    && batteryHistEntry.mConsumerType == ConvertUtils.CONSUMER_TYPE_UID_BATTERY
+                    && batteryHistEntry.mUserId == userId
+                    && packageName.equals(entry.getPackageName())) {
+                return entry;
+            }
+        }
+        return null;
+    }
+
     private static long[] getBatteryHistoryKeys(
             final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
         final List<Long> batteryHistoryKeyList =
diff --git a/src/com/android/settings/fuelgauge/BatteryDiffEntry.java b/src/com/android/settings/fuelgauge/BatteryDiffEntry.java
index e524e98..ca29cfe 100644
--- a/src/com/android/settings/fuelgauge/BatteryDiffEntry.java
+++ b/src/com/android/settings/fuelgauge/BatteryDiffEntry.java
@@ -25,6 +25,7 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.settings.R;
 import com.android.settingslib.utils.StringUtil;
 
 import java.util.Comparator;
@@ -52,6 +53,7 @@
     public double mConsumePower;
     // A BatteryHistEntry corresponding to this diff usage data.
     public final BatteryHistEntry mBatteryHistEntry;
+
     private double mTotalConsumePower;
     private double mPercentOfTotal;
 
@@ -151,8 +153,13 @@
             case ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY:
                 return true;
             case ConvertUtils.CONSUMER_TYPE_UID_BATTERY:
-                return isSystemUid((int) mBatteryHistEntry.mUid)
-                    || mBatteryHistEntry.mIsHidden;
+                if (mBatteryHistEntry.mIsHidden) {
+                    return true;
+                }
+                final boolean combineSystemComponents =
+                        mContext.getResources().getBoolean(
+                                R.bool.config_battery_combine_system_components);
+                return combineSystemComponents && isSystemUid((int) mBatteryHistEntry.mUid);
         }
         return false;
     }
diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java
index 73c473a..3ed305e 100644
--- a/src/com/android/settings/homepage/SettingsHomepageActivity.java
+++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java
@@ -182,6 +182,8 @@
             showSuggestionFragment(scrollNeeded);
             if (FeatureFlagUtils.isEnabled(this, FeatureFlags.CONTEXTUAL_HOME)) {
                 showFragment(() -> new ContextualCardsFragment(), R.id.contextual_cards_content);
+                ((FrameLayout) findViewById(R.id.main_content))
+                        .getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
             }
         }
         mMainFragment = showFragment(() -> {
@@ -191,9 +193,6 @@
             return fragment;
         }, R.id.main_content);
 
-        ((FrameLayout) findViewById(R.id.main_content))
-                .getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
-
         // Launch the intent from deep link for large screen devices.
         launchDeepLinkIntentToRight();
     }
diff --git a/src/com/android/settings/homepage/TopLevelHighlightMixin.java b/src/com/android/settings/homepage/TopLevelHighlightMixin.java
index ebfe7f2..c473e87 100644
--- a/src/com/android/settings/homepage/TopLevelHighlightMixin.java
+++ b/src/com/android/settings/homepage/TopLevelHighlightMixin.java
@@ -23,7 +23,6 @@
 import android.text.TextUtils;
 import android.util.Log;
 
-import androidx.fragment.app.FragmentActivity;
 import androidx.preference.PreferenceScreen;
 import androidx.recyclerview.widget.RecyclerView;
 
@@ -43,14 +42,17 @@
     private String mHiddenKey;
     private DialogInterface mDialog;
     private HighlightableTopLevelPreferenceAdapter mTopLevelAdapter;
+    private boolean mActivityEmbedded;
 
-    public TopLevelHighlightMixin() {
+    public TopLevelHighlightMixin(boolean activityEmbedded) {
+        mActivityEmbedded = activityEmbedded;
     }
 
     public TopLevelHighlightMixin(Parcel source) {
         mCurrentKey = source.readString();
         mPreviousKey = source.readString();
         mHiddenKey = source.readString();
+        mActivityEmbedded = source.readBoolean();
     }
 
     @Override
@@ -58,6 +60,7 @@
         dest.writeString(mCurrentKey);
         dest.writeString(mPreviousKey);
         dest.writeString(mHiddenKey);
+        dest.writeBoolean(mActivityEmbedded);
     }
 
     @Override
@@ -96,8 +99,16 @@
         }
     }
 
+    void setActivityEmbedded(boolean activityEmbedded) {
+        mActivityEmbedded = activityEmbedded;
+    }
+
+    boolean isActivityEmbedded() {
+        return mActivityEmbedded;
+    }
+
     RecyclerView.Adapter onCreateAdapter(TopLevelSettings topLevelSettings,
-            PreferenceScreen preferenceScreen) {
+            PreferenceScreen preferenceScreen, boolean scrollNeeded) {
         if (TextUtils.isEmpty(mCurrentKey)) {
             mCurrentKey = getHighlightPrefKeyFromArguments(topLevelSettings.getArguments());
         }
@@ -105,7 +116,7 @@
         Log.d(TAG, "onCreateAdapter, pref key: " + mCurrentKey);
         mTopLevelAdapter = new HighlightableTopLevelPreferenceAdapter(
                 (SettingsHomepageActivity) topLevelSettings.getActivity(), preferenceScreen,
-                topLevelSettings.getListView(), mCurrentKey);
+                topLevelSettings.getListView(), mCurrentKey, scrollNeeded);
         return mTopLevelAdapter;
     }
 
@@ -129,7 +140,7 @@
         }
     }
 
-    void highlightPreferenceIfNeeded(FragmentActivity activity) {
+    void highlightPreferenceIfNeeded() {
         if (mTopLevelAdapter != null) {
             mTopLevelAdapter.requestHighlight();
         }
diff --git a/src/com/android/settings/homepage/TopLevelSettings.java b/src/com/android/settings/homepage/TopLevelSettings.java
index 7d57834..334462b 100644
--- a/src/com/android/settings/homepage/TopLevelSettings.java
+++ b/src/com/android/settings/homepage/TopLevelSettings.java
@@ -58,6 +58,7 @@
 
     private boolean mIsEmbeddingActivityEnabled;
     private TopLevelHighlightMixin mHighlightMixin;
+    private boolean mScrollNeeded = true;
     private boolean mFirstStarted = true;
 
     public TopLevelSettings() {
@@ -133,11 +134,14 @@
             return;
         }
 
+        boolean activityEmbedded = SplitController.getInstance().isActivityEmbedded(getActivity());
         if (icicle != null) {
             mHighlightMixin = icicle.getParcelable(SAVED_HIGHLIGHT_MIXIN);
+            mScrollNeeded = !mHighlightMixin.isActivityEmbedded() && activityEmbedded;
+            mHighlightMixin.setActivityEmbedded(activityEmbedded);
         }
         if (mHighlightMixin == null) {
-            mHighlightMixin = new TopLevelHighlightMixin();
+            mHighlightMixin = new TopLevelHighlightMixin(activityEmbedded);
         }
     }
 
@@ -201,7 +205,7 @@
     @Override
     public void highlightPreferenceIfNeeded() {
         if (mHighlightMixin != null) {
-            mHighlightMixin.highlightPreferenceIfNeeded(getActivity());
+            mHighlightMixin.highlightPreferenceIfNeeded();
         }
     }
 
@@ -243,7 +247,7 @@
         if (!mIsEmbeddingActivityEnabled || !(getActivity() instanceof SettingsHomepageActivity)) {
             return super.onCreateAdapter(preferenceScreen);
         }
-        return mHighlightMixin.onCreateAdapter(this, preferenceScreen);
+        return mHighlightMixin.onCreateAdapter(this, preferenceScreen, mScrollNeeded);
     }
 
     @Override
diff --git a/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java b/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java
index 8e3f21a..6246b91 100644
--- a/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java
+++ b/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java
@@ -251,6 +251,10 @@
         }
 
         void setPreferenceEntries() {
+            mTelephonyManager = mTelephonyManager.createForSubscriptionId(mSubId);
+            final PersistableBundle carrierConfig = mCarrierConfigCache.getConfigForSubId(mSubId);
+            final boolean display2gOptions = carrierConfig
+                    .getBoolean(CarrierConfigManager.KEY_PREFER_2G_BOOL);
             clearAllEntries();
             String[] entryValues;
             int[] entryValuesInt;
@@ -266,7 +270,9 @@
                     add5gEntry(addNrToLteNetworkType(entryValuesInt[0]));
                     addLteEntry(entryValuesInt[0]);
                     add3gEntry(entryValuesInt[1]);
-                    add1xEntry(entryValuesInt[2]);
+                    if (display2gOptions) {
+                        add1xEntry(entryValuesInt[2]);
+                    }
                     addGlobalEntry(entryValuesInt[3]);
                     break;
                 case ENABLED_NETWORKS_CDMA_NO_LTE_CHOICES:
@@ -278,7 +284,9 @@
                                 "ENABLED_NETWORKS_CDMA_NO_LTE_CHOICES index error.");
                     }
                     add3gEntry(entryValuesInt[0]);
-                    add1xEntry(entryValuesInt[1]);
+                    if (display2gOptions) {
+                        add1xEntry(entryValuesInt[1]);
+                    }
                     break;
                 case ENABLED_NETWORKS_CDMA_ONLY_LTE_CHOICES:
                     entryValues = getResourcesForSubId().getStringArray(
@@ -302,7 +310,9 @@
                     add5gEntry(addNrToLteNetworkType(entryValuesInt[0]));
                     addLteEntry(entryValuesInt[0]);
                     add3gEntry(entryValuesInt[1]);
-                    add2gEntry(entryValuesInt[2]);
+                    if (display2gOptions) {
+                        add2gEntry(entryValuesInt[2]);
+                    }
                     break;
                 case ENABLED_NETWORKS_EXCEPT_GSM_LTE_CHOICES:
                     entryValues = getResourcesForSubId().getStringArray(
@@ -347,7 +357,9 @@
                                 "ENABLED_NETWORKS_EXCEPT_LTE_CHOICES index error.");
                     }
                     add3gEntry(entryValuesInt[0]);
-                    add2gEntry(entryValuesInt[1]);
+                    if (carrierConfig.getBoolean(CarrierConfigManager.KEY_PREFER_2G_BOOL)) {
+                        add2gEntry(entryValuesInt[1]);
+                    }
                     break;
                 case ENABLED_NETWORKS_4G_CHOICES:
                     entryValues = getResourcesForSubId().getStringArray(
@@ -361,7 +373,9 @@
                             entryValuesInt[0]));
                     add4gEntry(entryValuesInt[0]);
                     add3gEntry(entryValuesInt[1]);
-                    add2gEntry(entryValuesInt[2]);
+                    if (display2gOptions) {
+                        add2gEntry(entryValuesInt[2]);
+                    }
                     break;
                 case ENABLED_NETWORKS_CHOICES:
                     entryValues = getResourcesForSubId().getStringArray(
@@ -373,7 +387,9 @@
                     add5gEntry(addNrToLteNetworkType(entryValuesInt[0]));
                     addLteEntry(entryValuesInt[0]);
                     add3gEntry(entryValuesInt[1]);
-                    add2gEntry(entryValuesInt[2]);
+                    if (display2gOptions) {
+                        add2gEntry(entryValuesInt[2]);
+                    }
                     break;
                 case PREFERRED_NETWORK_MODE_CHOICES_WORLD_MODE:
                     entryValues = getResourcesForSubId().getStringArray(
diff --git a/src/com/android/settings/network/telephony/RenameMobileNetworkDialogFragment.java b/src/com/android/settings/network/telephony/RenameMobileNetworkDialogFragment.java
index 5f20894..ec48c82 100644
--- a/src/com/android/settings/network/telephony/RenameMobileNetworkDialogFragment.java
+++ b/src/com/android/settings/network/telephony/RenameMobileNetworkDialogFragment.java
@@ -184,7 +184,10 @@
         phoneTitle.setVisibility(info.isOpportunistic() ? View.GONE : View.VISIBLE);
 
         final TextView phoneNumber = view.findViewById(R.id.number_value);
-        phoneNumber.setText(DeviceInfoUtils.getBidiFormattedPhoneNumber(getContext(), info));
+        final String pn = DeviceInfoUtils.getBidiFormattedPhoneNumber(getContext(), info);
+        if (!TextUtils.isEmpty(pn)) {
+            phoneNumber.setText(pn);
+        }
     }
 
     @Override
diff --git a/src/com/android/settings/nfc/DefaultPaymentSettings.java b/src/com/android/settings/nfc/DefaultPaymentSettings.java
index 4dceefc..08b843d 100644
--- a/src/com/android/settings/nfc/DefaultPaymentSettings.java
+++ b/src/com/android/settings/nfc/DefaultPaymentSettings.java
@@ -143,7 +143,8 @@
             CandidateInfo info, String defaultKey, String systemDefaultKey) {
         final NfcPaymentCandidateInfo candidateInfo = (NfcPaymentCandidateInfo) info;
         if (candidateInfo.isManagedProfile()) {
-            pref.setSummary("Work");
+            final String textWork = getContext().getString(R.string.nfc_work_text);
+            pref.setSummary(textWork);
         }
     }
 
diff --git a/src/com/android/settings/nfc/PaymentDefaultDialog.java b/src/com/android/settings/nfc/PaymentDefaultDialog.java
index 75746ce..d333b3d 100644
--- a/src/com/android/settings/nfc/PaymentDefaultDialog.java
+++ b/src/com/android/settings/nfc/PaymentDefaultDialog.java
@@ -59,10 +59,13 @@
                 CardEmulation.EXTRA_SERVICE_COMPONENT);
         String category = intent.getStringExtra(CardEmulation.EXTRA_CATEGORY);
         UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER);
+
+        int userId;
         if (userHandle == null) {
-            userHandle = UserHandle.CURRENT;
+            userId = UserHandle.myUserId();
+        } else {
+            userId = userHandle.getIdentifier();
         }
-        int userId = userHandle.getIdentifier();
 
         setResult(RESULT_CANCELED);
         if (!buildDialog(component, category, userId)) {
diff --git a/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java b/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java
index 38458d9..dec06b0 100644
--- a/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java
+++ b/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java
@@ -26,6 +26,7 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.safetycenter.SafetyCenterManager;
 import android.safetycenter.SafetyEvent;
 
 import com.android.settings.security.ScreenLockPreferenceDetailsUtils;
@@ -37,8 +38,6 @@
 /** Broadcast receiver for handling requests from Safety Center for fresh data. */
 public class SafetySourceBroadcastReceiver extends BroadcastReceiver {
 
-    private static final SafetyEvent EVENT_REFRESH_REQUESTED =
-            new SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED).build();
     private static final SafetyEvent EVENT_DEVICE_REBOOTED =
             new SafetyEvent.Builder(SAFETY_EVENT_TYPE_DEVICE_REBOOTED).build();
 
@@ -52,10 +51,15 @@
             String[] sourceIdsExtra =
                     intent.getStringArrayExtra(EXTRA_REFRESH_SAFETY_SOURCE_IDS);
             if (sourceIdsExtra != null && sourceIdsExtra.length > 0) {
+                final String refreshBroadcastId = intent.getStringExtra(
+                        SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID);
+                final SafetyEvent safetyEvent = new SafetyEvent.Builder(
+                        SAFETY_EVENT_TYPE_REFRESH_REQUESTED)
+                        .setRefreshBroadcastId(refreshBroadcastId).build();
                 refreshSafetySources(
                         context,
                         ImmutableList.copyOf(sourceIdsExtra),
-                        EVENT_REFRESH_REQUESTED);
+                        safetyEvent);
             }
             return;
         }
diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java
index 8614c53..0f2eb66 100644
--- a/src/com/android/settings/users/UserSettings.java
+++ b/src/com/android/settings/users/UserSettings.java
@@ -1102,6 +1102,7 @@
             mUserListCategory.setTitle(R.string.user_list_title);
         } else {
             mUserListCategory.setTitle(null);
+            mUserListCategory.setLayoutResource(R.layout.empty_view);
         }
 
         // Remove everything from mUserListCategory and add new users.
diff --git a/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java b/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java
index ddd57f3..911aabb 100644
--- a/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java
+++ b/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java
@@ -67,10 +67,12 @@
     private SparseArray<PreferenceViewHolder> mViewHolders;
 
     public HighlightableTopLevelPreferenceAdapter(SettingsHomepageActivity homepageActivity,
-            PreferenceGroup preferenceGroup, RecyclerView recyclerView, String key) {
+            PreferenceGroup preferenceGroup, RecyclerView recyclerView, String key,
+            boolean scrollNeeded) {
         super(preferenceGroup);
         mRecyclerView = recyclerView;
         mHighlightKey = key;
+        mScrolled = !scrollNeeded;
         mViewHolders = new SparseArray<>();
         mContext = preferenceGroup.getContext();
         mHomepageActivity = homepageActivity;
diff --git a/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java b/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java
index d8bdf32..528fbc3 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java
@@ -168,10 +168,18 @@
                     break;
 
                 case MESSAGE_SCAN_ZXING_WIFI_FORMAT_SUCCESS:
+                    final Context context = getContext();
+                    if (context == null) {
+                        // Context may be null if the message is received after the Activity has
+                        // been destroyed
+                        Log.d(TAG, "Scan success but context is null");
+                        return;
+                    }
+
                     // We may get 2 WifiConfiguration if the QR code has no password in it,
                     // one for open network and one for enhanced open network.
                     final WifiManager wifiManager =
-                            getContext().getSystemService(WifiManager.class);
+                            context.getSystemService(WifiManager.class);
                     final WifiNetworkConfig qrCodeWifiNetworkConfig =
                             (WifiNetworkConfig)msg.obj;
                     final List<WifiConfiguration> qrCodeWifiConfigurations =
diff --git a/src/com/android/settings/wifi/qrcode/QrCamera.java b/src/com/android/settings/wifi/qrcode/QrCamera.java
index 3865eb1..d07c543 100644
--- a/src/com/android/settings/wifi/qrcode/QrCamera.java
+++ b/src/com/android/settings/wifi/qrcode/QrCamera.java
@@ -124,6 +124,7 @@
         }
         if (mCamera != null) {
             mCamera.stopPreview();
+            releaseCamera();
         }
     }
 
diff --git a/tests/robotests/assets/exempt_not_implementing_index_provider b/tests/robotests/assets/exempt_not_implementing_index_provider
index d4a1c2e..6a1a1ff 100644
--- a/tests/robotests/assets/exempt_not_implementing_index_provider
+++ b/tests/robotests/assets/exempt_not_implementing_index_provider
@@ -15,7 +15,6 @@
 com.android.settings.applications.appinfo.WriteSettingsDetails
 com.android.settings.applications.AppLaunchSettings
 com.android.settings.applications.AppStorageSettings
-com.android.settings.applications.ConfirmConvertToFbe
 com.android.settings.applications.ProcessStatsDetail
 com.android.settings.applications.ProcessStatsSummary
 com.android.settings.applications.ProcessStatsUi
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityFooterPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityFooterPreferenceControllerTest.java
index cc8520b..cce240d 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityFooterPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityFooterPreferenceControllerTest.java
@@ -121,4 +121,16 @@
         assertThat(learnMoreView.getVisibility()).isEqualTo(View.GONE);
         assertThat(mPreference.isLinkEnabled()).isFalse();
     }
+
+    @Test
+    public void onBindViewHolder_setHelpResource_expectSummaryViewIsNonFocusable() {
+        mController.setupHelpLink(R.string.help_url_timeout, TEST_CONTENT_DESCRIPTION);
+        mController.displayPreference(mScreen);
+
+        mPreference.onBindViewHolder(mPreferenceViewHolder);
+
+        final TextView summaryView = (TextView) mPreferenceViewHolder
+                .findViewById(android.R.id.title);
+        assertThat(summaryView.isFocusable()).isFalse();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityFooterPreferenceTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityFooterPreferenceTest.java
index 14c56e8..0f83bc3 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityFooterPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityFooterPreferenceTest.java
@@ -71,4 +71,15 @@
                 android.R.id.title);
         assertThat(summaryView.getMovementMethod()).isInstanceOf(MovementMethod.class);
     }
+
+    @Test
+    public void onBindViewHolder_setLinkEnabled_expectSummaryViewIsNonFocusable() {
+        mAccessibilityFooterPreference.setLinkEnabled(true);
+
+        mAccessibilityFooterPreference.onBindViewHolder(mPreferenceViewHolder);
+
+        final TextView summaryView = (TextView) mPreferenceViewHolder.findViewById(
+                android.R.id.title);
+        assertThat(summaryView.isFocusable()).isFalse();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java
index 04018a6..114f969 100644
--- a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java
@@ -322,7 +322,7 @@
                 (AccessibilityFooterPreference) mFragment.getPreferenceScreen().getPreference(
                         mFragment.getPreferenceScreen().getPreferenceCount() - 1);
         assertThat(accessibilityFooterPreference.getSummary()).isEqualTo(DEFAULT_SUMMARY);
-        assertThat(accessibilityFooterPreference.isSelectable()).isEqualTo(true);
+        assertThat(accessibilityFooterPreference.isSelectable()).isEqualTo(false);
         assertThat(accessibilityFooterPreference.getOrder()).isEqualTo(Integer.MAX_VALUE - 1);
     }
 
diff --git a/tests/robotests/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceControllerTest.java
index a1d479f..fcb3ea9 100644
--- a/tests/robotests/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceControllerTest.java
@@ -18,6 +18,8 @@
 
 import static com.android.settings.development.BluetoothA2dpHwOffloadPreferenceController
         .A2DP_OFFLOAD_DISABLED_PROPERTY;
+import static com.android.settings.development.BluetoothLeAudioHwOffloadPreferenceController
+        .LE_AUDIO_OFFLOAD_DISABLED_PROPERTY;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -62,15 +64,37 @@
     }
 
     @Test
-    public void onA2dpHwDialogConfirmed_shouldChangeProperty() {
+    public void onA2dpHwDialogConfirmedAsA2dpOffloadDisabled_shouldChangeProperty() {
         SystemProperties.set(A2DP_OFFLOAD_DISABLED_PROPERTY, Boolean.toString(false));
+        mController.mChanged = true;
 
-        mController.onA2dpHwDialogConfirmed();
+        mController.onHwOffloadDialogConfirmed();
         final boolean mode = SystemProperties.getBoolean(A2DP_OFFLOAD_DISABLED_PROPERTY, false);
         assertThat(mode).isTrue();
+    }
 
-        mController.onA2dpHwDialogConfirmed();
-        final boolean mode2 = SystemProperties.getBoolean(A2DP_OFFLOAD_DISABLED_PROPERTY, false);
-        assertThat(mode2).isFalse();
+    @Test
+    public void onA2dpHwDialogConfirmedAsA2dpOffloadEnabled_shouldChangeProperty() {
+        SystemProperties.set(A2DP_OFFLOAD_DISABLED_PROPERTY, Boolean.toString(true));
+        SystemProperties.set(LE_AUDIO_OFFLOAD_DISABLED_PROPERTY, Boolean.toString(true));
+
+        mController.mChanged = true;
+
+        mController.onHwOffloadDialogConfirmed();
+        final boolean a2dpMode = SystemProperties.getBoolean(A2DP_OFFLOAD_DISABLED_PROPERTY, true);
+        final boolean leAudioMode = SystemProperties
+                .getBoolean(LE_AUDIO_OFFLOAD_DISABLED_PROPERTY, true);
+        assertThat(a2dpMode).isFalse();
+        assertThat(leAudioMode).isFalse();
+    }
+
+    @Test
+    public void onA2dpHwDialogCanceled_shouldNotChangeProperty() {
+        SystemProperties.set(A2DP_OFFLOAD_DISABLED_PROPERTY, Boolean.toString(false));
+        mController.mChanged = true;
+
+        mController.onHwOffloadDialogCanceled();
+        final boolean mode = SystemProperties.getBoolean(A2DP_OFFLOAD_DISABLED_PROPERTY, false);
+        assertThat(mode).isFalse();
     }
 }
diff --git a/tests/robotests/src/com/android/settings/development/BluetoothGabeldorschePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BluetoothGabeldorschePreferenceControllerTest.java
deleted file mode 100644
index 1916bf2..0000000
--- a/tests/robotests/src/com/android/settings/development/BluetoothGabeldorschePreferenceControllerTest.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.development;
-
-import static com.android.settings.development.BluetoothGabeldorschePreferenceController
-        .CURRENT_GD_FLAG;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.provider.DeviceConfig;
-
-import androidx.preference.PreferenceScreen;
-import androidx.preference.SwitchPreference;
-
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-@RunWith(RobolectricTestRunner.class)
-public class BluetoothGabeldorschePreferenceControllerTest {
-
-    @Mock
-    private SwitchPreference mPreference;
-    @Mock
-    private PreferenceScreen mPreferenceScreen;
-
-    private BluetoothGabeldorschePreferenceController mController;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        Context context = RuntimeEnvironment.application;
-        mController = new BluetoothGabeldorschePreferenceController(context);
-        when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
-                .thenReturn(mPreference);
-        mController.displayPreference(mPreferenceScreen);
-    }
-
-    @Test
-    @Ignore
-    public void onPreferenceChanged_settingEnabled_shouldTurnOnBluetoothGabeldorsche() {
-        mController.onPreferenceChange(mPreference, true /* new value */);
-
-        boolean enabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH,
-                CURRENT_GD_FLAG, false /* defaultValue */);
-
-        assertThat(enabled).isTrue();
-    }
-
-    @Test
-    @Ignore
-    public void onPreferenceChanged_settingDisabled_shouldTurnOffBluetoothGabeldorsche() {
-        mController.onPreferenceChange(mPreference, false /* new value */);
-
-        boolean enabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH,
-                CURRENT_GD_FLAG, false /* defaultValue */);
-
-        assertThat(enabled).isFalse();
-    }
-
-    @Test
-    @Ignore
-    public void updateState_settingEnabled_preferenceShouldBeChecked() {
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BLUETOOTH,
-                CURRENT_GD_FLAG, "true", false /* makeDefault */);
-
-        mController.updateState(mPreference);
-
-        verify(mPreference).setChecked(true);
-    }
-
-    @Test
-    @Ignore
-    public void updateState_settingDisabled_preferenceShouldNotBeChecked() {
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BLUETOOTH,
-                CURRENT_GD_FLAG, "false", false /* makeDefault */);
-
-        mController.updateState(mPreference);
-
-        verify(mPreference).setChecked(false);
-    }
-
-    @Test
-    @Ignore
-    public void onDeveloperOptionsDisabled_shouldDisablePreference() {
-        mController.onDeveloperOptionsDisabled();
-
-        String configStr = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_BLUETOOTH,
-                CURRENT_GD_FLAG);
-
-        assertThat(configStr).isNull();
-        verify(mPreference).setEnabled(false);
-        verify(mPreference).setChecked(false);
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/development/BluetoothLeAudioHwOffloadPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BluetoothLeAudioHwOffloadPreferenceControllerTest.java
new file mode 100644
index 0000000..c82df40e
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/BluetoothLeAudioHwOffloadPreferenceControllerTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.development;
+
+import static com.android.settings.development.BluetoothLeAudioHwOffloadPreferenceController
+        .LE_AUDIO_OFFLOAD_DISABLED_PROPERTY;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.SystemProperties;
+
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class BluetoothLeAudioHwOffloadPreferenceControllerTest {
+
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+    @Mock
+    private DevelopmentSettingsDashboardFragment mFragment;
+
+    private Context mContext;
+    private SwitchPreference mPreference;
+    private BluetoothLeAudioHwOffloadPreferenceController mController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mPreference = new SwitchPreference(mContext);
+        mController = spy(new BluetoothLeAudioHwOffloadPreferenceController(mContext, mFragment));
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
+            .thenReturn(mPreference);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void onLeAudioHwDialogConfirmedAsLeAudioOffloadDisabled_shouldChangeProperty() {
+        SystemProperties.set(LE_AUDIO_OFFLOAD_DISABLED_PROPERTY, Boolean.toString(false));
+        mController.mChanged = true;
+
+        mController.onHwOffloadDialogConfirmed();
+        final boolean mode = SystemProperties.getBoolean(LE_AUDIO_OFFLOAD_DISABLED_PROPERTY, false);
+        assertThat(mode).isTrue();
+    }
+
+    @Test
+    public void onLeAudioHwDialogConfirmedAsLeAudioOffloadEnabled_shouldChangeProperty() {
+        SystemProperties.set(LE_AUDIO_OFFLOAD_DISABLED_PROPERTY, Boolean.toString(true));
+        mController.mChanged = true;
+
+        mController.onHwOffloadDialogConfirmed();
+        final boolean mode2 = SystemProperties.getBoolean(
+                LE_AUDIO_OFFLOAD_DISABLED_PROPERTY, true);
+        assertThat(mode2).isFalse();
+    }
+
+    @Test
+    public void onLeAudioHwDialogCanceled_shouldNotChangeProperty() {
+        SystemProperties.set(LE_AUDIO_OFFLOAD_DISABLED_PROPERTY, Boolean.toString(false));
+        mController.mChanged = true;
+
+        mController.onHwOffloadDialogCanceled();
+        final boolean mode = SystemProperties.getBoolean(LE_AUDIO_OFFLOAD_DISABLED_PROPERTY, false);
+        assertThat(mode).isFalse();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java
index 5e4be68..7db7141 100644
--- a/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java
@@ -208,9 +208,9 @@
         assertThat(dialog).isNotNull();
         ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
         assertThat(shadowDialog.getTitle()).isEqualTo(
-                mContext.getString(R.string.bluetooth_disable_a2dp_hw_offload_dialog_title));
+                mContext.getString(R.string.bluetooth_disable_hw_offload_dialog_title));
         assertThat(shadowDialog.getMessage()).isEqualTo(
-                mContext.getString(R.string.bluetooth_disable_a2dp_hw_offload_dialog_message));
+                mContext.getString(R.string.bluetooth_disable_hw_offload_dialog_message));
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/development/FileEncryptionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/FileEncryptionPreferenceControllerTest.java
deleted file mode 100644
index 0784a61..0000000
--- a/tests/robotests/src/com/android/settings/development/FileEncryptionPreferenceControllerTest.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.development;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.os.RemoteException;
-import android.os.storage.IStorageManager;
-import android.sysprop.CryptoProperties;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
-
-import com.android.settings.R;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.util.ReflectionHelpers;
-
-@RunWith(RobolectricTestRunner.class)
-public class FileEncryptionPreferenceControllerTest {
-
-    @Mock
-    private Preference mPreference;
-    @Mock
-    private PreferenceScreen mPreferenceScreen;
-    @Mock
-    private IStorageManager mStorageManager;
-
-    private Context mContext;
-    private FileEncryptionPreferenceController mController;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        mContext = RuntimeEnvironment.application;
-        mController = new FileEncryptionPreferenceController(mContext);
-        when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
-            .thenReturn(mPreference);
-    }
-
-    @Test
-    public void isAvailable_storageManagerNull_shouldBeFalse() {
-        ReflectionHelpers.setField(mController, "mStorageManager", null);
-
-        assertThat(mController.isAvailable()).isFalse();
-    }
-
-    @Test
-    public void isAvailable_notConvertibleToFBE_shouldBeFalse() throws RemoteException {
-        ReflectionHelpers.setField(mController, "mStorageManager", mStorageManager);
-        when(mStorageManager.isConvertibleToFBE()).thenReturn(false);
-
-        assertThat(mController.isAvailable()).isFalse();
-    }
-
-    @Test
-    public void isAvailable_convertibleToFBE_shouldBeTrue() throws RemoteException {
-        ReflectionHelpers.setField(mController, "mStorageManager", mStorageManager);
-        when(mStorageManager.isConvertibleToFBE()).thenReturn(true);
-
-        assertThat(mController.isAvailable()).isTrue();
-    }
-
-    @Test
-    public void updateState_settingIsNotFile_shouldDoNothing() throws RemoteException {
-        ReflectionHelpers.setField(mController, "mStorageManager", mStorageManager);
-        when(mStorageManager.isConvertibleToFBE()).thenReturn(true);
-        mController.displayPreference(mPreferenceScreen);
-        CryptoProperties.type(CryptoProperties.type_values.NONE);
-
-        mController.updateState(mPreference);
-
-        verify(mPreference, never()).setEnabled(anyBoolean());
-        verify(mPreference, never()).setSummary(anyString());
-    }
-
-    @Test
-    public void updateState_settingIsFile_shouldSetSummaryAndDisablePreference()
-            throws RemoteException {
-        ReflectionHelpers.setField(mController, "mStorageManager", mStorageManager);
-        when(mStorageManager.isConvertibleToFBE()).thenReturn(true);
-        mController.displayPreference(mPreferenceScreen);
-        CryptoProperties.type(CryptoProperties.type_values.FILE);
-
-        mController.updateState(mPreference);
-
-        verify(mPreference).setEnabled(false);
-        verify(mPreference).setSummary(mContext.getString(R.string.convert_to_file_encryption_done));
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/development/IngressRateLimitPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/IngressRateLimitPreferenceControllerTest.java
new file mode 100644
index 0000000..0f85a14
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/IngressRateLimitPreferenceControllerTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.development;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.ConnectivitySettingsManager;
+
+import androidx.preference.ListPreference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class IngressRateLimitPreferenceControllerTest {
+    private Context mContext = RuntimeEnvironment.application;
+    private ListPreference mPreference;
+    private IngressRateLimitPreferenceController mController;
+
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mPreference = new ListPreference(mContext);
+        mPreference.setEntries(R.array.ingress_rate_limit_entries);
+        mPreference.setEntryValues(R.array.ingress_rate_limit_values);
+
+        mController = new IngressRateLimitPreferenceController(mContext);
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
+                mPreference);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void onPreferenceChanged_select5Mbits_shouldEnableIngressRateLimit() {
+        final long newRateLimit = 625000; // 5mbit == 625000 B/s
+        assertThat(mController.onPreferenceChange(mPreference, newRateLimit)).isTrue();
+
+        final long configuredRateLimit =
+                ConnectivitySettingsManager.getIngressRateLimitInBytesPerSecond(mContext);
+        assertThat(configuredRateLimit).isEqualTo(newRateLimit);
+    }
+
+    @Test
+    public void onPreferenceChanged_selectDisabled_shouldDisableIngressRateLimit() {
+        final long disabledRateLimit = -1; // -1 == disabled
+        assertThat(mController.onPreferenceChange(mPreference, disabledRateLimit)).isTrue();
+
+        final long configuredRateLimit =
+                ConnectivitySettingsManager.getIngressRateLimitInBytesPerSecond(mContext);
+        assertThat(configuredRateLimit).isEqualTo(disabledRateLimit);
+    }
+
+    @Test
+    public void onPreferenceChanged_invalidValue_returnsFalse() {
+        final long invalidRateLimit = -123;
+        assertThat(mController.onPreferenceChange(mPreference, invalidRateLimit)).isFalse();
+    }
+
+    @Test
+    public void updateState_preferenceShouldBeSelected() {
+        final long newRateLimit = 625000; // 5mbit == 625000 B/s
+        ConnectivitySettingsManager.setIngressRateLimitInBytesPerSecond(mContext, newRateLimit);
+        mController.updateState(mPreference);
+        assertThat(Long.parseLong(mPreference.getValue())).isEqualTo(newRateLimit);
+    }
+
+    @Test
+    public void onDeveloperOptionsSwitchDisabled_shouldDisablePreference() {
+        final long newRateLimit = 625000; // 5mbit == 625000 B/s
+        ConnectivitySettingsManager.setIngressRateLimitInBytesPerSecond(mContext, newRateLimit);
+        mController.updateState(mPreference);
+
+        mController.onDeveloperOptionsSwitchDisabled();
+        assertThat(Long.parseLong(mPreference.getValue())).isEqualTo(-1);
+        assertThat(ConnectivitySettingsManager.getIngressRateLimitInBytesPerSecond(
+                mContext)).isEqualTo(-1);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryDiffEntryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryDiffEntryTest.java
index b1d8f0d..13ce29e 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryDiffEntryTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryDiffEntryTest.java
@@ -349,12 +349,12 @@
     }
 
     @Test
-    public void testIsSystemEntry_uidBatteryWithSystemProcess_returnTrue() {
+    public void testIsSystemEntry_uidBatteryWithSystemProcess_returnFalse() {
         final BatteryDiffEntry entry =
             createBatteryDiffEntry(
                 ConvertUtils.CONSUMER_TYPE_UID_BATTERY,
                 /*uid=*/ 1230, /*isHidden=*/ false);
-        assertThat(entry.isSystemEntry()).isTrue();
+        assertThat(entry.isSystemEntry()).isFalse();
     }
 
     @Test
diff --git a/tests/unit/src/com/android/settings/accessibility/HighTextContrastPreferenceControllerTest.java b/tests/unit/src/com/android/settings/accessibility/HighTextContrastPreferenceControllerTest.java
index 1ada051..9c4df36 100644
--- a/tests/unit/src/com/android/settings/accessibility/HighTextContrastPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/accessibility/HighTextContrastPreferenceControllerTest.java
@@ -19,8 +19,11 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.content.Context;
+import android.os.Looper;
 import android.provider.Settings;
 
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
 import androidx.preference.SwitchPreference;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -37,6 +40,7 @@
 @RunWith(AndroidJUnit4.class)
 public class HighTextContrastPreferenceControllerTest {
 
+    private static final String PREF_KEY = "text_contrast";
     private static final int ON = 1;
     private static final int OFF = 0;
     private static final int UNKNOWN = -1;
@@ -44,12 +48,20 @@
     private Context mContext;
     private SwitchPreference mPreference;
     private HighTextContrastPreferenceController mController;
+    private PreferenceScreen mScreen;
 
     @Before
     public void setUp() {
         mContext = ApplicationProvider.getApplicationContext();
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+        final PreferenceManager preferenceManager = new PreferenceManager(mContext);
+        mScreen = preferenceManager.createPreferenceScreen(mContext);
         mPreference = new SwitchPreference(mContext);
-        mController = new HighTextContrastPreferenceController(mContext, "text_contrast");
+        mPreference.setKey(PREF_KEY);
+        mScreen.addPreference(mPreference);
+        mController = new HighTextContrastPreferenceController(mContext, PREF_KEY);
     }
 
     @Test
@@ -99,10 +111,13 @@
 
     @Test
     public void resetState_shouldDisableTextContrast() {
+        mController.displayPreference(mScreen);
         mController.setChecked(true);
+        mPreference.setChecked(true);
 
         mController.resetState();
 
+        assertThat(mPreference.isChecked()).isFalse();
         assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, UNKNOWN)).isEqualTo(OFF);
     }
diff --git a/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java b/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java
index 8a4214c..8004d60 100644
--- a/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java
+++ b/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java
@@ -17,6 +17,7 @@
 package com.android.settings.safetycenter;
 
 import static android.safetycenter.SafetyCenterManager.ACTION_REFRESH_SAFETY_SOURCES;
+import static android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID;
 import static android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCE_IDS;
 import static android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_DEVICE_REBOOTED;
 import static android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_REFRESH_REQUESTED;
@@ -148,6 +149,29 @@
     }
 
     @Test
+    public void onReceive_onRefreshWithBroadcastId_setsRefreshEventWithBroadcastId() {
+        final String refreshBroadcastId = "REFRESH_BROADCAST_ID";
+        when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
+        Intent intent =
+                new Intent()
+                        .setAction(ACTION_REFRESH_SAFETY_SOURCES)
+                        .putExtra(
+                                EXTRA_REFRESH_SAFETY_SOURCE_IDS,
+                                new String[]{ LockScreenSafetySource.SAFETY_SOURCE_ID })
+                        .putExtra(EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID, refreshBroadcastId);
+
+        new SafetySourceBroadcastReceiver().onReceive(mApplicationContext, intent);
+        ArgumentCaptor<SafetyEvent> captor = ArgumentCaptor.forClass(SafetyEvent.class);
+        verify(mSafetyCenterManagerWrapper, times(1))
+                .setSafetySourceData(any(), any(), any(), captor.capture());
+
+        assertThat(captor.getValue().getRefreshBroadcastId()).isEqualTo(refreshBroadcastId);
+        assertThat(captor.getValue()).isEqualTo(
+                new SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED)
+                        .setRefreshBroadcastId(refreshBroadcastId).build());
+    }
+
+    @Test
     public void onReceive_onRefresh_withLockscreenSourceId_setsLockscreenData() {
         when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
         Intent intent =