From 27e4fcc6c69f641e6edb75ddfaca23ebe827bdf1 Mon Sep 17 00:00:00 2001 From: Yiyi Shen Date: Wed, 15 Jan 2025 15:25:32 +0800 Subject: [PATCH] [Audiosharing] Use setBroadcastToUnicastFallbackGroup to set primary Flag: com.android.settingslib.flags.adopt_primary_group_management_api Test: atest Bug: 381946931 Change-Id: Ib06dd2e202b07b9cdb25b1a671ee4d57246415ba --- ...oSharingCallAudioPreferenceController.java | 55 +++++++++++++------ ...udioSharingDevicePreferenceController.java | 3 +- .../AudioSharingDialogHandler.java | 3 +- .../audiosharing/AudioSharingUtils.java | 5 +- ...ringCallAudioPreferenceControllerTest.java | 46 +++++++++++++--- ...SharingDevicePreferenceControllerTest.java | 2 + .../AudioSharingDialogHandlerTest.java | 3 + 7 files changed, 88 insertions(+), 29 deletions(-) diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceController.java index 3130a0c3600..25180d8dbc4 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceController.java @@ -16,6 +16,8 @@ package com.android.settings.connecteddevice.audiosharing; +import static com.android.settingslib.Utils.isAudioModeOngoingCall; + import android.app.settings.SettingsEnums; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothCsipSetCoordinator; @@ -48,6 +50,7 @@ import com.android.settingslib.bluetooth.BluetoothEventManager; import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; +import com.android.settingslib.bluetooth.LeAudioProfile; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; @@ -91,6 +94,7 @@ public class AudioSharingCallAudioPreferenceController extends AudioSharingBaseP Map> mGroupedConnectedDevices = new HashMap<>(); private List mDeviceItemsInSharingSession = new ArrayList<>(); private final AtomicBoolean mCallbacksRegistered = new AtomicBoolean(false); + private AtomicBoolean mIsAudioModeOngoingCall = new AtomicBoolean(false); @VisibleForTesting final BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback = @@ -202,28 +206,15 @@ public class AudioSharingCallAudioPreferenceController extends AudioSharingBaseP mDeviceItemsInSharingSession, pair == null ? -1 : pair.first, (AudioSharingDeviceItem item) -> { - int currentGroupId = + int currentCallAudioGroupId = BluetoothUtils.getPrimaryGroupIdForBroadcast( mContext.getContentResolver()); int clickedGroupId = item.getGroupId(); - if (clickedGroupId == currentGroupId) { + if (clickedGroupId == currentCallAudioGroupId) { Log.d(TAG, "Skip set call audio device: unchanged"); return; } - List devices = - mGroupedConnectedDevices.getOrDefault( - clickedGroupId, ImmutableList.of()); - CachedBluetoothDevice lead = - AudioSharingUtils.getLeadDevice( - mCacheManager, devices); - if (lead != null) { - String addr = lead.getDevice().getAnonymizedAddress(); - Log.d(TAG, "Set call audio device: " + addr); - AudioSharingUtils.setPrimary(mContext, lead); - logCallAudioDeviceChange(currentGroupId, lead); - } else { - Log.d(TAG, "Skip set call audio device: no lead"); - } + setCallAudioGroup(clickedGroupId); }); } return true; @@ -269,6 +260,11 @@ public class AudioSharingCallAudioPreferenceController extends AudioSharingBaseP } } + @Override + public void onAudioModeChanged() { + mIsAudioModeOngoingCall.set(isAudioModeOngoingCall(mContext)); + } + /** * Initialize the controller. * @@ -311,6 +307,7 @@ public class AudioSharingCallAudioPreferenceController extends AudioSharingBaseP false, mSettingsObserver); mAssistant.registerServiceCallBack(mExecutor, mBroadcastAssistantCallback); + mIsAudioModeOngoingCall.set(isAudioModeOngoingCall(mContext)); mCallbacksRegistered.set(true); } } @@ -333,6 +330,32 @@ public class AudioSharingCallAudioPreferenceController extends AudioSharingBaseP } } + private void setCallAudioGroup(int groupId) { + List devices = + mGroupedConnectedDevices.getOrDefault( + groupId, ImmutableList.of()); + CachedBluetoothDevice lead = + AudioSharingUtils.getLeadDevice( + mCacheManager, devices); + if (lead != null) { + String addr = lead.getDevice().getAnonymizedAddress(); + Log.d(TAG, "Set call audio device: " + addr); + if (Flags.adoptPrimaryGroupManagementApi() && !mIsAudioModeOngoingCall.get()) { + LeAudioProfile leaProfile = mBtManager == null ? null + : mBtManager.getProfileManager().getLeAudioProfile(); + if (leaProfile != null) { + leaProfile.setBroadcastToUnicastFallbackGroup(groupId); + } + } else { + lead.setActive(); + } + AudioSharingUtils.setUserPreferredPrimary(mContext, lead); + logCallAudioDeviceChange(groupId, lead); + } else { + Log.d(TAG, "Skip set call audio device: no lead"); + } + } + /** * Update the preference summary: current headset for call audio. * diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java index c286ed6114e..ffbb13e9808 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java @@ -389,7 +389,8 @@ public class AudioSharingDevicePreferenceController extends BasePreferenceContro Log.d(TAG, "onDeviceClick, set active in call mode"); CachedBluetoothDevice cachedDevice = ((BluetoothDevicePreference) preference).getBluetoothDevice(); - AudioSharingUtils.setPrimary(mContext, cachedDevice); + cachedDevice.setActive(); + AudioSharingUtils.setUserPreferredPrimary(mContext, cachedDevice); } mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_AUDIO_SHARING_DEVICE_CLICK, isCallMode); diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java index 0c3448729b8..2a1f4da2cf8 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java @@ -192,7 +192,8 @@ public class AudioSharingDialogHandler { // If this method is called with user triggered, e.g. manual click on the // "Connected devices" page, we need call setActive for the device, since user // intend to switch active device for the call. - AudioSharingUtils.setPrimary(mContext, cachedDevice); + cachedDevice.setActive(); + AudioSharingUtils.setUserPreferredPrimary(mContext, cachedDevice); } return; } diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java index 5a15b6ae51c..7824b262d0d 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java @@ -346,11 +346,10 @@ public class AudioSharingUtils { return vc != null && vc.isProfileReady(); } - /** Set {@link CachedBluetoothDevice} as primary device for call audio */ - public static void setPrimary( + /** Set {@link CachedBluetoothDevice} as user preferred primary device for call audio */ + public static void setUserPreferredPrimary( @NonNull Context context, @Nullable CachedBluetoothDevice cachedDevice) { if (cachedDevice == null) return; - cachedDevice.setActive(); if (BluetoothUtils.isAudioSharingHysteresisModeFixAvailable(context)) { int groupId = BluetoothUtils.getGroupId(cachedDevice); // TODO: use real key name in SettingsProvider diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceControllerTest.java index 3075573b728..a2ac0cc99c9 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceControllerTest.java @@ -24,6 +24,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; 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; @@ -67,6 +68,7 @@ import com.android.settingslib.bluetooth.BluetoothEventManager; import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; +import com.android.settingslib.bluetooth.LeAudioProfile; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; import com.android.settingslib.bluetooth.LocalBluetoothManager; @@ -77,7 +79,6 @@ import com.android.settingslib.flags.Flags; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; import org.junit.After; import org.junit.Before; @@ -89,8 +90,10 @@ import org.mockito.Spy; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; +import org.robolectric.Shadows; import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; +import org.robolectric.shadows.ShadowListView; import org.robolectric.shadows.androidx.fragment.FragmentController; import java.util.ArrayList; @@ -483,19 +486,46 @@ public class AudioSharingCallAudioPreferenceControllerTest { AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog.isShowing()).isTrue(); assertThat(dialog.getListView().getCount()).isEqualTo(2); - ArrayList outViews = new ArrayList<>(); - dialog.getListView() - .findViewsWithText(outViews, TEST_DEVICE_NAME1, View.FIND_VIEWS_WITH_TEXT); - assertThat(outViews.size()).isEqualTo(1); - View view = Iterables.getOnlyElement(outViews); - assertThat(view instanceof CheckedTextView).isTrue(); - assertThat(((CheckedTextView) view).isChecked()).isTrue(); + ShadowListView listView = Shadows.shadowOf(dialog.getListView()); + View view1 = listView.findItemContainingText(TEST_DEVICE_NAME1); + assertThat(view1).isNotNull(); + assertThat(view1 instanceof CheckedTextView).isTrue(); + assertThat(((CheckedTextView) view1).isChecked()).isTrue(); + View view2 = listView.findItemContainingText(TEST_DEVICE_NAME2); + assertThat(view2).isNotNull(); + assertThat(view2 instanceof CheckedTextView).isTrue(); + assertThat(((CheckedTextView) view2).isChecked()).isFalse(); + verify(mFeatureFactory.metricsFeatureProvider) .visible( /* context= */ eq(null), /* source= */ anyInt(), eq(SettingsEnums.DIALOG_AUDIO_SHARING_CALL_AUDIO), /* latency= */ anyInt()); + + LeAudioProfile leAudioProfile = mock(LeAudioProfile.class); + when(mBtProfileManager.getLeAudioProfile()).thenReturn(leAudioProfile); + + // Perform click to switch call audio device by set active + mSetFlagsRule.disableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API); + int index = listView.findIndexOfItemContainingText(TEST_DEVICE_NAME2); + listView.performItemClick(index); + shadowOf(Looper.getMainLooper()).idle(); + assertThat(((CheckedTextView) view1).isChecked()).isFalse(); + assertThat(((CheckedTextView) view2).isChecked()).isTrue(); + verify(mCachedDevice3).setActive(); + verify(leAudioProfile, never()).setBroadcastToUnicastFallbackGroup(TEST_DEVICE_GROUP_ID2); + + // Perform click to switch call audio device with API + mSetFlagsRule.enableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API); + Settings.Secure.putInt(mContentResolver, TEST_SETTINGS_KEY, TEST_DEVICE_GROUP_ID2); + index = listView.findIndexOfItemContainingText(TEST_DEVICE_NAME1); + listView.performItemClick(index); + shadowOf(Looper.getMainLooper()).idle(); + assertThat(((CheckedTextView) view1).isChecked()).isTrue(); + assertThat(((CheckedTextView) view2).isChecked()).isFalse(); + verify(mCachedDevice1, never()).setActive(); + verify(leAudioProfile).setBroadcastToUnicastFallbackGroup(TEST_DEVICE_GROUP_ID1); } @Test diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceControllerTest.java index a49d0c13890..4a4a167622f 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceControllerTest.java @@ -584,6 +584,7 @@ public class AudioSharingDevicePreferenceControllerTest { public void testInCallState_showCallStateTitleAndSetActiveOnDeviceClick() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mSetFlagsRule.disableFlags(Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX); + mSetFlagsRule.enableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API); Settings.Secure.putInt(mContext.getContentResolver(), BLUETOOTH_LE_BROADCAST_PRIMARY_DEVICE_GROUP_ID, BluetoothCsipSetCoordinator.GROUP_ID_INVALID); @@ -609,6 +610,7 @@ public class AudioSharingDevicePreferenceControllerTest { public void testInCallState_enableHysteresisFix_setAndSaveActiveOnDeviceClick() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX); + mSetFlagsRule.enableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API); Settings.Secure.putInt(mContext.getContentResolver(), BLUETOOTH_LE_BROADCAST_PRIMARY_DEVICE_GROUP_ID, BluetoothCsipSetCoordinator.GROUP_ID_INVALID); diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandlerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandlerTest.java index c96a08623f8..b609cddfb8c 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandlerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandlerTest.java @@ -198,6 +198,7 @@ public class AudioSharingDialogHandlerTest { @Test public void handleUserTriggeredDeviceConnected_inCall_setActive() { mSetFlagsRule.disableFlags(Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX); + mSetFlagsRule.enableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API); Settings.Secure.putInt(mContext.getContentResolver(), BLUETOOTH_LE_BROADCAST_PRIMARY_DEVICE_GROUP_ID, BluetoothCsipSetCoordinator.GROUP_ID_INVALID); @@ -218,6 +219,7 @@ public class AudioSharingDialogHandlerTest { @Test public void handleUserTriggeredDeviceConnected_inCall_enableHysteresisFix_setAndSaveActive() { mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX); + mSetFlagsRule.enableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API); Settings.Secure.putInt(mContext.getContentResolver(), BLUETOOTH_LE_BROADCAST_PRIMARY_DEVICE_GROUP_ID, BluetoothCsipSetCoordinator.GROUP_ID_INVALID); @@ -452,6 +454,7 @@ public class AudioSharingDialogHandlerTest { @Test public void handleDeviceConnected_inCall_doNothing() { + mSetFlagsRule.enableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API); when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_IN_CALL); setUpBroadcast(true); when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of());