Description
After updating from 1.x.x to 2.3.10 of the video sdk, there are no longer any sound on our android app - iOS works fine, also when talking to the Android app.
We have however discovered that if we call
zoomSdk.testAudioDeviceHelper.startSpeakerTest()
zoomSdk.testAudioDeviceHelper.stopSpeakerTest()
Audio works after the second call, even though startSpeakerTest() returns an internal error on both attempts.
This suggests an audio routing/initialization issue fixed by the test helper rather than normal startAudio().
No exceptions are thrown and startAudio() returns Errors_Success, but there is no audio input/output on Android (complete silence).
Which Android Video SDK version?
- us.zoom.videosdk:zoomvideosdk-core:2.3.10
To Reproduce(If applicable)
100% reproducible: Happens every time we start a call on Android after the upgrade.
Troubleshooting Routes
Testing with the sample app is not relevant since we only experience problems when also integrating with Android Telecom.
No crashes, no exceptions. Return codes appear successful despite silent audio.
Smartphone (please complete the following information):
- Device: Pixel 9
- OS: Android 16
Additional context
We are also using Android Telecom to manage calls.
Urgent request
Please confirm whether this is a known regression in 2.3.10 related to Android Telecom integration and advise on a hotfix or configuration workaround. If a fixed version exists, please provide the version number and any migration notes.
If useful, we can supply SDK logs at verbose level and additional AudioManager state to accelerate triage.
Hi @michaelpetersen I have a couple of questions to better understand the issue
- Is your audio not working using startSpeakerTest() or startAudio() or both? Also, what internal error is being returned?
- Can you include the code samples of your startAudio function and audio related listeners?
- Can you give a brief description of how the SDK interacts with Android Telecom?
Hi Tocorrian
Thank you for replying.
-
Is your audio not working using startSpeakerTest() or startAudio() or both? Also, what internal error is being returned?
Neither works - or well it works for the second call if we call startSpeakerTest(), so that fixes it for the second call, but startAudio() doesn’t work. Speaker test just returns error code 2, while startAudio() returns success (aka code 0).
-
Can you include the code samples of your startAudio function and audio related listeners?
Yes of course, they are provided below.
-
Can you give a brief description of how the SDK interacts with Android Telecom?
I am not completely sure what you mean. Incoming calls are reported to the app using Android Telecom, then it is “responsible” for performing a call (showing notification etc.) while Zoom is running.
The most interesting part is probably that we have a Connection object (https://developer.android.com/reference/android/telecom/Connection?hl=en), from which we enable speaker in onCallAudioStateChanged() (see below). I know it is deprecated. We have also tried the new API and using neither - it doesn’t seem to matter.
The code below is what we use, except that I have removed our logging lines to make it more clear what is going on.
We configure the audio session in onSessionJoin().
Note that audioManager.registerAudioDeviceCallback(audioDeviceCallback, null) is only used for logging.
override fun onSessionJoin() {
super.onSessionJoin()
audioManager.registerAudioDeviceCallback(audioDeviceCallback, null)
onMyUserIdChanged?.invoke(zoomSdk.session.mySelf.userID)
_ownUser.tryEmit(zoomSdk.session.mySelf)
_otherUsers.tryEmit(zoomSdk.session.remoteUsers)
val audioHelper = zoomSdk.audioHelper
val startAudioStatus = audioHelper.startAudio()
if (ownAudioActive.value) {
audioHelper.unMuteAudio(zoomSdk.session.mySelf)
} else {
audioHelper.muteAudio(zoomSdk.session.mySelf)
}
val test = zoomSdk.testAudioDeviceHelper
val testRes = test.startSpeakerTest()
test.stopSpeakerTest()
}
A call is started using the following code:
zoomSdk.addListener(zoomDelegate)
val audioOptions = ZoomVideoSDKAudioOption()
audioOptions.connect = true // Auto connect to audio upon joining
audioOptions.mute = ownAudioActiveFlow.value // Mute audio upon joining
val videoOptions = ZoomVideoSDKVideoOption()
videoOptions.localVideoOn = ownVideoActiveFlow.value // Turn on local/self video upon joining
val sessionContext = ZoomVideoSDKSessionContext().apply {
sessionName = roomId
userName = currentUserName
token = accessToken
audioOption = audioOptions
videoOption = videoOptions
}
zoomSdk.joinSession(sessionContext)
The Connection class mentioned above:
class OurCallConnection : Connection(), KoinComponent {
init {
connectionProperties = PROPERTY_SELF_MANAGED
connectionCapabilities = CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL or
CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL
}
private val callManager by inject<BNearCallManager>()
private val callId get() = callManager.activeCallIdFlow.value
override fun onShowIncomingCallUi() {
setRinging()
}
// @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
// override fun onAvailableCallEndpointsChanged(availableEndpoints: List<CallEndpoint?>) {
// super.onAvailableCallEndpointsChanged(availableEndpoints)
//
// // Priority: Bluetooth > Wired Headset > Speaker
// val desiredEndpoint = availableEndpoints.firstOrNull {
// it?.endpointType == CallEndpoint.TYPE_BLUETOOTH
// } ?: availableEndpoints.firstOrNull {
// it?.endpointType == CallEndpoint.TYPE_WIRED_HEADSET
// } ?: availableEndpoints.firstOrNull {
// it?.endpointType == CallEndpoint.TYPE_SPEAKER
// }
//
// if (desiredEndpoint != null && desiredEndpoint != currentCallEndpoint) {
// requestCallEndpointChange(desiredEndpoint, Executors.newSingleThreadExecutor()) {}
// }
// }
override fun onCallAudioStateChanged(state: CallAudioState?) {
if (state == null)
return
// If bluetooth route available, and not already active, set it
if (state.supportedRouteMask and ROUTE_BLUETOOTH == ROUTE_BLUETOOTH) {
if (state.route != ROUTE_BLUETOOTH)
setAudioRoute(ROUTE_BLUETOOTH)
}
// Otherwise if wired headset route available, and not already active, set it
else if (state.supportedRouteMask and ROUTE_WIRED_HEADSET == ROUTE_WIRED_HEADSET) {
if (state.route != ROUTE_WIRED_HEADSET)
setAudioRoute(ROUTE_WIRED_HEADSET)
}
// Otherwise always prefer speaker, instead of earpiece
else if (state.route != ROUTE_SPEAKER) {
setAudioRoute(ROUTE_SPEAKER)
}
}
override fun onAnswer() {
callId?.let {
callManager.answerIncomingCall(it)
}
}
override fun onReject() {
callId?.let {
callManager.updateCallState(it, CallState.declined, false)
}
}
override fun onDisconnect() {
callId?.let {
callManager.updateCallState(it, CallState.ended, false)
}
}
}
If you have any other questions, please don’t hesitate to ask.
Thanks for providing this information @michaelpetersen ! If you log out the payload from the onMicSpeakerVolumeChanged event, are there values in the micVolume parameter?
@ticorrian.heard Not for the first call, but for the second call (where the audio works), when I say hello, I get something like the following:
onMicSpeakerVolumeChanged - micVolume: 6, speakerVolume: 0
onMicSpeakerVolumeChanged - micVolume: 7, speakerVolume: 0
onMicSpeakerVolumeChanged - micVolume: 0, speakerVolume: 0
But if disable my own mic, I don’t get any logs, even though there is sound coming out of the device.
@ticorrian.heard I Just tested using your newest update - 2.4.0. Nothing has changed unfortunately.
Hey @michaelpetersen thanks for testing! So the mic works on the second call to the startAudio() method? Also, What specific 1.x.x version did you upgrade from?