From a063d137b4307348a140ec6a2b6d254db294395e Mon Sep 17 00:00:00 2001 From: bachinger Date: Tue, 21 Nov 2023 14:29:56 -0800 Subject: [PATCH] Add session extras to the state of the controller This change adds `MediaController.getSessionExtras()` through which a controller can access the session extras. The session extras can be set for the entire session when building the session. This can be overridden for specific controllers in `MediaSession.Callback.onConnect`. PiperOrigin-RevId: 584430419 --- .../media3/session/ConnectionState.java | 10 +- .../media3/session/MediaController.java | 20 +++- .../session/MediaControllerImplBase.java | 9 ++ .../session/MediaControllerImplLegacy.java | 104 +++++++++++----- .../media3/session/MediaLibraryService.java | 37 ++++-- .../session/MediaLibrarySessionImpl.java | 2 + .../androidx/media3/session/MediaSession.java | 111 +++++++++++++++--- .../media3/session/MediaSessionImpl.java | 12 ++ .../media3/session/MediaSessionStub.java | 3 + .../common/IRemoteMediaController.aidl | 1 + .../session/MediaControllerListenerTest.java | 11 +- ...lerListenerWithMediaSessionCompatTest.java | 18 +++ .../media3/session/MediaControllerTest.java | 19 ++- .../session/MediaSessionCallbackTest.java | 74 ++++++++++++ .../MediaControllerProviderService.java | 5 + .../media3/session/RemoteMediaController.java | 4 + 16 files changed, 383 insertions(+), 57 deletions(-) diff --git a/libraries/session/src/main/java/androidx/media3/session/ConnectionState.java b/libraries/session/src/main/java/androidx/media3/session/ConnectionState.java index 43068cd3e0..72d7d31127 100644 --- a/libraries/session/src/main/java/androidx/media3/session/ConnectionState.java +++ b/libraries/session/src/main/java/androidx/media3/session/ConnectionState.java @@ -53,6 +53,8 @@ import java.util.List; public final Bundle tokenExtras; + public final Bundle sessionExtras; + public final PlayerInfo playerInfo; public final ImmutableList customLayout; @@ -67,6 +69,7 @@ import java.util.List; Player.Commands playerCommandsFromSession, Player.Commands playerCommandsFromPlayer, Bundle tokenExtras, + Bundle sessionExtras, PlayerInfo playerInfo) { this.libraryVersion = libraryVersion; this.sessionInterfaceVersion = sessionInterfaceVersion; @@ -77,6 +80,7 @@ import java.util.List; this.playerCommandsFromSession = playerCommandsFromSession; this.playerCommandsFromPlayer = playerCommandsFromPlayer; this.tokenExtras = tokenExtras; + this.sessionExtras = sessionExtras; this.playerInfo = playerInfo; } @@ -90,11 +94,12 @@ import java.util.List; private static final String FIELD_PLAYER_COMMANDS_FROM_SESSION = Util.intToStringMaxRadix(4); private static final String FIELD_PLAYER_COMMANDS_FROM_PLAYER = Util.intToStringMaxRadix(5); private static final String FIELD_TOKEN_EXTRAS = Util.intToStringMaxRadix(6); + private static final String FIELD_SESSION_EXTRAS = Util.intToStringMaxRadix(11); private static final String FIELD_PLAYER_INFO = Util.intToStringMaxRadix(7); private static final String FIELD_SESSION_INTERFACE_VERSION = Util.intToStringMaxRadix(8); private static final String FIELD_IN_PROCESS_BINDER = Util.intToStringMaxRadix(10); - // Next field key = 11 + // Next field key = 12 @Override public Bundle toBundle() { @@ -115,6 +120,7 @@ import java.util.List; bundle.putBundle(FIELD_PLAYER_COMMANDS_FROM_SESSION, playerCommandsFromSession.toBundle()); bundle.putBundle(FIELD_PLAYER_COMMANDS_FROM_PLAYER, playerCommandsFromPlayer.toBundle()); bundle.putBundle(FIELD_TOKEN_EXTRAS, tokenExtras); + bundle.putBundle(FIELD_SESSION_EXTRAS, sessionExtras); Player.Commands intersectedCommands = MediaUtils.intersect(playerCommandsFromSession, playerCommandsFromPlayer); bundle.putBundle( @@ -179,6 +185,7 @@ import java.util.List; ? Player.Commands.EMPTY : Player.Commands.fromBundle(playerCommandsFromSessionBundle); @Nullable Bundle tokenExtras = bundle.getBundle(FIELD_TOKEN_EXTRAS); + @Nullable Bundle sessionExtras = bundle.getBundle(FIELD_SESSION_EXTRAS); @Nullable Bundle playerInfoBundle = bundle.getBundle(FIELD_PLAYER_INFO); PlayerInfo playerInfo = playerInfoBundle == null ? PlayerInfo.DEFAULT : PlayerInfo.fromBundle(playerInfoBundle); @@ -192,6 +199,7 @@ import java.util.List; playerCommandsFromSession, playerCommandsFromPlayer, tokenExtras == null ? Bundle.EMPTY : tokenExtras, + sessionExtras == null ? Bundle.EMPTY : sessionExtras, playerInfo); } 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 3152f6a2ca..21aef646f1 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaController.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaController.java @@ -408,10 +408,10 @@ public class MediaController implements Player { } /** - * Called when the session extras have changed. + * Called when the session extras are set on the session side. * * @param controller The controller. - * @param extras The session extras that have changed. + * @param extras The session extras that have been set on the session. */ default void onExtrasChanged(MediaController controller, Bundle extras) {} @@ -967,6 +967,20 @@ public class MediaController implements Player { return isConnected() ? impl.getCustomLayout() : ImmutableList.of(); } + /** + * Returns the session extras. + * + *

