From 85a936ecb101264c2f7678327a1779347480d605 Mon Sep 17 00:00:00 2001 From: bachinger Date: Mon, 30 May 2022 14:24:42 +0000 Subject: [PATCH] Implement session extras for Media3 and legacy controllers This provides an (unstable) API for apps to broadcast session extras Bundle to all connected controllers and set the extras in the legacy session. Similar to the custom layout, the extras Bundle is not part of the Media3 session state. This means that when a Media3 controller connects to the session after the broadcast, the extras needs to be sent to that controller in `MediaSession.Callback.onPostConnect(MediaSession session, ControllerInfo controller)`. PiperOrigin-RevId: 451871731 --- .../media3/session/IMediaController.aidl | 3 +- .../media3/session/MediaConstants.java | 2 - .../media3/session/MediaController.java | 8 +++ .../session/MediaControllerImplBase.java | 7 +++ .../session/MediaControllerImplLegacy.java | 10 +--- .../media3/session/MediaControllerStub.java | 5 ++ .../androidx/media3/session/MediaSession.java | 28 +++++++++++ .../media3/session/MediaSessionImpl.java | 12 +++++ .../session/MediaSessionLegacyStub.java | 5 ++ .../media3/session/MediaSessionStub.java | 5 ++ .../session/common/IRemoteMediaSession.aidl | 2 + .../common/IRemoteMediaSessionCompat.aidl | 1 + .../session/common/MediaSessionConstants.java | 1 + ...patCallbackWithMediaSessionCompatTest.java | 39 +++++++++++++++ ...lerCompatCallbackWithMediaSessionTest.java | 24 +++++++++ .../session/MediaControllerListenerTest.java | 50 +++++++++++++++++++ ...lerListenerWithMediaSessionCompatTest.java | 23 +++++++++ .../MediaSessionCompatProviderService.java | 6 +++ .../session/MediaSessionProviderService.java | 24 +++++++++ .../media3/session/RemoteMediaSession.java | 8 +++ .../session/RemoteMediaSessionCompat.java | 4 ++ .../session/TestMediaBrowserListener.java | 5 ++ 22 files changed, 260 insertions(+), 12 deletions(-) diff --git a/libraries/session/src/main/aidl/androidx/media3/session/IMediaController.aidl b/libraries/session/src/main/aidl/androidx/media3/session/IMediaController.aidl index 2bebc6beeb..e3b1995540 100644 --- a/libraries/session/src/main/aidl/androidx/media3/session/IMediaController.aidl +++ b/libraries/session/src/main/aidl/androidx/media3/session/IMediaController.aidl @@ -42,7 +42,8 @@ oneway interface IMediaController { void onAvailableCommandsChangedFromSession( int seq, in Bundle sessionCommandsBundle, in Bundle playerCommandsBundle) = 3009; void onRenderedFirstFrame(int seq) = 3010; - // Next Id for MediaController: 3011 + void onExtrasChanged(int seq, in Bundle extras) = 3011; + // Next Id for MediaController: 3012 void onChildrenChanged( int seq, String parentId, int itemCount, in @nullable Bundle libraryParams) = 4000; diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaConstants.java b/libraries/session/src/main/java/androidx/media3/session/MediaConstants.java index 3b7935c79a..e97f6d7690 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaConstants.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaConstants.java @@ -151,8 +151,6 @@ public final class MediaConstants { */ public static final int ERROR_CODE_AUTHENTICATION_EXPIRED_COMPAT = 3; - /* package */ static final String SESSION_COMMAND_ON_EXTRAS_CHANGED = - "androidx.media3.session.SESSION_COMMAND_ON_EXTRAS_CHANGED"; /* package */ static final String SESSION_COMMAND_ON_CAPTIONING_ENABLED_CHANGED = "androidx.media3.session.SESSION_COMMAND_ON_CAPTIONING_ENABLED_CHANGED"; /* package */ static final String SESSION_COMMAND_REQUEST_SESSION3_TOKEN = diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaController.java b/libraries/session/src/main/java/androidx/media3/session/MediaController.java index 09a418fb24..8f5a0424b7 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaController.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaController.java @@ -322,6 +322,14 @@ public class MediaController implements Player { MediaController controller, SessionCommand command, Bundle args) { return Futures.immediateFuture(new SessionResult(SessionResult.RESULT_ERROR_NOT_SUPPORTED)); } + + /** + * Called when the session extras have changed. + * + * @param controller The controller. + * @param extras The session extras that have changed. + */ + default void onExtrasChanged(MediaController controller, Bundle extras) {} } /* package */ interface ConnectionCallback { diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java index e9d9b5a420..9fe1017622 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java @@ -2598,6 +2598,13 @@ import org.checkerframework.checker.nullness.qual.NonNull; }); } + public void onExtrasChanged(Bundle extras) { + if (!isConnected()) { + return; + } + instance.notifyControllerListener(listener -> listener.onExtrasChanged(instance, extras)); + } + public void onRenderedFirstFrame() { listeners.sendEvent(/* eventFlag= */ C.INDEX_UNSET, Listener::onRenderedFirstFrame); } diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java index ae8d72d10a..3855969830 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java @@ -38,7 +38,6 @@ import static androidx.media3.session.MediaConstants.MEDIA_URI_QUERY_QUERY; import static androidx.media3.session.MediaConstants.MEDIA_URI_QUERY_URI; import static androidx.media3.session.MediaConstants.MEDIA_URI_SET_MEDIA_URI_PREFIX; import static androidx.media3.session.MediaConstants.SESSION_COMMAND_ON_CAPTIONING_ENABLED_CHANGED; -import static androidx.media3.session.MediaConstants.SESSION_COMMAND_ON_EXTRAS_CHANGED; import static androidx.media3.session.MediaUtils.POSITION_DIFF_TOLERANCE_MS; import static androidx.media3.session.MediaUtils.calculateBufferedPercentage; import static androidx.media3.session.SessionResult.RESULT_INFO_SKIPPED; @@ -1606,14 +1605,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; @Override public void onExtrasChanged(Bundle extras) { - instance.notifyControllerListener( - listener -> - ignoreFuture( - listener.onCustomCommand( - instance, - new SessionCommand( - SESSION_COMMAND_ON_EXTRAS_CHANGED, /* extras= */ Bundle.EMPTY), - extras))); + instance.notifyControllerListener(listener -> listener.onExtrasChanged(instance, extras)); } @Override diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaControllerStub.java b/libraries/session/src/main/java/androidx/media3/session/MediaControllerStub.java index 1cb2a5e62b..9d86ac05be 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaControllerStub.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaControllerStub.java @@ -183,6 +183,11 @@ import java.util.List; isTimelineExcluded)); } + @Override + public void onExtrasChanged(int seq, Bundle extras) { + dispatchControllerTaskOnHandler(controller -> controller.onExtrasChanged(extras)); + } + @Override public void onRenderedFirstFrame(int seq) { dispatchControllerTaskOnHandler(MediaControllerImplBase::onRenderedFirstFrame); diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSession.java b/libraries/session/src/main/java/androidx/media3/session/MediaSession.java index 3a27833c68..23db7affef 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSession.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSession.java @@ -702,6 +702,32 @@ public class MediaSession { impl.broadcastCustomCommand(command, args); } + /** + * Sends the session extras to connected controllers. + * + *

This is a synchronous call and doesn't wait for results from the controllers. + * + * @param sessionExtras The session extras. + */ + public void setSessionExtras(Bundle sessionExtras) { + checkNotNull(sessionExtras); + impl.setSessionExtras(sessionExtras); + } + + /** + * Sends the session extras to the connected controller. + * + *

This is a synchronous call and doesn't wait for results from the controller. + * + * @param controller The controller to send the extras to. + * @param sessionExtras The session extras. + */ + public void setSessionExtras(ControllerInfo controller, Bundle sessionExtras) { + checkNotNull(controller, "controller must not be null"); + checkNotNull(sessionExtras); + impl.setSessionExtras(controller, sessionExtras); + } + /** * Sends a custom command to a specific controller. * @@ -1119,6 +1145,8 @@ public class MediaSession { default void setCustomLayout(int seq, List layout) throws RemoteException {} + default void onSessionExtrasChanged(int seq, Bundle sessionExtras) throws RemoteException {} + default void sendCustomCommand(int seq, SessionCommand command, Bundle args) throws RemoteException {} diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java index d82e1d54d1..57d1533c9a 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java @@ -342,6 +342,18 @@ import org.checkerframework.checker.initialization.qual.Initialized; (controller, seq) -> controller.setCustomLayout(seq, layout)); } + public void setSessionExtras(Bundle sessionExtras) { + dispatchRemoteControllerTaskWithoutReturn( + (controller, seq) -> controller.onSessionExtrasChanged(seq, sessionExtras)); + } + + public void setSessionExtras(ControllerInfo controller, Bundle sessionExtras) { + if (sessionStub.getConnectedControllersManager().isConnected(controller)) { + dispatchRemoteControllerTaskWithoutReturn( + controller, (callback, seq) -> callback.onSessionExtrasChanged(seq, sessionExtras)); + } + } + public void setAvailableCommands( ControllerInfo controller, SessionCommands sessionCommands, Player.Commands playerCommands) { if (sessionStub.getConnectedControllersManager().isConnected(controller)) { diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java index 87361873a9..088aed877b 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java @@ -886,6 +886,11 @@ import org.checkerframework.checker.initialization.qual.Initialized; .setPlaybackState(sessionImpl.getPlayerWrapper().createPlaybackStateCompat()); } + @Override + public void onSessionExtrasChanged(int seq, Bundle sessionExtras) { + sessionImpl.getSessionCompat().setExtras(sessionExtras); + } + @Override public void onPlayWhenReadyChanged( int seq, boolean playWhenReady, @Player.PlaybackSuppressionReason int reason) diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java index 743af2c08e..672edf3a30 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java @@ -1801,6 +1801,11 @@ import java.util.concurrent.ExecutionException; iController.onRenderedFirstFrame(seq); } + @Override + public void onSessionExtrasChanged(int seq, Bundle sessionExtras) throws RemoteException { + iController.onExtrasChanged(seq, sessionExtras); + } + @Override public int hashCode() { return ObjectsCompat.hash(getCallbackBinder()); diff --git a/libraries/test_session_common/src/main/aidl/androidx/media3/test/session/common/IRemoteMediaSession.aidl b/libraries/test_session_common/src/main/aidl/androidx/media3/test/session/common/IRemoteMediaSession.aidl index 30972501fa..4711fa62b1 100644 --- a/libraries/test_session_common/src/main/aidl/androidx/media3/test/session/common/IRemoteMediaSession.aidl +++ b/libraries/test_session_common/src/main/aidl/androidx/media3/test/session/common/IRemoteMediaSession.aidl @@ -32,6 +32,8 @@ interface IRemoteMediaSession { void release(String sessionId); void setAvailableCommands(String sessionId, in Bundle sessionCommands, in Bundle playerCommands); void setCustomLayout(String sessionId, in List layout); + void setSessionExtras(String sessionId, in Bundle extras); + void setSessionExtrasForController(String sessionId, in String controllerKey, in Bundle extras); // Player Methods void setPlayWhenReady(String sessionId, boolean playWhenReady, int reason); diff --git a/libraries/test_session_common/src/main/aidl/androidx/media3/test/session/common/IRemoteMediaSessionCompat.aidl b/libraries/test_session_common/src/main/aidl/androidx/media3/test/session/common/IRemoteMediaSessionCompat.aidl index f49dce56fa..3fe24ac8b9 100644 --- a/libraries/test_session_common/src/main/aidl/androidx/media3/test/session/common/IRemoteMediaSessionCompat.aidl +++ b/libraries/test_session_common/src/main/aidl/androidx/media3/test/session/common/IRemoteMediaSessionCompat.aidl @@ -41,4 +41,5 @@ interface IRemoteMediaSessionCompat { void setRatingType(String sessionTag, int type); void sendSessionEvent(String sessionTag, String event, in Bundle extras); void setCaptioningEnabled(String sessionTag, boolean enabled); + void setSessionExtras(String sessionTag, in Bundle extras); } diff --git a/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/MediaSessionConstants.java b/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/MediaSessionConstants.java index 4791aad8fc..bd08f997ca 100644 --- a/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/MediaSessionConstants.java +++ b/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/MediaSessionConstants.java @@ -29,6 +29,7 @@ public class MediaSessionConstants { // Bundle keys public static final String KEY_AVAILABLE_SESSION_COMMANDS = "availableSessionCommands"; + public static final String KEY_CONTROLLER = "controllerKey"; private MediaSessionConstants() {} } diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerCompatCallbackWithMediaSessionCompatTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerCompatCallbackWithMediaSessionCompatTest.java index 29e36e682d..551d03b109 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerCompatCallbackWithMediaSessionCompatTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerCompatCallbackWithMediaSessionCompatTest.java @@ -27,6 +27,7 @@ import android.support.v4.media.session.MediaSessionCompat; import android.support.v4.media.session.PlaybackStateCompat; import androidx.media3.test.session.common.HandlerThreadTestRule; import androidx.media3.test.session.common.MainLooperTestRule; +import androidx.media3.test.session.common.TestUtils; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; @@ -137,4 +138,42 @@ public class MediaControllerCompatCallbackWithMediaSessionCompatTest { assertThat(receivedIconResIds).containsExactly(1, 2).inOrder(); assertThat(receivedBundleValues).containsExactly("value-1", "value-2").inOrder(); } + + /** + * Setting the session extras is used for instance by + * Wear OS and System UI (starting with T) to receive extras for UI customization. An app + * needs a way to set the session extras that are stored in the legacy session and broadcast to + * the connected controllers. + */ + @Test + public void setExtras_onExtrasChangedCalled() throws Exception { + Bundle sessionExtras = new Bundle(); + sessionExtras.putString("key-1", "value-1"); + CountDownLatch countDownLatch = new CountDownLatch(1); + MediaSessionCompat.Token sessionToken = session.getSessionToken(); + List receivedSessionExtras = new ArrayList<>(); + threadTestRule + .getHandler() + .postAndSync( + () -> { + MediaControllerCompat mediaControllerCompat = + new MediaControllerCompat(context, sessionToken); + mediaControllerCompat.registerCallback( + new MediaControllerCompat.Callback() { + @Override + public void onExtrasChanged(Bundle extras) { + receivedSessionExtras.add(extras); + receivedSessionExtras.add(mediaControllerCompat.getExtras()); + countDownLatch.countDown(); + } + }); + }); + + session.setExtras(sessionExtras); + + assertThat(countDownLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); + assertThat(TestUtils.equals(receivedSessionExtras.get(0), sessionExtras)).isTrue(); + assertThat(TestUtils.equals(receivedSessionExtras.get(1), sessionExtras)).isTrue(); + } } diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerCompatCallbackWithMediaSessionTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerCompatCallbackWithMediaSessionTest.java index d025108b07..ac53cdf47f 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerCompatCallbackWithMediaSessionTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerCompatCallbackWithMediaSessionTest.java @@ -819,6 +819,30 @@ public class MediaControllerCompatCallbackWithMediaSessionTest { assertThat(receivedBundleValues).containsExactly("value-1", "value-2").inOrder(); } + @Test + public void setSessionExtras_cnExtrasChangedCalled() throws Exception { + Bundle sessionExtras = new Bundle(); + sessionExtras.putString("key-0", "value-0"); + CountDownLatch latch = new CountDownLatch(1); + List receivedSessionExtras = new ArrayList<>(); + MediaControllerCompat.Callback callback = + new MediaControllerCompat.Callback() { + @Override + public void onExtrasChanged(Bundle extras) { + receivedSessionExtras.add(extras); + receivedSessionExtras.add(controllerCompat.getExtras()); + latch.countDown(); + } + }; + controllerCompat.registerCallback(callback, handler); + + session.setSessionExtras(sessionExtras); + + assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); + assertThat(TestUtils.equals(receivedSessionExtras.get(0), sessionExtras)).isTrue(); + assertThat(TestUtils.equals(receivedSessionExtras.get(1), sessionExtras)).isTrue(); + } + @Test public void currentMediaItemChange() throws Exception { int testItemIndex = 3; diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerTest.java index 378506b08e..d4a597f410 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerTest.java @@ -28,6 +28,7 @@ import static androidx.media3.session.SessionResult.RESULT_SUCCESS; import static androidx.media3.test.session.common.CommonConstants.DEFAULT_TEST_NAME; import static androidx.media3.test.session.common.CommonConstants.MOCK_MEDIA3_LIBRARY_SERVICE; import static androidx.media3.test.session.common.CommonConstants.MOCK_MEDIA3_SESSION_SERVICE; +import static androidx.media3.test.session.common.MediaSessionConstants.KEY_CONTROLLER; import static androidx.media3.test.session.common.MediaSessionConstants.TEST_CONTROLLER_LISTENER_SESSION_REJECTS; import static androidx.media3.test.session.common.MediaSessionConstants.TEST_WITH_CUSTOM_COMMANDS; import static androidx.media3.test.session.common.TestUtils.LONG_TIMEOUT_MS; @@ -1800,6 +1801,55 @@ public class MediaControllerListenerTest { assertThat(receivedBundleValues).containsExactly("value-1", "value-2").inOrder(); } + @Test + public void setSessionExtras_onExtrasChangedCalled() throws Exception { + Bundle sessionExtras = TestUtils.createTestBundle(); + sessionExtras.putString("key-0", "value-0"); + CountDownLatch latch = new CountDownLatch(1); + List receivedSessionExtras = new ArrayList<>(); + MediaController.Listener listener = + new MediaController.Listener() { + @Override + public void onExtrasChanged(MediaController controller, Bundle extras) { + receivedSessionExtras.add(extras); + latch.countDown(); + } + }; + controllerTestRule.createController( + remoteSession.getToken(), /* connectionHints= */ null, listener); + + remoteSession.setSessionExtras(sessionExtras); + + assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); + assertThat(receivedSessionExtras).hasSize(1); + assertThat(TestUtils.equals(receivedSessionExtras.get(0), sessionExtras)).isTrue(); + } + + @Test + public void setSessionExtras_specificMedia3Controller_onExtrasChangedCalled() throws Exception { + Bundle sessionExtras = TestUtils.createTestBundle(); + sessionExtras.putString("key-0", "value-0"); + CountDownLatch latch = new CountDownLatch(1); + List receivedSessionExtras = new ArrayList<>(); + MediaController.Listener listener = + new MediaController.Listener() { + @Override + public void onExtrasChanged(MediaController controller, Bundle extras) { + receivedSessionExtras.add(extras); + latch.countDown(); + } + }; + Bundle connectionHints = new Bundle(); + connectionHints.putString(KEY_CONTROLLER, "controller_key_1"); + controllerTestRule.createController(remoteSession.getToken(), connectionHints, listener); + + remoteSession.setSessionExtras("controller_key_1", sessionExtras); + + assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); + assertThat(receivedSessionExtras).hasSize(1); + assertThat(TestUtils.equals(receivedSessionExtras.get(0), sessionExtras)).isTrue(); + } + @Test public void onVideoSizeChanged() throws Exception { VideoSize testVideoSize = diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerWithMediaSessionCompatTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerWithMediaSessionCompatTest.java index 67a02b3b44..3e20c1cdaf 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerWithMediaSessionCompatTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerWithMediaSessionCompatTest.java @@ -32,6 +32,7 @@ import androidx.media3.common.FlagSet; import androidx.media3.common.Player; import androidx.media3.test.session.common.HandlerThreadTestRule; import androidx.media3.test.session.common.MainLooperTestRule; +import androidx.media3.test.session.common.TestUtils; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; @@ -168,4 +169,26 @@ public class MediaControllerListenerWithMediaSessionCompatTest { assertThat(receivedIconResIds).containsExactly(1, 2).inOrder(); assertThat(receivedBundleValues).containsExactly("value-1", "value-2").inOrder(); } + + @Test + public void setSessionExtras_onExtrasChangedCalled() throws Exception { + Bundle sessionExtras = new Bundle(); + sessionExtras.putString("key-1", "value-1"); + CountDownLatch countDownLatch = new CountDownLatch(1); + List receivedSessionExtras = new ArrayList<>(); + controllerTestRule.createController( + session.getSessionToken(), + new MediaController.Listener() { + @Override + public void onExtrasChanged(MediaController controller, Bundle extras) { + receivedSessionExtras.add(extras); + countDownLatch.countDown(); + } + }); + + session.setExtras(sessionExtras); + + assertThat(countDownLatch.await(1_000, MILLISECONDS)).isTrue(); + assertThat(TestUtils.equals(receivedSessionExtras.get(0), sessionExtras)).isTrue(); + } } diff --git a/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionCompatProviderService.java b/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionCompatProviderService.java index be7d1b9c49..3fac9431e1 100644 --- a/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionCompatProviderService.java +++ b/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionCompatProviderService.java @@ -216,5 +216,11 @@ public class MediaSessionCompatProviderService extends Service { MediaSessionCompat session = sessionMap.get(sessionTag); session.setCaptioningEnabled(enabled); } + + @Override + public void setSessionExtras(String sessionTag, Bundle extras) throws RemoteException { + MediaSessionCompat session = sessionMap.get(sessionTag); + session.setExtras(extras); + } } } diff --git a/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionProviderService.java b/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionProviderService.java index 3b3b06e3bb..6bf44ca49f 100644 --- a/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionProviderService.java +++ b/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionProviderService.java @@ -54,6 +54,7 @@ import static androidx.media3.test.session.common.CommonConstants.KEY_TRACK_SELE import static androidx.media3.test.session.common.CommonConstants.KEY_VIDEO_SIZE; import static androidx.media3.test.session.common.CommonConstants.KEY_VOLUME; import static androidx.media3.test.session.common.MediaSessionConstants.KEY_AVAILABLE_SESSION_COMMANDS; +import static androidx.media3.test.session.common.MediaSessionConstants.KEY_CONTROLLER; import static androidx.media3.test.session.common.MediaSessionConstants.TEST_CONTROLLER_LISTENER_SESSION_REJECTS; import static androidx.media3.test.session.common.MediaSessionConstants.TEST_GET_SESSION_ACTIVITY; import static androidx.media3.test.session.common.MediaSessionConstants.TEST_IS_SESSION_COMMAND_AVAILABLE; @@ -427,6 +428,29 @@ public class MediaSessionProviderService extends Service { }); } + @Override + public void setSessionExtras(String sessionId, Bundle extras) throws RemoteException { + runOnHandler(() -> sessionMap.get(sessionId).setSessionExtras(extras)); + } + + @Override + public void setSessionExtrasForController(String sessionId, String controllerKey, Bundle extras) + throws RemoteException { + runOnHandler( + () -> { + MediaSession mediaSession = sessionMap.get(sessionId); + for (ControllerInfo controllerInfo : mediaSession.getConnectedControllers()) { + if (controllerInfo + .getConnectionHints() + .getString(KEY_CONTROLLER, /* defaultValue= */ "") + .equals(controllerKey)) { + mediaSession.setSessionExtras(controllerInfo, extras); + break; + } + } + }); + } + //////////////////////////////////////////////////////////////////////////////// // MockPlayer methods //////////////////////////////////////////////////////////////////////////////// diff --git a/libraries/test_session_current/src/main/java/androidx/media3/session/RemoteMediaSession.java b/libraries/test_session_current/src/main/java/androidx/media3/session/RemoteMediaSession.java index 65d249161b..9d22d6df39 100644 --- a/libraries/test_session_current/src/main/java/androidx/media3/session/RemoteMediaSession.java +++ b/libraries/test_session_current/src/main/java/androidx/media3/session/RemoteMediaSession.java @@ -197,6 +197,14 @@ public class RemoteMediaSession { binder.setCustomLayout(sessionId, bundleList); } + public void setSessionExtras(Bundle extras) throws RemoteException { + binder.setSessionExtras(sessionId, extras); + } + + public void setSessionExtras(String controllerKey, Bundle extras) throws RemoteException { + binder.setSessionExtrasForController(sessionId, controllerKey, extras); + } + //////////////////////////////////////////////////////////////////////////////// // RemoteMockPlayer methods //////////////////////////////////////////////////////////////////////////////// diff --git a/libraries/test_session_current/src/main/java/androidx/media3/session/RemoteMediaSessionCompat.java b/libraries/test_session_current/src/main/java/androidx/media3/session/RemoteMediaSessionCompat.java index a06760d460..da94920c59 100644 --- a/libraries/test_session_current/src/main/java/androidx/media3/session/RemoteMediaSessionCompat.java +++ b/libraries/test_session_current/src/main/java/androidx/media3/session/RemoteMediaSessionCompat.java @@ -173,6 +173,10 @@ public class RemoteMediaSessionCompat { binder.setCaptioningEnabled(sessionTag, enabled); } + public void setExtras(Bundle extras) throws RemoteException { + binder.setSessionExtras(sessionTag, extras); + } + //////////////////////////////////////////////////////////////////////////////// // Non-public methods //////////////////////////////////////////////////////////////////////////////// diff --git a/libraries/test_session_current/src/main/java/androidx/media3/session/TestMediaBrowserListener.java b/libraries/test_session_current/src/main/java/androidx/media3/session/TestMediaBrowserListener.java index f8e9071860..7b76cb7366 100644 --- a/libraries/test_session_current/src/main/java/androidx/media3/session/TestMediaBrowserListener.java +++ b/libraries/test_session_current/src/main/java/androidx/media3/session/TestMediaBrowserListener.java @@ -58,6 +58,11 @@ public final class TestMediaBrowserListener implements MediaBrowser.Listener { return delegate.onSetCustomLayout(controller, layout); } + @Override + public void onExtrasChanged(MediaController controller, Bundle extras) { + delegate.onExtrasChanged(controller, extras); + } + @Override public void onAvailableSessionCommandsChanged( MediaController controller, SessionCommands commands) {