Loading src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaService.java +93 −88 Original line number Diff line number Diff line Loading @@ -106,7 +106,7 @@ public class AudioStreamMediaService extends Service { // 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 final Object mLocalSessionLock = new Object(); private int mBroadcastId; @Nullable private List<BluetoothDevice> mDevices; @Nullable private LocalBluetoothManager mLocalBtManager; Loading @@ -125,7 +125,7 @@ public class AudioStreamMediaService extends Service { if (!BluetoothUtils.isAudioSharingEnabled()) { return; } Log.d(TAG, "onCreate()"); super.onCreate(); mLocalBtManager = Utils.getLocalBtManager(this); if (mLocalBtManager == null) { Loading @@ -146,6 +146,13 @@ public class AudioStreamMediaService extends Service { return; } mExecutor.execute( () -> { if (mLocalBtManager == null || mLeBroadcastAssistant == null || mNotificationManager == null) { return; } if (mNotificationManager.getNotificationChannel(CHANNEL_ID) == null) { NotificationChannel notificationChannel = new NotificationChannel( Loading @@ -165,7 +172,9 @@ public class AudioStreamMediaService extends Service { } mBroadcastAssistantCallback = new AssistantCallback(); mLeBroadcastAssistant.registerServiceCallBack(mExecutor, mBroadcastAssistantCallback); mLeBroadcastAssistant.registerServiceCallBack( mExecutor, mBroadcastAssistantCallback); }); } @Override Loading @@ -175,19 +184,29 @@ public class AudioStreamMediaService extends Service { if (!BluetoothUtils.isAudioSharingEnabled()) { return; } if (mDevices != null) { mDevices.clear(); mDevices = null; } synchronized (mLocalSessionLock) { if (mLocalSession != null) { mLocalSession.release(); mLocalSession = null; } } mExecutor.execute( () -> { if (mLocalBtManager != null) { mLocalBtManager.getEventManager().unregisterCallback(mBluetoothCallback); } if (mLeBroadcastAssistant != null && mBroadcastAssistantCallback != null) { mLeBroadcastAssistant.unregisterServiceCallBack(mBroadcastAssistantCallback); mLeBroadcastAssistant.unregisterServiceCallBack( mBroadcastAssistantCallback); } if (mVolumeControl != null && mVolumeControlCallback != null) { mVolumeControl.unregisterCallback(mVolumeControlCallback); } if (mLocalSession != null) { mLocalSession.release(); mLocalSession = null; } }); } @Override Loading @@ -195,33 +214,33 @@ public class AudioStreamMediaService extends Service { Log.d(TAG, "onStartCommand()"); if (intent == null) { 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) { Log.w(TAG, "Invalid broadcast ID. Service will not start."); mHasStopped.set(true); stopSelf(); return START_NOT_STICKY; } var extra = intent.getParcelableArrayListExtra(DEVICES, BluetoothDevice.class); if (extra == null || extra.isEmpty()) { Log.w(TAG, "No device. Service will not start."); mHasStopped.set(true); stopSelf(); return START_NOT_STICKY; } mDevices = Collections.synchronizedList(extra); createLocalMediaSession(intent.getStringExtra(BROADCAST_TITLE)); startForeground(NOTIFICATION_ID, buildNotification()); // Reset in case the service is previously stopped but not yet destroyed. mHasStopped.set(false); MediaSession.Token token = getOrCreateLocalMediaSession(intent.getStringExtra(BROADCAST_TITLE)); startForeground(NOTIFICATION_ID, buildNotification(token)); return START_NOT_STICKY; } private void createLocalMediaSession(String title) { private MediaSession.Token getOrCreateLocalMediaSession(String title) { synchronized (mLocalSessionLock) { if (mLocalSession != null) { return mLocalSession.getSessionToken(); } mLocalSession = new MediaSession(this, TAG); mLocalSession.setMetadata( new MediaMetadata.Builder() Loading @@ -232,6 +251,8 @@ public class AudioStreamMediaService extends Service { mLocalSession.setPlaybackState(getPlaybackState()); mMediaSessionCallback = new MediaSessionCallback(); mLocalSession.setCallback(mMediaSessionCallback); return mLocalSession.getSessionToken(); } } private PlaybackState getPlaybackState() { Loading @@ -252,12 +273,9 @@ public class AudioStreamMediaService extends Service { return device != null ? device.getName() : DEFAULT_DEVICE_NAME; } private Notification buildNotification() { private Notification buildNotification(MediaSession.Token token) { String deviceName = getDeviceName(); Notification.MediaStyle mediaStyle = new Notification.MediaStyle() .setMediaSession( mLocalSession != null ? mLocalSession.getSessionToken() : null); Notification.MediaStyle mediaStyle = new Notification.MediaStyle().setMediaSession(token); if (deviceName != null && !deviceName.isEmpty()) { mediaStyle.setRemotePlaybackInfo( deviceName, com.android.settingslib.R.drawable.ic_bt_le_audio, null); Loading Loading @@ -291,9 +309,6 @@ public class AudioStreamMediaService extends Service { } private void handleRemoveSource() { var unused = ThreadUtils.postOnBackgroundThread( () -> { List<BluetoothLeBroadcastReceiveState> connected = mAudioStreamsHelper == null ? emptyList() Loading @@ -301,10 +316,8 @@ public class AudioStreamMediaService extends Service { if (connected.stream() .map(BluetoothLeBroadcastReceiveState::getBroadcastId) .noneMatch(id -> id == mBroadcastId)) { mHasStopped.set(true); stopSelf(); } }); } } Loading @@ -326,7 +339,11 @@ public class AudioStreamMediaService extends Service { mIsMuted.set(false); mLatestPositiveVolume.set(volume); } updateNotification(getPlaybackState()); synchronized (mLocalSessionLock) { if (mLocalSession != null) { mLocalSession.setPlaybackState(getPlaybackState()); } } } } } Loading @@ -336,7 +353,6 @@ public class AudioStreamMediaService extends Service { public void onBluetoothStateChanged(int bluetoothState) { if (BluetoothAdapter.STATE_OFF == bluetoothState) { Log.d(TAG, "onBluetoothStateChanged() : stopSelf"); mHasStopped.set(true); stopSelf(); } } Loading @@ -362,7 +378,6 @@ public class AudioStreamMediaService extends Service { } if (mDevices == null || mDevices.isEmpty()) { Log.d(TAG, "onProfileConnectionStateChanged() : stopSelf"); mHasStopped.set(true); stopSelf(); } } Loading @@ -371,7 +386,11 @@ public class AudioStreamMediaService extends Service { private class MediaSessionCallback extends MediaSession.Callback { public void onSeekTo(long pos) { Log.d(TAG, "onSeekTo: " + pos); updateNotification(getPlaybackState()); synchronized (mLocalSessionLock) { if (mLocalSession != null) { mLocalSession.setPlaybackState(getPlaybackState()); } } } @Override Loading Loading @@ -425,18 +444,4 @@ public class AudioStreamMediaService extends Service { }); } } 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()); } } }); } } src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java +0 −2 Original line number Diff line number Diff line Loading @@ -139,7 +139,6 @@ public class AudioStreamsHelper { } /** Retrieves a list of all LE broadcast receive states from active sinks. */ @VisibleForTesting public List<BluetoothLeBroadcastReceiveState> getAllConnectedSources() { if (mLeBroadcastAssistant == null) { Log.w(TAG, "getAllSources(): LeBroadcastAssistant is null!"); Loading @@ -165,7 +164,6 @@ public class AudioStreamsHelper { } /** Retrieves LocalBluetoothLeBroadcastAssistant. */ @VisibleForTesting @Nullable public LocalBluetoothLeBroadcastAssistant getLeBroadcastAssistant() { return mLeBroadcastAssistant; Loading tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaServiceTest.java +3 −25 Original line number Diff line number Diff line Loading @@ -80,6 +80,7 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; import org.robolectric.android.util.concurrent.InlineExecutorService; import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; import org.robolectric.util.ReflectionHelpers; Loading Loading @@ -143,6 +144,8 @@ public class AudioStreamMediaServiceTest { mAudioStreamMediaService = spy(new AudioStreamMediaService()); ReflectionHelpers.setField(mAudioStreamMediaService, "mBase", mContext); ReflectionHelpers.setField( mAudioStreamMediaService, "mExecutor", new InlineExecutorService()); when(mAudioStreamMediaService.getSystemService(anyString())) .thenReturn(mMediaSessionManager); when(mMediaSessionManager.createSession(any(), anyString(), any())).thenReturn(mISession); Loading Loading @@ -352,18 +355,6 @@ public class AudioStreamMediaServiceTest { 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); Loading Loading @@ -415,19 +406,6 @@ public class AudioStreamMediaServiceTest { 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()); Loading Loading
src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaService.java +93 −88 Original line number Diff line number Diff line Loading @@ -106,7 +106,7 @@ public class AudioStreamMediaService extends Service { // 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 final Object mLocalSessionLock = new Object(); private int mBroadcastId; @Nullable private List<BluetoothDevice> mDevices; @Nullable private LocalBluetoothManager mLocalBtManager; Loading @@ -125,7 +125,7 @@ public class AudioStreamMediaService extends Service { if (!BluetoothUtils.isAudioSharingEnabled()) { return; } Log.d(TAG, "onCreate()"); super.onCreate(); mLocalBtManager = Utils.getLocalBtManager(this); if (mLocalBtManager == null) { Loading @@ -146,6 +146,13 @@ public class AudioStreamMediaService extends Service { return; } mExecutor.execute( () -> { if (mLocalBtManager == null || mLeBroadcastAssistant == null || mNotificationManager == null) { return; } if (mNotificationManager.getNotificationChannel(CHANNEL_ID) == null) { NotificationChannel notificationChannel = new NotificationChannel( Loading @@ -165,7 +172,9 @@ public class AudioStreamMediaService extends Service { } mBroadcastAssistantCallback = new AssistantCallback(); mLeBroadcastAssistant.registerServiceCallBack(mExecutor, mBroadcastAssistantCallback); mLeBroadcastAssistant.registerServiceCallBack( mExecutor, mBroadcastAssistantCallback); }); } @Override Loading @@ -175,19 +184,29 @@ public class AudioStreamMediaService extends Service { if (!BluetoothUtils.isAudioSharingEnabled()) { return; } if (mDevices != null) { mDevices.clear(); mDevices = null; } synchronized (mLocalSessionLock) { if (mLocalSession != null) { mLocalSession.release(); mLocalSession = null; } } mExecutor.execute( () -> { if (mLocalBtManager != null) { mLocalBtManager.getEventManager().unregisterCallback(mBluetoothCallback); } if (mLeBroadcastAssistant != null && mBroadcastAssistantCallback != null) { mLeBroadcastAssistant.unregisterServiceCallBack(mBroadcastAssistantCallback); mLeBroadcastAssistant.unregisterServiceCallBack( mBroadcastAssistantCallback); } if (mVolumeControl != null && mVolumeControlCallback != null) { mVolumeControl.unregisterCallback(mVolumeControlCallback); } if (mLocalSession != null) { mLocalSession.release(); mLocalSession = null; } }); } @Override Loading @@ -195,33 +214,33 @@ public class AudioStreamMediaService extends Service { Log.d(TAG, "onStartCommand()"); if (intent == null) { 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) { Log.w(TAG, "Invalid broadcast ID. Service will not start."); mHasStopped.set(true); stopSelf(); return START_NOT_STICKY; } var extra = intent.getParcelableArrayListExtra(DEVICES, BluetoothDevice.class); if (extra == null || extra.isEmpty()) { Log.w(TAG, "No device. Service will not start."); mHasStopped.set(true); stopSelf(); return START_NOT_STICKY; } mDevices = Collections.synchronizedList(extra); createLocalMediaSession(intent.getStringExtra(BROADCAST_TITLE)); startForeground(NOTIFICATION_ID, buildNotification()); // Reset in case the service is previously stopped but not yet destroyed. mHasStopped.set(false); MediaSession.Token token = getOrCreateLocalMediaSession(intent.getStringExtra(BROADCAST_TITLE)); startForeground(NOTIFICATION_ID, buildNotification(token)); return START_NOT_STICKY; } private void createLocalMediaSession(String title) { private MediaSession.Token getOrCreateLocalMediaSession(String title) { synchronized (mLocalSessionLock) { if (mLocalSession != null) { return mLocalSession.getSessionToken(); } mLocalSession = new MediaSession(this, TAG); mLocalSession.setMetadata( new MediaMetadata.Builder() Loading @@ -232,6 +251,8 @@ public class AudioStreamMediaService extends Service { mLocalSession.setPlaybackState(getPlaybackState()); mMediaSessionCallback = new MediaSessionCallback(); mLocalSession.setCallback(mMediaSessionCallback); return mLocalSession.getSessionToken(); } } private PlaybackState getPlaybackState() { Loading @@ -252,12 +273,9 @@ public class AudioStreamMediaService extends Service { return device != null ? device.getName() : DEFAULT_DEVICE_NAME; } private Notification buildNotification() { private Notification buildNotification(MediaSession.Token token) { String deviceName = getDeviceName(); Notification.MediaStyle mediaStyle = new Notification.MediaStyle() .setMediaSession( mLocalSession != null ? mLocalSession.getSessionToken() : null); Notification.MediaStyle mediaStyle = new Notification.MediaStyle().setMediaSession(token); if (deviceName != null && !deviceName.isEmpty()) { mediaStyle.setRemotePlaybackInfo( deviceName, com.android.settingslib.R.drawable.ic_bt_le_audio, null); Loading Loading @@ -291,9 +309,6 @@ public class AudioStreamMediaService extends Service { } private void handleRemoveSource() { var unused = ThreadUtils.postOnBackgroundThread( () -> { List<BluetoothLeBroadcastReceiveState> connected = mAudioStreamsHelper == null ? emptyList() Loading @@ -301,10 +316,8 @@ public class AudioStreamMediaService extends Service { if (connected.stream() .map(BluetoothLeBroadcastReceiveState::getBroadcastId) .noneMatch(id -> id == mBroadcastId)) { mHasStopped.set(true); stopSelf(); } }); } } Loading @@ -326,7 +339,11 @@ public class AudioStreamMediaService extends Service { mIsMuted.set(false); mLatestPositiveVolume.set(volume); } updateNotification(getPlaybackState()); synchronized (mLocalSessionLock) { if (mLocalSession != null) { mLocalSession.setPlaybackState(getPlaybackState()); } } } } } Loading @@ -336,7 +353,6 @@ public class AudioStreamMediaService extends Service { public void onBluetoothStateChanged(int bluetoothState) { if (BluetoothAdapter.STATE_OFF == bluetoothState) { Log.d(TAG, "onBluetoothStateChanged() : stopSelf"); mHasStopped.set(true); stopSelf(); } } Loading @@ -362,7 +378,6 @@ public class AudioStreamMediaService extends Service { } if (mDevices == null || mDevices.isEmpty()) { Log.d(TAG, "onProfileConnectionStateChanged() : stopSelf"); mHasStopped.set(true); stopSelf(); } } Loading @@ -371,7 +386,11 @@ public class AudioStreamMediaService extends Service { private class MediaSessionCallback extends MediaSession.Callback { public void onSeekTo(long pos) { Log.d(TAG, "onSeekTo: " + pos); updateNotification(getPlaybackState()); synchronized (mLocalSessionLock) { if (mLocalSession != null) { mLocalSession.setPlaybackState(getPlaybackState()); } } } @Override Loading Loading @@ -425,18 +444,4 @@ public class AudioStreamMediaService extends Service { }); } } 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()); } } }); } }
src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java +0 −2 Original line number Diff line number Diff line Loading @@ -139,7 +139,6 @@ public class AudioStreamsHelper { } /** Retrieves a list of all LE broadcast receive states from active sinks. */ @VisibleForTesting public List<BluetoothLeBroadcastReceiveState> getAllConnectedSources() { if (mLeBroadcastAssistant == null) { Log.w(TAG, "getAllSources(): LeBroadcastAssistant is null!"); Loading @@ -165,7 +164,6 @@ public class AudioStreamsHelper { } /** Retrieves LocalBluetoothLeBroadcastAssistant. */ @VisibleForTesting @Nullable public LocalBluetoothLeBroadcastAssistant getLeBroadcastAssistant() { return mLeBroadcastAssistant; Loading
tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaServiceTest.java +3 −25 Original line number Diff line number Diff line Loading @@ -80,6 +80,7 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; import org.robolectric.android.util.concurrent.InlineExecutorService; import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; import org.robolectric.util.ReflectionHelpers; Loading Loading @@ -143,6 +144,8 @@ public class AudioStreamMediaServiceTest { mAudioStreamMediaService = spy(new AudioStreamMediaService()); ReflectionHelpers.setField(mAudioStreamMediaService, "mBase", mContext); ReflectionHelpers.setField( mAudioStreamMediaService, "mExecutor", new InlineExecutorService()); when(mAudioStreamMediaService.getSystemService(anyString())) .thenReturn(mMediaSessionManager); when(mMediaSessionManager.createSession(any(), anyString(), any())).thenReturn(mISession); Loading Loading @@ -352,18 +355,6 @@ public class AudioStreamMediaServiceTest { 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); Loading Loading @@ -415,19 +406,6 @@ public class AudioStreamMediaServiceTest { 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()); Loading