After being connected, {@link Listener#onExtrasChanged(MediaController, Bundle)} is called + * when the extras on the session are set. + * + * @return The session extras. + */ + @UnstableApi + public final Bundle getSessionExtras() { + verifyApplicationThread(); + return isConnected() ? impl.getSessionExtras() : Bundle.EMPTY; + } + /** Returns {@code null}. */ @UnstableApi @Override @@ -2029,6 +2043,8 @@ public class MediaController implements Player { ImmutableList getCustomLayout(); + Bundle getSessionExtras(); + Timeline getCurrentTimeline(); void setMediaItem(MediaItem mediaItem); 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 70172f8a54..98f8ffdb12 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java @@ -134,6 +134,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; private long lastSetPlayWhenReadyCalledTimeMs; @Nullable private PlayerInfo pendingPlayerInfo; @Nullable private BundlingExclusions pendingBundlingExclusions; + private Bundle sessionExtras; public MediaControllerImplBase( Context context, @@ -173,6 +174,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; .getInstance() .runOnApplicationLooper(MediaControllerImplBase.this.getInstance()::release); surfaceCallback = new SurfaceCallback(); + sessionExtras = Bundle.EMPTY; serviceConnection = (this.token.getType() == SessionToken.TYPE_SESSION) @@ -727,6 +729,11 @@ import org.checkerframework.checker.nullness.qual.NonNull; return customLayout; } + @Override + public Bundle getSessionExtras() { + return sessionExtras; + } + @Override public Timeline getCurrentTimeline() { return playerInfo.timeline; @@ -2545,6 +2552,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; token.getPackageName(), result.sessionBinder, result.tokenExtras); + sessionExtras = result.sessionExtras; getInstance().notifyAccepted(); } @@ -2763,6 +2771,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; if (!isConnected()) { return; } + sessionExtras = extras; getInstance() .notifyControllerListener(listener -> listener.onExtrasChanged(getInstance(), extras)); } 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 cb76b99c12..4bcaeb81bd 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java @@ -191,7 +191,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; maskedPlayerInfo, controllerInfo.availableSessionCommands, controllerInfo.availablePlayerCommands, - controllerInfo.customLayout); + controllerInfo.customLayout, + controllerInfo.sessionExtras); updateStateMaskedControllerInfo( maskedControllerInfo, /* discontinuityReason= */ null, @@ -255,7 +256,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; /* playerError= */ null), controllerInfo.availableSessionCommands, controllerInfo.availablePlayerCommands, - controllerInfo.customLayout); + controllerInfo.customLayout, + controllerInfo.sessionExtras); updateStateMaskedControllerInfo( maskedControllerInfo, /* discontinuityReason= */ null, @@ -376,7 +378,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; maskedPlayerInfo, controllerInfo.availableSessionCommands, controllerInfo.availablePlayerCommands, - controllerInfo.customLayout); + controllerInfo.customLayout, + controllerInfo.sessionExtras); updateStateMaskedControllerInfo( maskedControllerInfo, discontinuityReason, mediaItemTransitionReason); } @@ -414,6 +417,11 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; return controllerInfo.customLayout; } + @Override + public Bundle getSessionExtras() { + return controllerInfo.sessionExtras; + } + @Override @Nullable public PlaybackException getPlayerError() { @@ -529,7 +537,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; controllerInfo.playerInfo.copyWithPlaybackParameters(playbackParameters), controllerInfo.availableSessionCommands, controllerInfo.availablePlayerCommands, - controllerInfo.customLayout); + controllerInfo.customLayout, + controllerInfo.sessionExtras); updateStateMaskedControllerInfo( maskedControllerInfo, /* discontinuityReason= */ null, @@ -548,7 +557,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; controllerInfo.playerInfo.copyWithPlaybackParameters(new PlaybackParameters(speed)), controllerInfo.availableSessionCommands, controllerInfo.availablePlayerCommands, - controllerInfo.customLayout); + controllerInfo.customLayout, + controllerInfo.sessionExtras); updateStateMaskedControllerInfo( maskedControllerInfo, /* discontinuityReason= */ null, @@ -640,7 +650,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; maskedPlayerInfo, controllerInfo.availableSessionCommands, controllerInfo.availablePlayerCommands, - controllerInfo.customLayout); + controllerInfo.customLayout, + controllerInfo.sessionExtras); updateStateMaskedControllerInfo( maskedControllerInfo, /* discontinuityReason= */ null, @@ -703,7 +714,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; maskedPlayerInfo, controllerInfo.availableSessionCommands, controllerInfo.availablePlayerCommands, - controllerInfo.customLayout); + controllerInfo.customLayout, + controllerInfo.sessionExtras); updateStateMaskedControllerInfo( maskedControllerInfo, /* discontinuityReason= */ null, @@ -755,7 +767,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; maskedPlayerInfo, controllerInfo.availableSessionCommands, controllerInfo.availablePlayerCommands, - controllerInfo.customLayout); + controllerInfo.customLayout, + controllerInfo.sessionExtras); updateStateMaskedControllerInfo( maskedControllerInfo, /* discontinuityReason= */ null, @@ -821,7 +834,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; maskedPlayerInfo, controllerInfo.availableSessionCommands, controllerInfo.availablePlayerCommands, - controllerInfo.customLayout); + controllerInfo.customLayout, + controllerInfo.sessionExtras); updateStateMaskedControllerInfo( maskedControllerInfo, /* discontinuityReason= */ null, @@ -933,7 +947,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; controllerInfo.playerInfo.copyWithRepeatMode(repeatMode), controllerInfo.availableSessionCommands, controllerInfo.availablePlayerCommands, - controllerInfo.customLayout); + controllerInfo.customLayout, + controllerInfo.sessionExtras); updateStateMaskedControllerInfo( maskedControllerInfo, /* discontinuityReason= */ null, @@ -959,7 +974,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; controllerInfo.playerInfo.copyWithShuffleModeEnabled(shuffleModeEnabled), controllerInfo.availableSessionCommands, controllerInfo.availablePlayerCommands, - controllerInfo.customLayout); + controllerInfo.customLayout, + controllerInfo.sessionExtras); updateStateMaskedControllerInfo( maskedControllerInfo, /* discontinuityReason= */ null, @@ -1081,7 +1097,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; controllerInfo.playerInfo.copyWithDeviceVolume(volume, isDeviceMuted), controllerInfo.availableSessionCommands, controllerInfo.availablePlayerCommands, - controllerInfo.customLayout); + controllerInfo.customLayout, + controllerInfo.sessionExtras); updateStateMaskedControllerInfo( maskedControllerInfo, /* discontinuityReason= */ null, @@ -1112,7 +1129,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; controllerInfo.playerInfo.copyWithDeviceVolume(volume + 1, isDeviceMuted), controllerInfo.availableSessionCommands, controllerInfo.availablePlayerCommands, - controllerInfo.customLayout); + controllerInfo.customLayout, + controllerInfo.sessionExtras); updateStateMaskedControllerInfo( maskedControllerInfo, /* discontinuityReason= */ null, @@ -1142,7 +1160,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; controllerInfo.playerInfo.copyWithDeviceVolume(volume - 1, isDeviceMuted), controllerInfo.availableSessionCommands, controllerInfo.availablePlayerCommands, - controllerInfo.customLayout); + controllerInfo.customLayout, + controllerInfo.sessionExtras); updateStateMaskedControllerInfo( maskedControllerInfo, /* discontinuityReason= */ null, @@ -1175,7 +1194,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; controllerInfo.playerInfo.copyWithDeviceVolume(volume, muted), controllerInfo.availableSessionCommands, controllerInfo.availablePlayerCommands, - controllerInfo.customLayout); + controllerInfo.customLayout, + controllerInfo.sessionExtras); updateStateMaskedControllerInfo( maskedControllerInfo, /* discontinuityReason= */ null, @@ -1212,7 +1232,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; Player.PLAYBACK_SUPPRESSION_REASON_NONE), controllerInfo.availableSessionCommands, controllerInfo.availablePlayerCommands, - controllerInfo.customLayout); + controllerInfo.customLayout, + controllerInfo.sessionExtras); updateStateMaskedControllerInfo( maskedControllerInfo, /* discontinuityReason= */ null, @@ -1308,7 +1329,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; convertToNonNullQueueItemList(controllerCompat.getQueue()), controllerCompat.getQueueTitle(), controllerCompat.getRepeatMode(), - controllerCompat.getShuffleMode()); + controllerCompat.getShuffleMode(), + controllerCompat.getExtras()); handleNewLegacyParameters(/* notifyConnected= */ true, newLegacyPlayerInfo); } @@ -1823,6 +1845,13 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; @Override public void onExtrasChanged(Bundle extras) { + controllerInfo = + new ControllerInfo( + controllerInfo.playerInfo, + controllerInfo.availableSessionCommands, + controllerInfo.availablePlayerCommands, + controllerInfo.customLayout, + extras); getInstance() .notifyControllerListener(listener -> listener.onExtrasChanged(getInstance(), extras)); } @@ -2068,6 +2097,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; availableSessionCommands, availablePlayerCommands, customLayout, + newLegacyPlayerInfo.sessionExtras, playerError, durationMs, currentPositionMs, @@ -2235,6 +2265,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; SessionCommands availableSessionCommands, Commands availablePlayerCommands, ImmutableList customLayout, + Bundle sessionExtras, @Nullable PlaybackException playerError, long durationMs, long currentPositionMs, @@ -2305,7 +2336,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; /* parameters= */ TrackSelectionParameters.DEFAULT_WITHOUT_CONTEXT); return new ControllerInfo( - playerInfo, availableSessionCommands, availablePlayerCommands, customLayout); + playerInfo, availableSessionCommands, availablePlayerCommands, customLayout, sessionExtras); } private static PositionInfo createPositionInfo( @@ -2355,6 +2386,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; @Nullable public final CharSequence queueTitle; @PlaybackStateCompat.RepeatMode public final int repeatMode; @PlaybackStateCompat.ShuffleMode public final int shuffleMode; + public final Bundle sessionExtras; public LegacyPlayerInfo() { playbackInfoCompat = null; @@ -2364,6 +2396,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; queueTitle = null; repeatMode = PlaybackStateCompat.REPEAT_MODE_NONE; shuffleMode = PlaybackStateCompat.SHUFFLE_MODE_NONE; + sessionExtras = Bundle.EMPTY; } public LegacyPlayerInfo( @@ -2373,7 +2406,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; List queue, @Nullable CharSequence queueTitle, @PlaybackStateCompat.RepeatMode int repeatMode, - @PlaybackStateCompat.ShuffleMode int shuffleMode) { + @PlaybackStateCompat.ShuffleMode int shuffleMode, + Bundle sessionExtras) { this.playbackInfoCompat = playbackInfoCompat; this.playbackStateCompat = playbackStateCompat; this.mediaMetadataCompat = mediaMetadataCompat; @@ -2381,6 +2415,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; this.queueTitle = queueTitle; this.repeatMode = repeatMode; this.shuffleMode = shuffleMode; + this.sessionExtras = sessionExtras; } public LegacyPlayerInfo(LegacyPlayerInfo other) { @@ -2391,6 +2426,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; queueTitle = other.queueTitle; repeatMode = other.repeatMode; shuffleMode = other.shuffleMode; + sessionExtras = other.sessionExtras; } @CheckResult @@ -2405,7 +2441,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; queue, queueTitle, repeatMode, - shuffleMode); + shuffleMode, + sessionExtras); } @CheckResult @@ -2418,7 +2455,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; queue, queueTitle, repeatMode, - shuffleMode); + shuffleMode, + sessionExtras); } @CheckResult @@ -2431,7 +2469,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; queue, queueTitle, repeatMode, - shuffleMode); + shuffleMode, + sessionExtras); } @CheckResult @@ -2443,7 +2482,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; queue, queueTitle, repeatMode, - shuffleMode); + shuffleMode, + sessionExtras); } @CheckResult @@ -2455,7 +2495,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; queue, queueTitle, repeatMode, - shuffleMode); + shuffleMode, + sessionExtras); } @CheckResult @@ -2468,7 +2509,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; queue, queueTitle, repeatMode, - shuffleMode); + shuffleMode, + sessionExtras); } @CheckResult @@ -2480,7 +2522,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; queue, queueTitle, repeatMode, - shuffleMode); + shuffleMode, + sessionExtras); } @CheckResult @@ -2492,7 +2535,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; queue, queueTitle, repeatMode, - shuffleMode); + shuffleMode, + sessionExtras); } } @@ -2502,23 +2546,27 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; public final SessionCommands availableSessionCommands; public final Commands availablePlayerCommands; public final ImmutableList customLayout; + public final Bundle sessionExtras; public ControllerInfo() { playerInfo = PlayerInfo.DEFAULT.copyWithTimeline(QueueTimeline.DEFAULT); availableSessionCommands = SessionCommands.EMPTY; availablePlayerCommands = Commands.EMPTY; customLayout = ImmutableList.of(); + sessionExtras = Bundle.EMPTY; } public ControllerInfo( PlayerInfo playerInfo, SessionCommands availableSessionCommands, Commands availablePlayerCommands, - ImmutableList customLayout) { + ImmutableList customLayout, + Bundle sessionExtras) { this.playerInfo = playerInfo; this.availableSessionCommands = availableSessionCommands; this.availablePlayerCommands = availablePlayerCommands; this.customLayout = customLayout; + this.sessionExtras = sessionExtras; } } } diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java b/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java index 855b87691b..b9bff77d88 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java @@ -440,16 +440,34 @@ public abstract class MediaLibraryService extends MediaSessionService { } /** - * Sets an extra {@link Bundle} for the {@link MediaLibrarySession}. The {@link - * MediaLibrarySession#getToken()} session token} will have the {@link - * SessionToken#getExtras() extras}. If not set, an empty {@link Bundle} will be used. + * Sets an extras {@link Bundle} for the {@linkplain MediaLibrarySession#getToken() session + * token}. If not set, {@link Bundle#EMPTY} is used. * - * @param extras The extra {@link Bundle}. + *

A controller has access to these extras through the {@linkplain + * MediaController#getConnectedToken() connected token}. + * + * @param tokenExtras The extras {@link Bundle}. * @return The builder to allow chaining. */ @Override - public Builder setExtras(Bundle extras) { - return super.setExtras(extras); + public Builder setExtras(Bundle tokenExtras) { + return super.setExtras(tokenExtras); + } + + /** + * Sets the {@linkplain MediaLibrarySession#getSessionExtras() session extras}. If not set, + * {@link Bundle#EMPTY} is used. + * + *

A controller has access to session extras through {@link + * MediaController#getSessionExtras()}. + * + * @param sessionExtras The session extras {@link Bundle}. + * @return The builder to allow chaining. + */ + @UnstableApi + @Override + public Builder setSessionExtras(Bundle sessionExtras) { + return super.setSessionExtras(sessionExtras); } /** @@ -551,7 +569,8 @@ public abstract class MediaLibraryService extends MediaSessionService { sessionActivity, customLayout, callback, - extras, + tokenExtras, + sessionExtras, checkNotNull(bitmapLoader), playIfSuppressed, isPeriodicPositionUpdateEnabled); @@ -566,6 +585,7 @@ public abstract class MediaLibraryService extends MediaSessionService { ImmutableList customLayout, MediaSession.Callback callback, Bundle tokenExtras, + Bundle sessionExtras, BitmapLoader bitmapLoader, boolean playIfSuppressed, boolean isPeriodicPositionUpdateEnabled) { @@ -577,6 +597,7 @@ public abstract class MediaLibraryService extends MediaSessionService { customLayout, callback, tokenExtras, + sessionExtras, bitmapLoader, playIfSuppressed, isPeriodicPositionUpdateEnabled); @@ -591,6 +612,7 @@ public abstract class MediaLibraryService extends MediaSessionService { ImmutableList customLayout, MediaSession.Callback callback, Bundle tokenExtras, + Bundle sessionExtras, BitmapLoader bitmapLoader, boolean playIfSuppressed, boolean isPeriodicPositionUpdateEnabled) { @@ -603,6 +625,7 @@ public abstract class MediaLibraryService extends MediaSessionService { customLayout, (Callback) callback, tokenExtras, + sessionExtras, bitmapLoader, playIfSuppressed, isPeriodicPositionUpdateEnabled); diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java b/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java index 53f2098091..27dfad70c4 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java @@ -74,6 +74,7 @@ import java.util.concurrent.Future; ImmutableList customLayout, MediaLibrarySession.Callback callback, Bundle tokenExtras, + Bundle sessionExtras, BitmapLoader bitmapLoader, boolean playIfSuppressed, boolean isPeriodicPositionUpdateEnabled) { @@ -86,6 +87,7 @@ import java.util.concurrent.Future; customLayout, callback, tokenExtras, + sessionExtras, bitmapLoader, playIfSuppressed, isPeriodicPositionUpdateEnabled); 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 63f064bea5..11e7325b6a 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSession.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSession.java @@ -318,16 +318,34 @@ public class MediaSession { } /** - * Sets an extra {@link Bundle} for the {@link MediaSession}. The {@link - * MediaSession#getToken()} session token} will have the {@link SessionToken#getExtras() - * extras}. If not set, an empty {@link Bundle} will be used. + * Sets an extras {@link Bundle} for the {@linkplain MediaSession#getToken() session token}. If + * not set, {@link Bundle#EMPTY} is used. * - * @param extras The extra {@link Bundle}. + *

A controller has access to these extras through the {@linkplain + * MediaController#getConnectedToken() connected token}. + * + * @param tokenExtras The extras {@link Bundle}. * @return The builder to allow chaining. */ @Override - public Builder setExtras(Bundle extras) { - return super.setExtras(extras); + public Builder setExtras(Bundle tokenExtras) { + return super.setExtras(tokenExtras); + } + + /** + * Sets the {@linkplain MediaSession#getSessionExtras() session extras}. If not set, {@link + * Bundle#EMPTY} is used. + * + *

A controller has access to session extras through {@linkplain + * MediaController#getSessionExtras()}. + * + * @param sessionExtras The session extras {@link Bundle}. + * @return The builder to allow chaining. + */ + @UnstableApi + @Override + public Builder setSessionExtras(Bundle sessionExtras) { + return super.setSessionExtras(sessionExtras); } /** @@ -426,7 +444,8 @@ public class MediaSession { sessionActivity, customLayout, callback, - extras, + tokenExtras, + sessionExtras, checkNotNull(bitmapLoader), playIfSuppressed, isPeriodicPositionUpdateEnabled); @@ -621,6 +640,7 @@ public class MediaSession { ImmutableList customLayout, Callback callback, Bundle tokenExtras, + Bundle sessionExtras, BitmapLoader bitmapLoader, boolean playIfSuppressed, boolean isPeriodicPositionUpdateEnabled) { @@ -639,6 +659,7 @@ public class MediaSession { customLayout, callback, tokenExtras, + sessionExtras, bitmapLoader, playIfSuppressed, isPeriodicPositionUpdateEnabled); @@ -652,6 +673,7 @@ public class MediaSession { ImmutableList customLayout, Callback callback, Bundle tokenExtras, + Bundle sessionExtras, BitmapLoader bitmapLoader, boolean playIfSuppressed, boolean isPeriodicPositionUpdateEnabled) { @@ -664,6 +686,7 @@ public class MediaSession { customLayout, callback, tokenExtras, + sessionExtras, bitmapLoader, playIfSuppressed, isPeriodicPositionUpdateEnabled); @@ -982,7 +1005,23 @@ public class MediaSession { } /** - * Sends the session extras to connected controllers. + * Returns the session extras. + * + *

For informational purpose only. Mutations on the {@link Bundle} do not have immediate + * effect. To change the session extras use {@link #setSessionExtras(Bundle)} or {@link + * #setSessionExtras(ControllerInfo, Bundle)}. + */ + @UnstableApi + public Bundle getSessionExtras() { + return impl.getSessionExtras(); + } + + /** + * Sets the {@linkplain #getSessionExtras() session extras} and sends them to connected + * controllers. + * + *

The initial extras can be set {@linkplain Builder#setSessionExtras(Bundle) when building the + * session}. * *

This is a synchronous call and doesn't wait for results from the controllers. * @@ -996,6 +1035,11 @@ public class MediaSession { /** * Sends the session extras to the connected controller. * + *

The initial extras for a specific controller can be set in {@link + * Callback#onConnect(MediaSession, ControllerInfo)} when {@link + * ConnectionResult.AcceptedResultBuilder#setSessionExtras(Bundle) building the connection + * result}. + * *

This is a synchronous call and doesn't wait for results from the controller. * *

Interoperability: This call has no effect when called for a {@linkplain @@ -1549,6 +1593,7 @@ public class MediaSession { private SessionCommands availableSessionCommands; private Player.Commands availablePlayerCommands = DEFAULT_PLAYER_COMMANDS; @Nullable private ImmutableList customLayout; + @Nullable private Bundle sessionExtras; /** * Creates an instance. @@ -1611,10 +1656,26 @@ public class MediaSession { return this; } + /** + * Sets the session extras, overriding the {@linkplain MediaSession#getSessionExtras() extras + * of the session}. + * + *

The default is null to indicate that the extras of the session should be used. + */ + @CanIgnoreReturnValue + public AcceptedResultBuilder setSessionExtras(Bundle sessionExtras) { + this.sessionExtras = sessionExtras; + return this; + } + /** Returns a new {@link ConnectionResult} instance for accepting a connection. */ public ConnectionResult build() { return new ConnectionResult( - /* accepted= */ true, availableSessionCommands, availablePlayerCommands, customLayout); + /* accepted= */ true, + availableSessionCommands, + availablePlayerCommands, + customLayout, + sessionExtras); } } @@ -1642,16 +1703,21 @@ public class MediaSession { /** The custom layout or null if the custom layout of the session should be used. */ @UnstableApi @Nullable public final ImmutableList customLayout; + /** The session extras. */ + @UnstableApi @Nullable public final Bundle sessionExtras; + /** Creates a new instance with the given available session and player commands. */ private ConnectionResult( boolean accepted, SessionCommands availableSessionCommands, Player.Commands availablePlayerCommands, - @Nullable ImmutableList customLayout) { + @Nullable ImmutableList customLayout, + @Nullable Bundle sessionExtras) { isAccepted = accepted; this.availableSessionCommands = availableSessionCommands; this.availablePlayerCommands = availablePlayerCommands; this.customLayout = customLayout; + this.sessionExtras = sessionExtras; } /** @@ -1670,13 +1736,18 @@ public class MediaSession { /* accepted= */ true, availableSessionCommands, availablePlayerCommands, - /* customLayout= */ null); + /* customLayout= */ null, + /* sessionExtras= */ null); } /** Creates a {@link ConnectionResult} to reject a connection. */ public static ConnectionResult reject() { return new ConnectionResult( - /* accepted= */ false, SessionCommands.EMPTY, Player.Commands.EMPTY, ImmutableList.of()); + /* accepted= */ false, + SessionCommands.EMPTY, + Player.Commands.EMPTY, + /* customLayout= */ ImmutableList.of(), + /* sessionExtras= */ Bundle.EMPTY); } } @@ -1850,7 +1921,8 @@ public class MediaSession { /* package */ String id; /* package */ CallbackT callback; /* package */ @Nullable PendingIntent sessionActivity; - /* package */ Bundle extras; + /* package */ Bundle tokenExtras; + /* package */ Bundle sessionExtras; /* package */ @MonotonicNonNull BitmapLoader bitmapLoader; /* package */ boolean playIfSuppressed; /* package */ ImmutableList customLayout; @@ -1862,7 +1934,8 @@ public class MediaSession { checkArgument(player.canAdvertiseSession()); id = ""; this.callback = callback; - extras = Bundle.EMPTY; + tokenExtras = Bundle.EMPTY; + sessionExtras = Bundle.EMPTY; customLayout = ImmutableList.of(); playIfSuppressed = true; isPeriodicPositionUpdateEnabled = true; @@ -1887,8 +1960,14 @@ public class MediaSession { } @SuppressWarnings("unchecked") - public BuilderT setExtras(Bundle extras) { - this.extras = new Bundle(checkNotNull(extras)); + public BuilderT setExtras(Bundle tokenExtras) { + this.tokenExtras = new Bundle(checkNotNull(tokenExtras)); + return (BuilderT) this; + } + + @SuppressWarnings("unchecked") + public BuilderT setSessionExtras(Bundle sessionExtras) { + this.sessionExtras = new Bundle(checkNotNull(sessionExtras)); return (BuilderT) this; } 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 4193ff3e40..af7981cc7d 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java @@ -149,6 +149,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private long sessionPositionUpdateDelayMs; private boolean isMediaNotificationControllerConnected; private ImmutableList customLayout; + private Bundle sessionExtras; public MediaSessionImpl( MediaSession instance, @@ -159,6 +160,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; ImmutableList customLayout, MediaSession.Callback callback, Bundle tokenExtras, + Bundle sessionExtras, BitmapLoader bitmapLoader, boolean playIfSuppressed, boolean isPeriodicPositionUpdateEnabled) { @@ -168,6 +170,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; this.sessionActivity = sessionActivity; this.customLayout = customLayout; this.callback = callback; + this.sessionExtras = sessionExtras; this.bitmapLoader = bitmapLoader; this.playIfSuppressed = playIfSuppressed; this.isPeriodicPositionUpdateEnabled = isPeriodicPositionUpdateEnabled; @@ -481,6 +484,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } public void setSessionExtras(Bundle sessionExtras) { + this.sessionExtras = sessionExtras; dispatchRemoteControllerTaskWithoutReturn( (controller, seq) -> controller.onSessionExtrasChanged(seq, sessionExtras)); } @@ -489,9 +493,17 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; if (sessionStub.getConnectedControllersManager().isConnected(controller)) { dispatchRemoteControllerTaskWithoutReturn( controller, (callback, seq) -> callback.onSessionExtrasChanged(seq, sessionExtras)); + if (isMediaNotificationController(controller)) { + dispatchRemoteControllerTaskToLegacyStub( + (callback, seq) -> callback.onSessionExtrasChanged(seq, sessionExtras)); + } } } + public Bundle getSessionExtras() { + return sessionExtras; + } + public BitmapLoader getBitmapLoader() { return bitmapLoader; } 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 29a2473e91..8f00442c97 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java @@ -528,6 +528,9 @@ import java.util.concurrent.ExecutionException; connectionResult.availablePlayerCommands, playerWrapper.getAvailableCommands(), sessionImpl.getToken().getExtras(), + connectionResult.sessionExtras != null + ? connectionResult.sessionExtras + : sessionImpl.getSessionExtras(), playerInfo); // Double check if session is still there, because release() can be called in diff --git a/libraries/test_session_common/src/main/aidl/androidx/media3/test/session/common/IRemoteMediaController.aidl b/libraries/test_session_common/src/main/aidl/androidx/media3/test/session/common/IRemoteMediaController.aidl index 34812bc1be..2ff7a39d88 100644 --- a/libraries/test_session_common/src/main/aidl/androidx/media3/test/session/common/IRemoteMediaController.aidl +++ b/libraries/test_session_common/src/main/aidl/androidx/media3/test/session/common/IRemoteMediaController.aidl @@ -29,6 +29,7 @@ interface IRemoteMediaController { // MediaController Methods Bundle getConnectedSessionToken(String controllerId); + Bundle getSessionExtras(String controllerId); void play(String controllerId); void pause(String controllerId); void setPlayWhenReady(String controllerId, boolean playWhenReady); 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 9433249f4e..7152ce25ef 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 @@ -1895,7 +1895,7 @@ public class MediaControllerListenerTest { .getMockPlayer() .setPlayWhenReady(/* playWhenReady= */ true, Player.PLAYBACK_SUPPRESSION_REASON_NONE); MediaController controller = controllerTestRule.createController(remoteSession.getToken()); - threadTestRule.getHandler().postAndSync(() -> controller.setTimeDiffMs(/* timeDiff= */ 0L)); + threadTestRule.getHandler().postAndSync(() -> controller.setTimeDiffMs(/* timeDiffMs= */ 0L)); CountDownLatch latch = new CountDownLatch(2); AtomicBoolean isPlayingRef = new AtomicBoolean(); AtomicLong currentPositionMsRef = new AtomicLong(); @@ -2136,7 +2136,7 @@ public class MediaControllerListenerTest { remoteMockPlayer.setCurrentAdGroupIndex(testCurrentAdGroupIndex); remoteMockPlayer.setCurrentAdIndexInAdGroup(testCurrentAdIndexInAdGroup); remoteMockPlayer.notifyPositionDiscontinuity( - /* oldPositionInfo= */ SessionPositionInfo.DEFAULT_POSITION_INFO, + /* oldPosition= */ SessionPositionInfo.DEFAULT_POSITION_INFO, newPositionInfo, Player.DISCONTINUITY_REASON_INTERNAL); @@ -2470,11 +2470,13 @@ public class MediaControllerListenerTest { sessionExtras.putString("key-0", "value-0"); CountDownLatch latch = new CountDownLatch(1); List receivedSessionExtras = new ArrayList<>(); + List getterSessionExtras = new ArrayList<>(); MediaController.Listener listener = new MediaController.Listener() { @Override public void onExtrasChanged(MediaController controller, Bundle extras) { receivedSessionExtras.add(extras); + getterSessionExtras.add(controller.getSessionExtras()); latch.countDown(); } }; @@ -2487,6 +2489,8 @@ public class MediaControllerListenerTest { assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); assertThat(receivedSessionExtras).hasSize(1); assertThat(TestUtils.equals(receivedSessionExtras.get(0), sessionExtras)).isTrue(); + assertThat(getterSessionExtras).hasSize(1); + assertThat(TestUtils.equals(getterSessionExtras.get(0), sessionExtras)).isTrue(); } @Test @@ -2495,11 +2499,13 @@ public class MediaControllerListenerTest { sessionExtras.putString("key-0", "value-0"); CountDownLatch latch = new CountDownLatch(1); List receivedSessionExtras = new ArrayList<>(); + List getterSessionExtras = new ArrayList<>(); MediaController.Listener listener = new MediaController.Listener() { @Override public void onExtrasChanged(MediaController controller, Bundle extras) { receivedSessionExtras.add(extras); + getterSessionExtras.add(controller.getSessionExtras()); latch.countDown(); } }; @@ -2513,6 +2519,7 @@ public class MediaControllerListenerTest { assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); assertThat(receivedSessionExtras).hasSize(1); assertThat(TestUtils.equals(receivedSessionExtras.get(0), sessionExtras)).isTrue(); + assertThat(TestUtils.equals(getterSessionExtras.get(0), sessionExtras)).isTrue(); } @Test 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 4b40548227..cdd8768b06 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 @@ -188,12 +188,14 @@ public class MediaControllerListenerWithMediaSessionCompatTest { sessionExtras.putString("key-1", "value-1"); CountDownLatch countDownLatch = new CountDownLatch(1); List receivedSessionExtras = new ArrayList<>(); + List getterSessionExtras = new ArrayList<>(); controllerTestRule.createController( session.getSessionToken(), new MediaController.Listener() { @Override public void onExtrasChanged(MediaController controller, Bundle extras) { receivedSessionExtras.add(extras); + getterSessionExtras.add(controller.getSessionExtras()); countDownLatch.countDown(); } }); @@ -202,6 +204,22 @@ public class MediaControllerListenerWithMediaSessionCompatTest { assertThat(countDownLatch.await(1_000, MILLISECONDS)).isTrue(); assertThat(TestUtils.equals(receivedSessionExtras.get(0), sessionExtras)).isTrue(); + assertThat(TestUtils.equals(getterSessionExtras.get(0), sessionExtras)).isTrue(); + } + + @Test + public void setSessionExtras_includedWhenConnecting() throws Exception { + Bundle sessionExtras = new Bundle(); + sessionExtras.putString("key-1", "value-1"); + session.setExtras(sessionExtras); + + MediaController controller = controllerTestRule.createController(session.getSessionToken()); + + assertThat( + TestUtils.equals( + threadTestRule.getHandler().postAndSync(controller::getSessionExtras), + sessionExtras)) + .isTrue(); } @Test diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerTest.java index 17a8d5d3ff..dd5c28079c 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerTest.java @@ -422,6 +422,23 @@ public class MediaControllerTest { session.cleanUp(); } + @Test + public void getSessionExtras_includedInConnectionStateWhenConnecting() throws Exception { + RemoteMediaSession session = + createRemoteMediaSession(TEST_GET_CUSTOM_LAYOUT, /* tokenExtras= */ null); + Bundle sessionExtras = new Bundle(); + sessionExtras.putString("key1", "value1"); + session.setSessionExtras(sessionExtras); + + MediaController controller = controllerTestRule.createController(session.getToken()); + + assertThat( + threadTestRule.getHandler().postAndSync(controller::getSessionExtras).getString("key1")) + .isEqualTo("value1"); + + session.cleanUp(); + } + @Test public void getAvailableCommands_emptyPlayerCommands_commandReleaseStillAvailable() throws Exception { @@ -772,7 +789,7 @@ public class MediaControllerTest { session.setAvailableCommands(builder.build(), Player.Commands.EMPTY); String testMediaId = "testMediaId"; - Rating testRating = new HeartRating(/* hasHeart= */ true); + Rating testRating = new HeartRating(/* isHeart= */ true); threadTestRule .getHandler() .postAndSync( diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCallbackTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCallbackTest.java index 7c9446b188..cca0b652f1 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCallbackTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCallbackTest.java @@ -234,6 +234,80 @@ public class MediaSessionCallbackTest { assertThat(remoteController.getAvailableCommands().contains(Player.COMMAND_RELEASE)).isTrue(); } + @Test + public void onConnect_connectionResultExtrasAreNull_usesSessionExtras() throws Exception { + MediaSession.Callback callback = + new MediaSession.Callback() { + @Override + public MediaSession.ConnectionResult onConnect( + MediaSession session, ControllerInfo controller) { + return new AcceptedResultBuilder(session) + .setAvailablePlayerCommands(Player.Commands.EMPTY) + .build(); + } + }; + Bundle sessionExtras = new Bundle(); + sessionExtras.putString("origin", "session"); + MediaSession session = + sessionTestRule.ensureReleaseAfterTest( + new MediaSession.Builder(context, player) + .setSessionExtras(sessionExtras) + .setCallback(callback) + .setId("onConnect_connectionResultExtrasAreNull_usesGlobalSessionExtras") + .build()); + + RemoteMediaController remoteController = + remoteControllerTestRule.createRemoteController(session.getToken()); + + assertThat(remoteController.getSessionExtras().getString("origin")).isEqualTo("session"); + } + + @Test + public void onConnect_connectionResultExtrasAreSet_usesControllerSpecificSessionExtras() + throws Exception { + MediaSession.Callback callback = + new MediaSession.Callback() { + @Override + public MediaSession.ConnectionResult onConnect( + MediaSession session, ControllerInfo controller) { + Bundle sessionExtras = new Bundle(); + sessionExtras.putString("origin", "controller"); + return new AcceptedResultBuilder(session) + .setSessionExtras(sessionExtras) + .setAvailablePlayerCommands(Player.Commands.EMPTY) + .build(); + } + }; + Bundle sessionExtras = new Bundle(); + sessionExtras.putString("origin", "session"); + MediaSession session = + sessionTestRule.ensureReleaseAfterTest( + new MediaSession.Builder(context, player) + .setSessionExtras(sessionExtras) + .setCallback(callback) + .setId("onConnect_connectionResultExtrasAreSet_usesControllerSpecificSessionExtras") + .build()); + + RemoteMediaController remoteController = + remoteControllerTestRule.createRemoteController(session.getToken()); + + assertThat(remoteController.getSessionExtras().getString("origin")).isEqualTo("controller"); + } + + @Test + public void onConnect_connectionResultDefault_emptySessionExtras() throws Exception { + MediaSession session = + sessionTestRule.ensureReleaseAfterTest( + new MediaSession.Builder(context, player) + .setId("onConnect_connectionResultDefault_emptySessionExtras") + .build()); + + RemoteMediaController remoteController = + remoteControllerTestRule.createRemoteController(session.getToken()); + + assertThat(remoteController.getSessionExtras().size()).isEqualTo(0); + } + @Test public void onPostConnect_afterConnected() throws Exception { CountDownLatch latch = new CountDownLatch(1); diff --git a/libraries/test_session_current/src/main/java/androidx/media3/session/MediaControllerProviderService.java b/libraries/test_session_current/src/main/java/androidx/media3/session/MediaControllerProviderService.java index e500a9497c..9067a45506 100644 --- a/libraries/test_session_current/src/main/java/androidx/media3/session/MediaControllerProviderService.java +++ b/libraries/test_session_current/src/main/java/androidx/media3/session/MediaControllerProviderService.java @@ -175,6 +175,11 @@ public class MediaControllerProviderService extends Service { }); } + @Override + public Bundle getSessionExtras(String controllerId) throws RemoteException { + return runOnHandler(mediaControllerMap.get(controllerId)::getSessionExtras); + } + @Override public void play(String controllerId) throws RemoteException { runOnHandler( diff --git a/libraries/test_session_current/src/main/java/androidx/media3/session/RemoteMediaController.java b/libraries/test_session_current/src/main/java/androidx/media3/session/RemoteMediaController.java index d7eb1bb1fc..a470ac7b2b 100644 --- a/libraries/test_session_current/src/main/java/androidx/media3/session/RemoteMediaController.java +++ b/libraries/test_session_current/src/main/java/androidx/media3/session/RemoteMediaController.java @@ -97,6 +97,10 @@ public class RemoteMediaController { return sessionTokenBundle == null ? null : SessionToken.fromBundle(sessionTokenBundle); } + public Bundle getSessionExtras() throws RemoteException { + return binder.getSessionExtras(controllerId); + } + public void play() throws RemoteException { binder.play(controllerId); }