Skip to content

fix: album art missing for same-album tracks over Bluetooth on Tesla (#470)#719

Open
chunjiw wants to merge 2 commits into
eddyizm:developmentfrom
chunjiw:fix/tesla-art
Open

fix: album art missing for same-album tracks over Bluetooth on Tesla (#470)#719
chunjiw wants to merge 2 commits into
eddyizm:developmentfrom
chunjiw:fix/tesla-art

Conversation

@chunjiw
Copy link
Copy Markdown

@chunjiw chunjiw commented May 30, 2026

Problem

On a Tesla (phone paired over Bluetooth), album art is shown only for the first track of an album. Every following track on the same album shows no art. Switching to a track from a different album restores the art; returning to the same album drops it again. Reproducible every time.

Root cause

Album art reaches the car over AVRCP/BIP, which the app feeds indirectly through Media3's MediaSession. On a track change, MediaSessionLegacyStub.updateMetadataIfChanged loads the artwork via a BitmapLoader and pushes metadata to the legacy/AVRCP bridge roughly like this (media3 1.8.0, L1628–1704):

Bitmap bitmap = null;
if (bitmapFuture.isDone()) {
    bitmap = bitmapFuture.get();
} else {
    bitmapFuture.addListener(() -> setMetadata(..., bitmapFuture.get())); // pushed later
}
setMetadata(..., bitmap); // fires now, possibly with a null bitmap

The default BitmapLoader always resolves on a background thread, so isDone() is false when Media3 checks it. Every track change therefore pushes metadata twice: once immediately with a null bitmap, then again with the real bitmap once it finishes loading.

  • Different album: the follow-up bitmap differs from what the car last had, so Tesla redraws and art appears.
  • Same album: the follow-up bitmap is byte-identical to the previous track's. After the initial null push, the car's dedup (AVRCP image-handle caching / BIP) treats the identical bytes as "nothing changed" and draws nothing.

For comparison, DSub2000 (a Subsonic client that works on Tesla) attaches the bitmap synchronously per track and never produces the null-first push.

Fix

Add a custom BitmapLoader (SyncBitmapLoader) so that isDone() is true on the first check. Media3 then takes its synchronous branch and the first setMetadata already carries the bitmap — the intermediate null state never leaves the phone.

  • loadBitmap(uri) checks an LRU cache and returns an already-completed future on a hit; falls back to async loading on a miss.
  • decodeBitmap(bytes) decodes inline and returns an immediate future.
  • The cache is prewarmed from onTimelineChanged: when the queue changes, artwork for every queued item is fetched in the background, so the bitmap is almost always cached before the user advances to the next track.

Registered via MediaLibrarySession.Builder.setBitmapLoader(...).

The change is additive and does not modify any Media3 internals. The library's null-first code path still exists; the inputs we hand Media3 simply land in the isDone() == true branch, so that path is not reached.

Scope

  • New file: SyncBitmapLoader.kt
  • BaseMediaService.kt (+15 lines): build and register the loader, prewarm in onTimelineChanged, shut down the executor in onDestroy.

Testing

Tested on a Tesla Model 3 over Bluetooth for several weeks. Album art now displays for every track, including consecutive same-album tracks. No regressions observed for notification, lock-screen, or Android Auto artwork.

Fixes #470.

…dyizm#470)

Media3's MediaSessionLegacyStub pushes track metadata to the legacy/AVRCP
bridge with a null bitmap whenever the BitmapLoader future isn't already
done, then pushes again once the bitmap finishes loading. The default
BitmapLoader always resolves asynchronously, so this null-first push happens
on every track change. Tesla redraws on a different-album bitmap but dedups
the byte-identical bitmap of a same-album track, leaving those tracks with
no art.

Add SyncBitmapLoader, which returns an already-completed future on LRU cache
hits (and decodes inline), so Media3 takes its synchronous branch and the
first setMetadata already carries the bitmap. Queue artwork is prewarmed in
onTimelineChanged so the cache is populated before the user advances.

Registered via MediaLibrarySession.Builder.setBitmapLoader(); the change is
additive and modifies no Media3 internals.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

[BUG] - On Tesla Model 3, if a track has the same album art as the previous track, the art would not show up on car player

2 participants