Loading src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java +2 −95 Original line number Diff line number Diff line Loading @@ -419,32 +419,7 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll mContext, SettingsEnums.ACTION_BLUETOOTH_PROFILE_LE_AUDIO_OFF, isCurrentDeviceInOrByPassAllowList()); LocalBluetoothProfile asha = mProfileManager.getHearingAidProfile(); LocalBluetoothProfile broadcastAssistant = mProfileManager.getLeAudioBroadcastAssistantProfile(); for (CachedBluetoothDevice leAudioDevice : mProfileDeviceMap.get(profile.toString())) { Log.d(TAG, "device:" + leAudioDevice.getDevice().getAnonymizedAddress() + " disable LE profile"); profile.setEnabled(leAudioDevice.getDevice(), false); if (asha != null) { asha.setEnabled(leAudioDevice.getDevice(), true); } if (broadcastAssistant != null) { Log.d(TAG, "device:" + leAudioDevice.getDevice().getAnonymizedAddress() + " disable LE broadcast assistant profile"); broadcastAssistant.setEnabled(leAudioDevice.getDevice(), false); } } if (!SystemProperties.getBoolean(ENABLE_DUAL_MODE_AUDIO, false)) { Log.i(TAG, "Enabling classic audio profiles because dual mode is disabled"); enableProfileAfterUserDisablesLeAudio(mProfileManager.getA2dpProfile()); enableProfileAfterUserDisablesLeAudio(mProfileManager.getHeadsetProfile()); } Utils.setLeAudioEnabled(mManager, List.copyOf(mCachedDeviceGroup), false); } /** Loading @@ -462,75 +437,7 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll mContext, SettingsEnums.ACTION_BLUETOOTH_PROFILE_LE_AUDIO_ON, isCurrentDeviceInOrByPassAllowList()); if (!SystemProperties.getBoolean(ENABLE_DUAL_MODE_AUDIO, false)) { Log.i(TAG, "Disabling classic audio profiles because dual mode is disabled"); disableProfileBeforeUserEnablesLeAudio(mProfileManager.getA2dpProfile()); disableProfileBeforeUserEnablesLeAudio(mProfileManager.getHeadsetProfile()); } LocalBluetoothProfile asha = mProfileManager.getHearingAidProfile(); LocalBluetoothProfile broadcastAssistant = mProfileManager.getLeAudioBroadcastAssistantProfile(); for (CachedBluetoothDevice leAudioDevice : mProfileDeviceMap.get(profile.toString())) { Log.d(TAG, "device:" + leAudioDevice.getDevice().getAnonymizedAddress() + " enable LE profile"); profile.setEnabled(leAudioDevice.getDevice(), true); if (asha != null) { asha.setEnabled(leAudioDevice.getDevice(), false); } if (broadcastAssistant != null) { Log.d(TAG, "device:" + leAudioDevice.getDevice().getAnonymizedAddress() + " enable LE broadcast assistant profile"); broadcastAssistant.setEnabled(leAudioDevice.getDevice(), true); } } } private void disableProfileBeforeUserEnablesLeAudio(LocalBluetoothProfile profile) { if (profile != null && mProfileDeviceMap.get(profile.toString()) != null) { Log.d(TAG, "Disable " + profile.toString() + " before user enables LE"); for (CachedBluetoothDevice profileDevice : mProfileDeviceMap.get(profile.toString())) { if (profile.isEnabled(profileDevice.getDevice())) { Log.d(TAG, "The " + profileDevice.getDevice().getAnonymizedAddress() + ":" + profile.toString() + " set disable"); profile.setEnabled(profileDevice.getDevice(), false); } else { Log.d(TAG, "The " + profileDevice.getDevice().getAnonymizedAddress() + ":" + profile.toString() + " profile is disabled. Do nothing."); } } } else { if (profile == null) { Log.w(TAG, "profile is null"); } else { Log.w(TAG, profile.toString() + " is not in " + mProfileDeviceMap); } } } private void enableProfileAfterUserDisablesLeAudio(LocalBluetoothProfile profile) { if (profile != null && mProfileDeviceMap.get(profile.toString()) != null) { Log.d(TAG, "enable " + profile.toString() + "after user disables LE"); for (CachedBluetoothDevice profileDevice : mProfileDeviceMap.get(profile.toString())) { if (!profile.isEnabled(profileDevice.getDevice())) { Log.d(TAG, "The " + profileDevice.getDevice().getAnonymizedAddress() + ":" + profile.toString() + " set enable"); profile.setEnabled(profileDevice.getDevice(), true); } else { Log.d(TAG, "The " + profileDevice.getDevice().getAnonymizedAddress() + ":" + profile.toString() + " profile is enabled. Do nothing."); } } } else { if (profile == null) { Log.w(TAG, "profile is null"); } else { Log.w(TAG, profile.toString() + " is not in " + mProfileDeviceMap); } } Utils.setLeAudioEnabled(mManager, List.copyOf(mCachedDeviceGroup), true); } /** Loading src/com/android/settings/bluetooth/Utils.java +122 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; import android.util.Log; Loading @@ -45,15 +46,20 @@ import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.bluetooth.BluetoothUtils.ErrorListener; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; import com.android.settingslib.bluetooth.HearingAidProfile; import com.android.settingslib.bluetooth.LeAudioProfile; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothManager.BluetoothManagerCallback; import com.android.settingslib.bluetooth.LocalBluetoothProfile; import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import com.android.settingslib.utils.ThreadUtils; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableList; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; Loading @@ -70,6 +76,7 @@ import java.util.stream.Collectors; public final class Utils { private static final String TAG = "BluetoothUtils"; private static final String ENABLE_DUAL_MODE_AUDIO = "persist.bluetooth.enable_dual_mode_audio"; static final boolean V = BluetoothUtils.V; // verbose logging static final boolean D = BluetoothUtils.D; // regular logging Loading Loading @@ -360,4 +367,119 @@ public final class Utils { dialog.show(); return dialog; } /** Enables/disables LE Audio profile for the device. */ public static void setLeAudioEnabled( @NonNull LocalBluetoothManager manager, @NonNull CachedBluetoothDevice cachedDevice, boolean enable) { List<CachedBluetoothDevice> devices = List.copyOf(findAllCachedBluetoothDevicesByGroupId(manager, cachedDevice)); setLeAudioEnabled(manager, devices, enable); } /** Enables/disables LE Audio profile for the devices in the same csip group. */ public static void setLeAudioEnabled( @NonNull LocalBluetoothManager manager, @NonNull List<CachedBluetoothDevice> devicesWithSameGroupId, boolean enable) { LocalBluetoothProfileManager profileManager = manager.getProfileManager(); LeAudioProfile leAudioProfile = profileManager.getLeAudioProfile(); List<CachedBluetoothDevice> leAudioDevices = getDevicesWithProfile(devicesWithSameGroupId, leAudioProfile); if (leAudioDevices.isEmpty()) { Log.i(TAG, "Fail to setLeAudioEnabled, no LE Audio profile found."); } boolean dualModeEnabled = SystemProperties.getBoolean(ENABLE_DUAL_MODE_AUDIO, false); if (enable && !dualModeEnabled) { Log.i(TAG, "Disabling classic audio profiles because dual mode is disabled"); setProfileEnabledWhenChangingLeAudio( devicesWithSameGroupId, profileManager.getA2dpProfile(), false); setProfileEnabledWhenChangingLeAudio( devicesWithSameGroupId, profileManager.getHeadsetProfile(), false); } HearingAidProfile asha = profileManager.getHearingAidProfile(); LocalBluetoothLeBroadcastAssistant broadcastAssistant = profileManager.getLeAudioBroadcastAssistantProfile(); for (CachedBluetoothDevice leAudioDevice : leAudioDevices) { Log.d( TAG, "device:" + leAudioDevice.getDevice().getAnonymizedAddress() + " set LE profile enabled: " + enable); leAudioProfile.setEnabled(leAudioDevice.getDevice(), enable); if (asha != null) { asha.setEnabled(leAudioDevice.getDevice(), !enable); } if (broadcastAssistant != null) { Log.d( TAG, "device:" + leAudioDevice.getDevice().getAnonymizedAddress() + " enable LE broadcast assistant profile: " + enable); broadcastAssistant.setEnabled(leAudioDevice.getDevice(), enable); } } if (!enable && !dualModeEnabled) { Log.i(TAG, "Enabling classic audio profiles because dual mode is disabled"); setProfileEnabledWhenChangingLeAudio( devicesWithSameGroupId, profileManager.getA2dpProfile(), true); setProfileEnabledWhenChangingLeAudio( devicesWithSameGroupId, profileManager.getHeadsetProfile(), true); } } private static List<CachedBluetoothDevice> getDevicesWithProfile( List<CachedBluetoothDevice> devices, LocalBluetoothProfile profile) { List<CachedBluetoothDevice> devicesWithProfile = new ArrayList<>(); for (CachedBluetoothDevice device : devices) { for (LocalBluetoothProfile currentProfile : device.getProfiles()) { if (currentProfile.toString().equals(profile.toString())) { devicesWithProfile.add(device); } } } return devicesWithProfile; } private static void setProfileEnabledWhenChangingLeAudio( List<CachedBluetoothDevice> devices, @Nullable LocalBluetoothProfile profile, boolean enable) { if (profile == null) { Log.i(TAG, "profile is null"); return; } List<CachedBluetoothDevice> deviceWithProfile = getDevicesWithProfile(devices, profile); Log.d(TAG, "Set " + profile + " enabled:" + enable + " when switching LE Audio"); for (CachedBluetoothDevice profileDevice : deviceWithProfile) { if (profile.isEnabled(profileDevice.getDevice()) != enable) { Log.d( TAG, "The " + profileDevice.getDevice().getAnonymizedAddress() + ":" + profile + " set to " + enable); profile.setEnabled(profileDevice.getDevice(), enable); } else { Log.d( TAG, "The " + profileDevice.getDevice().getAnonymizedAddress() + ":" + profile + " profile is already " + enable + ". Do nothing."); } } } } tests/robotests/src/com/android/settings/bluetooth/UtilsTest.java +165 −11 Original line number Diff line number Diff line Loading @@ -18,22 +18,29 @@ package com.android.settings.bluetooth; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.bluetooth.BluetoothCsipSetCoordinator; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeBroadcastReceiveState; import android.content.Context; import android.os.SystemProperties; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowBluetoothUtils; import com.android.settingslib.bluetooth.A2dpProfile; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; import com.android.settingslib.bluetooth.HeadsetProfile; import com.android.settingslib.bluetooth.HearingAidProfile; import com.android.settingslib.bluetooth.LeAudioProfile; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; import com.android.settingslib.bluetooth.LocalBluetoothManager; Loading @@ -41,8 +48,8 @@ import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; Loading @@ -52,10 +59,8 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; @RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowBluetoothUtils.class}) public class UtilsTest { private static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25; private static final String TEMP_BOND_METADATA = Loading @@ -73,6 +78,14 @@ public class UtilsTest { @Mock private LocalBluetoothLeBroadcastAssistant mAssistant; @Mock private A2dpProfile mA2dpProfile; @Mock private HeadsetProfile mHeadsetProfile; @Mock private LeAudioProfile mLeAudioProfile; @Mock private HearingAidProfile mHearingAidProfile; @Mock private CachedBluetoothDeviceManager mDeviceManager; private MetricsFeatureProvider mMetricsFeatureProvider; Loading @@ -80,17 +93,14 @@ public class UtilsTest { @Before public void setUp() { mMetricsFeatureProvider = FakeFeatureFactory.setupForTest().getMetricsFeatureProvider(); ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager; mLocalBtManager = Utils.getLocalBtManager(mContext); when(mLocalBtManager.getProfileManager()).thenReturn(mProfileManager); when(mLocalBtManager.getCachedDeviceManager()).thenReturn(mDeviceManager); when(mProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast); when(mProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant); } @After public void tearDown() { ShadowBluetoothUtils.reset(); when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile); when(mProfileManager.getHeadsetProfile()).thenReturn(mHeadsetProfile); when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile); when(mProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile); } @Test Loading Loading @@ -170,4 +180,148 @@ public class UtilsTest { when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(state)); assertThat(Utils.shouldBlockPairingInAudioSharing(mLocalBtManager)).isTrue(); } @Test public void enableLeAudioProfile_multipleDeviceInGroup() { CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class); CachedBluetoothDevice cachedDevice2 = mock(CachedBluetoothDevice.class); CachedBluetoothDevice cachedDevice3 = mock(CachedBluetoothDevice.class); BluetoothDevice device1 = mock(BluetoothDevice.class); BluetoothDevice device2 = mock(BluetoothDevice.class); BluetoothDevice device3 = mock(BluetoothDevice.class); when(cachedDevice1.getDevice()).thenReturn(device1); when(cachedDevice2.getDevice()).thenReturn(device2); when(cachedDevice3.getDevice()).thenReturn(device3); when(cachedDevice1.getMemberDevice()).thenReturn(ImmutableSet.of(cachedDevice2)); when(mDeviceManager.getCachedDevicesCopy()) .thenReturn(ImmutableList.of(cachedDevice1, cachedDevice3)); when(cachedDevice1.getGroupId()).thenReturn(1); when(cachedDevice2.getGroupId()).thenReturn(1); when(cachedDevice3.getGroupId()).thenReturn(2); when(cachedDevice1.getProfiles()) .thenReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile, mLeAudioProfile)); when(cachedDevice2.getProfiles()).thenReturn(ImmutableList.of(mLeAudioProfile)); when(cachedDevice3.getProfiles()) .thenReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile, mLeAudioProfile)); Utils.setLeAudioEnabled(mLocalBtManager, cachedDevice2, true); verify(mLeAudioProfile).setEnabled(device1, true); verify(mLeAudioProfile).setEnabled(device2, true); verify(mHearingAidProfile).setEnabled(device1, false); verify(mAssistant).setEnabled(device1, true); verify(mLeAudioProfile, never()).setEnabled(eq(device3), anyBoolean()); verify(mA2dpProfile, never()).setEnabled(eq(device3), anyBoolean()); verify(mHeadsetProfile, never()).setEnabled(eq(device3), anyBoolean()); } @Test public void enableLeAudioProfile_dualModeEnabled_a2dpAndHfpNotChanged() { SystemProperties.set("persist.bluetooth.enable_dual_mode_audio", "true"); CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class); BluetoothDevice device1 = mock(BluetoothDevice.class); when(cachedDevice1.getDevice()).thenReturn(device1); when(cachedDevice1.getGroupId()).thenReturn(BluetoothCsipSetCoordinator.GROUP_ID_INVALID); when(cachedDevice1.getProfiles()) .thenReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile, mLeAudioProfile)); when(mA2dpProfile.isEnabled(device1)).thenReturn(true); when(mHeadsetProfile.isEnabled(device1)).thenReturn(true); Utils.setLeAudioEnabled(mLocalBtManager, cachedDevice1, true); verify(mLeAudioProfile).setEnabled(device1, true); verify(mA2dpProfile, never()).setEnabled(device1, false); verify(mHeadsetProfile, never()).setEnabled(device1, false); } @Test public void enableLeAudioProfile_dualModeDisabled_disableA2dpAndHfp() { SystemProperties.set("persist.bluetooth.enable_dual_mode_audio", "false"); CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class); BluetoothDevice device1 = mock(BluetoothDevice.class); when(cachedDevice1.getDevice()).thenReturn(device1); when(cachedDevice1.getGroupId()).thenReturn(BluetoothCsipSetCoordinator.GROUP_ID_INVALID); when(cachedDevice1.getProfiles()) .thenReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile, mLeAudioProfile)); when(mA2dpProfile.isEnabled(device1)).thenReturn(true); when(mHeadsetProfile.isEnabled(device1)).thenReturn(true); Utils.setLeAudioEnabled(mLocalBtManager, cachedDevice1, true); verify(mLeAudioProfile).setEnabled(device1, true); verify(mA2dpProfile).setEnabled(device1, false); verify(mHeadsetProfile).setEnabled(device1, false); } @Test public void disableLeAudioProfile_multipleDeviceInGroup() { CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class); CachedBluetoothDevice cachedDevice2 = mock(CachedBluetoothDevice.class); CachedBluetoothDevice cachedDevice3 = mock(CachedBluetoothDevice.class); BluetoothDevice device1 = mock(BluetoothDevice.class); BluetoothDevice device2 = mock(BluetoothDevice.class); BluetoothDevice device3 = mock(BluetoothDevice.class); when(cachedDevice1.getDevice()).thenReturn(device1); when(cachedDevice2.getDevice()).thenReturn(device2); when(cachedDevice3.getDevice()).thenReturn(device3); when(cachedDevice1.getMemberDevice()).thenReturn(ImmutableSet.of(cachedDevice2)); when(mDeviceManager.getCachedDevicesCopy()) .thenReturn(ImmutableList.of(cachedDevice1, cachedDevice3)); when(cachedDevice1.getGroupId()).thenReturn(1); when(cachedDevice2.getGroupId()).thenReturn(1); when(cachedDevice3.getGroupId()).thenReturn(2); when(cachedDevice1.getProfiles()) .thenReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile, mLeAudioProfile)); when(cachedDevice2.getProfiles()).thenReturn(ImmutableList.of(mLeAudioProfile)); when(cachedDevice3.getProfiles()) .thenReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile, mLeAudioProfile)); Utils.setLeAudioEnabled(mLocalBtManager, cachedDevice2, false); verify(mLeAudioProfile).setEnabled(device1, false); verify(mLeAudioProfile).setEnabled(device2, false); verify(mHearingAidProfile).setEnabled(device1, true); verify(mAssistant).setEnabled(device1, false); verify(mLeAudioProfile, never()).setEnabled(eq(device3), anyBoolean()); verify(mA2dpProfile, never()).setEnabled(eq(device3), anyBoolean()); verify(mHeadsetProfile, never()).setEnabled(eq(device3), anyBoolean()); } @Test public void disableLeAudioProfile_dualModeEnabled_a2dpAndHfpNotChanged() { SystemProperties.set("persist.bluetooth.enable_dual_mode_audio", "true"); CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class); BluetoothDevice device1 = mock(BluetoothDevice.class); when(cachedDevice1.getDevice()).thenReturn(device1); when(cachedDevice1.getGroupId()).thenReturn(BluetoothCsipSetCoordinator.GROUP_ID_INVALID); when(cachedDevice1.getProfiles()) .thenReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile, mLeAudioProfile)); when(mA2dpProfile.isEnabled(device1)).thenReturn(false); when(mHeadsetProfile.isEnabled(device1)).thenReturn(false); Utils.setLeAudioEnabled(mLocalBtManager, cachedDevice1, false); verify(mLeAudioProfile).setEnabled(device1, false); verify(mA2dpProfile, never()).setEnabled(device1, true); verify(mHeadsetProfile, never()).setEnabled(device1, true); } @Test public void disableLeAudioProfile_dualModeDisabled_enableA2dpAndHfp() { SystemProperties.set("persist.bluetooth.enable_dual_mode_audio", "false"); CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class); BluetoothDevice device1 = mock(BluetoothDevice.class); when(cachedDevice1.getDevice()).thenReturn(device1); when(cachedDevice1.getGroupId()).thenReturn(BluetoothCsipSetCoordinator.GROUP_ID_INVALID); when(cachedDevice1.getProfiles()) .thenReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile, mLeAudioProfile)); when(mA2dpProfile.isEnabled(device1)).thenReturn(false); when(mHeadsetProfile.isEnabled(device1)).thenReturn(false); Utils.setLeAudioEnabled(mLocalBtManager, cachedDevice1, false); verify(mLeAudioProfile).setEnabled(device1, false); verify(mA2dpProfile).setEnabled(device1, true); verify(mHeadsetProfile).setEnabled(device1, true); } } tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowBluetoothUtils.java +24 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java +2 −95 Original line number Diff line number Diff line Loading @@ -419,32 +419,7 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll mContext, SettingsEnums.ACTION_BLUETOOTH_PROFILE_LE_AUDIO_OFF, isCurrentDeviceInOrByPassAllowList()); LocalBluetoothProfile asha = mProfileManager.getHearingAidProfile(); LocalBluetoothProfile broadcastAssistant = mProfileManager.getLeAudioBroadcastAssistantProfile(); for (CachedBluetoothDevice leAudioDevice : mProfileDeviceMap.get(profile.toString())) { Log.d(TAG, "device:" + leAudioDevice.getDevice().getAnonymizedAddress() + " disable LE profile"); profile.setEnabled(leAudioDevice.getDevice(), false); if (asha != null) { asha.setEnabled(leAudioDevice.getDevice(), true); } if (broadcastAssistant != null) { Log.d(TAG, "device:" + leAudioDevice.getDevice().getAnonymizedAddress() + " disable LE broadcast assistant profile"); broadcastAssistant.setEnabled(leAudioDevice.getDevice(), false); } } if (!SystemProperties.getBoolean(ENABLE_DUAL_MODE_AUDIO, false)) { Log.i(TAG, "Enabling classic audio profiles because dual mode is disabled"); enableProfileAfterUserDisablesLeAudio(mProfileManager.getA2dpProfile()); enableProfileAfterUserDisablesLeAudio(mProfileManager.getHeadsetProfile()); } Utils.setLeAudioEnabled(mManager, List.copyOf(mCachedDeviceGroup), false); } /** Loading @@ -462,75 +437,7 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll mContext, SettingsEnums.ACTION_BLUETOOTH_PROFILE_LE_AUDIO_ON, isCurrentDeviceInOrByPassAllowList()); if (!SystemProperties.getBoolean(ENABLE_DUAL_MODE_AUDIO, false)) { Log.i(TAG, "Disabling classic audio profiles because dual mode is disabled"); disableProfileBeforeUserEnablesLeAudio(mProfileManager.getA2dpProfile()); disableProfileBeforeUserEnablesLeAudio(mProfileManager.getHeadsetProfile()); } LocalBluetoothProfile asha = mProfileManager.getHearingAidProfile(); LocalBluetoothProfile broadcastAssistant = mProfileManager.getLeAudioBroadcastAssistantProfile(); for (CachedBluetoothDevice leAudioDevice : mProfileDeviceMap.get(profile.toString())) { Log.d(TAG, "device:" + leAudioDevice.getDevice().getAnonymizedAddress() + " enable LE profile"); profile.setEnabled(leAudioDevice.getDevice(), true); if (asha != null) { asha.setEnabled(leAudioDevice.getDevice(), false); } if (broadcastAssistant != null) { Log.d(TAG, "device:" + leAudioDevice.getDevice().getAnonymizedAddress() + " enable LE broadcast assistant profile"); broadcastAssistant.setEnabled(leAudioDevice.getDevice(), true); } } } private void disableProfileBeforeUserEnablesLeAudio(LocalBluetoothProfile profile) { if (profile != null && mProfileDeviceMap.get(profile.toString()) != null) { Log.d(TAG, "Disable " + profile.toString() + " before user enables LE"); for (CachedBluetoothDevice profileDevice : mProfileDeviceMap.get(profile.toString())) { if (profile.isEnabled(profileDevice.getDevice())) { Log.d(TAG, "The " + profileDevice.getDevice().getAnonymizedAddress() + ":" + profile.toString() + " set disable"); profile.setEnabled(profileDevice.getDevice(), false); } else { Log.d(TAG, "The " + profileDevice.getDevice().getAnonymizedAddress() + ":" + profile.toString() + " profile is disabled. Do nothing."); } } } else { if (profile == null) { Log.w(TAG, "profile is null"); } else { Log.w(TAG, profile.toString() + " is not in " + mProfileDeviceMap); } } } private void enableProfileAfterUserDisablesLeAudio(LocalBluetoothProfile profile) { if (profile != null && mProfileDeviceMap.get(profile.toString()) != null) { Log.d(TAG, "enable " + profile.toString() + "after user disables LE"); for (CachedBluetoothDevice profileDevice : mProfileDeviceMap.get(profile.toString())) { if (!profile.isEnabled(profileDevice.getDevice())) { Log.d(TAG, "The " + profileDevice.getDevice().getAnonymizedAddress() + ":" + profile.toString() + " set enable"); profile.setEnabled(profileDevice.getDevice(), true); } else { Log.d(TAG, "The " + profileDevice.getDevice().getAnonymizedAddress() + ":" + profile.toString() + " profile is enabled. Do nothing."); } } } else { if (profile == null) { Log.w(TAG, "profile is null"); } else { Log.w(TAG, profile.toString() + " is not in " + mProfileDeviceMap); } } Utils.setLeAudioEnabled(mManager, List.copyOf(mCachedDeviceGroup), true); } /** Loading
src/com/android/settings/bluetooth/Utils.java +122 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; import android.util.Log; Loading @@ -45,15 +46,20 @@ import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.bluetooth.BluetoothUtils.ErrorListener; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; import com.android.settingslib.bluetooth.HearingAidProfile; import com.android.settingslib.bluetooth.LeAudioProfile; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothManager.BluetoothManagerCallback; import com.android.settingslib.bluetooth.LocalBluetoothProfile; import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import com.android.settingslib.utils.ThreadUtils; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableList; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; Loading @@ -70,6 +76,7 @@ import java.util.stream.Collectors; public final class Utils { private static final String TAG = "BluetoothUtils"; private static final String ENABLE_DUAL_MODE_AUDIO = "persist.bluetooth.enable_dual_mode_audio"; static final boolean V = BluetoothUtils.V; // verbose logging static final boolean D = BluetoothUtils.D; // regular logging Loading Loading @@ -360,4 +367,119 @@ public final class Utils { dialog.show(); return dialog; } /** Enables/disables LE Audio profile for the device. */ public static void setLeAudioEnabled( @NonNull LocalBluetoothManager manager, @NonNull CachedBluetoothDevice cachedDevice, boolean enable) { List<CachedBluetoothDevice> devices = List.copyOf(findAllCachedBluetoothDevicesByGroupId(manager, cachedDevice)); setLeAudioEnabled(manager, devices, enable); } /** Enables/disables LE Audio profile for the devices in the same csip group. */ public static void setLeAudioEnabled( @NonNull LocalBluetoothManager manager, @NonNull List<CachedBluetoothDevice> devicesWithSameGroupId, boolean enable) { LocalBluetoothProfileManager profileManager = manager.getProfileManager(); LeAudioProfile leAudioProfile = profileManager.getLeAudioProfile(); List<CachedBluetoothDevice> leAudioDevices = getDevicesWithProfile(devicesWithSameGroupId, leAudioProfile); if (leAudioDevices.isEmpty()) { Log.i(TAG, "Fail to setLeAudioEnabled, no LE Audio profile found."); } boolean dualModeEnabled = SystemProperties.getBoolean(ENABLE_DUAL_MODE_AUDIO, false); if (enable && !dualModeEnabled) { Log.i(TAG, "Disabling classic audio profiles because dual mode is disabled"); setProfileEnabledWhenChangingLeAudio( devicesWithSameGroupId, profileManager.getA2dpProfile(), false); setProfileEnabledWhenChangingLeAudio( devicesWithSameGroupId, profileManager.getHeadsetProfile(), false); } HearingAidProfile asha = profileManager.getHearingAidProfile(); LocalBluetoothLeBroadcastAssistant broadcastAssistant = profileManager.getLeAudioBroadcastAssistantProfile(); for (CachedBluetoothDevice leAudioDevice : leAudioDevices) { Log.d( TAG, "device:" + leAudioDevice.getDevice().getAnonymizedAddress() + " set LE profile enabled: " + enable); leAudioProfile.setEnabled(leAudioDevice.getDevice(), enable); if (asha != null) { asha.setEnabled(leAudioDevice.getDevice(), !enable); } if (broadcastAssistant != null) { Log.d( TAG, "device:" + leAudioDevice.getDevice().getAnonymizedAddress() + " enable LE broadcast assistant profile: " + enable); broadcastAssistant.setEnabled(leAudioDevice.getDevice(), enable); } } if (!enable && !dualModeEnabled) { Log.i(TAG, "Enabling classic audio profiles because dual mode is disabled"); setProfileEnabledWhenChangingLeAudio( devicesWithSameGroupId, profileManager.getA2dpProfile(), true); setProfileEnabledWhenChangingLeAudio( devicesWithSameGroupId, profileManager.getHeadsetProfile(), true); } } private static List<CachedBluetoothDevice> getDevicesWithProfile( List<CachedBluetoothDevice> devices, LocalBluetoothProfile profile) { List<CachedBluetoothDevice> devicesWithProfile = new ArrayList<>(); for (CachedBluetoothDevice device : devices) { for (LocalBluetoothProfile currentProfile : device.getProfiles()) { if (currentProfile.toString().equals(profile.toString())) { devicesWithProfile.add(device); } } } return devicesWithProfile; } private static void setProfileEnabledWhenChangingLeAudio( List<CachedBluetoothDevice> devices, @Nullable LocalBluetoothProfile profile, boolean enable) { if (profile == null) { Log.i(TAG, "profile is null"); return; } List<CachedBluetoothDevice> deviceWithProfile = getDevicesWithProfile(devices, profile); Log.d(TAG, "Set " + profile + " enabled:" + enable + " when switching LE Audio"); for (CachedBluetoothDevice profileDevice : deviceWithProfile) { if (profile.isEnabled(profileDevice.getDevice()) != enable) { Log.d( TAG, "The " + profileDevice.getDevice().getAnonymizedAddress() + ":" + profile + " set to " + enable); profile.setEnabled(profileDevice.getDevice(), enable); } else { Log.d( TAG, "The " + profileDevice.getDevice().getAnonymizedAddress() + ":" + profile + " profile is already " + enable + ". Do nothing."); } } } }
tests/robotests/src/com/android/settings/bluetooth/UtilsTest.java +165 −11 Original line number Diff line number Diff line Loading @@ -18,22 +18,29 @@ package com.android.settings.bluetooth; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.bluetooth.BluetoothCsipSetCoordinator; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeBroadcastReceiveState; import android.content.Context; import android.os.SystemProperties; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowBluetoothUtils; import com.android.settingslib.bluetooth.A2dpProfile; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; import com.android.settingslib.bluetooth.HeadsetProfile; import com.android.settingslib.bluetooth.HearingAidProfile; import com.android.settingslib.bluetooth.LeAudioProfile; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; import com.android.settingslib.bluetooth.LocalBluetoothManager; Loading @@ -41,8 +48,8 @@ import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; Loading @@ -52,10 +59,8 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; @RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowBluetoothUtils.class}) public class UtilsTest { private static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25; private static final String TEMP_BOND_METADATA = Loading @@ -73,6 +78,14 @@ public class UtilsTest { @Mock private LocalBluetoothLeBroadcastAssistant mAssistant; @Mock private A2dpProfile mA2dpProfile; @Mock private HeadsetProfile mHeadsetProfile; @Mock private LeAudioProfile mLeAudioProfile; @Mock private HearingAidProfile mHearingAidProfile; @Mock private CachedBluetoothDeviceManager mDeviceManager; private MetricsFeatureProvider mMetricsFeatureProvider; Loading @@ -80,17 +93,14 @@ public class UtilsTest { @Before public void setUp() { mMetricsFeatureProvider = FakeFeatureFactory.setupForTest().getMetricsFeatureProvider(); ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager; mLocalBtManager = Utils.getLocalBtManager(mContext); when(mLocalBtManager.getProfileManager()).thenReturn(mProfileManager); when(mLocalBtManager.getCachedDeviceManager()).thenReturn(mDeviceManager); when(mProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast); when(mProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant); } @After public void tearDown() { ShadowBluetoothUtils.reset(); when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile); when(mProfileManager.getHeadsetProfile()).thenReturn(mHeadsetProfile); when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile); when(mProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile); } @Test Loading Loading @@ -170,4 +180,148 @@ public class UtilsTest { when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(state)); assertThat(Utils.shouldBlockPairingInAudioSharing(mLocalBtManager)).isTrue(); } @Test public void enableLeAudioProfile_multipleDeviceInGroup() { CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class); CachedBluetoothDevice cachedDevice2 = mock(CachedBluetoothDevice.class); CachedBluetoothDevice cachedDevice3 = mock(CachedBluetoothDevice.class); BluetoothDevice device1 = mock(BluetoothDevice.class); BluetoothDevice device2 = mock(BluetoothDevice.class); BluetoothDevice device3 = mock(BluetoothDevice.class); when(cachedDevice1.getDevice()).thenReturn(device1); when(cachedDevice2.getDevice()).thenReturn(device2); when(cachedDevice3.getDevice()).thenReturn(device3); when(cachedDevice1.getMemberDevice()).thenReturn(ImmutableSet.of(cachedDevice2)); when(mDeviceManager.getCachedDevicesCopy()) .thenReturn(ImmutableList.of(cachedDevice1, cachedDevice3)); when(cachedDevice1.getGroupId()).thenReturn(1); when(cachedDevice2.getGroupId()).thenReturn(1); when(cachedDevice3.getGroupId()).thenReturn(2); when(cachedDevice1.getProfiles()) .thenReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile, mLeAudioProfile)); when(cachedDevice2.getProfiles()).thenReturn(ImmutableList.of(mLeAudioProfile)); when(cachedDevice3.getProfiles()) .thenReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile, mLeAudioProfile)); Utils.setLeAudioEnabled(mLocalBtManager, cachedDevice2, true); verify(mLeAudioProfile).setEnabled(device1, true); verify(mLeAudioProfile).setEnabled(device2, true); verify(mHearingAidProfile).setEnabled(device1, false); verify(mAssistant).setEnabled(device1, true); verify(mLeAudioProfile, never()).setEnabled(eq(device3), anyBoolean()); verify(mA2dpProfile, never()).setEnabled(eq(device3), anyBoolean()); verify(mHeadsetProfile, never()).setEnabled(eq(device3), anyBoolean()); } @Test public void enableLeAudioProfile_dualModeEnabled_a2dpAndHfpNotChanged() { SystemProperties.set("persist.bluetooth.enable_dual_mode_audio", "true"); CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class); BluetoothDevice device1 = mock(BluetoothDevice.class); when(cachedDevice1.getDevice()).thenReturn(device1); when(cachedDevice1.getGroupId()).thenReturn(BluetoothCsipSetCoordinator.GROUP_ID_INVALID); when(cachedDevice1.getProfiles()) .thenReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile, mLeAudioProfile)); when(mA2dpProfile.isEnabled(device1)).thenReturn(true); when(mHeadsetProfile.isEnabled(device1)).thenReturn(true); Utils.setLeAudioEnabled(mLocalBtManager, cachedDevice1, true); verify(mLeAudioProfile).setEnabled(device1, true); verify(mA2dpProfile, never()).setEnabled(device1, false); verify(mHeadsetProfile, never()).setEnabled(device1, false); } @Test public void enableLeAudioProfile_dualModeDisabled_disableA2dpAndHfp() { SystemProperties.set("persist.bluetooth.enable_dual_mode_audio", "false"); CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class); BluetoothDevice device1 = mock(BluetoothDevice.class); when(cachedDevice1.getDevice()).thenReturn(device1); when(cachedDevice1.getGroupId()).thenReturn(BluetoothCsipSetCoordinator.GROUP_ID_INVALID); when(cachedDevice1.getProfiles()) .thenReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile, mLeAudioProfile)); when(mA2dpProfile.isEnabled(device1)).thenReturn(true); when(mHeadsetProfile.isEnabled(device1)).thenReturn(true); Utils.setLeAudioEnabled(mLocalBtManager, cachedDevice1, true); verify(mLeAudioProfile).setEnabled(device1, true); verify(mA2dpProfile).setEnabled(device1, false); verify(mHeadsetProfile).setEnabled(device1, false); } @Test public void disableLeAudioProfile_multipleDeviceInGroup() { CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class); CachedBluetoothDevice cachedDevice2 = mock(CachedBluetoothDevice.class); CachedBluetoothDevice cachedDevice3 = mock(CachedBluetoothDevice.class); BluetoothDevice device1 = mock(BluetoothDevice.class); BluetoothDevice device2 = mock(BluetoothDevice.class); BluetoothDevice device3 = mock(BluetoothDevice.class); when(cachedDevice1.getDevice()).thenReturn(device1); when(cachedDevice2.getDevice()).thenReturn(device2); when(cachedDevice3.getDevice()).thenReturn(device3); when(cachedDevice1.getMemberDevice()).thenReturn(ImmutableSet.of(cachedDevice2)); when(mDeviceManager.getCachedDevicesCopy()) .thenReturn(ImmutableList.of(cachedDevice1, cachedDevice3)); when(cachedDevice1.getGroupId()).thenReturn(1); when(cachedDevice2.getGroupId()).thenReturn(1); when(cachedDevice3.getGroupId()).thenReturn(2); when(cachedDevice1.getProfiles()) .thenReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile, mLeAudioProfile)); when(cachedDevice2.getProfiles()).thenReturn(ImmutableList.of(mLeAudioProfile)); when(cachedDevice3.getProfiles()) .thenReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile, mLeAudioProfile)); Utils.setLeAudioEnabled(mLocalBtManager, cachedDevice2, false); verify(mLeAudioProfile).setEnabled(device1, false); verify(mLeAudioProfile).setEnabled(device2, false); verify(mHearingAidProfile).setEnabled(device1, true); verify(mAssistant).setEnabled(device1, false); verify(mLeAudioProfile, never()).setEnabled(eq(device3), anyBoolean()); verify(mA2dpProfile, never()).setEnabled(eq(device3), anyBoolean()); verify(mHeadsetProfile, never()).setEnabled(eq(device3), anyBoolean()); } @Test public void disableLeAudioProfile_dualModeEnabled_a2dpAndHfpNotChanged() { SystemProperties.set("persist.bluetooth.enable_dual_mode_audio", "true"); CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class); BluetoothDevice device1 = mock(BluetoothDevice.class); when(cachedDevice1.getDevice()).thenReturn(device1); when(cachedDevice1.getGroupId()).thenReturn(BluetoothCsipSetCoordinator.GROUP_ID_INVALID); when(cachedDevice1.getProfiles()) .thenReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile, mLeAudioProfile)); when(mA2dpProfile.isEnabled(device1)).thenReturn(false); when(mHeadsetProfile.isEnabled(device1)).thenReturn(false); Utils.setLeAudioEnabled(mLocalBtManager, cachedDevice1, false); verify(mLeAudioProfile).setEnabled(device1, false); verify(mA2dpProfile, never()).setEnabled(device1, true); verify(mHeadsetProfile, never()).setEnabled(device1, true); } @Test public void disableLeAudioProfile_dualModeDisabled_enableA2dpAndHfp() { SystemProperties.set("persist.bluetooth.enable_dual_mode_audio", "false"); CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class); BluetoothDevice device1 = mock(BluetoothDevice.class); when(cachedDevice1.getDevice()).thenReturn(device1); when(cachedDevice1.getGroupId()).thenReturn(BluetoothCsipSetCoordinator.GROUP_ID_INVALID); when(cachedDevice1.getProfiles()) .thenReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile, mLeAudioProfile)); when(mA2dpProfile.isEnabled(device1)).thenReturn(false); when(mHeadsetProfile.isEnabled(device1)).thenReturn(false); Utils.setLeAudioEnabled(mLocalBtManager, cachedDevice1, false); verify(mLeAudioProfile).setEnabled(device1, false); verify(mA2dpProfile).setEnabled(device1, true); verify(mHeadsetProfile).setEnabled(device1, true); } }
tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowBluetoothUtils.java +24 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes