Skip to content

Fix session command queue stall that detaches Now Playing (fixes #31)#32

Merged
chamika merged 2 commits into
mainfrom
fix-issue-31-session-queue-stall
Jul 5, 2026
Merged

Fix session command queue stall that detaches Now Playing (fixes #31)#32
chamika merged 2 commits into
mainfrom
fix-issue-31-session-queue-stall

Conversation

@chamika

@chamika chamika commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Fixes #31

Root cause

Media3 serializes controller commands per controller: ConnectedControllersManager.flushCommandQueue() only advances when the previous callback's ListenableFuture completes. Four callbacks in DashTuneSessionCallback could return futures that never complete — onAddMediaItems, onSetMediaItems, onGetItem, and onPlaybackResumption had no timeout guards (unlike onGetChildren/onSearch, which already use an 8s withTimeoutOrNull).

Resolving a large uncached playlist chains one Jellyfin API call per track (each retried ×3 with backoff in JellyfinMediaTree.retryOnFailure), so on flaky car connectivity a single onSetMediaItems could hang effectively forever. One wedged future permanently stalls the AAOS Media Center's command queue, which explains every reported symptom:

  • audio keeps playing (ExoPlayer's internal playback thread is unaffected)
  • Now Playing freezes on the previous track (metadata updates never reach the controller)
  • playlist switches and search are silently swallowed (queued behind the stuck command)
  • only an infotainment restart recovers

Changes

  • DashTuneSessionCallback: all four unguarded callbacks now complete within 8–10s. onAddMediaItems/onSetMediaItems complete exceptionally on timeout/failure — verified in media3-session 1.9.2 sources that Media3 drops the failed command and keeps the queue alive (the current playlist keeps playing). onGetItem returns the existing error-with-Retry result; onPlaybackResumption falls back to an empty restore. The onSetMediaItems body was extracted verbatim into resolveSetMediaItems() to sit under the guard.
  • DashTuneMusicService: explicit Jellyfin SDK HTTP timeouts (5s connect / 7s request / 7s socket) instead of the 30s defaults, so a single call always fails before the callback guard fires.
  • JellyfinMediaTree.retryOnFailure: 10s per-attempt hard cap as defense in depth for all 9 tree network paths, and rethrows CancellationException instead of swallowing it (which would have broken the outer guards).
  • New test JellyfinApiTimeoutTest: pins the timeout config against a server that accepts TCP connections but never responds — passes in 7.2s, exactly the configured request timeout.

Also includes the v1.2.9(25) release commit.

Testing

  • ./gradlew :automotive:assembleDebug
  • ./gradlew :automotive:testDebugUnitTest ✅ (including the new timeout test)
  • Manual repro suggestion: start playback against a real server, then replace it with nc -lk 8096 > /dev/null (accepts connections, never responds) and switch playlists — commands should fail within ~10s with the session staying responsive, and recover without an infotainment restart once the server is back.

🤖 Generated with Claude Code

chamika and others added 2 commits July 5, 2026 09:03
Media3 serializes controller commands behind each session callback's
ListenableFuture; a future that never completes permanently wedges the
controller's command queue. onAddMediaItems/onSetMediaItems/onGetItem/
onPlaybackResumption had no timeout guards, so resolving a large uncached
playlist on flaky connectivity (one API call per track, each retried x3)
could hang forever - Now Playing froze on the old track while playback
continued, and playlist switches/search were ignored until an
infotainment restart.

- Guard all queue-mutating callbacks with timeouts so every future
  completes; failures complete exceptionally, which Media3 drops while
  keeping the queue alive
- Configure explicit Jellyfin SDK HTTP timeouts (5s connect / 7s
  request / 7s socket)
- Add a 10s per-attempt hard cap in JellyfinMediaTree.retryOnFailure and
  stop it swallowing CancellationException from outer guards
- Add JellyfinApiTimeoutTest pinning the timeout config against a server
  that accepts connections but never responds

Co-Authored-By: Claude Fable 5 <[email protected]>
Fix the frozen Now Playing screen that required an infotainment restart, plus QuickConnect sign-in crashes

Full release notes:

- Fix the intermittent freeze where Now Playing kept showing a previous song while other tracks played, and playlists, search, and browsing stopped responding until the infotainment system was restarted — the app now recovers on its own within seconds on flaky connections
- Fix QuickConnect showing a wrong sign-in code when the code had leading zeros, and a crash on short codes
- Fix a crash when QuickConnect polling hit a network error or an expired code — it now shows "Unavailable" instead
- Fix a possible crash when a connected controller sent an unsupported rating type while favouriting
- Fix playback starting at the wrong track when the cached library was out of date
- More robust handling of corrupted settings values and low-level stability fixes (thread-safe album art lookups, coroutine cleanup on service shutdown, safer account and keyboard handling)
- Security hardening: app data is no longer included in device backups
@chamika chamika merged commit e0bca50 into main Jul 5, 2026
1 check passed
@chamika chamika deleted the fix-issue-31-session-queue-stall branch July 5, 2026 10:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The player libraries detaches with Now Playing screen and cannot switch to a different playlist

1 participant