Merge "Small clean up to get ready for receive state based triggering." into main

This commit is contained in:
Chelsea Hao
2025-03-06 00:57:56 -08:00
committed by Android (Google) Code Review
2 changed files with 148 additions and 168 deletions

View File

@@ -34,7 +34,10 @@ import android.media.MediaMetadata;
import android.media.session.MediaSession; import android.media.session.MediaSession;
import android.media.session.PlaybackState; import android.media.session.PlaybackState;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder; import android.os.IBinder;
import android.os.Process;
import android.util.Log; import android.util.Log;
import android.view.KeyEvent; import android.view.KeyEvent;
@@ -51,24 +54,21 @@ import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState;
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 com.android.settingslib.utils.ThreadUtils;
import java.util.Collections; import java.util.HashMap;
import java.util.List; import java.util.Map;
import java.util.concurrent.ExecutorService;
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";
static final String BROADCAST_TITLE = "audio_stream_media_service_broadcast_title"; static final String BROADCAST_TITLE = "audio_stream_media_service_broadcast_title";
static final String DEVICES = "audio_stream_media_service_devices"; static final String DEVICES = "audio_stream_media_service_devices";
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 = R.string.audio_streams_title;
private static final int BROADCAST_LISTENING_NOW_TEXT = R.string.audio_streams_listening_now; private static final int BROADCAST_LISTENING_NOW_TEXT = R.string.audio_streams_listening_now;
private static final int BROADCAST_STREAM_PAUSED_TEXT = R.string.audio_streams_present_now; private static final int BROADCAST_STREAM_PAUSED_TEXT = R.string.audio_streams_present_now;
@VisibleForTesting static final String LEAVE_BROADCAST_ACTION = "leave_broadcast_action"; @VisibleForTesting static final String LEAVE_BROADCAST_ACTION = "leave_broadcast_action";
@@ -113,17 +113,16 @@ 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 HandlerThread mHandlerThread = new HandlerThread(TAG,
private final AtomicBoolean mIsMuted = new AtomicBoolean(false); Process.THREAD_PRIORITY_BACKGROUND);
private final AtomicBoolean mIsHysteresis = new AtomicBoolean(false); private boolean mIsMuted = false;
// Set 25 as default as the volume range from `VolumeControlProfile` is from 0 to 255. // 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 // 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. // override this value. Otherwise, we raise the volume to 25 when the play button is clicked.
private final AtomicInteger mLatestPositiveVolume = new AtomicInteger(25); private int mLatestPositiveVolume = 25;
private final Object mLocalSessionLock = new Object();
private boolean mHysteresisModeFixAvailable; private boolean mHysteresisModeFixAvailable;
private int mBroadcastId; private int mBroadcastId;
@Nullable private List<BluetoothDevice> mDevices; @Nullable private Map<BluetoothDevice, LocalBluetoothLeBroadcastSourceState> mStateByDevice;
@Nullable private LocalBluetoothManager mLocalBtManager; @Nullable private LocalBluetoothManager mLocalBtManager;
@Nullable private AudioStreamsHelper mAudioStreamsHelper; @Nullable private AudioStreamsHelper mAudioStreamsHelper;
@Nullable private LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant; @Nullable private LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant;
@@ -154,7 +153,6 @@ public class AudioStreamMediaService extends Service {
Log.w(TAG, "onCreate() : mLeBroadcastAssistant is null!"); Log.w(TAG, "onCreate() : mLeBroadcastAssistant is null!");
return; return;
} }
mHysteresisModeFixAvailable = BluetoothUtils.isAudioSharingHysteresisModeFixAvailable(this);
mNotificationManager = getSystemService(NotificationManager.class); mNotificationManager = getSystemService(NotificationManager.class);
if (mNotificationManager == null) { if (mNotificationManager == null) {
@@ -162,7 +160,8 @@ public class AudioStreamMediaService extends Service {
return; return;
} }
mExecutor.execute( mHandlerThread.start();
getHandler().post(
() -> { () -> {
if (mLocalBtManager == null if (mLocalBtManager == null
|| mLeBroadcastAssistant == null || mLeBroadcastAssistant == null
@@ -184,32 +183,36 @@ public class AudioStreamMediaService extends Service {
mVolumeControl = mLocalBtManager.getProfileManager().getVolumeControlProfile(); mVolumeControl = mLocalBtManager.getProfileManager().getVolumeControlProfile();
if (mVolumeControl != null) { if (mVolumeControl != null) {
mVolumeControlCallback = new VolumeControlCallback(); mVolumeControlCallback = new VolumeControlCallback();
mVolumeControl.registerCallback(mExecutor, mVolumeControlCallback); mVolumeControl.registerCallback(getHandler()::post, mVolumeControlCallback);
} }
mBroadcastAssistantCallback = new AssistantCallback(); mBroadcastAssistantCallback = new AssistantCallback();
mLeBroadcastAssistant.registerServiceCallBack( mLeBroadcastAssistant.registerServiceCallBack(
mExecutor, mBroadcastAssistantCallback); getHandler()::post, mBroadcastAssistantCallback);
mHysteresisModeFixAvailable =
BluetoothUtils.isAudioSharingHysteresisModeFixAvailable(this);
}); });
} }
@VisibleForTesting
Handler getHandler() {
return mHandlerThread.getThreadHandler();
}
@Override @Override
public void onDestroy() { public void onDestroy() {
Log.d(TAG, "onDestroy()"); Log.d(TAG, "onDestroy()");
super.onDestroy(); getHandler().post(
if (BluetoothUtils.isAudioSharingUIAvailable(this)) { () -> {
if (mDevices != null) { if (mStateByDevice != null) {
mDevices.clear(); mStateByDevice.clear();
mDevices = null; mStateByDevice = null;
} }
synchronized (mLocalSessionLock) {
if (mLocalSession != null) { if (mLocalSession != null) {
mLocalSession.release(); mLocalSession.release();
mLocalSession = null; mLocalSession = null;
} }
}
mExecutor.execute(
() -> {
if (mLocalBtManager != null) { if (mLocalBtManager != null) {
mLocalBtManager.getEventManager().unregisterCallback( mLocalBtManager.getEventManager().unregisterCallback(
mBluetoothCallback); mBluetoothCallback);
@@ -222,7 +225,7 @@ public class AudioStreamMediaService extends Service {
mVolumeControl.unregisterCallback(mVolumeControlCallback); mVolumeControl.unregisterCallback(mVolumeControlCallback);
} }
}); });
} mHandlerThread.quitSafely();
} }
@Override @Override
@@ -233,27 +236,29 @@ public class AudioStreamMediaService extends Service {
stopSelf(); stopSelf();
return START_NOT_STICKY; return START_NOT_STICKY;
} }
getHandler().post(() -> {
mBroadcastId = intent.getIntExtra(BROADCAST_ID, -1); 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.");
stopSelf(); stopSelf();
return START_NOT_STICKY; return;
} }
var extra = intent.getParcelableArrayListExtra(DEVICES, BluetoothDevice.class); var devices = intent.getParcelableArrayListExtra(DEVICES, BluetoothDevice.class);
if (extra == null || extra.isEmpty()) { if (devices == null || devices.isEmpty()) {
Log.w(TAG, "No device. Service will not start."); Log.w(TAG, "No device. Service will not start.");
stopSelf(); stopSelf();
return START_NOT_STICKY; } else {
} mStateByDevice = new HashMap<>();
mDevices = Collections.synchronizedList(extra); devices.forEach(d -> mStateByDevice.put(d, STREAMING));
MediaSession.Token token = MediaSession.Token token =
getOrCreateLocalMediaSession(intent.getStringExtra(BROADCAST_TITLE)); getOrCreateLocalMediaSession(intent.getStringExtra(BROADCAST_TITLE));
startForeground(NOTIFICATION_ID, buildNotification(token)); startForeground(NOTIFICATION_ID, buildNotification(token));
}
});
return START_NOT_STICKY; return START_NOT_STICKY;
} }
private MediaSession.Token getOrCreateLocalMediaSession(String title) { private MediaSession.Token getOrCreateLocalMediaSession(String title) {
synchronized (mLocalSessionLock) {
if (mLocalSession != null) { if (mLocalSession != null) {
return mLocalSession.getSessionToken(); return mLocalSession.getSessionToken();
} }
@@ -266,20 +271,24 @@ public class AudioStreamMediaService extends Service {
mLocalSession.setActive(true); mLocalSession.setActive(true);
mLocalSession.setPlaybackState(getPlaybackState()); mLocalSession.setPlaybackState(getPlaybackState());
mMediaSessionCallback = new MediaSessionCallback(); mMediaSessionCallback = new MediaSessionCallback();
mLocalSession.setCallback(mMediaSessionCallback); mLocalSession.setCallback(mMediaSessionCallback, getHandler());
return mLocalSession.getSessionToken(); return mLocalSession.getSessionToken();
} }
}
private PlaybackState getPlaybackState() { private PlaybackState getPlaybackState() {
if (mIsHysteresis.get()) { if (isAllDeviceHysteresis()) {
return mPlayStateHysteresisBuilder.build(); return mPlayStateHysteresisBuilder.build();
} }
return mIsMuted.get() ? mPlayStatePausingBuilder.build() : mPlayStatePlayingBuilder.build(); return mIsMuted ? mPlayStatePausingBuilder.build() : mPlayStatePlayingBuilder.build();
}
private boolean isAllDeviceHysteresis() {
return mHysteresisModeFixAvailable && mStateByDevice != null
&& mStateByDevice.values().stream().allMatch(v -> v == PAUSED);
} }
private String getDeviceName() { private String getDeviceName() {
if (mDevices == null || mDevices.isEmpty() || mLocalBtManager == null) { if (mStateByDevice == null || mStateByDevice.isEmpty() || mLocalBtManager == null) {
return DEFAULT_DEVICE_NAME; return DEFAULT_DEVICE_NAME;
} }
@@ -288,7 +297,8 @@ public class AudioStreamMediaService extends Service {
return DEFAULT_DEVICE_NAME; return DEFAULT_DEVICE_NAME;
} }
CachedBluetoothDevice device = manager.findDevice(mDevices.get(0)); CachedBluetoothDevice device = manager.findDevice(
mStateByDevice.keySet().iterator().next());
return device != null ? device.getName() : DEFAULT_DEVICE_NAME; return device != null ? device.getName() : DEFAULT_DEVICE_NAME;
} }
@@ -304,7 +314,7 @@ public class AudioStreamMediaService extends Service {
.setSmallIcon(com.android.settingslib.R.drawable.ic_bt_le_audio_sharing) .setSmallIcon(com.android.settingslib.R.drawable.ic_bt_le_audio_sharing)
.setStyle(mediaStyle) .setStyle(mediaStyle)
.setContentText(getString( .setContentText(getString(
mIsHysteresis.get() ? BROADCAST_STREAM_PAUSED_TEXT : isAllDeviceHysteresis() ? BROADCAST_STREAM_PAUSED_TEXT :
BROADCAST_LISTENING_NOW_TEXT)) BROADCAST_LISTENING_NOW_TEXT))
.setSilent(true); .setSilent(true);
return notificationBuilder.build(); return notificationBuilder.build();
@@ -333,7 +343,8 @@ public class AudioStreamMediaService extends Service {
public void onReceiveStateChanged( public void onReceiveStateChanged(
BluetoothDevice sink, int sourceId, BluetoothLeBroadcastReceiveState state) { BluetoothDevice sink, int sourceId, BluetoothLeBroadcastReceiveState state) {
super.onReceiveStateChanged(sink, sourceId, state); super.onReceiveStateChanged(sink, sourceId, state);
if (!mHysteresisModeFixAvailable || mDevices == null || !mDevices.contains(sink)) { if (!mHysteresisModeFixAvailable || mStateByDevice == null
|| !mStateByDevice.containsKey(sink)) {
return; return;
} }
var sourceState = LocalBluetoothLeBroadcastAssistant.getLocalSourceState(state); var sourceState = LocalBluetoothLeBroadcastAssistant.getLocalSourceState(state);
@@ -343,12 +354,10 @@ public class AudioStreamMediaService extends Service {
if (!streaming && !paused) { if (!streaming && !paused) {
return; return;
} }
// Atomically update mIsHysteresis if its current value is not the current paused state boolean shouldUpdate = mStateByDevice.get(sink) != sourceState;
if (mIsHysteresis.compareAndSet(!paused, paused)) { if (shouldUpdate) {
synchronized (mLocalSessionLock) { mStateByDevice.put(sink, sourceState);
if (mLocalSession == null) { if (mLocalSession != null) {
return;
}
mLocalSession.setPlaybackState(getPlaybackState()); mLocalSession.setPlaybackState(getPlaybackState());
if (mNotificationManager != null) { if (mNotificationManager != null) {
mNotificationManager.notify( mNotificationManager.notify(
@@ -356,7 +365,7 @@ public class AudioStreamMediaService extends Service {
buildNotification(mLocalSession.getSessionToken()) buildNotification(mLocalSession.getSessionToken())
); );
} }
Log.d(TAG, "updating hysteresis mode to : " + paused); Log.d(TAG, "updating source state to : " + sourceState);
} }
} }
} }
@@ -374,36 +383,36 @@ public class AudioStreamMediaService extends Service {
@Override @Override
public void onDeviceVolumeChanged( public void onDeviceVolumeChanged(
@NonNull BluetoothDevice device, @IntRange(from = -255, to = 255) int volume) { @NonNull BluetoothDevice device, @IntRange(from = -255, to = 255) int volume) {
if (mDevices == null || mDevices.isEmpty()) { if (mStateByDevice == null || mStateByDevice.isEmpty()) {
Log.w(TAG, "active device or device has source is null!"); Log.w(TAG, "active device or device has source is null!");
return; return;
} }
Log.d( Log.d(
TAG, TAG,
"onDeviceVolumeChanged() bluetoothDevice : " + device + " volume: " + volume); "onDeviceVolumeChanged() bluetoothDevice : " + device + " volume: " + volume);
if (mDevices.contains(device)) { if (mStateByDevice.containsKey(device)) {
if (volume == 0) { if (volume == 0) {
mIsMuted.set(true); mIsMuted = true;
} else { } else {
mIsMuted.set(false); mIsMuted = false;
mLatestPositiveVolume.set(volume); mLatestPositiveVolume = volume;
} }
synchronized (mLocalSessionLock) {
if (mLocalSession != null) { if (mLocalSession != null) {
mLocalSession.setPlaybackState(getPlaybackState()); mLocalSession.setPlaybackState(getPlaybackState());
} }
} }
} }
} }
}
private class BtCallback implements BluetoothCallback { private class BtCallback implements BluetoothCallback {
@Override @Override
public void onBluetoothStateChanged(int bluetoothState) { public void onBluetoothStateChanged(int bluetoothState) {
getHandler().post(() -> {
if (BluetoothAdapter.STATE_OFF == bluetoothState) { if (BluetoothAdapter.STATE_OFF == bluetoothState) {
Log.d(TAG, "onBluetoothStateChanged() : stopSelf"); Log.d(TAG, "onBluetoothStateChanged() : stopSelf");
stopSelf(); stopSelf();
} }
});
} }
@Override @Override
@@ -411,24 +420,17 @@ public class AudioStreamMediaService extends Service {
@NonNull CachedBluetoothDevice cachedDevice, @NonNull CachedBluetoothDevice cachedDevice,
@ConnectionState int state, @ConnectionState int state,
int bluetoothProfile) { int bluetoothProfile) {
getHandler().post(() -> {
if (state == BluetoothAdapter.STATE_DISCONNECTED if (state == BluetoothAdapter.STATE_DISCONNECTED
&& bluetoothProfile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT && bluetoothProfile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT
&& mDevices != null) { && mStateByDevice != null) {
mDevices.remove(cachedDevice.getDevice()); mStateByDevice.remove(cachedDevice.getDevice());
cachedDevice
.getMemberDevice()
.forEach(
m -> {
// Check nullability to pass NullAway check
if (mDevices != null) {
mDevices.remove(m.getDevice());
} }
}); if (mStateByDevice == null || mStateByDevice.isEmpty()) {
}
if (mDevices == null || mDevices.isEmpty()) {
Log.d(TAG, "onProfileConnectionStateChanged() : stopSelf"); Log.d(TAG, "onProfileConnectionStateChanged() : stopSelf");
stopSelf(); stopSelf();
} }
});
} }
} }
@@ -454,12 +456,10 @@ public class AudioStreamMediaService extends Service {
@Override @Override
public void onSeekTo(long pos) { public void onSeekTo(long pos) {
Log.d(TAG, "onSeekTo: " + pos); Log.d(TAG, "onSeekTo: " + pos);
synchronized (mLocalSessionLock) {
if (mLocalSession != null) { if (mLocalSession != null) {
mLocalSession.setPlaybackState(getPlaybackState()); mLocalSession.setPlaybackState(getPlaybackState());
} }
} }
}
@Override @Override
public void onPause() { public void onPause() {
@@ -484,28 +484,26 @@ public class AudioStreamMediaService extends Service {
} }
private void handleOnPlay() { private void handleOnPlay() {
if (mDevices == null || mDevices.isEmpty()) { if (mStateByDevice == null || mStateByDevice.isEmpty()) {
Log.w(TAG, "active device or device has source is null!"); Log.w(TAG, "active device or device has source is null!");
return; return;
} }
Log.d( mStateByDevice.keySet().forEach(device -> {
TAG, Log.d(TAG, "onPlay() setting volume for device : " + device + " volume: "
"onPlay() setting volume for device : " + mLatestPositiveVolume);
+ mDevices.getFirst() setDeviceVolume(device, mLatestPositiveVolume);
+ " volume: " });
+ mLatestPositiveVolume.get());
setDeviceVolume(mDevices.getFirst(), mLatestPositiveVolume.get());
} }
private void handleOnPause() { private void handleOnPause() {
if (mDevices == null || mDevices.isEmpty()) { if (mStateByDevice == null || mStateByDevice.isEmpty()) {
Log.w(TAG, "active device or device has source is null!"); Log.w(TAG, "active device or device has source is null!");
return; return;
} }
Log.d( mStateByDevice.keySet().forEach(device -> {
TAG, Log.d(TAG, "onPause() setting volume for device : " + device + " volume: " + 0);
"onPause() setting volume for device : " + mDevices.getFirst() + " volume: " + 0); setDeviceVolume(device, /* volume= */ 0);
setDeviceVolume(mDevices.getFirst(), /* volume= */ 0); });
} }
private void setDeviceVolume(BluetoothDevice device, int volume) { private void setDeviceVolume(BluetoothDevice device, int volume) {
@@ -514,7 +512,7 @@ public class AudioStreamMediaService extends Service {
ThreadUtils.postOnBackgroundThread( ThreadUtils.postOnBackgroundThread(
() -> { () -> {
if (mVolumeControl != null) { if (mVolumeControl != null) {
mVolumeControl.setDeviceVolume(device, volume, true); mVolumeControl.setDeviceVolume(device, volume, false);
mMetricsFeatureProvider.action( mMetricsFeatureProvider.action(
getApplicationContext(), event, volume == 0 ? 1 : 0); getApplicationContext(), event, volume == 0 ? 1 : 0);
} }

View File

@@ -28,7 +28,6 @@ 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.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;
@@ -52,7 +51,9 @@ 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.Bundle;
import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.Looper;
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;
@@ -81,14 +82,12 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule; import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.android.util.concurrent.InlineExecutorService;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow; 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.List; import java.util.List;
import java.util.Set;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@Config( @Config(
@@ -122,6 +121,7 @@ public class AudioStreamMediaServiceTest {
@Mock private PackageManager mPackageManager; @Mock private PackageManager mPackageManager;
@Mock private DisplayMetrics mDisplayMetrics; @Mock private DisplayMetrics mDisplayMetrics;
@Mock private Context mContext; @Mock private Context mContext;
@Mock private Handler mHandler;
private FakeFeatureFactory mFeatureFactory; private FakeFeatureFactory mFeatureFactory;
private AudioStreamMediaService mAudioStreamMediaService; private AudioStreamMediaService mAudioStreamMediaService;
@@ -145,11 +145,18 @@ public class AudioStreamMediaServiceTest {
when(mCachedBluetoothDevice.getName()).thenReturn(DEVICE_NAME); when(mCachedBluetoothDevice.getName()).thenReturn(DEVICE_NAME);
when(mLocalBluetoothProfileManager.getVolumeControlProfile()) when(mLocalBluetoothProfileManager.getVolumeControlProfile())
.thenReturn(mVolumeControlProfile); .thenReturn(mVolumeControlProfile);
when(mHandler.post(any(Runnable.class))).thenAnswer(invocation -> {
mAudioStreamMediaService = spy(new AudioStreamMediaService()); ((Runnable) invocation.getArgument(0)).run();
return null;
});
when(mHandler.getLooper()).thenReturn(Looper.getMainLooper());
mAudioStreamMediaService = spy(new AudioStreamMediaService() {
@Override
Handler getHandler() {
return mHandler;
}
});
ReflectionHelpers.setField(mAudioStreamMediaService, "mBase", mContext); ReflectionHelpers.setField(mAudioStreamMediaService, "mBase", mContext);
ReflectionHelpers.setField(
mAudioStreamMediaService, "mExecutor", new InlineExecutorService());
when(mAudioStreamMediaService.getSystemService(anyString())) when(mAudioStreamMediaService.getSystemService(anyString()))
.thenReturn(mMediaSessionManager); .thenReturn(mMediaSessionManager);
when(mMediaSessionManager.createSession(any(), anyString(), any())).thenReturn(mISession); when(mMediaSessionManager.createSession(any(), anyString(), any())).thenReturn(mISession);
@@ -391,31 +398,6 @@ public class AudioStreamMediaServiceTest {
verify(mAudioStreamMediaService).stopSelf(); 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 @Test
public void mediaSessionCallback_onPause_setVolume() { public void mediaSessionCallback_onPause_setVolume() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);