Merge "[LE]Gray out the a2dp and hfp when LeAudio is enabled"
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/values/dimens.xml b/res/values/dimens.xml
index 5fabd09..f225150 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -392,6 +392,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 5042302..0c3b8fd 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -11396,11 +11396,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.
diff --git a/res/xml/bluetooth_device_details_fragment.xml b/res/xml/bluetooth_device_details_fragment.xml
index 9df1955..34599f7 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 cd37f4c..a0f0151 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -88,12 +88,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"
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/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/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
index 4980ba3..425d1c4 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/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index 7556d23..47f47ce 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -477,7 +477,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));
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/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/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));
-    }
-}