diff --git a/app/src/main/kotlin/io/flamenet/messenger/app/CallManager.kt b/app/src/main/kotlin/io/flamenet/messenger/app/CallManager.kt index c10395b..eec168e 100644 --- a/app/src/main/kotlin/io/flamenet/messenger/app/CallManager.kt +++ b/app/src/main/kotlin/io/flamenet/messenger/app/CallManager.kt @@ -140,10 +140,14 @@ class CallManager( "fnmsg-call", "ring expired with no answer, releasing so the next call is not refused", ) - // "timeout", not a new word: the far end already renders - // that as "No answer", which is what this is. An unknown - // reason falls through to a flat "Call ended". - hangUp(notifyPeer = true, reason = "timeout") + // Released here and nothing sent. The caller gave up on its + // own timer long before this fires, so telling it was + // redundant, and on an account signed in on more than one + // device it was destructive: this phone keeps ringing when + // the call was answered on another one, because nothing + // tells it, and a timeout end sent from here reached the + // caller mid-call with that other device and hung it up. + hangUp(notifyPeer = false, reason = "timeout") } } ringGuard = guard @@ -293,7 +297,9 @@ class CallManager( } // CLOSED is what our OWN close() produces. - PeerConnection.IceConnectionState.CLOSED -> Unit + PeerConnection.IceConnectionState.CLOSED -> { + Unit + } // The far end went away. Transient by definition, // WebRTC recovers from it often, so this is a grace @@ -301,11 +307,17 @@ class CallManager( // is cancelled below. Without it a lost `call-end` // left the screen up until the dial guard gave up // three quarters of a minute later. - PeerConnection.IceConnectionState.DISCONNECTED -> armGoneGuard() + PeerConnection.IceConnectionState.DISCONNECTED -> { + armGoneGuard() + } - PeerConnection.IceConnectionState.FAILED -> hangUp(notifyPeer = true) + PeerConnection.IceConnectionState.FAILED -> { + hangUp(notifyPeer = true) + } - else -> Unit + else -> { + Unit + } } } } @@ -510,6 +522,31 @@ class CallManager( } Content.CallSignal.END -> { + // An end for some other call must not hang up this one. This + // branch used to hang up on any end at all, for any call id. + if (incoming.callId != current?.callId) { + android.util.Log.i( + "fnmsg-call", + "end for ${incoming.callId} ignored: not the current call", + ) + return + } + // A call answered on one of the callee's devices is still + // ringing on the others, and they all share this call id. Once + // this call has connected, an end whose reason means the ring + // was never picked up came from one of those other devices and + // is not about the call in progress: somebody silencing their + // tablet, or a ring guard from a build that still announces its + // timeout. Honouring it hung up a live call. A plain hang-up + // carries no such reason and still ends it. + val neverPickedUp = incoming.reason.orEmpty() in setOf("declined", "busy", "timeout") + if (neverPickedUp && (state == State.CONNECTING || state == State.ACTIVE)) { + android.util.Log.i( + "fnmsg-call", + "'${incoming.reason}' end ignored: this call was already picked up", + ) + return + } hangUp(notifyPeer = false) } }