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:
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
package com.android.settings.connecteddevice.audiosharing.audiostreams;
|
||||||
|
|
||||||
|
import static java.util.Collections.emptyList;
|
||||||
|
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationChannel;
|
import android.app.NotificationChannel;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
@@ -50,10 +52,14 @@ import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
|
|||||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||||
import com.android.settingslib.bluetooth.VolumeControlProfile;
|
import com.android.settingslib.bluetooth.VolumeControlProfile;
|
||||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||||
|
import com.android.settingslib.utils.ThreadUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
public class AudioStreamMediaService extends Service {
|
public class AudioStreamMediaService extends Service {
|
||||||
static final String BROADCAST_ID = "audio_stream_media_service_broadcast_id";
|
static final String BROADCAST_ID = "audio_stream_media_service_broadcast_id";
|
||||||
@@ -62,118 +68,13 @@ public class AudioStreamMediaService extends Service {
|
|||||||
private static final String TAG = "AudioStreamMediaService";
|
private static final String TAG = "AudioStreamMediaService";
|
||||||
private static final int NOTIFICATION_ID = 1;
|
private static final int NOTIFICATION_ID = 1;
|
||||||
private static final int BROADCAST_CONTENT_TEXT = R.string.audio_streams_listening_now;
|
private static final int BROADCAST_CONTENT_TEXT = R.string.audio_streams_listening_now;
|
||||||
private static final String LEAVE_BROADCAST_ACTION = "leave_broadcast_action";
|
@VisibleForTesting static final String LEAVE_BROADCAST_ACTION = "leave_broadcast_action";
|
||||||
private static final String LEAVE_BROADCAST_TEXT = "Leave Broadcast";
|
private static final String LEAVE_BROADCAST_TEXT = "Leave Broadcast";
|
||||||
private static final String CHANNEL_ID = "bluetooth_notification_channel";
|
private static final String CHANNEL_ID = "bluetooth_notification_channel";
|
||||||
private static final String DEFAULT_DEVICE_NAME = "";
|
private static final String DEFAULT_DEVICE_NAME = "";
|
||||||
private static final int STATIC_PLAYBACK_DURATION = 100;
|
private static final int STATIC_PLAYBACK_DURATION = 100;
|
||||||
private static final int STATIC_PLAYBACK_POSITION = 30;
|
private static final int STATIC_PLAYBACK_POSITION = 30;
|
||||||
private static final int ZERO_PLAYBACK_SPEED = 0;
|
private static final int ZERO_PLAYBACK_SPEED = 0;
|
||||||
private final AudioStreamsBroadcastAssistantCallback mBroadcastAssistantCallback =
|
|
||||||
new AudioStreamsBroadcastAssistantCallback() {
|
|
||||||
@Override
|
|
||||||
public void onSourceLost(int broadcastId) {
|
|
||||||
super.onSourceLost(broadcastId);
|
|
||||||
if (broadcastId == mBroadcastId) {
|
|
||||||
Log.d(TAG, "onSourceLost() : stopSelf");
|
|
||||||
if (mNotificationManager != null) {
|
|
||||||
mNotificationManager.cancel(NOTIFICATION_ID);
|
|
||||||
}
|
|
||||||
stopSelf();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSourceRemoved(BluetoothDevice sink, int sourceId, int reason) {
|
|
||||||
super.onSourceRemoved(sink, sourceId, reason);
|
|
||||||
if (mAudioStreamsHelper != null
|
|
||||||
&& mAudioStreamsHelper.getAllConnectedSources().stream()
|
|
||||||
.map(BluetoothLeBroadcastReceiveState::getBroadcastId)
|
|
||||||
.noneMatch(id -> id == mBroadcastId)) {
|
|
||||||
Log.d(TAG, "onSourceRemoved() : stopSelf");
|
|
||||||
if (mNotificationManager != null) {
|
|
||||||
mNotificationManager.cancel(NOTIFICATION_ID);
|
|
||||||
}
|
|
||||||
stopSelf();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final BluetoothCallback mBluetoothCallback =
|
|
||||||
new BluetoothCallback() {
|
|
||||||
@Override
|
|
||||||
public void onBluetoothStateChanged(int bluetoothState) {
|
|
||||||
if (BluetoothAdapter.STATE_OFF == bluetoothState) {
|
|
||||||
Log.d(TAG, "onBluetoothStateChanged() : stopSelf");
|
|
||||||
if (mNotificationManager != null) {
|
|
||||||
mNotificationManager.cancel(NOTIFICATION_ID);
|
|
||||||
}
|
|
||||||
stopSelf();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onProfileConnectionStateChanged(
|
|
||||||
@NonNull CachedBluetoothDevice cachedDevice,
|
|
||||||
@ConnectionState int state,
|
|
||||||
int bluetoothProfile) {
|
|
||||||
if (state == BluetoothAdapter.STATE_DISCONNECTED
|
|
||||||
&& bluetoothProfile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT
|
|
||||||
&& mDevices != null) {
|
|
||||||
mDevices.remove(cachedDevice.getDevice());
|
|
||||||
cachedDevice
|
|
||||||
.getMemberDevice()
|
|
||||||
.forEach(
|
|
||||||
m -> {
|
|
||||||
// Check nullability to pass NullAway check
|
|
||||||
if (mDevices != null) {
|
|
||||||
mDevices.remove(m.getDevice());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (mDevices == null || mDevices.isEmpty()) {
|
|
||||||
Log.d(TAG, "onProfileConnectionStateChanged() : stopSelf");
|
|
||||||
if (mNotificationManager != null) {
|
|
||||||
mNotificationManager.cancel(NOTIFICATION_ID);
|
|
||||||
}
|
|
||||||
stopSelf();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final BluetoothVolumeControl.Callback mVolumeControlCallback =
|
|
||||||
new BluetoothVolumeControl.Callback() {
|
|
||||||
@Override
|
|
||||||
public void onDeviceVolumeChanged(
|
|
||||||
@NonNull BluetoothDevice device,
|
|
||||||
@IntRange(from = -255, to = 255) int volume) {
|
|
||||||
if (mDevices == null || mDevices.isEmpty()) {
|
|
||||||
Log.w(TAG, "active device or device has source is null!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (mDevices.contains(device)) {
|
|
||||||
Log.d(
|
|
||||||
TAG,
|
|
||||||
"onDeviceVolumeChanged() bluetoothDevice : "
|
|
||||||
+ device
|
|
||||||
+ " volume: "
|
|
||||||
+ volume);
|
|
||||||
if (volume == 0) {
|
|
||||||
mIsMuted = true;
|
|
||||||
} else {
|
|
||||||
mIsMuted = false;
|
|
||||||
mLatestPositiveVolume = volume;
|
|
||||||
}
|
|
||||||
if (mLocalSession != null) {
|
|
||||||
mLocalSession.setPlaybackState(getPlaybackState());
|
|
||||||
if (mNotificationManager != null) {
|
|
||||||
mNotificationManager.notify(NOTIFICATION_ID, buildNotification());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final PlaybackState.Builder mPlayStatePlayingBuilder =
|
private final PlaybackState.Builder mPlayStatePlayingBuilder =
|
||||||
new PlaybackState.Builder()
|
new PlaybackState.Builder()
|
||||||
.setActions(PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_SEEK_TO)
|
.setActions(PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_SEEK_TO)
|
||||||
@@ -200,20 +101,24 @@ public class AudioStreamMediaService extends Service {
|
|||||||
private final MetricsFeatureProvider mMetricsFeatureProvider =
|
private final MetricsFeatureProvider mMetricsFeatureProvider =
|
||||||
FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
|
FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
|
||||||
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
|
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
|
||||||
|
private final AtomicBoolean mIsMuted = new AtomicBoolean(false);
|
||||||
|
// Set 25 as default as the volume range from `VolumeControlProfile` is from 0 to 255.
|
||||||
|
// If the initial volume from `onDeviceVolumeChanged` is larger than zero (not muted), we will
|
||||||
|
// override this value. Otherwise, we raise the volume to 25 when the play button is clicked.
|
||||||
|
private final AtomicInteger mLatestPositiveVolume = new AtomicInteger(25);
|
||||||
|
private final AtomicBoolean mHasStopped = new AtomicBoolean(false);
|
||||||
private int mBroadcastId;
|
private int mBroadcastId;
|
||||||
@Nullable private ArrayList<BluetoothDevice> mDevices;
|
@Nullable private List<BluetoothDevice> mDevices;
|
||||||
@Nullable private LocalBluetoothManager mLocalBtManager;
|
@Nullable private LocalBluetoothManager mLocalBtManager;
|
||||||
@Nullable private AudioStreamsHelper mAudioStreamsHelper;
|
@Nullable private AudioStreamsHelper mAudioStreamsHelper;
|
||||||
@Nullable private LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant;
|
@Nullable private LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant;
|
||||||
@Nullable private VolumeControlProfile mVolumeControl;
|
@Nullable private VolumeControlProfile mVolumeControl;
|
||||||
@Nullable private NotificationManager mNotificationManager;
|
@Nullable private NotificationManager mNotificationManager;
|
||||||
|
@Nullable private MediaSession mLocalSession;
|
||||||
// Set 25 as default as the volume range from `VolumeControlProfile` is from 0 to 255.
|
@VisibleForTesting @Nullable AudioStreamsBroadcastAssistantCallback mBroadcastAssistantCallback;
|
||||||
// If the initial volume from `onDeviceVolumeChanged` is larger than zero (not muted), we will
|
@VisibleForTesting @Nullable BluetoothCallback mBluetoothCallback;
|
||||||
// override this value. Otherwise, we raise the volume to 25 when the play button is clicked.
|
@VisibleForTesting @Nullable BluetoothVolumeControl.Callback mVolumeControlCallback;
|
||||||
private int mLatestPositiveVolume = 25;
|
@VisibleForTesting @Nullable MediaSession.Callback mMediaSessionCallback;
|
||||||
private boolean mIsMuted = false;
|
|
||||||
@VisibleForTesting @Nullable MediaSession mLocalSession;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
@@ -250,13 +155,16 @@ public class AudioStreamMediaService extends Service {
|
|||||||
mNotificationManager.createNotificationChannel(notificationChannel);
|
mNotificationManager.createNotificationChannel(notificationChannel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mBluetoothCallback = new BtCallback();
|
||||||
mLocalBtManager.getEventManager().registerCallback(mBluetoothCallback);
|
mLocalBtManager.getEventManager().registerCallback(mBluetoothCallback);
|
||||||
|
|
||||||
mVolumeControl = mLocalBtManager.getProfileManager().getVolumeControlProfile();
|
mVolumeControl = mLocalBtManager.getProfileManager().getVolumeControlProfile();
|
||||||
if (mVolumeControl != null) {
|
if (mVolumeControl != null) {
|
||||||
|
mVolumeControlCallback = new VolumeControlCallback();
|
||||||
mVolumeControl.registerCallback(mExecutor, mVolumeControlCallback);
|
mVolumeControl.registerCallback(mExecutor, mVolumeControlCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mBroadcastAssistantCallback = new AssistantCallback();
|
||||||
mLeBroadcastAssistant.registerServiceCallBack(mExecutor, mBroadcastAssistantCallback);
|
mLeBroadcastAssistant.registerServiceCallBack(mExecutor, mBroadcastAssistantCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,25 +172,19 @@ public class AudioStreamMediaService extends Service {
|
|||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
Log.d(TAG, "onDestroy()");
|
Log.d(TAG, "onDestroy()");
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
|
|
||||||
if (!AudioSharingUtils.isFeatureEnabled()) {
|
if (!AudioSharingUtils.isFeatureEnabled()) {
|
||||||
Log.d(TAG, "onDestroy() : skip due to feature not enabled");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (mLocalBtManager != null) {
|
if (mLocalBtManager != null) {
|
||||||
Log.d(TAG, "onDestroy() : unregister mBluetoothCallback");
|
|
||||||
mLocalBtManager.getEventManager().unregisterCallback(mBluetoothCallback);
|
mLocalBtManager.getEventManager().unregisterCallback(mBluetoothCallback);
|
||||||
}
|
}
|
||||||
if (mLeBroadcastAssistant != null) {
|
if (mLeBroadcastAssistant != null && mBroadcastAssistantCallback != null) {
|
||||||
Log.d(TAG, "onDestroy() : unregister mBroadcastAssistantCallback");
|
|
||||||
mLeBroadcastAssistant.unregisterServiceCallBack(mBroadcastAssistantCallback);
|
mLeBroadcastAssistant.unregisterServiceCallBack(mBroadcastAssistantCallback);
|
||||||
}
|
}
|
||||||
if (mVolumeControl != null) {
|
if (mVolumeControl != null && mVolumeControlCallback != null) {
|
||||||
Log.d(TAG, "onDestroy() : unregister mVolumeControlCallback");
|
|
||||||
mVolumeControl.unregisterCallback(mVolumeControlCallback);
|
mVolumeControl.unregisterCallback(mVolumeControlCallback);
|
||||||
}
|
}
|
||||||
if (mLocalSession != null) {
|
if (mLocalSession != null) {
|
||||||
Log.d(TAG, "onDestroy() : release mLocalSession");
|
|
||||||
mLocalSession.release();
|
mLocalSession.release();
|
||||||
mLocalSession = null;
|
mLocalSession = null;
|
||||||
}
|
}
|
||||||
@@ -291,33 +193,31 @@ public class AudioStreamMediaService extends Service {
|
|||||||
@Override
|
@Override
|
||||||
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
|
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
|
||||||
Log.d(TAG, "onStartCommand()");
|
Log.d(TAG, "onStartCommand()");
|
||||||
|
if (intent == null) {
|
||||||
mBroadcastId = intent != null ? intent.getIntExtra(BROADCAST_ID, -1) : -1;
|
Log.w(TAG, "Intent is null. Service will not start.");
|
||||||
|
mHasStopped.set(true);
|
||||||
|
stopSelf();
|
||||||
|
return START_NOT_STICKY;
|
||||||
|
}
|
||||||
|
mBroadcastId = intent.getIntExtra(BROADCAST_ID, -1);
|
||||||
if (mBroadcastId == -1) {
|
if (mBroadcastId == -1) {
|
||||||
Log.w(TAG, "Invalid broadcast ID. Service will not start.");
|
Log.w(TAG, "Invalid broadcast ID. Service will not start.");
|
||||||
if (mNotificationManager != null) {
|
mHasStopped.set(true);
|
||||||
mNotificationManager.cancel(NOTIFICATION_ID);
|
|
||||||
}
|
|
||||||
stopSelf();
|
stopSelf();
|
||||||
return START_NOT_STICKY;
|
return START_NOT_STICKY;
|
||||||
}
|
}
|
||||||
|
var extra = intent.getParcelableArrayListExtra(DEVICES, BluetoothDevice.class);
|
||||||
if (intent != null) {
|
if (extra == null || extra.isEmpty()) {
|
||||||
mDevices = intent.getParcelableArrayListExtra(DEVICES, BluetoothDevice.class);
|
|
||||||
}
|
|
||||||
if (mDevices == null || mDevices.isEmpty()) {
|
|
||||||
Log.w(TAG, "No device. Service will not start.");
|
Log.w(TAG, "No device. Service will not start.");
|
||||||
if (mNotificationManager != null) {
|
mHasStopped.set(true);
|
||||||
mNotificationManager.cancel(NOTIFICATION_ID);
|
|
||||||
}
|
|
||||||
stopSelf();
|
stopSelf();
|
||||||
return START_NOT_STICKY;
|
return START_NOT_STICKY;
|
||||||
}
|
}
|
||||||
if (intent != null) {
|
mDevices = Collections.synchronizedList(extra);
|
||||||
createLocalMediaSession(intent.getStringExtra(BROADCAST_TITLE));
|
createLocalMediaSession(intent.getStringExtra(BROADCAST_TITLE));
|
||||||
startForeground(NOTIFICATION_ID, buildNotification());
|
startForeground(NOTIFICATION_ID, buildNotification());
|
||||||
}
|
// Reset in case the service is previously stopped but not yet destroyed.
|
||||||
|
mHasStopped.set(false);
|
||||||
return START_NOT_STICKY;
|
return START_NOT_STICKY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,78 +230,12 @@ public class AudioStreamMediaService extends Service {
|
|||||||
.build());
|
.build());
|
||||||
mLocalSession.setActive(true);
|
mLocalSession.setActive(true);
|
||||||
mLocalSession.setPlaybackState(getPlaybackState());
|
mLocalSession.setPlaybackState(getPlaybackState());
|
||||||
mLocalSession.setCallback(
|
mMediaSessionCallback = new MediaSessionCallback();
|
||||||
new MediaSession.Callback() {
|
mLocalSession.setCallback(mMediaSessionCallback);
|
||||||
public void onSeekTo(long pos) {
|
|
||||||
Log.d(TAG, "onSeekTo: " + pos);
|
|
||||||
if (mLocalSession != null) {
|
|
||||||
mLocalSession.setPlaybackState(getPlaybackState());
|
|
||||||
if (mNotificationManager != null) {
|
|
||||||
mNotificationManager.notify(NOTIFICATION_ID, buildNotification());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPause() {
|
|
||||||
if (mDevices == null || mDevices.isEmpty()) {
|
|
||||||
Log.w(TAG, "active device or device has source is null!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Log.d(
|
|
||||||
TAG,
|
|
||||||
"onPause() setting volume for device : "
|
|
||||||
+ mDevices.get(0)
|
|
||||||
+ " volume: "
|
|
||||||
+ 0);
|
|
||||||
if (mVolumeControl != null) {
|
|
||||||
mVolumeControl.setDeviceVolume(mDevices.get(0), 0, true);
|
|
||||||
mMetricsFeatureProvider.action(
|
|
||||||
getApplicationContext(),
|
|
||||||
SettingsEnums
|
|
||||||
.ACTION_AUDIO_STREAM_NOTIFICATION_MUTE_BUTTON_CLICK,
|
|
||||||
1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPlay() {
|
|
||||||
if (mDevices == null || mDevices.isEmpty()) {
|
|
||||||
Log.w(TAG, "active device or device has source is null!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Log.d(
|
|
||||||
TAG,
|
|
||||||
"onPlay() setting volume for device : "
|
|
||||||
+ mDevices.get(0)
|
|
||||||
+ " volume: "
|
|
||||||
+ mLatestPositiveVolume);
|
|
||||||
if (mVolumeControl != null) {
|
|
||||||
mVolumeControl.setDeviceVolume(
|
|
||||||
mDevices.get(0), mLatestPositiveVolume, true);
|
|
||||||
}
|
|
||||||
mMetricsFeatureProvider.action(
|
|
||||||
getApplicationContext(),
|
|
||||||
SettingsEnums.ACTION_AUDIO_STREAM_NOTIFICATION_MUTE_BUTTON_CLICK,
|
|
||||||
0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCustomAction(@NonNull String action, Bundle extras) {
|
|
||||||
Log.d(TAG, "onCustomAction: " + action);
|
|
||||||
if (action.equals(LEAVE_BROADCAST_ACTION) && mAudioStreamsHelper != null) {
|
|
||||||
mAudioStreamsHelper.removeSource(mBroadcastId);
|
|
||||||
mMetricsFeatureProvider.action(
|
|
||||||
getApplicationContext(),
|
|
||||||
SettingsEnums
|
|
||||||
.ACTION_AUDIO_STREAM_NOTIFICATION_LEAVE_BUTTON_CLICK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private PlaybackState getPlaybackState() {
|
private PlaybackState getPlaybackState() {
|
||||||
return mIsMuted ? mPlayStatePausingBuilder.build() : mPlayStatePlayingBuilder.build();
|
return mIsMuted.get() ? mPlayStatePausingBuilder.build() : mPlayStatePlayingBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getDeviceName() {
|
private String getDeviceName() {
|
||||||
@@ -442,4 +276,167 @@ public class AudioStreamMediaService extends Service {
|
|||||||
public IBinder onBind(Intent intent) {
|
public IBinder onBind(Intent intent) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class AssistantCallback extends AudioStreamsBroadcastAssistantCallback {
|
||||||
|
@Override
|
||||||
|
public void onSourceLost(int broadcastId) {
|
||||||
|
super.onSourceLost(broadcastId);
|
||||||
|
handleRemoveSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSourceRemoved(BluetoothDevice sink, int sourceId, int reason) {
|
||||||
|
super.onSourceRemoved(sink, sourceId, reason);
|
||||||
|
handleRemoveSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleRemoveSource() {
|
||||||
|
var unused =
|
||||||
|
ThreadUtils.postOnBackgroundThread(
|
||||||
|
() -> {
|
||||||
|
List<BluetoothLeBroadcastReceiveState> connected =
|
||||||
|
mAudioStreamsHelper == null
|
||||||
|
? emptyList()
|
||||||
|
: mAudioStreamsHelper.getAllConnectedSources();
|
||||||
|
if (connected.stream()
|
||||||
|
.map(BluetoothLeBroadcastReceiveState::getBroadcastId)
|
||||||
|
.noneMatch(id -> id == mBroadcastId)) {
|
||||||
|
mHasStopped.set(true);
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class VolumeControlCallback implements BluetoothVolumeControl.Callback {
|
||||||
|
@Override
|
||||||
|
public void onDeviceVolumeChanged(
|
||||||
|
@NonNull BluetoothDevice device, @IntRange(from = -255, to = 255) int volume) {
|
||||||
|
if (mDevices == null || mDevices.isEmpty()) {
|
||||||
|
Log.w(TAG, "active device or device has source is null!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Log.d(
|
||||||
|
TAG,
|
||||||
|
"onDeviceVolumeChanged() bluetoothDevice : " + device + " volume: " + volume);
|
||||||
|
if (mDevices.contains(device)) {
|
||||||
|
if (volume == 0) {
|
||||||
|
mIsMuted.set(true);
|
||||||
|
} else {
|
||||||
|
mIsMuted.set(false);
|
||||||
|
mLatestPositiveVolume.set(volume);
|
||||||
|
}
|
||||||
|
updateNotification(getPlaybackState());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BtCallback implements BluetoothCallback {
|
||||||
|
@Override
|
||||||
|
public void onBluetoothStateChanged(int bluetoothState) {
|
||||||
|
if (BluetoothAdapter.STATE_OFF == bluetoothState) {
|
||||||
|
Log.d(TAG, "onBluetoothStateChanged() : stopSelf");
|
||||||
|
mHasStopped.set(true);
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProfileConnectionStateChanged(
|
||||||
|
@NonNull CachedBluetoothDevice cachedDevice,
|
||||||
|
@ConnectionState int state,
|
||||||
|
int bluetoothProfile) {
|
||||||
|
if (state == BluetoothAdapter.STATE_DISCONNECTED
|
||||||
|
&& bluetoothProfile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT
|
||||||
|
&& mDevices != null) {
|
||||||
|
mDevices.remove(cachedDevice.getDevice());
|
||||||
|
cachedDevice
|
||||||
|
.getMemberDevice()
|
||||||
|
.forEach(
|
||||||
|
m -> {
|
||||||
|
// Check nullability to pass NullAway check
|
||||||
|
if (mDevices != null) {
|
||||||
|
mDevices.remove(m.getDevice());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (mDevices == null || mDevices.isEmpty()) {
|
||||||
|
Log.d(TAG, "onProfileConnectionStateChanged() : stopSelf");
|
||||||
|
mHasStopped.set(true);
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MediaSessionCallback extends MediaSession.Callback {
|
||||||
|
public void onSeekTo(long pos) {
|
||||||
|
Log.d(TAG, "onSeekTo: " + pos);
|
||||||
|
updateNotification(getPlaybackState());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
if (mDevices == null || mDevices.isEmpty()) {
|
||||||
|
Log.w(TAG, "active device or device has source is null!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Log.d(
|
||||||
|
TAG,
|
||||||
|
"onPause() setting volume for device : " + mDevices.get(0) + " volume: " + 0);
|
||||||
|
setDeviceVolume(mDevices.get(0), /* volume= */ 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlay() {
|
||||||
|
if (mDevices == null || mDevices.isEmpty()) {
|
||||||
|
Log.w(TAG, "active device or device has source is null!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Log.d(
|
||||||
|
TAG,
|
||||||
|
"onPlay() setting volume for device : "
|
||||||
|
+ mDevices.get(0)
|
||||||
|
+ " volume: "
|
||||||
|
+ mLatestPositiveVolume.get());
|
||||||
|
setDeviceVolume(mDevices.get(0), mLatestPositiveVolume.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCustomAction(@NonNull String action, Bundle extras) {
|
||||||
|
Log.d(TAG, "onCustomAction: " + action);
|
||||||
|
if (action.equals(LEAVE_BROADCAST_ACTION) && mAudioStreamsHelper != null) {
|
||||||
|
mAudioStreamsHelper.removeSource(mBroadcastId);
|
||||||
|
mMetricsFeatureProvider.action(
|
||||||
|
getApplicationContext(),
|
||||||
|
SettingsEnums.ACTION_AUDIO_STREAM_NOTIFICATION_LEAVE_BUTTON_CLICK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDeviceVolume(BluetoothDevice device, int volume) {
|
||||||
|
int event = SettingsEnums.ACTION_AUDIO_STREAM_NOTIFICATION_MUTE_BUTTON_CLICK;
|
||||||
|
var unused =
|
||||||
|
ThreadUtils.postOnBackgroundThread(
|
||||||
|
() -> {
|
||||||
|
if (mVolumeControl != null) {
|
||||||
|
mVolumeControl.setDeviceVolume(device, volume, true);
|
||||||
|
mMetricsFeatureProvider.action(
|
||||||
|
getApplicationContext(), event, volume == 0 ? 1 : 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateNotification(PlaybackState playbackState) {
|
||||||
|
var unused =
|
||||||
|
ThreadUtils.postOnBackgroundThread(
|
||||||
|
() -> {
|
||||||
|
if (mLocalSession != null) {
|
||||||
|
mLocalSession.setPlaybackState(playbackState);
|
||||||
|
if (mNotificationManager != null && !mHasStopped.get()) {
|
||||||
|
mNotificationManager.notify(
|
||||||
|
NOTIFICATION_ID, buildNotification());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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.BROADCAST_ID;
|
||||||
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamMediaService.DEVICES;
|
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 com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.doReturn;
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
|
import android.app.NotificationChannel;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
|
import android.app.settings.SettingsEnums;
|
||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothProfile;
|
||||||
import android.bluetooth.BluetoothStatusCodes;
|
import android.bluetooth.BluetoothStatusCodes;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -43,14 +50,20 @@ import android.content.res.Resources;
|
|||||||
import android.media.session.ISession;
|
import android.media.session.ISession;
|
||||||
import android.media.session.ISessionController;
|
import android.media.session.ISessionController;
|
||||||
import android.media.session.MediaSessionManager;
|
import android.media.session.MediaSessionManager;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.platform.test.flag.junit.SetFlagsRule;
|
import android.platform.test.flag.junit.SetFlagsRule;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
|
|
||||||
import com.android.settings.connecteddevice.audiosharing.audiostreams.testshadows.ShadowAudioStreamsHelper;
|
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.ShadowBluetoothAdapter;
|
||||||
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
|
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.BluetoothEventManager;
|
||||||
|
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||||
|
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
|
||||||
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
|
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
|
||||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||||
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
|
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
|
||||||
@@ -72,10 +85,12 @@ import org.robolectric.shadow.api.Shadow;
|
|||||||
import org.robolectric.util.ReflectionHelpers;
|
import org.robolectric.util.ReflectionHelpers;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(RobolectricTestRunner.class)
|
||||||
@Config(
|
@Config(
|
||||||
shadows = {
|
shadows = {
|
||||||
|
ShadowThreadUtils.class,
|
||||||
ShadowBluetoothAdapter.class,
|
ShadowBluetoothAdapter.class,
|
||||||
ShadowBluetoothUtils.class,
|
ShadowBluetoothUtils.class,
|
||||||
ShadowAudioStreamsHelper.class,
|
ShadowAudioStreamsHelper.class,
|
||||||
@@ -83,6 +98,8 @@ import java.util.ArrayList;
|
|||||||
public class AudioStreamMediaServiceTest {
|
public class AudioStreamMediaServiceTest {
|
||||||
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||||
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
@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 Resources mResources;
|
||||||
@Mock private LocalBluetoothManager mLocalBtManager;
|
@Mock private LocalBluetoothManager mLocalBtManager;
|
||||||
@Mock private LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant;
|
@Mock private LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant;
|
||||||
@@ -91,17 +108,21 @@ public class AudioStreamMediaServiceTest {
|
|||||||
@Mock private MediaSessionManager mMediaSessionManager;
|
@Mock private MediaSessionManager mMediaSessionManager;
|
||||||
@Mock private BluetoothEventManager mBluetoothEventManager;
|
@Mock private BluetoothEventManager mBluetoothEventManager;
|
||||||
@Mock private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
|
@Mock private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
|
||||||
|
@Mock private CachedBluetoothDeviceManager mCachedDeviceManager;
|
||||||
@Mock private VolumeControlProfile mVolumeControlProfile;
|
@Mock private VolumeControlProfile mVolumeControlProfile;
|
||||||
|
@Mock private CachedBluetoothDevice mCachedBluetoothDevice;
|
||||||
@Mock private BluetoothDevice mDevice;
|
@Mock private BluetoothDevice mDevice;
|
||||||
@Mock private ISession mISession;
|
@Mock private ISession mISession;
|
||||||
@Mock private ISessionController mISessionController;
|
@Mock private ISessionController mISessionController;
|
||||||
@Mock private PackageManager mPackageManager;
|
@Mock private PackageManager mPackageManager;
|
||||||
@Mock private DisplayMetrics mDisplayMetrics;
|
@Mock private DisplayMetrics mDisplayMetrics;
|
||||||
@Mock private Context mContext;
|
@Mock private Context mContext;
|
||||||
|
private FakeFeatureFactory mFeatureFactory;
|
||||||
private AudioStreamMediaService mAudioStreamMediaService;
|
private AudioStreamMediaService mAudioStreamMediaService;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
|
mFeatureFactory = FakeFeatureFactory.setupForTest();
|
||||||
ShadowAudioStreamsHelper.setUseMock(mAudioStreamsHelper);
|
ShadowAudioStreamsHelper.setUseMock(mAudioStreamsHelper);
|
||||||
when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(mLeBroadcastAssistant);
|
when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(mLeBroadcastAssistant);
|
||||||
ShadowBluetoothAdapter shadowBluetoothAdapter =
|
ShadowBluetoothAdapter shadowBluetoothAdapter =
|
||||||
@@ -114,6 +135,9 @@ public class AudioStreamMediaServiceTest {
|
|||||||
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager;
|
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager;
|
||||||
when(mLocalBtManager.getEventManager()).thenReturn(mBluetoothEventManager);
|
when(mLocalBtManager.getEventManager()).thenReturn(mBluetoothEventManager);
|
||||||
when(mLocalBtManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
|
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())
|
when(mLocalBluetoothProfileManager.getVolumeControlProfile())
|
||||||
.thenReturn(mVolumeControlProfile);
|
.thenReturn(mVolumeControlProfile);
|
||||||
|
|
||||||
@@ -168,6 +192,25 @@ public class AudioStreamMediaServiceTest {
|
|||||||
verify(mVolumeControlProfile).registerCallback(any(), any());
|
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
|
@Test
|
||||||
public void onDestroy_flagOff_doNothing() {
|
public void onDestroy_flagOff_doNothing() {
|
||||||
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
@@ -183,8 +226,15 @@ public class AudioStreamMediaServiceTest {
|
|||||||
@Test
|
@Test
|
||||||
public void onDestroy_flagOn_cleanup() {
|
public void onDestroy_flagOn_cleanup() {
|
||||||
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
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.onCreate();
|
||||||
|
mAudioStreamMediaService.onStartCommand(intent, /* flags= */ 0, /* startId= */ 0);
|
||||||
mAudioStreamMediaService.onDestroy();
|
mAudioStreamMediaService.onDestroy();
|
||||||
|
|
||||||
verify(mBluetoothEventManager).unregisterCallback(any());
|
verify(mBluetoothEventManager).unregisterCallback(any());
|
||||||
@@ -196,7 +246,6 @@ public class AudioStreamMediaServiceTest {
|
|||||||
public void onStartCommand_noBroadcastId_stopSelf() {
|
public void onStartCommand_noBroadcastId_stopSelf() {
|
||||||
mAudioStreamMediaService.onStartCommand(new Intent(), /* flags= */ 0, /* startId= */ 0);
|
mAudioStreamMediaService.onStartCommand(new Intent(), /* flags= */ 0, /* startId= */ 0);
|
||||||
|
|
||||||
assertThat(mAudioStreamMediaService.mLocalSession).isNull();
|
|
||||||
verify(mAudioStreamMediaService).stopSelf();
|
verify(mAudioStreamMediaService).stopSelf();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +256,6 @@ public class AudioStreamMediaServiceTest {
|
|||||||
|
|
||||||
mAudioStreamMediaService.onStartCommand(intent, /* flags= */ 0, /* startId= */ 0);
|
mAudioStreamMediaService.onStartCommand(intent, /* flags= */ 0, /* startId= */ 0);
|
||||||
|
|
||||||
assertThat(mAudioStreamMediaService.mLocalSession).isNull();
|
|
||||||
verify(mAudioStreamMediaService).stopSelf();
|
verify(mAudioStreamMediaService).stopSelf();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,12 +270,179 @@ public class AudioStreamMediaServiceTest {
|
|||||||
|
|
||||||
mAudioStreamMediaService.onStartCommand(intent, /* flags= */ 0, /* startId= */ 0);
|
mAudioStreamMediaService.onStartCommand(intent, /* flags= */ 0, /* startId= */ 0);
|
||||||
|
|
||||||
assertThat(mAudioStreamMediaService.mLocalSession).isNotNull();
|
ArgumentCaptor<Notification> notificationCapture =
|
||||||
verify(mAudioStreamMediaService, never()).stopSelf();
|
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, never()).stopSelf();
|
||||||
verify(mAudioStreamMediaService).startForeground(anyInt(), notification.capture());
|
}
|
||||||
assertThat(notification.getValue().getSmallIcon()).isNotNull();
|
|
||||||
assertThat(notification.getValue().isStyle(Notification.MediaStyle.class)).isTrue();
|
@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