Фикс: дубликат CallKit вызова, disconnect recovery, WebRTC packet buffering и E2EE rebind loop
This commit is contained in:
@@ -95,6 +95,10 @@ final class ProtocolManager: @unchecked Sendable {
|
||||
private var webRTCHandlers: [UUID: (PacketWebRTC) -> Void] = [:]
|
||||
private var iceServersHandlers: [UUID: (PacketIceServers) -> Void] = [:]
|
||||
|
||||
/// Background task to keep WebSocket alive during brief background periods (active call).
|
||||
/// iOS gives ~30s; enough for the call to survive app switching / notification interactions.
|
||||
private var callBackgroundTask: UIBackgroundTaskIdentifier = .invalid
|
||||
|
||||
// Saved credentials for auto-reconnect
|
||||
private var savedPublicKey: String?
|
||||
private var savedPrivateHash: String?
|
||||
@@ -185,6 +189,16 @@ final class ProtocolManager: @unchecked Sendable {
|
||||
func forceReconnectOnForeground() {
|
||||
guard savedPublicKey != nil, savedPrivateHash != nil else { return }
|
||||
|
||||
// During an active call the WebSocket may still be alive (background task
|
||||
// keeps the process running for ~30s). Tearing it down would break signaling
|
||||
// and trigger server re-delivery of .call — causing endCallBecauseBusy.
|
||||
// If the connection is authenticated, trust it and skip reconnect.
|
||||
if CallManager.shared.uiState.phase != .idle,
|
||||
connectionState == .authenticated {
|
||||
Self.logger.info("⚡ Foreground reconnect skipped — active call, WS authenticated")
|
||||
return
|
||||
}
|
||||
|
||||
// Android parity: skip if handshake or device verification is in progress.
|
||||
// These are active flows that should not be interrupted.
|
||||
switch connectionState {
|
||||
@@ -209,6 +223,25 @@ final class ProtocolManager: @unchecked Sendable {
|
||||
client.forceReconnect()
|
||||
}
|
||||
|
||||
// MARK: - Call Background Task
|
||||
|
||||
/// Keeps the process alive during active calls so WebSocket survives brief background.
|
||||
func beginCallBackgroundTask() {
|
||||
guard callBackgroundTask == .invalid else { return }
|
||||
callBackgroundTask = UIApplication.shared.beginBackgroundTask(withName: "RosettaCall") { [weak self] in
|
||||
self?.endCallBackgroundTask()
|
||||
}
|
||||
Self.logger.info("📞 Background task started for call")
|
||||
}
|
||||
|
||||
func endCallBackgroundTask() {
|
||||
guard callBackgroundTask != .invalid else { return }
|
||||
let task = callBackgroundTask
|
||||
callBackgroundTask = .invalid
|
||||
UIApplication.shared.endBackgroundTask(task)
|
||||
Self.logger.info("📞 Background task ended for call")
|
||||
}
|
||||
|
||||
/// Android parity: `reconnectNowIfNeeded()` — if already in an active state,
|
||||
/// skip reconnect. Otherwise reset backoff and connect immediately.
|
||||
func reconnectIfNeeded() {
|
||||
|
||||
Reference in New Issue
Block a user