AudioCapabilitiesReceiver invokes audio capability listener on a looper-less OEM thread
@icbaker is already working on this.
Since Aug 21, 2026.
- Dominant language
- Java
- Stars
- 3k
- Forks
- 955
- Avg merge
- 12d 14h
- Merged PRs (30d)
- 2
Description
Version
Media3 1.7.1 (same as 1.6.1)
More version details
Media3 1.7.1
Devices that reproduce the issue
- Jio STB devices running Android 10 or 11.
- SkyworthDigital devices running Android 10 or 11.
Both onAudioDevicesAdded() and onAudioDevicesRemoved() appear in the crash reports.
Devices that do not reproduce the issue
We do not have a controlled negative-device test. No other device families have confirmed reports.
Reproducible in the demo app?
No
Reproduction steps
Reported production flow on both Jio STB and SkyworthDigital devices:
- Start or resume video playback.
- While playback is starting or active, connect, reconnect, wake, switch, or
renegotiate the HDMI/audio route. - Observe the crash.
Deterministic unit-test reproduction:
- Create an
AudioCapabilitiesReceiverusing a dedicated playback
HandlerThreadwith a prepared Looper. - Capture the registered
AudioDeviceCallback. - Invoke
onAudioDevicesAdded()from a plain thread named
AudioMonitorHdmiThreadwithout callingLooper.prepare(). - Repeat with
onAudioDevicesRemoved(). - Observe that the callback reaches the listener on the foreign thread.
The test uses a real playback HandlerThread and a plain callback thread with
no Looper. It does not require an affected device, HDMI cable, or media file.
Expected result
Capability evaluation, state changes, and listener delivery should run on the
handler/Looper captured by AudioCapabilitiesReceiver, regardless of the
callback thread used by the framework or OEM.
No exception is thrown.
Actual result
AudioCapabilitiesReceiverexecutes the callback body directly on the OEM callback thread. When the capabilities change, the listener is synchronously called from that thread.Looper.myLooper()isnull, while the audio sink's playback Looper is main`, causing the process to crash:
Fatal Exception: java.lang.IllegalStateException:
Current looper (null) is not the playback looper (main)
at androidx.media3.exoplayer.audio.DefaultAudioSink.onAudioCapabilitiesChanged(DefaultAudioSink.java:1600)
at androidx.media3.exoplayer.audio.AudioCapabilitiesReceiver.onNewAudioCapabilities(AudioCapabilitiesReceiver.java:199)
at androidx.media3.exoplayer.audio.AudioCapabilitiesReceiver$AudioDeviceCallbackV23.onAudioDevicesAdded(AudioCapabilitiesReceiver.java:246)
at android.media.AudioManager$AudioMonitorHdmiThread.run(AudioManager.java:5692)
The removal path has the same failure:
Fatal Exception: java.lang.IllegalStateException:
Current looper (null) is not the playback looper (main)
at androidx.media3.exoplayer.audio.DefaultAudioSink.onAudioCapabilitiesChanged(DefaultAudioSink.java:1600)
at androidx.media3.exoplayer.audio.AudioCapabilitiesReceiver.onNewAudioCapabilities(AudioCapabilitiesReceiver.java:199)
at androidx.media3.exoplayer.audio.AudioCapabilitiesReceiver$AudioDeviceCallbackV23.onAudioDevicesRemoved(AudioCapabilitiesReceiver.java:255)
at android.media.AudioManager$AudioMonitorHdmiThread.run(AudioManager.java:5047)
The primary local assertion against the unmodified implementation is:
expected: ExoPlayer:Playback
but was : AudioMonitorHdmiThread
Media
Not applicable. The crash is caused by audio-device callback threading and does not depend on the media being played.
Bug Report
- You will email the zip file produced by
adb bugreportto android-media-github@google.com after filing this issue.
Contributor 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.
Assessment
This issue has not been assessed yet.