Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IndexOutOfBoundsException is occasionally raised in MediaControllerImplLegacy.initializeLegacyPlaylist() #241

Closed
1 task
h6ah4i opened this issue Jan 14, 2023 · 3 comments
Assignees
Labels

Comments

@h6ah4i
Copy link
Contributor

h6ah4i commented Jan 14, 2023

Media3 Version

1.0.0-beta03

Devices that reproduce the issue

from Crashlytics error reports:

  • OPPO Reno7 Z 5G running Android 13
  • Galaxy A13 running Android 12
  • moto g(9) power running Android 11
  • Redmi Note 8T running Android 10
  • moto e6 play running Android 9
  • Galaxy J7(2016) running Android 8.1.0
  • etc...

Devices that do not reproduce the issue

I think this issue is not device-specific.

Reproducible in the demo app?

No

Reproduction steps

Sorry, I have not been able to reproduce it myself. I can see crash reports on the Crashlytics dashboard for this issue.

  1. Create a MediaController that is connected to the other app like YouTube, Spotify, etc...
  2. Call MediaController#prepare()
  3. Sometimes it gets IndexOutOfBoundsException

Expected result

The MediaController#prepare() should not raise an IndexOutOfBoundsException.

Actual result

Gets IndexOutOfBoundsException on MediaController#prepare() sometimes.

Example 1

Target MediaSession app package: com.google.android.youtube.

Caused by java.lang.IndexOutOfBoundsException: index (0) must be less than size (0)
       at com.google.common.base.Preconditions.checkElementIndex(Preconditions.java:1355)
       at com.google.common.base.Preconditions.checkElementIndex(Preconditions.java:1337)
       at com.google.common.collect.RegularImmutableList.get(RegularImmutableList.java:82)
       at androidx.media3.session.QueueTimeline.getQueueId(QueueTimeline.java:124)
       at androidx.media3.session.MediaControllerImplLegacy.initializeLegacyPlaylist(MediaControllerImplLegacy.java:1286)
       at androidx.media3.session.MediaControllerImplLegacy.prepare(MediaControllerImplLegacy.java:291)
       at androidx.media3.session.MediaController.prepare(MediaController.java:552)
    ...

Example 2

Target MediaSession app package: in.krosbits.musicolet

Fatal Exception: java.lang.IndexOutOfBoundsException: index (147) must be less than size (147)
       at com.google.common.base.Preconditions.checkElementIndex(Preconditions.java:1355)
       at com.google.common.base.Preconditions.checkElementIndex(Preconditions.java:1337)
       at com.google.common.collect.RegularImmutableList.get(RegularImmutableList.java:82)
       at androidx.media3.session.QueueTimeline.getQueueId(QueueTimeline.java:124)
       at androidx.media3.session.MediaControllerImplLegacy.initializeLegacyPlaylist(MediaControllerImplLegacy.java:1286)
       at androidx.media3.session.MediaControllerImplLegacy.prepare(MediaControllerImplLegacy.java:291)
       at androidx.media3.session.MediaController.prepare(MediaController.java:552)
    ...

I can see the same issue for other apps like:

  • com.spotify.music
  • deezer.android.app
  • com.musixmatch.android.lyrify
  • com.audiomack
  • etc...

Media

n/a

Bug Report

  • You will email the zip file produced by adb bugreport to [email protected] after filing this issue.
@h6ah4i
Copy link
Contributor Author

h6ah4i commented Jan 14, 2023

NOTE: I am using a slightly modified version of 1.0.0-beta03. I have added some debug logging code to the library, so the line numbers in the stack traces are not identical to the original 1.0.0-beta03.

@marcbaechinger marcbaechinger self-assigned this Jan 18, 2023
@marcbaechinger
Copy link
Contributor

I tried to repro this when connecting against the session of YouTube Music but this wasn't successful (meaning it worked without problems :)).

However, I was able to create my own legacy session and set it up to produce the exception and got the same stack trace as above.

I can for instance repro with a session without a queue and in state PlaybackStateCompat.STATE_NONE.

mediaSessionCompat = new MediaSessionCompat(this, "test-session-m3");
PlaybackStateCompat playbackStateCompat = new PlaybackStateCompat.Builder()
    .setState(PlaybackStateCompat.STATE_NONE, /* position= */ 200_000, /* speed= */ 1.0f)
    .setActiveQueueItemId(1)
    .build();
mediaSessionCompat.setPlaybackState(playbackStateCompat);
mediaSessionCompat.setMetadata(
new MediaMetadataCompat.Builder()
    .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, "Supersong")
    .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, "Subtitle")
    .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, "Singersongwriter")
    .build());
mediaSessionCompat.setQueue(null);
mediaSessionCompat.setActive(true);

Calling prepare() then caused this exemption like you report.

Process: com.example.myapplication, PID: 9098
java.lang.IndexOutOfBoundsException: index (0) must be less than size (0)
at com.google.common.base.Preconditions.checkElementIndex(Preconditions.java:1355)
at com.google.common.base.Preconditions.checkElementIndex(Preconditions.java:1337)
at com.google.common.collect.RegularImmutableList.get(RegularImmutableList.java:82)
at androidx.media3.session.QueueTimeline.getQueueId(QueueTimeline.java:124)
at androidx.media3.session.MediaControllerImplLegacy.initializeLegacyPlaylist(MediaControllerImplLegacy.java:1289)
at androidx.media3.session.MediaControllerImplLegacy.prepare(MediaControllerImplLegacy.java:297)
at androidx.media3.session.MediaController.prepare(MediaController.java:577)
at com.example.myapplication.MainActivity.lambda$setSessionToken$1$com-example-myapplication-MainActivity(MainActivity.java:159)
at com.example.myapplication.MainActivity$$ExternalSyntheticLambda1.run(Unknown Source:4)
at android.os.Handler.handleCallback(Handler.java:942)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7872)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)

I think there are several points we should look at.

First we apparently need to be more careful in assuming the state of a session of another app is valid. Further we should respect the settings and advertised actions of a session as this can make the controller more robust as it would probably not attempt to call some logic that assumes a certain state of the session.

When I for instance look at the sessions of YouTube or YT Music, I see that they don't support ACTION_PREPARE_FROM_MEDIA_ID or FLAG_HANDLES_QUEUE_COMMANDS. It's probably quite common that apps like YT advertise their session in a rather read-only way or that allow play and pause but probably not setting random media items. Currently MediaControllerImplLegacy tries this either way, which we probably should revisit. Then at least for this case, even if the call to prepare() wouldn't throw, the operation would be a no-op.

@h6ah4i
Copy link
Contributor Author

h6ah4i commented Apr 14, 2023

I have confirmed this issue has been fixed in media3 v1.0.0. Thanks 🎉

@h6ah4i h6ah4i closed this as completed Apr 14, 2023
@androidx androidx locked and limited conversation to collaborators Jun 14, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

2 participants