From fca184598dda763ec2a9f174ce556ff991639433 Mon Sep 17 00:00:00 2001 From: Yiyi Shen Date: Wed, 19 Feb 2025 17:08:31 +0800 Subject: [PATCH] [Audiosharing] Handle add source from notif in receiver Test: atest Bug: 395786392 Flag: com.android.settingslib.flags.enable_le_audio_sharing Change-Id: Ia9a74a48224974c10524f1ae379cd9de2ff42430 --- .../audiosharing/AudioSharingReceiver.java | 52 +++++ .../AudioSharingReceiverTest.java | 205 +++++++++++++++++- 2 files changed, 248 insertions(+), 9 deletions(-) diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiver.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiver.java index c070e6ce0fc..a7c7984c4c0 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiver.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiver.java @@ -25,6 +25,7 @@ import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.settings.SettingsEnums; +import android.bluetooth.BluetoothCsipSetCoordinator; import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; @@ -45,6 +46,11 @@ import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Map; + public class AudioSharingReceiver extends BroadcastReceiver { private static final String TAG = "AudioSharingReceiver"; private static final String ACTION_LE_AUDIO_SHARING_SETTINGS = @@ -141,6 +147,52 @@ public class AudioSharingReceiver extends BroadcastReceiver { showAddSourceNotification(context, device); } break; + case ACTION_LE_AUDIO_SHARING_ADD_SOURCE: + if (!BluetoothUtils.isAudioSharingUIAvailable(context)) { + Log.d(TAG, "Skip ACTION_LE_AUDIO_SHARING_ADD_SOURCE, feature disabled."); + cancelSharingNotification(context, ADD_SOURCE_NOTIFICATION_ID); + return; + } + BluetoothDevice sink = intent.getParcelableExtra(EXTRA_BLUETOOTH_DEVICE, + BluetoothDevice.class); + if (sink == null) { + Log.d(TAG, "Skip ACTION_LE_AUDIO_SHARING_ADD_SOURCE, null device"); + cancelSharingNotification(context, ADD_SOURCE_NOTIFICATION_ID); + return; + } + LocalBluetoothManager manager = Utils.getLocalBtManager(context); + boolean isBroadcasting = BluetoothUtils.isBroadcasting(manager); + if (!isBroadcasting) { + Log.d(TAG, "Skip ACTION_LE_AUDIO_SHARING_ADD_SOURCE, not broadcasting"); + cancelSharingNotification(context, ADD_SOURCE_NOTIFICATION_ID); + return; + } + Map> groupedDevices = + AudioSharingUtils.fetchConnectedDevicesByGroupId(manager); + int groupId = groupedDevices.entrySet().stream().filter( + entry -> entry.getValue().contains(sink)).findFirst().map( + Map.Entry::getKey).orElse(BluetoothCsipSetCoordinator.GROUP_ID_INVALID); + if (groupId == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) { + Log.d(TAG, "Skip ACTION_LE_AUDIO_SHARING_ADD_SOURCE, no valid group id"); + cancelSharingNotification(context, ADD_SOURCE_NOTIFICATION_ID); + return; + } + List sinksToAdd = groupedDevices.getOrDefault(groupId, + ImmutableList.of()).stream().filter( + d -> !BluetoothUtils.hasConnectedBroadcastSourceForBtDevice(d, + manager)).toList(); + if (sinksToAdd.isEmpty()) { + Log.d(TAG, "Skip ACTION_LE_AUDIO_SHARING_ADD_SOURCE, already has source"); + } else if (groupedDevices.entrySet().stream().filter( + entry -> entry.getKey() != groupId && entry.getValue().stream().anyMatch( + d -> BluetoothUtils.hasConnectedBroadcastSourceForBtDevice(d, + manager))).toList().size() >= 2) { + Log.d(TAG, "Skip ACTION_LE_AUDIO_SHARING_ADD_SOURCE, already 2 sinks"); + } else { + AudioSharingUtils.addSourceToTargetSinks(sinksToAdd, manager); + } + cancelSharingNotification(context, ADD_SOURCE_NOTIFICATION_ID); + break; case ACTION_LE_AUDIO_SHARING_CANCEL_NOTIF: int notifId = intent.getIntExtra(EXTRA_NOTIF_ID, -1); if (notifId != -1) { diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiverTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiverTest.java index 6b8ec727a1c..dfe5171c004 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiverTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiverTest.java @@ -26,6 +26,7 @@ import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.EXTRA_ 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.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -41,6 +42,8 @@ import android.app.NotificationManager; import android.app.settings.SettingsEnums; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothLeBroadcastMetadata; +import android.bluetooth.BluetoothLeBroadcastReceiveState; import android.bluetooth.BluetoothStatusCodes; import android.content.BroadcastReceiver; import android.content.Context; @@ -55,11 +58,16 @@ import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settings.testutils.shadow.ShadowBluetoothUtils; import com.android.settingslib.R; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast; +import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import com.android.settingslib.flags.Flags; +import com.google.common.collect.ImmutableList; + import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -82,6 +90,8 @@ import java.util.stream.Collectors; public class AudioSharingReceiverTest { private static final String ACTION_LE_AUDIO_SHARING_STOP = "com.android.settings.action.BLUETOOTH_LE_AUDIO_SHARING_STOP"; + private static final String ACTION_LE_AUDIO_SHARING_ADD_SOURCE = + "com.android.settings.action.BLUETOOTH_LE_AUDIO_SHARING_ADD_SOURCE"; private static final String ACTION_LE_AUDIO_SHARING_CANCEL_NOTIF = "com.android.settings.action.BLUETOOTH_LE_AUDIO_SHARING_CANCEL_NOTIF"; private static final String EXTRA_NOTIF_ID = "NOTIF_ID"; @@ -97,7 +107,9 @@ public class AudioSharingReceiverTest { private FakeFeatureFactory mFeatureFactory; @Mock private LocalBluetoothProfileManager mLocalBtProfileManager; @Mock private LocalBluetoothLeBroadcast mBroadcast; + @Mock private LocalBluetoothLeBroadcastAssistant mAssistant; @Mock private LocalBluetoothManager mLocalBtManager; + @Mock private BluetoothDevice mDevice; @Mock private NotificationManager mNm; @Before @@ -115,6 +127,8 @@ public class AudioSharingReceiverTest { mLocalBluetoothManager = Utils.getLocalBtManager(mContext); when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBtProfileManager); when(mLocalBtProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast); + when(mLocalBtProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant); + when(mDevice.getAlias()).thenReturn(TEST_DEVICE_NAME); mFeatureFactory = FakeFeatureFactory.setupForTest(); } @@ -297,12 +311,10 @@ public class AudioSharingReceiverTest { mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED); - BluetoothDevice device = mock(BluetoothDevice.class); - when(device.getAlias()).thenReturn(TEST_DEVICE_NAME); setAppInForeground(false); Intent intent = new Intent(ACTION_LE_AUDIO_SHARING_DEVICE_CONNECTED); intent.setPackage(mContext.getPackageName()); - intent.putExtra(EXTRA_BLUETOOTH_DEVICE, device); + intent.putExtra(EXTRA_BLUETOOTH_DEVICE, mDevice); AudioSharingReceiver audioSharingReceiver = getAudioSharingReceiver(intent); audioSharingReceiver.onReceive(mContext, intent); @@ -314,12 +326,10 @@ public class AudioSharingReceiverTest { @Test @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING) public void broadcastReceiver_receiveAudioSharingDeviceConnected_showDialog() { - BluetoothDevice device = mock(BluetoothDevice.class); - when(device.getAlias()).thenReturn(TEST_DEVICE_NAME); setAppInForeground(true); Intent intent = new Intent(ACTION_LE_AUDIO_SHARING_DEVICE_CONNECTED); intent.setPackage(mContext.getPackageName()); - intent.putExtra(EXTRA_BLUETOOTH_DEVICE, device); + intent.putExtra(EXTRA_BLUETOOTH_DEVICE, mDevice); AudioSharingReceiver audioSharingReceiver = getAudioSharingReceiver(intent); audioSharingReceiver.onReceive(mContext, intent); @@ -332,12 +342,10 @@ public class AudioSharingReceiverTest { @Test @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING) public void broadcastReceiver_receiveAudioSharingDeviceConnected_showNotification() { - BluetoothDevice device = mock(BluetoothDevice.class); - when(device.getAlias()).thenReturn(TEST_DEVICE_NAME); setAppInForeground(false); Intent intent = new Intent(ACTION_LE_AUDIO_SHARING_DEVICE_CONNECTED); intent.setPackage(mContext.getPackageName()); - intent.putExtra(EXTRA_BLUETOOTH_DEVICE, device); + intent.putExtra(EXTRA_BLUETOOTH_DEVICE, mDevice); AudioSharingReceiver audioSharingReceiver = getAudioSharingReceiver(intent); audioSharingReceiver.onReceive(mContext, intent); @@ -346,6 +354,185 @@ public class AudioSharingReceiverTest { any(Notification.class)); } + @Test + @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING) + public void broadcastReceiver_receiveAudioSharingAddSource_broadcastDisabled_cancelNotif() { + mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( + BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED); + + Intent intent = new Intent(ACTION_LE_AUDIO_SHARING_ADD_SOURCE); + intent.setPackage(mContext.getPackageName()); + intent.putExtra(EXTRA_BLUETOOTH_DEVICE, mDevice); + AudioSharingReceiver audioSharingReceiver = getAudioSharingReceiver(intent); + audioSharingReceiver.onReceive(mContext, intent); + + verify(mAssistant, never()).addSource(eq(mDevice), any(BluetoothLeBroadcastMetadata.class), + anyBoolean()); + verify(mNm).cancel(com.android.settings.R.string.share_audio_notification_title); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING) + public void broadcastReceiver_receiveAudioSharingAddSource_nullArg_cancelNotif() { + Intent intent = new Intent(ACTION_LE_AUDIO_SHARING_ADD_SOURCE); + intent.setPackage(mContext.getPackageName()); + AudioSharingReceiver audioSharingReceiver = getAudioSharingReceiver(intent); + audioSharingReceiver.onReceive(mContext, intent); + + verify(mAssistant, never()).addSource(any(BluetoothDevice.class), + any(BluetoothLeBroadcastMetadata.class), anyBoolean()); + verify(mNm).cancel(com.android.settings.R.string.share_audio_notification_title); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING) + public void broadcastReceiver_receiveAudioSharingAddSource_notInBroadcast_cancelNotif() { + when(mBroadcast.isEnabled(null)).thenReturn(false); + + Intent intent = new Intent(ACTION_LE_AUDIO_SHARING_ADD_SOURCE); + intent.setPackage(mContext.getPackageName()); + intent.putExtra(EXTRA_BLUETOOTH_DEVICE, mDevice); + AudioSharingReceiver audioSharingReceiver = getAudioSharingReceiver(intent); + audioSharingReceiver.onReceive(mContext, intent); + + verify(mAssistant, never()).addSource(eq(mDevice), any(BluetoothLeBroadcastMetadata.class), + anyBoolean()); + verify(mNm).cancel(com.android.settings.R.string.share_audio_notification_title); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING) + public void broadcastReceiver_receiveAudioSharingAddSource_notConnected_cancelNotif() { + when(mBroadcast.isEnabled(null)).thenReturn(true); + when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of()); + + Intent intent = new Intent(ACTION_LE_AUDIO_SHARING_ADD_SOURCE); + intent.setPackage(mContext.getPackageName()); + intent.putExtra(EXTRA_BLUETOOTH_DEVICE, mDevice); + AudioSharingReceiver audioSharingReceiver = getAudioSharingReceiver(intent); + audioSharingReceiver.onReceive(mContext, intent); + + verify(mAssistant, never()).addSource(eq(mDevice), any(BluetoothLeBroadcastMetadata.class), + anyBoolean()); + verify(mNm).cancel(com.android.settings.R.string.share_audio_notification_title); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING) + public void broadcastReceiver_receiveAudioSharingAddSource_invalidGroupId_cancelNotif() { + when(mBroadcast.isEnabled(null)).thenReturn(true); + CachedBluetoothDeviceManager deviceManager = mock(CachedBluetoothDeviceManager.class); + when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(deviceManager); + CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class); + when(deviceManager.findDevice(mDevice)).thenReturn(cachedDevice); + when(cachedDevice.getDevice()).thenReturn(mDevice); + when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice)); + + Intent intent = new Intent(ACTION_LE_AUDIO_SHARING_ADD_SOURCE); + intent.setPackage(mContext.getPackageName()); + intent.putExtra(EXTRA_BLUETOOTH_DEVICE, mDevice); + AudioSharingReceiver audioSharingReceiver = getAudioSharingReceiver(intent); + audioSharingReceiver.onReceive(mContext, intent); + + verify(mAssistant, never()).addSource(eq(mDevice), any(BluetoothLeBroadcastMetadata.class), + anyBoolean()); + verify(mNm).cancel(com.android.settings.R.string.share_audio_notification_title); + } + + @Test + @EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX}) + public void broadcastReceiver_receiveAudioSharingAddSource_alreadyTwoSinks_cancelNotif() { + when(mBroadcast.isEnabled(null)).thenReturn(true); + when(mBroadcast.getLatestBroadcastId()).thenReturn(1); + CachedBluetoothDeviceManager deviceManager = mock(CachedBluetoothDeviceManager.class); + when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(deviceManager); + CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class); + when(deviceManager.findDevice(mDevice)).thenReturn(cachedDevice1); + BluetoothDevice device2 = mock(BluetoothDevice.class); + CachedBluetoothDevice cachedDevice2 = mock(CachedBluetoothDevice.class); + when(deviceManager.findDevice(device2)).thenReturn(cachedDevice2); + when(cachedDevice1.getGroupId()).thenReturn(1); + when(cachedDevice1.getDevice()).thenReturn(mDevice); + when(cachedDevice1.getName()).thenReturn(TEST_DEVICE_NAME); + when(cachedDevice2.getGroupId()).thenReturn(2); + when(cachedDevice2.getDevice()).thenReturn(device2); + when(cachedDevice2.getName()).thenReturn(TEST_DEVICE_NAME); + when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice, device2)); + BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class); + when(state.getBroadcastId()).thenReturn(1); + when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(state)); + + Intent intent = new Intent(ACTION_LE_AUDIO_SHARING_ADD_SOURCE); + intent.setPackage(mContext.getPackageName()); + intent.putExtra(EXTRA_BLUETOOTH_DEVICE, mDevice); + AudioSharingReceiver audioSharingReceiver = getAudioSharingReceiver(intent); + audioSharingReceiver.onReceive(mContext, intent); + + verify(mAssistant, never()).addSource(eq(mDevice), any(BluetoothLeBroadcastMetadata.class), + anyBoolean()); + verify(mNm).cancel(com.android.settings.R.string.share_audio_notification_title); + } + + @Test + @EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX}) + public void broadcastReceiver_receiveAudioSharingAddSource_alreadyHasSource_cancelNotif() { + when(mBroadcast.isEnabled(null)).thenReturn(true); + when(mBroadcast.getLatestBroadcastId()).thenReturn(1); + CachedBluetoothDeviceManager deviceManager = mock(CachedBluetoothDeviceManager.class); + when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(deviceManager); + CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class); + when(deviceManager.findDevice(mDevice)).thenReturn(cachedDevice); + when(cachedDevice.getGroupId()).thenReturn(1); + when(cachedDevice.getDevice()).thenReturn(mDevice); + when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice)); + BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class); + when(state.getBroadcastId()).thenReturn(1); + when(mAssistant.getAllSources(mDevice)).thenReturn(ImmutableList.of(state)); + + Intent intent = new Intent(ACTION_LE_AUDIO_SHARING_ADD_SOURCE); + intent.setPackage(mContext.getPackageName()); + intent.putExtra(EXTRA_BLUETOOTH_DEVICE, mDevice); + AudioSharingReceiver audioSharingReceiver = getAudioSharingReceiver(intent); + audioSharingReceiver.onReceive(mContext, intent); + + verify(mAssistant, never()).addSource(eq(mDevice), any(BluetoothLeBroadcastMetadata.class), + anyBoolean()); + verify(mNm).cancel(com.android.settings.R.string.share_audio_notification_title); + } + + @Test + @EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX}) + public void broadcastReceiver_receiveAudioSharingAddSource_addSource() { + when(mBroadcast.isEnabled(null)).thenReturn(true); + when(mBroadcast.getLatestBroadcastId()).thenReturn(1); + BluetoothLeBroadcastMetadata metadata = mock(BluetoothLeBroadcastMetadata.class); + when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(metadata); + CachedBluetoothDeviceManager deviceManager = mock(CachedBluetoothDeviceManager.class); + when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(deviceManager); + CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class); + when(deviceManager.findDevice(mDevice)).thenReturn(cachedDevice1); + BluetoothDevice device2 = mock(BluetoothDevice.class); + CachedBluetoothDevice cachedDevice2 = mock(CachedBluetoothDevice.class); + when(deviceManager.findDevice(device2)).thenReturn(cachedDevice2); + when(cachedDevice1.getGroupId()).thenReturn(1); + when(cachedDevice1.getDevice()).thenReturn(mDevice); + when(cachedDevice2.getGroupId()).thenReturn(2); + when(cachedDevice2.getDevice()).thenReturn(device2); + when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice, device2)); + BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class); + when(state.getBroadcastId()).thenReturn(1); + when(mAssistant.getAllSources(device2)).thenReturn(ImmutableList.of(state)); + + Intent intent = new Intent(ACTION_LE_AUDIO_SHARING_ADD_SOURCE); + intent.setPackage(mContext.getPackageName()); + intent.putExtra(EXTRA_BLUETOOTH_DEVICE, mDevice); + AudioSharingReceiver audioSharingReceiver = getAudioSharingReceiver(intent); + audioSharingReceiver.onReceive(mContext, intent); + + verify(mAssistant).addSource(mDevice, metadata, /* isGroupOp= */ false); + verify(mNm).cancel(com.android.settings.R.string.share_audio_notification_title); + } + @Test @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING) public void broadcastReceiver_receiveAudioSharingCancelNotif_cancel() {