Skip to content

Attempt to fix ExoPlayer crash#5380

Open
sztomek wants to merge 3 commits into
mainfrom
fix/attempt-exoplayer-classcastexception
Open

Attempt to fix ExoPlayer crash#5380
sztomek wants to merge 3 commits into
mainfrom
fix/attempt-exoplayer-classcastexception

Conversation

@sztomek

@sztomek sztomek commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Description

Noticed this crash in 8.13-rc-1, see ticket for details.

Claude's investigation:
ExoPlayer.seekTo() internally accesses playbackInfo.periodId.periodUid and passes it to PlaylistTimeline.getPeriodByUid(), which casts it to Pair. If the UID is a plain Object (not yet wrapped in a Pair by the PlaylistTimeline), it throws ClassCastException.
Why it happens: SimplePlayer.prepare() calls player.setMediaSource(source) then player.prepare(). The prepare() call is asynchronous — it kicks off work on ExoPlayer's internal playback thread. But our code immediately sets prepared = true and proceeds to playIfAllowed(), which calls player.seekTo(). At this point, ExoPlayer's internal playbackInfo may not yet have consistent period UIDs — the internal thread hasn't finished processing, and the masking state between the application thread and the playback thread can be momentarily inconsistent. We're seeking before the player's timeline is actually ready for timeline-dependent operations.

How the fix works:

  1. Deferred seek: Before seeking, we check player.playbackState. If it's STATE_READY or STATE_BUFFERING, the timeline is initialized and seeking is safe. If it's still STATE_IDLE (the state right after prepare() is called but before the internal thread has processed it), we register a one-shot listener and seek
    when the player transitions to BUFFERING/READY. This respects Media3's async prepare contract.
  2. Try-catch safety net: Even with the deferred seek, there may be other code paths where a seek reaches the ExoPlayer at a bad time (e.g., a Bluetooth controller sending a seek command through the MediaSession during player initialization). The ClassCastException catch prevents those from crashing.

UPDATE
after a discussion with @geekygecko we have decided not to include this fix in 8.13 as it only affected 2 users in the beta cycle. i will change target to main and milestone to 8.14

Fixes PCDROID-591 https://linear.app/a8c/issue/PCDROID-591/attempt-to-fix-classcastexception-crash-of-exoplayer

Testing Instructions

I was not able to reproduce it :(
Just smoke test playback, seeking, resuming from previous position on cold app start.

Checklist

  • If this is a user-facing change, I have added an entry in CHANGELOG.md
  • Ensure the linter passes (./gradlew spotlessApply to automatically apply formatting/linting)
  • I have considered whether it makes sense to add tests for my changes
  • All strings that need to be localized are in modules/services/localization/src/main/res/values/strings.xml
  • Any jetpack compose components I added or changed are covered by compose previews
  • I have updated (or requested that someone edit) the spreadsheet to reflect any new or changed analytics.

@sztomek sztomek added this to the 8.13 ❄️ milestone Jun 3, 2026
Copilot AI review requested due to automatic review settings June 3, 2026 20:07
@sztomek sztomek requested a review from a team as a code owner June 3, 2026 20:07
@sztomek sztomek added the [Type] Bug Not functioning as intended. label Jun 3, 2026
@sztomek sztomek requested review from geekygecko and removed request for a team June 3, 2026 20:07
@sztomek sztomek added the [Area] Playback Episode playback issue label Jun 3, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR attempts to prevent a Media3/ExoPlayer ClassCastException crash during seekTo() by deferring seeks until the player’s timeline is initialized (READY/BUFFERING) and adding a defensive try/catch around seekTo().

Changes:

  • Defer seeks when ExoPlayer is still in STATE_IDLE, performing the seek once playback reaches STATE_BUFFERING/STATE_READY.
  • Add a ClassCastException catch around ExoPlayer.seekTo() to avoid crashing on the reported Media3 timeline race.

}
}
currentPlayer.addListener(listener)
if (currentPlayer.isReadyForSeek()) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could there be an issue if the user changes episodes before isReadyForSeek is true?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

When the user changes episodes, handleStop() calls player.stop() which synchronously transitions the player to STATE_IDLE. The listener catches STATE_IDLE (line 148-149) and removes itself without performing the seek. This cleanup happens before release() is called, so the listener never fires a stale seek on the old player instance.
So in theory everything should be fine.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

as per or discussion, i made another change to only attach the listener when the player is idle 211872d

Copilot AI review requested due to automatic review settings June 5, 2026 07:53
@sztomek sztomek changed the base branch from release/8.13 to main June 5, 2026 07:55
@sztomek sztomek modified the milestones: 8.13 ❄️, 8.14 Jun 5, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

@sztomek sztomek force-pushed the fix/attempt-exoplayer-classcastexception branch from 211872d to dec0cbf Compare June 5, 2026 07:58
@wpmobilebot wpmobilebot modified the milestones: 8.14, 8.15 Jun 8, 2026
@wpmobilebot

Copy link
Copy Markdown
Collaborator

Version 8.14 has now entered code-freeze, so the milestone of this PR has been updated to 8.15.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Area] Playback Episode playback issue [Type] Bug Not functioning as intended.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants