Android Calling App not resetting Audio Stream

292 Views Asked by At

I have a single activity application set up to use a Self-managed Connection Service since we are using audio and video and wanted to be able to leverage the system. HOWEVER, something about the way that we are closing the connection or perhaps changing the audio stream is causing an issue that I will try to describe to the best of my ability here.

When I start our application's call, everything works the way we want it to, it starts in speakerphone but reacts well to the button presses of the speakerphone button and audio works great! When the call ends, however, my phone is stuck in a mode where any notifications do not play out through the speaker but rather come through the earpiece, meaning that all my notifications are rather quiet. I believe this to be because we are not properly resetting the audio stream. I'm not sure if that's meant to happen in the destroy() of the Connection object or rather in the Activity where we request audio focus etc... Below is the code I believe to be the culprit

This method initialize audio is called from onResume() of the single Activity. We go down two different requesting paths depending on the Build version of the user's device

private fun initializeAudio() {
    if (!audioManager.isInCall()) {
        volumeControlStream = AudioManager.STREAM_VOICE_CALL
        // Request audio focus for playback
        val result: Int = if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
            audioManager.requestAudioFocus(focusRequest)
        } else {
            @Suppress("DEPRECATION")
            audioManager.requestAudioFocus(
                afChangeListener,
                AudioManager.STREAM_VOICE_CALL,
                AudioManager.AUDIOFOCUS_GAIN
            )
        }

        if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
            StreemLog.logError(TAG, "Audio focus request denied", sendBreadcrumb = true)
        }
    }
}

So here is the AudioFocusRequest for newer versions

private val focusRequest: AudioFocusRequest by lazy {
    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
        AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN).run {
            setAudioAttributes(
                AudioAttributes.Builder().run {
                    setLegacyStreamType(AudioManager.STREAM_VOICE_CALL)
                    setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
                    build()
                }
            )
            setOnAudioFocusChangeListener(afChangeListener)
            build()
        }
    } else throw IllegalStateException("Trying to use AudioFocusRequest below minimum API 26")
}

And here is the more dated OnAudioFocusChangeListener

private val afChangeListener: AudioManager.OnAudioFocusChangeListener =
    AudioManager.OnAudioFocusChangeListener { focusChange ->
        when (focusChange) {
            AudioManager.AUDIOFOCUS_LOSS -> {
                tearDownAudio()
            }
            AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
                // noop
            }
            AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
                // noop
            }
            AudioManager.AUDIOFOCUS_GAIN -> {
                initializeAudio()
            }
        }
    }

Finally, we have our tearDownAudio() method that is called from onPause() in the Activity

private fun tearDownAudio() {
    volumeControlStream = AudioManager.USE_DEFAULT_STREAM_TYPE
    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
        audioManager.abandonAudioFocusRequest(focusRequest)
    } else {
        @Suppress("DEPRECATION")
        audioManager.abandonAudioFocus(afChangeListener)
    }
}

My guess is the issue is somewhere in the tearDownAudio() method because the audio on the device works great to start and during the call but is not returning to the normal stream after the call ends.

It is also possible that the issue exists within the ConnectionService implementation since we are using a self managed Connection Service but my first instinct is that the above code is the problem.

1

There are 1 best solutions below

0
On

One of my team members found the issue! After a lot of debugging...

When the call ends we were only closing the WebRTCPeer connection for the local participant, not all participants. So there was one WebRtcAudioTrack object left running. Calling close on all of them releases the audio resources and all stream routings are restored to their correct states.

So moral of the story is make sure you close down all connections when ending the call!