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 1dae1d41f3b..d07801ea2a1 100644
--- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java
+++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java
@@ -938,20 +938,23 @@ protected MediaSessionServiceLegacyStub getLegacyBrowserService() {
         });
   }
 
-  /* package */ boolean onPlayRequested() {
+  /* package */ ListenableFuture<Boolean> onPlayRequested() {
     if (Looper.myLooper() != Looper.getMainLooper()) {
       SettableFuture<Boolean> playRequested = SettableFuture.create();
-      mainHandler.post(() -> playRequested.set(onPlayRequested()));
-      try {
-        return playRequested.get();
-      } catch (InterruptedException | ExecutionException e) {
-        throw new IllegalStateException(e);
-      }
+      mainHandler.post(
+          () -> {
+            try {
+              playRequested.set(onPlayRequested().get());
+            } catch (ExecutionException | InterruptedException e) {
+              playRequested.setException(new IllegalStateException(e));
+            }
+          });
+      return playRequested;
     }
     if (this.mediaSessionListener != null) {
-      return this.mediaSessionListener.onPlayRequested(instance);
+      return Futures.immediateFuture(this.mediaSessionListener.onPlayRequested(instance));
     }
-    return true;
+    return Futures.immediateFuture(true);
   }
 
   /**
@@ -962,83 +965,102 @@ protected MediaSessionServiceLegacyStub getLegacyBrowserService() {
    *
    * @param controller The controller requesting to play.
    */
-  /* package */ void handleMediaControllerPlayRequest(
+  /* package */ ListenableFuture<SessionResult> handleMediaControllerPlayRequest(
       ControllerInfo controller, boolean callOnPlayerInteractionFinished) {
-    if (!onPlayRequested()) {
-      // Request denied, e.g. due to missing foreground service abilities.
-      return;
-    }
-    boolean hasCurrentMediaItem =
-        playerWrapper.isCommandAvailable(Player.COMMAND_GET_CURRENT_MEDIA_ITEM)
-            && playerWrapper.getCurrentMediaItem() != null;
-    boolean canAddMediaItems =
-        playerWrapper.isCommandAvailable(COMMAND_SET_MEDIA_ITEM)
-            || playerWrapper.isCommandAvailable(COMMAND_CHANGE_MEDIA_ITEMS);
-    ControllerInfo controllerForRequest = resolveControllerInfoForCallback(controller);
-    Player.Commands playCommand =
-        new Player.Commands.Builder().add(Player.COMMAND_PLAY_PAUSE).build();
-    if (hasCurrentMediaItem || !canAddMediaItems) {
-      // No playback resumption needed or possible.
-      if (!hasCurrentMediaItem) {
-        Log.w(
-            TAG,
-            "Play requested without current MediaItem, but playback resumption prevented by"
-                + " missing available commands");
-      }
-      Util.handlePlayButtonAction(playerWrapper);
-      if (callOnPlayerInteractionFinished) {
-        onPlayerInteractionFinishedOnHandler(controllerForRequest, playCommand);
-      }
-    } else {
-      @Nullable
-      ListenableFuture<MediaItemsWithStartPosition> future =
-          checkNotNull(
-              callback.onPlaybackResumption(instance, controllerForRequest),
-              "Callback.onPlaybackResumption must return a non-null future");
-      Futures.addCallback(
-          future,
-          new FutureCallback<MediaItemsWithStartPosition>() {
-            @Override
-            public void onSuccess(MediaItemsWithStartPosition mediaItemsWithStartPosition) {
-              callWithControllerForCurrentRequestSet(
-                      controllerForRequest,
-                      () -> {
-                        MediaUtils.setMediaItemsWithStartIndexAndPosition(
-                            playerWrapper, mediaItemsWithStartPosition);
-                        Util.handlePlayButtonAction(playerWrapper);
-                        if (callOnPlayerInteractionFinished) {
-                          onPlayerInteractionFinishedOnHandler(controllerForRequest, playCommand);
-                        }
-                      })
-                  .run();
+    SettableFuture<SessionResult> sessionFuture = SettableFuture.create();
+    ListenableFuture<Boolean> playRequestedFuture = onPlayRequested();
+    playRequestedFuture.addListener(
+        () -> {
+          boolean playRequested;
+          try {
+            playRequested = playRequestedFuture.get();
+          } catch (ExecutionException | InterruptedException e) {
+            sessionFuture.setException(new IllegalStateException(e));
+            return;
+          }
+          if (!playRequested) {
+            // Request denied, e.g. due to missing foreground service abilities.
+            sessionFuture.set(new SessionResult(SessionResult.RESULT_ERROR_UNKNOWN));
+            return;
+          }
+          boolean hasCurrentMediaItem =
+              playerWrapper.isCommandAvailable(Player.COMMAND_GET_CURRENT_MEDIA_ITEM)
+                  && playerWrapper.getCurrentMediaItem() != null;
+          boolean canAddMediaItems =
+              playerWrapper.isCommandAvailable(COMMAND_SET_MEDIA_ITEM)
+                  || playerWrapper.isCommandAvailable(COMMAND_CHANGE_MEDIA_ITEMS);
+          ControllerInfo controllerForRequest = resolveControllerInfoForCallback(controller);
+          Player.Commands playCommand =
+              new Player.Commands.Builder().add(Player.COMMAND_PLAY_PAUSE).build();
+          if (hasCurrentMediaItem || !canAddMediaItems) {
+            // No playback resumption needed or possible.
+            if (!hasCurrentMediaItem) {
+              Log.w(
+                  TAG,
+                  "Play requested without current MediaItem, but playback resumption prevented by"
+                      + " missing available commands");
             }
-
-            @Override
-            public void onFailure(Throwable t) {
-              if (t instanceof UnsupportedOperationException) {
-                Log.w(
-                    TAG,
-                    "UnsupportedOperationException: Make sure to implement"
-                        + " MediaSession.Callback.onPlaybackResumption() if you add a"
-                        + " media button receiver to your manifest or if you implement the recent"
-                        + " media item contract with your MediaLibraryService.",
-                    t);
-              } else {
-                Log.e(
-                    TAG,
-                    "Failure calling MediaSession.Callback.onPlaybackResumption(): "
-                        + t.getMessage(),
-                    t);
-              }
-              // Play as requested even if playback resumption fails.
-              Util.handlePlayButtonAction(playerWrapper);
-              if (callOnPlayerInteractionFinished) {
-                onPlayerInteractionFinishedOnHandler(controllerForRequest, playCommand);
-              }
+            Util.handlePlayButtonAction(playerWrapper);
+            sessionFuture.set(new SessionResult(SessionResult.RESULT_SUCCESS));
+            if (callOnPlayerInteractionFinished) {
+              onPlayerInteractionFinishedOnHandler(controllerForRequest, playCommand);
             }
-          },
-          this::postOrRunOnApplicationHandler);
-    }
+          } else {
+            @Nullable
+            ListenableFuture<MediaItemsWithStartPosition> future =
+                checkNotNull(
+                    callback.onPlaybackResumption(instance, controllerForRequest),
+                    "Callback.onPlaybackResumption must return a non-null future");
+            Futures.addCallback(
+                future,
+                new FutureCallback<MediaItemsWithStartPosition>() {
+                  @Override
+                  public void onSuccess(MediaItemsWithStartPosition mediaItemsWithStartPosition) {
+                    callWithControllerForCurrentRequestSet(
+                            controllerForRequest,
+                            () -> {
+                              MediaUtils.setMediaItemsWithStartIndexAndPosition(
+                                  playerWrapper, mediaItemsWithStartPosition);
+                              Util.handlePlayButtonAction(playerWrapper);
+                              sessionFuture.set(new SessionResult(SessionResult.RESULT_SUCCESS));
+                              if (callOnPlayerInteractionFinished) {
+                                onPlayerInteractionFinishedOnHandler(
+                                    controllerForRequest, playCommand);
+                              }
+                            })
+                        .run();
+                  }
+
+                  @Override
+                  public void onFailure(Throwable t) {
+                    if (t instanceof UnsupportedOperationException) {
+                      Log.w(
+                          TAG,
+                          "UnsupportedOperationException: Make sure to implement"
+                              + " MediaSession.Callback.onPlaybackResumption() if you add a media"
+                              + " button receiver to your manifest or if you implement the recent"
+                              + " media item contract with your MediaLibraryService.",
+                          t);
+                    } else {
+                      Log.e(
+                          TAG,
+                          "Failure calling MediaSession.Callback.onPlaybackResumption(): "
+                              + t.getMessage(),
+                          t);
+                    }
+                    // Play as requested even if playback resumption fails.
+                    Util.handlePlayButtonAction(playerWrapper);
+                    sessionFuture.set(new SessionResult(SessionResult.RESULT_SUCCESS));
+                    if (callOnPlayerInteractionFinished) {
+                      onPlayerInteractionFinishedOnHandler(controllerForRequest, playCommand);
+                    }
+                  }
+                },
+                this::postOrRunOnApplicationHandler);
+          }
+        },
+        this::postOrRunOnApplicationHandler);
+    return sessionFuture;
   }
 
   private void setLegacyMediaButtonPreferences(
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 affbc7de1f3..31d40b0fd53 100644
--- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java
+++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java
@@ -390,9 +390,21 @@ public void onPrepareFromUri(@Nullable Uri mediaUri, @Nullable Bundle extras) {
   public void onPlay() {
     dispatchSessionTaskWithPlayerCommand(
         COMMAND_PLAY_PAUSE,
-        controller ->
-            sessionImpl.handleMediaControllerPlayRequest(
-                controller, /* callOnPlayerInteractionFinished= */ true),
+        controller -> {
+          try {
+            // Call get() to make sure the call is blocking.
+            SessionResult result =
+                sessionImpl
+                    .handleMediaControllerPlayRequest(
+                        controller, /* callOnPlayerInteractionFinished= */ true)
+                    .get();
+            if (result.resultCode != RESULT_SUCCESS) {
+              Log.w(TAG, "onPlay() failed: " + result + " (from: " + controller + ")");
+            }
+          } catch (ExecutionException | InterruptedException e) {
+            throw new IllegalStateException("Unexpected exception in onPlay() of " + controller, e);
+          }
+        },
         sessionCompat.getCurrentControllerInfo(),
         /* callOnPlayerInteractionFinished= */ false);
   }
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 98461818968..213be425a3b 100644
--- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java
+++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java
@@ -732,15 +732,10 @@ public void playForControllerInfo(ControllerInfo controller, int sequenceNumber)
         controller,
         sequenceNumber,
         COMMAND_PLAY_PAUSE,
-        sendSessionResultSuccess(
-            player -> {
-              @Nullable MediaSessionImpl impl = sessionImpl.get();
-              if (impl == null || impl.isReleased()) {
-                return;
-              }
-              impl.handleMediaControllerPlayRequest(
-                  controller, /* callOnPlayerInteractionFinished= */ false);
-            }));
+        sendSessionResultWhenReady(
+            (session, theController, sequenceId) ->
+                session.handleMediaControllerPlayRequest(
+                    theController, /* callOnPlayerInteractionFinished= */ false)));
   }
 
   @Override