Skip to content

Crash: "Methods marked with @UiThread must be executed on the main thread" from AudioDeviceCallback on Android TV (HDMI) devices #188

Description

@under3415

Description

Audio device changes on certain Android TV boxes cause a fatal crash:
java.lang.RuntimeException: Methods marked with @UiThread must be executed on the main thread.

This happens because AudioManager$AudioMonitorHdmiThread delivers
AudioDeviceCallback.onAudioDevicesAdded/onAudioDevicesRemoved directly on a
background thread, ignoring the main-looper Handler passed to
AudioManager.registerAudioDeviceCallback(). The callback then calls
MethodChannel.invokeMethod(), which is annotated @UiThread, from that
background thread.

The crash is rare per device but affects specific low-end TV boxes:

  • 99% SkyworthDigital TV — XStream-Smart-Box-002 — Android 10
  • <1% Inspur TV — STB6252C — Android 11
  • Also reported on Amazon Fire TV devices

Steps to reproduce

Cannot reproduce in the emulator or on most hardware. It only occurs when the
OS delivers audio device change callbacks on AudioManager$AudioMonitorHdmiThread.
This thread only exists on devices with HDMI audio (TV boxes), which is why it is
not seen on phones.

Code sample (if applicable)

The relevant code path in this package:

// AndroidAudioManager.kt
override fun onAudioDevicesAdded(addedDevices: Array<AudioDeviceInfo>) {
    invokeMethod("onAudioDevicesAdded", encodeAudioDevices(addedDevices))
}
where `invokeMethod` calls `MethodChannel.invokeMethod` directly.

__Error stack trace__

```javascript
Fatal Exception: java.lang.RuntimeException: Methods marked with @UiThread must be executed on the main thread. Current thread: Thread-5
       at io.flutter.embedding.engine.FlutterJNI.ensureRunningOnMainThread(r8-map-id-06f4a25a21f5b6d8c6d7e8dc6be290926624f2defb1b30c7d4ceb8c8b94b8cc7:37)
       at io.flutter.embedding.engine.FlutterJNI.dispatchPlatformMessage(r8-map-id-06f4a25a21f5b6d8c6d7e8dc6be290926624f2defb1b30c7d4ceb8c8b94b8cc7:1)
       at io.flutter.embedding.engine.dart.DartMessenger.send(r8-map-id-06f4a25a21f5b6d8c6d7e8dc6be290926624f2defb1b30c7d4ceb8c8b94b8cc7:8)
       at io.flutter.embedding.engine.dart.DartExecutor$DefaultBinaryMessenger.send(r8-map-id-06f4a25a21f5b6d8c6d7e8dc6be290926624f2defb1b30c7d4ceb8c8b94b8cc7:2)
       at io.flutter.embedding.engine.dart.DartExecutor.send(r8-map-id-06f4a25a21f5b6d8c6d7e8dc6be290926624f2defb1b30c7d4ceb8c8b94b8cc7:2)
       at io.flutter.plugin.common.MethodChannel.invokeMethod(r8-map-id-06f4a25a21f5b6d8c6d7e8dc6be290926624f2defb1b30c7d4ceb8c8b94b8cc7:5)
       at io.flutter.plugin.common.MethodChannel.invokeMethod(r8-map-id-06f4a25a21f5b6d8c6d7e8dc6be290926624f2defb1b30c7d4ceb8c8b94b8cc7:1)
       at f6.d.A(r8-map-id-06f4a25a21f5b6d8c6d7e8dc6be290926624f2defb1b30c7d4ceb8c8b94b8cc7:40)
       at f6.d$b.onAudioDevicesAdded(r8-map-id-06f4a25a21f5b6d8c6d7e8dc6be290926624f2defb1b30c7d4ceb8c8b94b8cc7:20)
       at android.media.AudioManager$AudioMonitorHdmiThread.run(AudioManager.java:5040)

Device / OS

  • SkyworthDigital TV — XStream-Smart-Box-002 — Android 10
  • Inspur TV — STB6252C — Android 11
  • Amazon Fire TV devices (low-end)

Logs and outputs

See stack trace above. onAudioDevicesAdded is the failing frame; the same crash class also applies to onAudioDevicesRemoved.

Additional context

  • A minimal, low-risk fix is ready — see the attached PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions