Merge "[Audiosharing] onDestroy() is not guaranteed to be called after stopSelf(). In this case callbacks are not unregistered timely and the notification kept being brought up again if any callback is received." into main
This commit is contained in:
@@ -18,22 +18,29 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
||||
|
||||
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamMediaService.BROADCAST_ID;
|
||||
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamMediaService.DEVICES;
|
||||
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamMediaService.LEAVE_BROADCAST_ACTION;
|
||||
|
||||
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.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.bluetooth.BluetoothStatusCodes;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -43,14 +50,20 @@ import android.content.res.Resources;
|
||||
import android.media.session.ISession;
|
||||
import android.media.session.ISessionController;
|
||||
import android.media.session.MediaSessionManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.platform.test.flag.junit.SetFlagsRule;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
import com.android.settings.connecteddevice.audiosharing.audiostreams.testshadows.ShadowAudioStreamsHelper;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
|
||||
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
|
||||
import com.android.settings.testutils.shadow.ShadowThreadUtils;
|
||||
import com.android.settingslib.bluetooth.BluetoothEventManager;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
|
||||
@@ -72,10 +85,12 @@ import org.robolectric.shadow.api.Shadow;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Set;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(
|
||||
shadows = {
|
||||
ShadowThreadUtils.class,
|
||||
ShadowBluetoothAdapter.class,
|
||||
ShadowBluetoothUtils.class,
|
||||
ShadowAudioStreamsHelper.class,
|
||||
@@ -83,6 +98,8 @@ import java.util.ArrayList;
|
||||
public class AudioStreamMediaServiceTest {
|
||||
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
||||
private static final String CHANNEL_ID = "bluetooth_notification_channel";
|
||||
private static final String DEVICE_NAME = "name";
|
||||
@Mock private Resources mResources;
|
||||
@Mock private LocalBluetoothManager mLocalBtManager;
|
||||
@Mock private LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant;
|
||||
@@ -91,17 +108,21 @@ public class AudioStreamMediaServiceTest {
|
||||
@Mock private MediaSessionManager mMediaSessionManager;
|
||||
@Mock private BluetoothEventManager mBluetoothEventManager;
|
||||
@Mock private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
|
||||
@Mock private CachedBluetoothDeviceManager mCachedDeviceManager;
|
||||
@Mock private VolumeControlProfile mVolumeControlProfile;
|
||||
@Mock private CachedBluetoothDevice mCachedBluetoothDevice;
|
||||
@Mock private BluetoothDevice mDevice;
|
||||
@Mock private ISession mISession;
|
||||
@Mock private ISessionController mISessionController;
|
||||
@Mock private PackageManager mPackageManager;
|
||||
@Mock private DisplayMetrics mDisplayMetrics;
|
||||
@Mock private Context mContext;
|
||||
private FakeFeatureFactory mFeatureFactory;
|
||||
private AudioStreamMediaService mAudioStreamMediaService;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mFeatureFactory = FakeFeatureFactory.setupForTest();
|
||||
ShadowAudioStreamsHelper.setUseMock(mAudioStreamsHelper);
|
||||
when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(mLeBroadcastAssistant);
|
||||
ShadowBluetoothAdapter shadowBluetoothAdapter =
|
||||
@@ -114,6 +135,9 @@ public class AudioStreamMediaServiceTest {
|
||||
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager;
|
||||
when(mLocalBtManager.getEventManager()).thenReturn(mBluetoothEventManager);
|
||||
when(mLocalBtManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
|
||||
when(mLocalBtManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
|
||||
when(mCachedDeviceManager.findDevice(any())).thenReturn(mCachedBluetoothDevice);
|
||||
when(mCachedBluetoothDevice.getName()).thenReturn(DEVICE_NAME);
|
||||
when(mLocalBluetoothProfileManager.getVolumeControlProfile())
|
||||
.thenReturn(mVolumeControlProfile);
|
||||
|
||||
@@ -168,6 +192,25 @@ public class AudioStreamMediaServiceTest {
|
||||
verify(mVolumeControlProfile).registerCallback(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onCreate_flagOn_createNewChannel() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||
when(mNotificationManager.getNotificationChannel(anyString())).thenReturn(null);
|
||||
|
||||
mAudioStreamMediaService.onCreate();
|
||||
|
||||
ArgumentCaptor<NotificationChannel> notificationChannelCapture =
|
||||
ArgumentCaptor.forClass(NotificationChannel.class);
|
||||
verify(mNotificationManager)
|
||||
.createNotificationChannel(notificationChannelCapture.capture());
|
||||
NotificationChannel newChannel = notificationChannelCapture.getValue();
|
||||
assertThat(newChannel).isNotNull();
|
||||
assertThat(newChannel.getId()).isEqualTo(CHANNEL_ID);
|
||||
assertThat(newChannel.getName())
|
||||
.isEqualTo(mContext.getString(com.android.settings.R.string.bluetooth));
|
||||
assertThat(newChannel.getImportance()).isEqualTo(NotificationManager.IMPORTANCE_HIGH);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDestroy_flagOff_doNothing() {
|
||||
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||
@@ -183,8 +226,15 @@ public class AudioStreamMediaServiceTest {
|
||||
@Test
|
||||
public void onDestroy_flagOn_cleanup() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||
var devices = new ArrayList<BluetoothDevice>();
|
||||
devices.add(mDevice);
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(BROADCAST_ID, 1);
|
||||
intent.putParcelableArrayListExtra(DEVICES, devices);
|
||||
|
||||
mAudioStreamMediaService.onCreate();
|
||||
mAudioStreamMediaService.onStartCommand(intent, /* flags= */ 0, /* startId= */ 0);
|
||||
mAudioStreamMediaService.onDestroy();
|
||||
|
||||
verify(mBluetoothEventManager).unregisterCallback(any());
|
||||
@@ -196,7 +246,6 @@ public class AudioStreamMediaServiceTest {
|
||||
public void onStartCommand_noBroadcastId_stopSelf() {
|
||||
mAudioStreamMediaService.onStartCommand(new Intent(), /* flags= */ 0, /* startId= */ 0);
|
||||
|
||||
assertThat(mAudioStreamMediaService.mLocalSession).isNull();
|
||||
verify(mAudioStreamMediaService).stopSelf();
|
||||
}
|
||||
|
||||
@@ -207,7 +256,6 @@ public class AudioStreamMediaServiceTest {
|
||||
|
||||
mAudioStreamMediaService.onStartCommand(intent, /* flags= */ 0, /* startId= */ 0);
|
||||
|
||||
assertThat(mAudioStreamMediaService.mLocalSession).isNull();
|
||||
verify(mAudioStreamMediaService).stopSelf();
|
||||
}
|
||||
|
||||
@@ -222,12 +270,179 @@ public class AudioStreamMediaServiceTest {
|
||||
|
||||
mAudioStreamMediaService.onStartCommand(intent, /* flags= */ 0, /* startId= */ 0);
|
||||
|
||||
assertThat(mAudioStreamMediaService.mLocalSession).isNotNull();
|
||||
verify(mAudioStreamMediaService, never()).stopSelf();
|
||||
ArgumentCaptor<Notification> notificationCapture =
|
||||
ArgumentCaptor.forClass(Notification.class);
|
||||
verify(mAudioStreamMediaService).startForeground(anyInt(), notificationCapture.capture());
|
||||
var notification = notificationCapture.getValue();
|
||||
assertThat(notification.getSmallIcon()).isNotNull();
|
||||
assertThat(notification.isStyle(Notification.MediaStyle.class)).isTrue();
|
||||
|
||||
ArgumentCaptor<Notification> notification = ArgumentCaptor.forClass(Notification.class);
|
||||
verify(mAudioStreamMediaService).startForeground(anyInt(), notification.capture());
|
||||
assertThat(notification.getValue().getSmallIcon()).isNotNull();
|
||||
assertThat(notification.getValue().isStyle(Notification.MediaStyle.class)).isTrue();
|
||||
verify(mAudioStreamMediaService, never()).stopSelf();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void assistantCallback_onSourceLost_stopSelf() {
|
||||
mAudioStreamMediaService.onCreate();
|
||||
|
||||
assertThat(mAudioStreamMediaService.mBroadcastAssistantCallback).isNotNull();
|
||||
mAudioStreamMediaService.mBroadcastAssistantCallback.onSourceLost(/* broadcastId= */ 0);
|
||||
|
||||
verify(mAudioStreamMediaService).stopSelf();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void assistantCallback_onSourceRemoved_stopSelf() {
|
||||
mAudioStreamMediaService.onCreate();
|
||||
|
||||
assertThat(mAudioStreamMediaService.mBroadcastAssistantCallback).isNotNull();
|
||||
mAudioStreamMediaService.mBroadcastAssistantCallback.onSourceRemoved(
|
||||
mDevice, /* sourceId= */ 0, /* reason= */ 0);
|
||||
|
||||
verify(mAudioStreamMediaService).stopSelf();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bluetoothCallback_onBluetoothOff_stopSelf() {
|
||||
mAudioStreamMediaService.onCreate();
|
||||
|
||||
assertThat(mAudioStreamMediaService.mBluetoothCallback).isNotNull();
|
||||
mAudioStreamMediaService.mBluetoothCallback.onBluetoothStateChanged(
|
||||
BluetoothAdapter.STATE_OFF);
|
||||
|
||||
verify(mAudioStreamMediaService).stopSelf();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bluetoothCallback_onDeviceDisconnect_stopSelf() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||
mAudioStreamMediaService.onCreate();
|
||||
assertThat(mAudioStreamMediaService.mBluetoothCallback).isNotNull();
|
||||
mAudioStreamMediaService.onStartCommand(setupIntent(), /* flags= */ 0, /* startId= */ 0);
|
||||
|
||||
mAudioStreamMediaService.mBluetoothCallback.onProfileConnectionStateChanged(
|
||||
mCachedBluetoothDevice,
|
||||
BluetoothAdapter.STATE_DISCONNECTED,
|
||||
BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
|
||||
|
||||
verify(mAudioStreamMediaService).stopSelf();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bluetoothCallback_onMemberDeviceDisconnect_stopSelf() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||
when(mCachedBluetoothDevice.getDevice()).thenReturn(mock(BluetoothDevice.class));
|
||||
CachedBluetoothDevice member = mock(CachedBluetoothDevice.class);
|
||||
when(mCachedBluetoothDevice.getMemberDevice()).thenReturn(Set.of(member));
|
||||
when(member.getDevice()).thenReturn(mDevice);
|
||||
var devices = new ArrayList<BluetoothDevice>();
|
||||
devices.add(mDevice);
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(BROADCAST_ID, 1);
|
||||
intent.putParcelableArrayListExtra(DEVICES, devices);
|
||||
|
||||
mAudioStreamMediaService.onCreate();
|
||||
assertThat(mAudioStreamMediaService.mBluetoothCallback).isNotNull();
|
||||
mAudioStreamMediaService.onStartCommand(intent, /* flags= */ 0, /* startId= */ 0);
|
||||
mAudioStreamMediaService.mBluetoothCallback.onProfileConnectionStateChanged(
|
||||
mCachedBluetoothDevice,
|
||||
BluetoothAdapter.STATE_DISCONNECTED,
|
||||
BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
|
||||
|
||||
verify(mAudioStreamMediaService).stopSelf();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mediaSessionCallback_onSeekTo_updateNotification() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||
|
||||
mAudioStreamMediaService.onCreate();
|
||||
mAudioStreamMediaService.onStartCommand(setupIntent(), /* flags= */ 0, /* startId= */ 0);
|
||||
assertThat(mAudioStreamMediaService.mMediaSessionCallback).isNotNull();
|
||||
mAudioStreamMediaService.mMediaSessionCallback.onSeekTo(100);
|
||||
|
||||
verify(mNotificationManager).notify(anyInt(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mediaSessionCallback_onPause_setVolume() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||
|
||||
mAudioStreamMediaService.onCreate();
|
||||
mAudioStreamMediaService.onStartCommand(setupIntent(), /* flags= */ 0, /* startId= */ 0);
|
||||
assertThat(mAudioStreamMediaService.mMediaSessionCallback).isNotNull();
|
||||
mAudioStreamMediaService.mMediaSessionCallback.onPause();
|
||||
|
||||
verify(mVolumeControlProfile).setDeviceVolume(any(), anyInt(), anyBoolean());
|
||||
verify(mFeatureFactory.metricsFeatureProvider)
|
||||
.action(
|
||||
any(),
|
||||
eq(SettingsEnums.ACTION_AUDIO_STREAM_NOTIFICATION_MUTE_BUTTON_CLICK),
|
||||
eq(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mediaSessionCallback_onPlay_setVolume() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||
|
||||
mAudioStreamMediaService.onCreate();
|
||||
mAudioStreamMediaService.onStartCommand(setupIntent(), /* flags= */ 0, /* startId= */ 0);
|
||||
assertThat(mAudioStreamMediaService.mMediaSessionCallback).isNotNull();
|
||||
mAudioStreamMediaService.mMediaSessionCallback.onPlay();
|
||||
|
||||
verify(mVolumeControlProfile).setDeviceVolume(any(), anyInt(), anyBoolean());
|
||||
verify(mFeatureFactory.metricsFeatureProvider)
|
||||
.action(
|
||||
any(),
|
||||
eq(SettingsEnums.ACTION_AUDIO_STREAM_NOTIFICATION_MUTE_BUTTON_CLICK),
|
||||
eq(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mediaSessionCallback_onCustomAction_leaveBroadcast() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||
|
||||
mAudioStreamMediaService.onCreate();
|
||||
mAudioStreamMediaService.onStartCommand(setupIntent(), /* flags= */ 0, /* startId= */ 0);
|
||||
assertThat(mAudioStreamMediaService.mMediaSessionCallback).isNotNull();
|
||||
mAudioStreamMediaService.mMediaSessionCallback.onCustomAction(
|
||||
LEAVE_BROADCAST_ACTION, Bundle.EMPTY);
|
||||
|
||||
verify(mAudioStreamsHelper).removeSource(anyInt());
|
||||
verify(mFeatureFactory.metricsFeatureProvider)
|
||||
.action(
|
||||
any(),
|
||||
eq(SettingsEnums.ACTION_AUDIO_STREAM_NOTIFICATION_LEAVE_BUTTON_CLICK));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void volumeControlCallback_onDeviceVolumeChanged_updateNotification() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||
|
||||
mAudioStreamMediaService.onCreate();
|
||||
assertThat(mAudioStreamMediaService.mVolumeControlCallback).isNotNull();
|
||||
mAudioStreamMediaService.onStartCommand(setupIntent(), /* flags= */ 0, /* startId= */ 0);
|
||||
mAudioStreamMediaService.mVolumeControlCallback.onDeviceVolumeChanged(
|
||||
mDevice, /* volume= */ 0);
|
||||
|
||||
verify(mNotificationManager).notify(anyInt(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBind_returnNull() {
|
||||
IBinder binder = mAudioStreamMediaService.onBind(new Intent());
|
||||
|
||||
assertThat(binder).isNull();
|
||||
}
|
||||
|
||||
private Intent setupIntent() {
|
||||
when(mCachedBluetoothDevice.getDevice()).thenReturn(mDevice);
|
||||
var devices = new ArrayList<BluetoothDevice>();
|
||||
devices.add(mDevice);
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(BROADCAST_ID, 1);
|
||||
intent.putParcelableArrayListExtra(DEVICES, devices);
|
||||
return intent;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user