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
AudioCapabilitiesReceiver using a dedicated playback
HandlerThread with a prepared Looper.
- Capture the registered
AudioDeviceCallback.
- Invoke
onAudioDevicesAdded() from a plain thread named
AudioMonitorHdmiThread without calling Looper.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
Version
Media3 1.7.1 (same as 1.6.1)
More version details
Media3
1.7.1Devices that reproduce the issue
Both
onAudioDevicesAdded()andonAudioDevicesRemoved()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:
renegotiate the HDMI/audio route.
Deterministic unit-test reproduction:
AudioCapabilitiesReceiverusing a dedicated playbackHandlerThreadwith a prepared Looper.AudioDeviceCallback.onAudioDevicesAdded()from a plain thread namedAudioMonitorHdmiThreadwithout callingLooper.prepare().onAudioDevicesRemoved().The test uses a real playback
HandlerThreadand a plain callback thread withno 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 thecallback thread used by the framework or OEM.
No exception is thrown.
Actual result
AudioCapabilitiesReceiver
executes 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 ismain`, causing the process to crash:The removal path has the same failure:
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
adb bugreportto android-media-github@google.com after filing this issue.