Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
* Muxers:
* IMA extension:
* Session:
* Fix bug where calling `setSessionExtras` from the main thread when
running the player from a different application thread then the main
thread caused an `IllegalStateException`
([#2265](https://github.com/androidx/media/pull/2265)).
* UI:
* Downloads:
* OkHttp extension:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1135,7 +1135,7 @@ public void onSessionExtrasChanged(int seq, Bundle sessionExtras) {
PlayerWrapper playerWrapper = sessionImpl.getPlayerWrapper();
playerWrapper.setLegacyExtras(sessionExtras);
sessionCompat.setExtras(playerWrapper.getLegacyExtras());
sessionCompat.setPlaybackState(sessionImpl.getPlayerWrapper().createPlaybackStateCompat());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you point me to the line where the IllegalStateException is originating?

I can't find a line where this would be caused by sessionCompat.setPlaybackState(state). Similarly, for playerWrapper.createPlaybackStateCompat(). I probably just haven't find it.

Best would be a stack trace of when this IllegalStateException happens.

I'm asking because if that would be an issue then there is a general problem in other methods around that call site I think. We'd need to investigate this some further, so a stack trace would be very helpful.

You say this is for when calling from a thread different than the main thread. The session is using the application thread which which the player was build [1], so if there was an issue regarding main thread vs. application thread, then we either have a more fundamental problem to discover, or then the player/session may not have been setup properly in the app.

I'd be great to clarify this first to see whether and where we need changes.

[1] https://github.com/androidx/media/blob/release/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java#L202-L203

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @marcbaechinger,

the full stack trace is:


2025-05-16 11:00:55.166  4788-4788  AndroidRuntime          androidx.media3.demo.session         E  FATAL EXCEPTION: main
                                                                                                    Process: androidx.media3.demo.session, PID: 4788
                                                                                                    java.lang.RuntimeException: Unable to create service androidx.media3.demo.session.PlaybackService: java.lang.IllegalStateException
                                                                                                    	at android.app.ActivityThread.handleCreateService(ActivityThread.java:5295)
                                                                                                    	at android.app.ActivityThread.-$$Nest$mhandleCreateService(Unknown Source:0)
                                                                                                    	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2597)
                                                                                                    	at android.os.Handler.dispatchMessage(Handler.java:110)
                                                                                                    	at android.os.Looper.loopOnce(Looper.java:248)
                                                                                                    	at android.os.Looper.loop(Looper.java:338)
                                                                                                    	at android.app.ActivityThread.main(ActivityThread.java:9067)
                                                                                                    	at java.lang.reflect.Method.invoke(Native Method)
                                                                                                    	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:593)
                                                                                                    	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:932)
                                                                                                    Caused by: java.lang.IllegalStateException
                                                                                                    	at androidx.media3.common.util.Assertions.checkState(Assertions.java:85)
                                                                                                    	at androidx.media3.session.PlayerWrapper.verifyApplicationThread(PlayerWrapper.java:1294)
                                                                                                    	at androidx.media3.session.PlayerWrapper.getPlayerError(PlayerWrapper.java:253)
                                                                                                    	at androidx.media3.session.PlayerWrapper.createPlaybackStateCompat(PlayerWrapper.java:1049)
                                                                                                    	at androidx.media3.session.MediaSessionLegacyStub$ControllerLegacyCbForBroadcast.onSessionExtrasChanged(MediaSessionLegacyStub.java:1150)
                                                                                                    	at androidx.media3.session.MediaSessionImpl.lambda$setSessionExtras$8(MediaSessionImpl.java:565)
                                                                                                    	at androidx.media3.session.MediaSessionImpl$$ExternalSyntheticLambda35.run(D8$$SyntheticClass:0)
                                                                                                    	at androidx.media3.session.MediaSessionImpl.dispatchRemoteControllerTaskWithoutReturn(MediaSessionImpl.java:1143)
                                                                                                    	at androidx.media3.session.MediaLibrarySessionImpl.dispatchRemoteControllerTaskWithoutReturn(MediaLibrarySessionImpl.java:366)
                                                                                                    	at androidx.media3.session.MediaSessionImpl.setSessionExtras(MediaSessionImpl.java:564)
                                                                                                    	at androidx.media3.session.MediaSession.setSessionExtras(MediaSession.java:1218)
                                                                                                    	at androidx.media3.demo.session.DemoPlaybackService.initializeSessionAndPlayer(DemoPlaybackService.kt:172)
                                                                                                    	at androidx.media3.demo.session.DemoPlaybackService.onCreate(DemoPlaybackService.kt:122)
                                                                                                    	at android.app.ActivityThread.handleCreateService(ActivityThread.java:5282)
                                                                                                    	at android.app.ActivityThread.-$$Nest$mhandleCreateService(Unknown Source:0) 
                                                                                                    	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2597) 
                                                                                                    	at android.os.Handler.dispatchMessage(Handler.java:110) 
                                                                                                    	at android.os.Looper.loopOnce(Looper.java:248) 
                                                                                                    	at android.os.Looper.loop(Looper.java:338) 
                                                                                                    	at android.app.ActivityThread.main(ActivityThread.java:9067) 
                                                                                                    	at java.lang.reflect.Method.invoke(Native Method) 
                                                                                                    	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:593) 
                                                                                                    	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:932) 

I have edited the demo app to create the player on a different thread. However, the MediaSession will still be created on the main thread, and setSessionExtras will be called on the main thread, too.

I have thought about wheter it is just incorrect to call this on the main thread if the application thread is not the main thread, but:

  • Some methods in MediaSession do say in the javadoc they must be called on the application thread. This is not one of them.
  • The method javadoc specifies it is asynchronous. Running a part of the method on another thread will not break the method semantics.
  • Almost every other method in MediaSessionLegacyStub uses updateLegacySessionPlaybackState which is a postOrRun variant of this. Only onSessionExtrasChanged does not use postOrRun which leads to this crash. Aligning this with the other methods makes the crash go away.

Based on the fact that:

  • Other methods use the same pattern and hence are fine to call from any thread
  • This method doesn't seem to be intended to enforce application thread as it does not enforce it itself
  • The javadoc doesn't mention thread restrictions either

I think this makes sense to fix like this, instead of calling setSessionExtras on another thread. Many generic methods like the constructor of MediaSession work on any thread, and it makes the app code simpler.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Many thanks for the response and the stack trace to help me understand the issue better. It's coming from calling methods on the player in createPlaybackStateCompat.

I figure then we'd have the same issue when calling for instance MediaSession.sendError() that is calling through to ControllerLegacyCbForBroadcast.onError() which then calls playerWrapper.createPlaybackStateCompat() from the main thread as well.

Ok, thanks for reporting. I'm going to take that PR, send it for review and then will file an internal bug, so we can look into other possible cases we have to cover. Thank you.

updateLegacySessionPlaybackState(sessionImpl.getPlayerWrapper());
}

@Override
Expand Down