# AudioSwitchManager speaker route silently fails on Android — isSpeakerphoneOn never applies, no error, no readback

**URL:** <https://community.livekit.io/t/audioswitchmanager-speaker-route-silently-fails-on-android-isspeakerphoneon-never-applies-no-error-no-readback/2018>\
**Category:** Getting Started\
**Created:** [September 8, 2026, 8:16pm UTC](https://community.livekit.io/t/audioswitchmanager-speaker-route-silently-fails-on-android-isspeakerphoneon-never-applies-no-error-no-readback/2018 "2026-09-08T20:16:29Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![jeanphil.marck](https://avatars.discourse-cdn.com/v4/letter/j/b38774/32.png) [@jeanphil.marck](https://community.livekit.io/u/jeanphil.marck)\
**Post date:** [September 8, 2026, 8:16pm UTC](https://community.livekit.io/t/audioswitchmanager-speaker-route-silently-fails-on-android-isspeakerphoneon-never-applies-no-error-no-readback/2018/1 "2026-09-08T20:16:29Z")

</div>

On Android, calling `setAudioRoute('speaker')` (via `@livekit/react-native`) resolves without error and correctly lists `'speaker'` as an available output, but the native audio route never actually changes — confirmed via `dumpsys audio`’s “Active communication device,” which stays on `earpiece` before and after the call. Traced through source to `AudioSwitchManager.java`’s dependency on `com.github.davidliu/audioswitch`, which routes speaker toggling through the legacy `audioManager.isSpeakerphoneOn = enable`, a pre-Android-12 API that Google’s docs mark as superseded by `setCommunicationDevice()`. There’s no error, exception, or event fired on failure, and no public method to read back the actual applied route — so the app-layer has no way to detect this without an external signal (in our case, Android’s own Telecom `didChangeAudioRoute` callback, which is unrelated to the SDK).

**Repro steps:** join a room on Android, call `setAudioRoute('speaker')`, check `dumpsys audio | grep "Active communication device"` before and after.

**Confirmed on:** Samsung Galaxy S24 (Android 16 / API 36) and Samsung A03s (Android 13 / API 33) — two different device generations, same result on both, so this doesn’t look device-specific.

**Expected:** the native audio route actually switches to speaker, or the call rejects/errors so the app can detect and surface the failure.

---

<div class="post-metadata">

**Author:** ![Muhammad\_Usman\_Bashir](https://sea1.discourse-cdn.com/flex001/user_avatar/community.livekit.io/muhammad_usman_bashir/32/1149_2.png) [@Muhammad\_Usman\_Bashir](https://community.livekit.io/u/Muhammad_Usman_Bashir)\
**Post date:** [September 8, 2026, 10:10pm UTC](https://community.livekit.io/t/audioswitchmanager-speaker-route-silently-fails-on-android-isspeakerphoneon-never-applies-no-error-no-readback/2018/2 "2026-09-08T22:10:00Z")

</div>

@jeanphil.marck, Your trace is right, and it sits in the dependency exactly where you point. audioswitch’s `enableSpeakerphone` is one line, `audioManager.isSpeakerphoneOn = enable` ([AudioDeviceManager.kt#L81-L82](https://github.com/davidliu/audioswitch/blob/master/audioswitch/src/main/java/com/twilio/audioswitch/AudioDeviceManager.kt#L81-L82)), and nothing in that class promotes it to setCommunicationDevice on newer Android. isSpeakerphoneOn was deprecated in Android 12 (API 31) in favor of setCommunicationDevice, so on your `S24 (API 36)` and `A03s (API 33)` the write is accepted and silently ignored. That matches your dumpsys result and the missing readback, since the setter returns nothing and the class never queries the applied device.

It is not tracked yet. The closest open report is #966, earpiece routing after a reused Room on Android 12+, the same setCommunicationDevice gap from a different trigger ([client-sdk-android#966](https://github.com/livekit/client-sdk-android/issues/966)). Yours is worth its own issue on client-sdk-android, since that is where the audioswitch dependency is owned. Your repro and the two API levels are already a clean report, and linking `#966` helps whoever picks it up see it is one root cause..

Until the SDK migrates, you can force the route yourself on API 31+ with a small native module: call `AudioManager.setCommunicationDevice` with the `TYPE_BUILTIN_SPEAKER` entry from `getAvailableCommunicationDevices`, and confirm it with `getCommunicationDevice` so you are no longer blind to failures. Call it after `setAudioRoute`, since the SDK’s `isSpeakerphoneOn` write is a no-op on `31+` and will not fight you..

---

<div class="post-metadata">

**Author:** ![darryncampbell](https://sea1.discourse-cdn.com/flex001/user_avatar/community.livekit.io/darryncampbell/32/20_2.png) [@darryncampbell](https://community.livekit.io/u/darryncampbell)\
**Post date:** [September 9, 2026, 9:43am UTC](https://community.livekit.io/t/audioswitchmanager-speaker-route-silently-fails-on-android-isspeakerphoneon-never-applies-no-error-no-readback/2018/3 "2026-09-09T09:43:14Z")

</div>

Just trying to reproduce this:

1. [GitHub - livekit-examples/agent-starter-react-native · GitHub](https://github.com/livekit-examples/agent-starter-react-native) modified at this line: [agent-starter-react-native/app/assistant/index.tsx at main · livekit-examples/agent-starter-react-native · GitHub](https://github.com/livekit-examples/agent-starter-react-native/blob/main/app/assistant/index.tsx#L37) to add: `await AudioSession.selectAudioOutput('speaker'); `
2. Running on physical Pixel device (I don’t have a Samsung device)

Behaviour of `adb shell dumpsys audio | grep -A2 -i "Active communication device"` :

- When not on a call: Active communication device: AudioDeviceAttributes: role:output type:bt\_a2dp addr:XX:XX:XX:XX:D2:62 name:LE-Darryn Headphones profiles:
- When I join a call: Active communication device: AudioDeviceAttributes: role:output type:speaker addr: name:Pixel 9a profiles:

So, I’m unable to reproduce this - perhaps it is device specific, I know there have been Samsung-specific issues in the past.

---

<div class="post-metadata">

**Author:** ![jeanphil.marck](https://avatars.discourse-cdn.com/v4/letter/j/b38774/32.png) [@jeanphil.marck](https://community.livekit.io/u/jeanphil.marck)\
**Post date:** [September 11, 2026, 11:15pm UTC](https://community.livekit.io/t/audioswitchmanager-speaker-route-silently-fails-on-android-isspeakerphoneon-never-applies-no-error-no-readback/2018/4 "2026-09-11T23:15:15Z")

</div>

Thank you, @Muhammad Usman Bashir — your root cause was exactly right, and the trace matched what we found independently. One important addition worth flagging for anyone else hitting this on a **Telecom self-managed ConnectionService** (e.g. via `react-native-callkeep`’s Android Self Managed mode): calling `AudioManager.setCommunicationDevice()` directly, as suggested above, worked on one of our test devices (Samsung A03s, API 33) but silently failed on another (Samsung Galaxy S24, API 36) — the call returned success, but the real hardware route never changed.

Root cause of that second failure: Android’s own docs describe raw `AudioManager` routing and Telecom’s `Connection.setAudioRoute()` as two separate, non-overlapping mechanisms — they don’t cross-reference each other anywhere. If an app is already a Telecom self-managed call (which `react-native-callkeep`’s Self Managed mode is), a stricter Android build appears to silently reassert its own route ownership over a raw `AudioManager` write, while still returning success from that call. Our A03s “working” was a looser/older enforcement, not confirmation the approach was actually correct.

The fix that ended up working reliably on both devices: route through `Connection.setAudioRoute()` itself — for anyone on `react-native-callkeep`, that’s the already-exposed `toggleAudioRouteSpeaker(uuid, routeSpeaker)` method. Since that _is_ Telecom routing the call, it can’t conflict with Telecom’s own ownership the way a raw `AudioManager` call can. Confirmed bidirectionally on both devices with three independently-agreeing signals (the native call, Telecom’s own `didChangeAudioRoute` event, and a `dumpsys audio` hardware readback).

Worth a note in `#966` or wherever this ends up tracked: the fix likely needs to branch on whether the app is a Telecom self-managed call at all, not just on API level — `setCommunicationDevice()` may be fine for apps that _aren’t_ using ConnectionService, but is the wrong tool for ones that are.

---

<div class="post-metadata">

**Author:** ![system](https://sea1.discourse-cdn.com/flex001/user_avatar/community.livekit.io/system/32/26_2.png) [@system](https://community.livekit.io/u/system)\
**Post date:** [September 26, 2026, 5:04pm UTC](https://community.livekit.io/t/audioswitchmanager-speaker-route-silently-fails-on-android-isspeakerphoneon-never-applies-no-error-no-readback/2018/5 "2026-09-26T17:04:28Z")

</div>

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.
