fix: stop ringback tone when Connecting state is skipped by StateFlow conflation

StateFlow is conflated — when the state transitions rapidly from
Offering → Connecting → Connected, the collector may skip Connecting
entirely and only see Connected. Since stopRingbackTone() was only
called in the Connecting handler, the ringback tone would keep playing
through the entire call.

Fix: duplicate the ringing/notification cleanup in the Connected handler
so the tone is always stopped regardless of which state the collector
sees first. Also make switchToCallAudioMode() idempotent and clean up
the previous ToneGenerator in startRingbackTone() to prevent leaks.

https://claude.ai/code/session_01GWRdrVAa29BsDkv8R7Z3Y9
This commit is contained in:
Claude
2026-04-03 18:11:22 +00:00
parent 9be9117362
commit 286685c0bd
2 changed files with 10 additions and 0 deletions
@@ -107,6 +107,7 @@ class CallAudioManager(
}
fun startRingbackTone() {
stopRingbackTone()
try {
ringbackTone =
ToneGenerator(AudioManager.STREAM_VOICE_CALL, 80).also {
@@ -123,6 +124,7 @@ class CallAudioManager(
}
fun switchToCallAudioMode() {
if (audioManager.mode == AudioManager.MODE_IN_COMMUNICATION) return
previousAudioMode = audioManager.mode
audioManager.mode = AudioManager.MODE_IN_COMMUNICATION
@@ -177,7 +177,15 @@ class CallController(
}
is CallState.Connected -> {
// Stop ringing/ringback in case the Connecting state
// was skipped due to StateFlow conflation (the value
// can change Offering → Connecting → Connected before
// the collector processes Connecting).
audioManager.stopRinging()
audioManager.stopRingbackTone()
withContext(Dispatchers.IO) { audioManager.switchToCallAudioMode() }
audioManager.acquireProximityWakeLock()
NotificationUtils.cancelCallNotification(context)
}
is CallState.Ended -> {