fix: WebRTC call resource leaks, thread safety, and error handling
- Send hangup/reject to peer when WebRTC init or PeerConnection creation fails, so remote phone stops ringing instead of timing out after 60s - Throw on null PeerConnection from factory to fail fast instead of silently no-oping all subsequent WebRTC operations - Start foreground service during IncomingCall to protect ringtone playback from being killed on Android 14+ - Make cleanup() idempotent with AtomicBoolean guard to prevent double disposal when Ended state and ViewModel.onCleared race - Replace mutableMapOf with ConcurrentHashMap for videoSenders accessed from UI and WebRTC callback threads - Add @Volatile to peerConnection, videoPausedByProximity, and foregroundServiceStarted for cross-thread visibility - Capture peerConnection into local variable in dispose() to prevent TOCTOU race between close() and null assignment - Replace leaked MainScope() in CallNotificationReceiver with structured CoroutineScope that is cancelled after work completes - Remove self-wraps in group answer/reject to avoid wasting bandwidth sending encrypted messages to ourselves - Move startTimeout inside stateMutex in initiateCall for consistency https://claude.ai/code/session_017HrFJNxD6zrGwiZ3s69xTh
This commit is contained in:
+7
-6
@@ -201,9 +201,9 @@ class CallManager(
|
||||
val result = factory.createCallOffer(sdpOffer, calleePubKey, callId, callType, signer)
|
||||
stateMutex.withLock {
|
||||
_state.value = CallState.Offering(callId, setOf(calleePubKey), callType)
|
||||
startTimeout(callId)
|
||||
}
|
||||
publishEvent(result.wrap)
|
||||
startTimeout(callId)
|
||||
Log.d("CallManager") { "initiateCall: offer published, timeout started" }
|
||||
}
|
||||
|
||||
@@ -281,9 +281,10 @@ class CallManager(
|
||||
discoveredCalleePeers.clear()
|
||||
}
|
||||
|
||||
val allRecipients = current.groupMembers + signer.pubKey
|
||||
Log.d("CallManager") { "acceptCall: publishing answer to ${allRecipients.size} recipients" }
|
||||
val result = factory.createGroupCallAnswer(sdpAnswer, allRecipients, current.callId, signer)
|
||||
val allMembers = current.groupMembers + signer.pubKey
|
||||
val otherMembers = allMembers - signer.pubKey
|
||||
Log.d("CallManager") { "acceptCall: publishing answer to ${otherMembers.size} recipients" }
|
||||
val result = factory.createGroupCallAnswer(sdpAnswer, otherMembers, current.callId, signer)
|
||||
result.wraps.forEach { publishEvent(it) }
|
||||
Log.d("CallManager") { "acceptCall: answer published, now in Connecting state" }
|
||||
|
||||
@@ -306,8 +307,8 @@ class CallManager(
|
||||
transitionToEnded(current.callId, current.peerPubKeys(), EndReason.REJECTED)
|
||||
}
|
||||
|
||||
val allRecipients = current.groupMembers + signer.pubKey
|
||||
val result = factory.createGroupReject(allRecipients, current.callId, signer = signer)
|
||||
val otherMembers = current.groupMembers - signer.pubKey
|
||||
val result = factory.createGroupReject(otherMembers, current.callId, signer = signer)
|
||||
result.wraps.forEach { publishEvent(it) }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user