Remove the main thread restriction of `AdsMediaSource`
Maintainers usually reply within 4 days
@marcbaechinger is already working on this.
Since Aug 18, 2026.
Assessment
This issue has not been assessed yet.
Description
Version
Media3 1.11.0
Description
AdsMediaSource drives its AdsLoader callbacks (start(), stop(), handlePrepareComplete(), handlePrepareError()) through a Handler hardcoded to Looper.getMainLooper():
// AdsMediaSource constructor
mainHandler = new Handler(Looper.getMainLooper());
// releaseSourceInternal()
mainHandler.post(() -> adsLoader.stop(/* adsMediaSource= */ this, componentListener));
But ExoPlayer.Builder.setLooper(Looper) documents a different thread as the one the player expects to be used for all calls:
Sets the Looper that must be used for all calls to the player and that is used to call listeners on.
and ExoPlayerImpl's public API enforces exactly that looper via verifyApplicationThread(), e.g. in removeListener():
public void removeListener(Listener listener) {
verifyApplicationThread(); // throws if Thread.currentThread() != getApplicationLooper().getThread()
listeners.remove(checkNotNull(listener));
}
When an app builds its player with a non-default looper via setLooper(...) — a documented, supported way to keep player calls off the main thread — these two "the thread you're supposed to touch the player on" notions diverge. Any AdsLoader whose stop() calls player.removeListener(...) (the natural, symmetric counterpart to an addListener() in start()) will crash:
java.lang.IllegalStateException: Player is accessed on the wrong thread.
Current thread: 'main'
Expected thread: ''
Root cause
AdsMediaSource.mainHandler is unconditionally new Handler(Looper.getMainLooper()) — both in the current release and on main, unchanged across multiple major versions.
addListener() is documented and implemented to be safe from any thread (no verifyApplicationThread() call), which hides the mismatch during start().
removeListener() does call verifyApplicationThread(), so it's the first place the mismatch surfaces — but it's a symptom, not the cause: any thread-checked Player/ExoPlayer method called from code AdsMediaSource dispatches (start, stop, handlePrepareComplete, handlePrepareError) has the same exposure.
Devices that reproduce the issue
Was reproduced on a proprietary Android device.
Reproducible in the demo app?
No
Reproduction steps
- Build a player with a non-default looper: new ExoPlayer.Builder(context, ...).setLooper(customLooper).build(), where customLooper belongs to a dedicated thread, not the main thread.
- Wrap a MediaSource with HlsInterstitialsAdsLoader.AdsMediaSourceFactory (or any other AdsMediaSource-based AdsLoader) and play an HLS stream containing #EXT-X-DATERANGE interstitials.
- Once at least one ad group has started — AdsMediaSource has called adsLoader.start(), which calls player.addListener(...); this succeeds silently because addListener isn't thread-checked — stop playback, release the source, or switch media items, so AdsMediaSource.releaseSourceInternal() runs.
- Observe the crash when HlsInterstitialsAdsLoader.stop() calls player.removeListener(...).
Expected result
AdsMediaSource should dispatch to the player's actual getApplicationLooper() rather than a hardcoded Looper.getMainLooper(), so AdsLoader callbacks are safe to call any Player method from — matching the contract setLooper() establishes everywhere else in the library.
Actual result
An exception in HlsInterstitialsAdsLoader.stop()
- Dominant language
- Java
- Stars
- 3.1k
- Forks
- 998
- Avg merge
- 6d 8h
- Merged PRs (30d)
- 2
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from androidx/media
-
HLS: muxed Dolby TrueHD audio is dropped during chunkless preparation when CODECS lists "mlpa"Possibly taken @marcbaechinger claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
androidx/media#3485 · 1 assignee ·
Maintainers usually reply within 4 days
-
MediaSessionService: the platform session keeps every player command and publishes the whole queue until the session is added, e.g. after a sticky restartPossibly taken @marcbaechinger claimed this 1 day ago. Openbug
Difficulty 4/5 3-5 days Newbie friendliness 45/100
androidx/media#3490 · 3 comments · 1 assignee ·
Maintainers usually reply within 4 days
-
Gapless MP4/AAC from FFmpeg: padding derived as 0 from the edit list, so the rest of the last frame plays at each joinPossibly taken @rohitjoins claimed this 1 day ago. Openbug needs triage
Difficulty 4/5 3-5 days Newbie friendliness 48/100
androidx/media#3489 · 1 assignee ·
Maintainers usually reply within 4 days
-
DTS-HD passthrough never retries a failed AudioTrack since the 1-second retry minimum (#3207)Possibly taken @microkatz claimed this 1 day ago. Open
Difficulty 4/5 3-5 days Newbie friendliness 57/100
androidx/media#3487 · 1 assignee ·
Maintainers usually reply within 4 days
-
Dolby Vision Profile 7 is sent to a Profile 5 decoder on DV displays without a Profile 7 decoderPossibly taken @microkatz claimed this 1 day ago. Open
Difficulty 4/5 1-2 days Newbie friendliness 48/100
androidx/media#3486 · 1 assignee ·
Maintainers usually reply within 4 days
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
geonetwork/core-geonetwork#9592 ·
Maintainers usually reply within 2 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 63/100
Maintainers usually reply within 1 day
-
[Bug] ServerHttpRequest.getURI() throws NumberFormatException when Host contains an explicit portOpencomponent/need-triage type/need-triage
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
apache/rocketmq-dashboard#7361 ·
Maintainers usually reply within 3 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
higress-group/himarket#358 ·