fix(core): re-apply USB mic preference after AudioSwitch activation#1652
fix(core): re-apply USB mic preference after AudioSwitch activation#1652PratimMallick wants to merge 2 commits intodevelopfrom
Conversation
AudioSwitch.activate() sets AudioManager.MODE_IN_COMMUNICATION which resets system audio routing, overriding the preferred USB input device set via JavaAudioDeviceModule.setPreferredInputDevice. This caused USB microphones (e.g. Rode Wireless Go) to be detected and selected but audio was still captured from the phone's built-in mic. Re-apply the USB device preference after every AudioSwitch activation: in MicrophoneManager.select() and in the audioDeviceChangeListener. Made-with: Cursor
PR checklist ❌The following issues were detected:
What we check
|
WalkthroughThe PR modifies Changes
Sequence DiagramsequenceDiagram
participant Client as Client Code
participant MicMgr as MicrophoneManager
participant AudioSwitch as AudioSwitch
participant PCF as peerConnectionFactory
Client->>MicMgr: select(usbDevice)
MicMgr->>MicMgr: _selectedDevice = device
MicMgr->>AudioSwitch: activate(device)
AudioSwitch->>AudioSwitch: Apply routing changes
MicMgr->>MicMgr: reapplyUsbDevicePreference()
MicMgr->>PCF: setPreferredAudioInputDevice(deviceInfo)
PCF->>PCF: Restore USB preference
AudioSwitch-->>MicMgr: onAudioDeviceChanged
MicMgr->>MicMgr: Update _devices & _selectedDevice
MicMgr->>MicMgr: reapplyUsbDevicePreference()
MicMgr->>PCF: setPreferredAudioInputDevice(deviceInfo)
PCF->>PCF: Maintain USB preference
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/MediaManager.kt (1)
950-963:⚠️ Potential issue | 🟡 MinorDispatch
reapplyUsbDevicePreference()to the media scope.AudioSwitch invokes
audioDeviceChangeListenerfrom background threads (per Twilio's AudioSwitch library behavior with BroadcastReceiver and Bluetooth callbacks), not the main thread. CallingsetPreferredAudioInputDevice()→ nativeADM.setPreferredInputDevice()directly from this callback risks thread-safety issues with the underlying WebRTC audio device module.Wrap the call in
mediaManager.scope.launch { reapplyUsbDevicePreference() }to ensure it runs on a consistent thread, matching how camera flip and screen share operations dispatch to the scope.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/MediaManager.kt` around lines 950 - 963, The audioDeviceChangeListener currently calls reapplyUsbDevicePreference() directly from background threads; change that to dispatch into the MediaManager's coroutine scope (wrap the call in scope.launch { reapplyUsbDevicePreference() } — or mediaManager.scope.launch if used externally) so reapplyUsbDevicePreference() runs on the media coroutine thread, avoiding thread-safety issues with setPreferredAudioInputDevice()/ADM; update the audioDeviceChangeListener block to call scope.launch { reapplyUsbDevicePreference() } instead of invoking reapplyUsbDevicePreference() directly.
🧹 Nitpick comments (1)
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/MediaManager.kt (1)
806-820: Minor: log verbosity and broken KDoc reference.Two small nits on the new helper:
- The KDoc references
[JavaAudioDeviceModule.setPreferredInputDevice], butJavaAudioDeviceModuleisn't imported in this file, so the doc link won't resolve. Either import it or reference the wrapperStreamPeerConnectionFactory.setPreferredAudioInputDevicethat is actually invoked here.reapplyUsbDevicePreference()is invoked from bothselect()and theaudioDeviceChangeListenercallback (which can fire repeatedly as devices come/go). Logging at.ieach time will be noisy in normal call sessions. Per the guidelines ("Monitor logging verbosity"), consider demoting to.d.♻️ Suggested tweak
private fun reapplyUsbDevicePreference() { val usbDevice = _selectedUsbDevice.value ?: return - logger.i { + logger.d { "[reapplyUsbDevicePreference] Re-applying USB device after AudioSwitch activation: ${usbDevice.name}" } mediaManager.call.peerConnectionFactory.setPreferredAudioInputDevice(usbDevice.deviceInfo) }As per coding guidelines: "Monitor logging verbosity; rely on
StreamVideoImpl.developmentModefor guardrails".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/MediaManager.kt` around lines 806 - 820, The KDoc and logging are noisy/broken in reapplyUsbDevicePreference: update the doc link to reference the actual API used (StreamPeerConnectionFactory.setPreferredAudioInputDevice) instead of the unresolved JavaAudioDeviceModule.setPreferredInputDevice, and reduce log verbosity by changing logger.i to logger.d (or guard it behind developmentMode) in reapplyUsbDevicePreference which reads _selectedUsbDevice and calls mediaManager.call.peerConnectionFactory.setPreferredAudioInputDevice(…); this fixes the broken KDoc link and prevents excessive info-level logs when select() or audioDeviceChangeListener repeatedly invoke the method.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In
`@stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/MediaManager.kt`:
- Around line 950-963: The audioDeviceChangeListener currently calls
reapplyUsbDevicePreference() directly from background threads; change that to
dispatch into the MediaManager's coroutine scope (wrap the call in scope.launch
{ reapplyUsbDevicePreference() } — or mediaManager.scope.launch if used
externally) so reapplyUsbDevicePreference() runs on the media coroutine thread,
avoiding thread-safety issues with setPreferredAudioInputDevice()/ADM; update
the audioDeviceChangeListener block to call scope.launch {
reapplyUsbDevicePreference() } instead of invoking reapplyUsbDevicePreference()
directly.
---
Nitpick comments:
In
`@stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/MediaManager.kt`:
- Around line 806-820: The KDoc and logging are noisy/broken in
reapplyUsbDevicePreference: update the doc link to reference the actual API used
(StreamPeerConnectionFactory.setPreferredAudioInputDevice) instead of the
unresolved JavaAudioDeviceModule.setPreferredInputDevice, and reduce log
verbosity by changing logger.i to logger.d (or guard it behind developmentMode)
in reapplyUsbDevicePreference which reads _selectedUsbDevice and calls
mediaManager.call.peerConnectionFactory.setPreferredAudioInputDevice(…); this
fixes the broken KDoc link and prevents excessive info-level logs when select()
or audioDeviceChangeListener repeatedly invoke the method.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 73ae9b4d-e232-4c65-9223-968bb0f809b3
📒 Files selected for processing (1)
stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/MediaManager.kt
SDK Size Comparison 📏
|
Early calls to setPreferredAudioInputDevice were silently dropped when the JavaAudioDeviceModule had not yet been created. This stores a pending preference and applies it once the ADM is initialized, and re-applies the preference in onWebRtcAudioRecordStart as a safety net. Made-with: Cursor
|


Goal
Fixes an issue where USB microphones (e.g. Rode Wireless Go II) were detected and selected via
setPreferredInputDevice, but audio was still captured from the phone's built-in micImplementation
Root cause: AudioSwitch's
activate()setsAudioManager.MODE_IN_COMMUNICATION, which resets system audio routing and overrides the preferred USB input deviceAdds
reapplyUsbDevicePreference()inMicrophoneManagerthat re-applies the USB device preference after every AudioSwitch activation — both inselect()and in theaudioDeviceChangeListenercallbackTesting
[reapplyUsbDevicePreference] Re-applying USB device after AudioSwitch activation: <device name>after each AudioSwitch callback./gradlew :stream-video-android-core:testDebugUnitTest— all tests pass☑️Contributor Checklist
General
developbranchCode & documentation
stream-video-examples)☑️Reviewer Checklist
🎉 GIF
Please provide a suitable gif that describes your work on this pull request
Summary by CodeRabbit
Bug Fixes