Merge "[LE]Gray out the a2dp and hfp when LeAudio is enabled"
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();
     }