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();
}