diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 6324295640..bb663b186b 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -94,6 +94,8 @@ items are available for both, `MediaBrowser` and `MediaController`. See Custom Browse actions of AAOS. + * Fix bug where a Media3 controller was sometimes unable to let a session + app start a foreground service after requesting `play()`. * UI: * Make the stretched/cropped video in `PlayerView`-in-Compose-`AndroidView` workaround opt-in, due to issues 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 caeb9c19ed..ba171466e9 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaConstants.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaConstants.java @@ -497,6 +497,8 @@ public final class MediaConstants { "androidx.media3.session.SESSION_COMMAND_ON_CAPTIONING_ENABLED_CHANGED"; /* package */ static final String SESSION_COMMAND_REQUEST_SESSION3_TOKEN = "androidx.media3.session.SESSION_COMMAND_REQUEST_SESSION3_TOKEN"; + /* package */ static final String SESSION_COMMAND_MEDIA3_PLAY_REQUEST = + "androidx.media3.session.SESSION_COMMAND_MEDIA3_PLAY_REQUEST"; /* package */ static final String ARGUMENT_CAPTIONING_ENABLED = "androidx.media3.session.ARGUMENT_CAPTIONING_ENABLED"; 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 c1e1c95699..e59c0bb81d 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java @@ -137,6 +137,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; @Nullable private TextureView videoTextureView; private Size surfaceSize; @Nullable private IMediaSession iSession; + @Nullable private android.media.session.MediaController platformController; private long currentPositionMs; private long lastSetPlayWhenReadyCalledTimeMs; @Nullable private PlayerInfo pendingPlayerInfo; @@ -405,6 +406,13 @@ import org.checkerframework.checker.nullness.qual.NonNull; return; } + if (Util.SDK_INT >= 31 && platformController != null) { + // Ensure the platform session gets allow-listed to start a foreground service after receiving + // the play command. + platformController.sendCommand( + MediaConstants.SESSION_COMMAND_MEDIA3_PLAY_REQUEST, /* args= */ null, /* cb= */ null); + } + dispatchRemoteSessionTaskWithPlayerCommand( (iSession, seq) -> iSession.play(controllerStub, seq)); @@ -2644,6 +2652,9 @@ import org.checkerframework.checker.nullness.qual.NonNull; playerInfo = result.playerInfo; MediaSession.Token platformToken = result.platformToken == null ? token.getPlatformToken() : result.platformToken; + if (platformToken != null) { + platformController = new android.media.session.MediaController(context, platformToken); + } try { // Implementation for the local binder is no-op, // so can be used without worrying about deadlock. 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 fccc3a49b4..1019e04351 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java @@ -280,8 +280,11 @@ import org.checkerframework.checker.initialization.qual.Initialized; @Override public void onCommand(String commandName, @Nullable Bundle args, @Nullable ResultReceiver cb) { checkStateNotNull(commandName); - if (TextUtils.equals(MediaConstants.SESSION_COMMAND_REQUEST_SESSION3_TOKEN, commandName) - && cb != null) { + if (commandName.equals(MediaConstants.SESSION_COMMAND_MEDIA3_PLAY_REQUEST)) { + // Ignore, no need to handle this command here. + return; + } + if (commandName.equals(MediaConstants.SESSION_COMMAND_REQUEST_SESSION3_TOKEN) && cb != null) { cb.send(RESULT_SUCCESS, sessionImpl.getToken().toBundle()); return; }