diff --git a/Signal/src/call/CallService.swift b/Signal/src/call/CallService.swift index 414b368090..651fa288be 100644 --- a/Signal/src/call/CallService.swift +++ b/Signal/src/call/CallService.swift @@ -98,6 +98,108 @@ protocol CallServiceObserver: class { remoteVideoTrack: RTCVideoTrack?) } +// Gather all per-call state in one place. +private class SignalCallData: NSObject { + public let call: SignalCall + + // Used to coordinate promises across delegate methods + let fulfillCallConnectedPromise: (() -> Void) + let rejectCallConnectedPromise: ((Error) -> Void) + let callConnectedPromise: Promise + + // Used to ensure any received ICE messages wait until the peer connection client is set up. + let fulfillPeerConnectionClientPromise: (() -> Void) + let rejectPeerConnectionClientPromise: ((Error) -> Void) + let peerConnectionClientPromise: Promise + + // Used to ensure CallOffer was sent before sending any ICE updates. + let fulfillReadyToSendIceUpdatesPromise: (() -> Void) + let rejectReadyToSendIceUpdatesPromise: ((Error) -> Void) + let readyToSendIceUpdatesPromise: Promise + + weak var localVideoTrack: RTCVideoTrack? { + didSet { + SwiftAssertIsOnMainThread(#function) + + Logger.info("\(self.logTag) \(#function)") + } + } + + weak var remoteVideoTrack: RTCVideoTrack? { + didSet { + SwiftAssertIsOnMainThread(#function) + + Logger.info("\(self.logTag) \(#function)") + } + } + + var isRemoteVideoEnabled = false { + didSet { + SwiftAssertIsOnMainThread(#function) + + Logger.info("\(self.logTag) \(#function): \(isRemoteVideoEnabled)") + } + } + + var peerConnectionClient: PeerConnectionClient? { + didSet { + SwiftAssertIsOnMainThread(#function) + + Logger.debug("\(self.logTag) .peerConnectionClient setter: \(oldValue != nil) -> \(peerConnectionClient != nil) \(String(describing: peerConnectionClient))") + } + } + + required init(call: SignalCall) { + self.call = call + + let (callConnectedPromise, fulfillCallConnectedPromise, rejectCallConnectedPromise) = Promise.pending() + self.callConnectedPromise = callConnectedPromise + self.fulfillCallConnectedPromise = fulfillCallConnectedPromise + self.rejectCallConnectedPromise = rejectCallConnectedPromise + + let (peerConnectionClientPromise, fulfillPeerConnectionClientPromise, rejectPeerConnectionClientPromise) = Promise.pending() + self.peerConnectionClientPromise = peerConnectionClientPromise + self.fulfillPeerConnectionClientPromise = fulfillPeerConnectionClientPromise + self.rejectPeerConnectionClientPromise = rejectPeerConnectionClientPromise + + let (readyToSendIceUpdatesPromise, fulfillReadyToSendIceUpdatesPromise, rejectReadyToSendIceUpdatesPromise) = Promise.pending() + self.readyToSendIceUpdatesPromise = readyToSendIceUpdatesPromise + self.fulfillReadyToSendIceUpdatesPromise = fulfillReadyToSendIceUpdatesPromise + self.rejectReadyToSendIceUpdatesPromise = rejectReadyToSendIceUpdatesPromise + + super.init() + } + + deinit { + Logger.debug("[SignalCallData] deinit") + } + + // MARK: - + + public func terminate() { + SwiftAssertIsOnMainThread(#function) + + Logger.debug("\(self.logTag) in \(#function)") + + self.call.removeAllObservers() + + // In case we're still waiting on this promise somewhere, we need to reject it to avoid a memory leak. + // There is no harm in rejecting a previously fulfilled promise. + rejectCallConnectedPromise(CallError.obsoleteCall(description: "Terminating call")) + + // In case we're still waiting on the peer connection setup somewhere, we need to reject it to avoid a memory leak. + // There is no harm in rejecting a previously fulfilled promise. + rejectPeerConnectionClientPromise(CallError.obsoleteCall(description: "Terminating call")) + + // In case we're still waiting on this promise somewhere, we need to reject it to avoid a memory leak. + // There is no harm in rejecting a previously fulfilled promise. + rejectReadyToSendIceUpdatesPromise(CallError.obsoleteCall(description: "Terminating call")) + + peerConnectionClient?.terminate() + Logger.debug("\(self.logTag) setting peerConnectionClient in \(#function)") + } +} + // This class' state should only be accessed on the main queue. @objc class CallService: NSObject, CallObserver, PeerConnectionClientDelegate { @@ -122,85 +224,71 @@ protocol CallServiceObserver: class { // MARK: Ivars - var peerConnectionClient: PeerConnectionClient? { + fileprivate var callData: SignalCallData? { didSet { SwiftAssertIsOnMainThread(#function) - Logger.debug("\(self.logTag) .peerConnectionClient setter: \(oldValue != nil) -> \(peerConnectionClient != nil) \(String(describing: peerConnectionClient))") - } - } - - var call: SignalCall? { - didSet { - SwiftAssertIsOnMainThread(#function) - - oldValue?.removeObserver(self) - call?.addObserverAndSyncState(observer: self) + oldValue?.call.removeObserver(self) + callData?.call.addObserverAndSyncState(observer: self) updateIsVideoEnabled() // Prevent device from sleeping while we have an active call. - if oldValue != call { + if oldValue != callData { if let oldValue = oldValue { DeviceSleepManager.sharedInstance.removeBlock(blockObject: oldValue) } stopAnyCallTimer() - if let call = call { - DeviceSleepManager.sharedInstance.addBlock(blockObject: call) + if let callData = callData { + DeviceSleepManager.sharedInstance.addBlock(blockObject: callData) self.startCallTimer() } } - Logger.debug("\(self.logTag) .call setter: \(oldValue?.identifiersForLogs as Optional) -> \(call?.identifiersForLogs as Optional)") + Logger.debug("\(self.logTag) .callData setter: \(oldValue?.call.identifiersForLogs as Optional) -> \(callData?.call.identifiersForLogs as Optional)") for observer in observers { - observer.value?.didUpdateCall(call: call) + observer.value?.didUpdateCall(call: callData?.call) } } } - // Used to coordinate promises across delegate methods - private var fulfillCallConnectedPromise: (() -> Void)? - private var rejectCallConnectedPromise: ((Error) -> Void)? - - // Used by waitForPeerConnectionClient to make sure any received - // ICE messages wait until the peer connection client is set up. - private var fulfillPeerConnectionClientPromise: (() -> Void)? - private var rejectPeerConnectionClientPromise: ((Error) -> Void)? - private var peerConnectionClientPromise: Promise? - - // Used by waituntilReadyToSendIceUpdates to make sure CallOffer was - // sent before sending any ICE updates. - private var fulfillReadyToSendIceUpdatesPromise: (() -> Void)? - private var rejectReadyToSendIceUpdatesPromise: ((Error) -> Void)? - private var readyToSendIceUpdatesPromise: Promise? - - weak var localVideoTrack: RTCVideoTrack? { - didSet { + var call: SignalCall? { + get { SwiftAssertIsOnMainThread(#function) - Logger.info("\(self.logTag) \(#function)") - - fireDidUpdateVideoTracks() + return callData?.call } } - - weak var remoteVideoTrack: RTCVideoTrack? { - didSet { + var peerConnectionClient: PeerConnectionClient? { + get { SwiftAssertIsOnMainThread(#function) - Logger.info("\(self.logTag) \(#function)") - - fireDidUpdateVideoTracks() + return callData?.peerConnectionClient } } - var isRemoteVideoEnabled = false { - didSet { + var localVideoTrack: RTCVideoTrack? { + get { SwiftAssertIsOnMainThread(#function) - Logger.info("\(self.logTag) \(#function): \(isRemoteVideoEnabled)") + return callData?.localVideoTrack + } + } + var remoteVideoTrack: RTCVideoTrack? { + get { + SwiftAssertIsOnMainThread(#function) - fireDidUpdateVideoTracks() + return callData?.remoteVideoTrack + } + } + var isRemoteVideoEnabled: Bool { + get { + SwiftAssertIsOnMainThread(#function) + + guard let callData = callData else { + return false + } + return callData.isRemoteVideoEnabled } } @@ -270,7 +358,8 @@ protocol CallServiceObserver: class { return Promise(error: CallError.assertionError(description: errorDescription)) } - self.call = call + let callData = SignalCallData(call: call) + self.callData = callData let callRecord = TSCall(timestamp: NSDate.ows_millisecondTimeStamp(), withCallNumber: call.remotePhoneNumber, callType: RPRecentCallTypeOutgoingIncomplete, in: call.thread) callRecord.save() @@ -283,7 +372,7 @@ protocol CallServiceObserver: class { throw CallError.obsoleteCall(description: "obsolete call in \(#function)") } - guard self.peerConnectionClient == nil else { + guard callData.peerConnectionClient == nil else { let errorDescription = "\(self.logTag) peerconnection was unexpectedly already set." Logger.error(errorDescription) OWSProdError(OWSAnalyticsEvents.callServicePeerConnectionAlreadySet(), file: #file, function: #function, line: #line) @@ -294,8 +383,8 @@ protocol CallServiceObserver: class { let peerConnectionClient = PeerConnectionClient(iceServers: iceServers, delegate: self, callDirection: .outgoing, useTurnOnly: useTurnOnly) Logger.debug("\(self.logTag) setting peerConnectionClient in \(#function) for call: \(call.identifiersForLogs)") - self.peerConnectionClient = peerConnectionClient - self.fulfillPeerConnectionClientPromise?() + callData.peerConnectionClient = peerConnectionClient + callData.fulfillPeerConnectionClientPromise() return peerConnectionClient.createOffer() }.then { (sessionDescription: HardenedRTCSessionDescription) -> Promise in @@ -321,10 +410,6 @@ protocol CallServiceObserver: class { // clients that don't support receiving ICE updates before receiving the call offer. self.readyToSendIceUpdates(call: call) - let (callConnectedPromise, fulfill, reject) = Promise.pending() - self.fulfillCallConnectedPromise = fulfill - self.rejectCallConnectedPromise = reject - // Don't let the outgoing call ring forever. We don't support inbound ringing forever anyway. let timeout: Promise = after(interval: connectingTimeoutSeconds).then { () -> Void in // This code will always be called, whether or not the call has timed out. @@ -333,7 +418,7 @@ protocol CallServiceObserver: class { throw CallError.timeout(description: "timed out waiting to receive call answer") } - return race(timeout, callConnectedPromise) + return race(timeout, callData.callConnectedPromise) }.then { Logger.info(self.call == call ? "\(self.logTag) outgoing call connected: \(call.identifiersForLogs)." @@ -360,22 +445,17 @@ protocol CallServiceObserver: class { func readyToSendIceUpdates(call: SignalCall) { SwiftAssertIsOnMainThread(#function) - guard self.call == call else { + guard let callData = self.callData else { self.handleFailedCall(failedCall: call, error: .obsoleteCall(description:"obsolete call in \(#function)")) return } - if self.fulfillReadyToSendIceUpdatesPromise == nil { - createReadyToSendIceUpdatesPromise() - } - - guard let fulfillReadyToSendIceUpdatesPromise = self.fulfillReadyToSendIceUpdatesPromise else { - OWSProdError(OWSAnalyticsEvents.callServiceMissingFulfillReadyToSendIceUpdatesPromise(), file: #file, function: #function, line: #line) - self.handleFailedCall(failedCall: call, error: CallError.assertionError(description: "failed to create fulfillReadyToSendIceUpdatesPromise")) + guard callData.call == call else { + self.handleFailedCall(failedCall: call, error: .obsoleteCall(description:"obsolete call in \(#function)")) return } - fulfillReadyToSendIceUpdatesPromise() + callData.fulfillReadyToSendIceUpdatesPromise() } /** @@ -559,7 +639,8 @@ protocol CallServiceObserver: class { Logger.info("\(self.logTag) starting new call: \(newCall.identifiersForLogs)") - self.call = newCall + let callData = SignalCallData(call: newCall) + self.callData = callData var backgroundTask: OWSBackgroundTask? = OWSBackgroundTask(label: "\(#function)", completionBlock: { [weak self] status in SwiftAssertIsOnMainThread(#function) @@ -598,8 +679,8 @@ protocol CallServiceObserver: class { Logger.debug("\(self.logTag) setting peerConnectionClient in \(#function) for: \(newCall.identifiersForLogs)") let peerConnectionClient = PeerConnectionClient(iceServers: iceServers, delegate: self, callDirection: .incoming, useTurnOnly: useTurnOnly) - self.peerConnectionClient = peerConnectionClient - self.fulfillPeerConnectionClientPromise?() + callData.peerConnectionClient = peerConnectionClient + callData.fulfillPeerConnectionClientPromise() let offerSessionDescription = RTCSessionDescription(type: .offer, sdp: callerSessionDescription) let constraints = RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: nil) @@ -627,8 +708,6 @@ protocol CallServiceObserver: class { // a more intuitive ordering. self.readyToSendIceUpdates(call: newCall) - let (promise, fulfill, reject) = Promise.pending() - let timeout: Promise = after(interval: connectingTimeoutSeconds).then { () -> Void in // rejecting a promise by throwing is safely a no-op if the promise has already been fulfilled OWSProdInfo(OWSAnalyticsEvents.callServiceErrorTimeoutWhileConnectingIncoming(), file: #file, function: #function, line: #line) @@ -636,10 +715,7 @@ protocol CallServiceObserver: class { } // This will be fulfilled (potentially) by the RTCDataChannel delegate method - self.fulfillCallConnectedPromise = fulfill - self.rejectCallConnectedPromise = reject - - return race(promise, timeout) + return race(callData.callConnectedPromise, timeout) }.then { Logger.info(self.call == newCall ? "\(self.logTag) incoming call connected: \(newCall.identifiersForLogs)." @@ -660,6 +736,7 @@ protocol CallServiceObserver: class { }.always { Logger.debug("\(self.logTag) ending background task awaiting inbound call connection") + assert(backgroundTask != nil) backgroundTask = nil } incomingCallPromise.retainUntilComplete() @@ -669,7 +746,15 @@ protocol CallServiceObserver: class { * Remote client (could be caller or callee) sent us a connectivity update */ public func handleRemoteAddedIceCandidate(thread: TSContactThread, callId: UInt64, sdp: String, lineIndex: Int32, mid: String) { - waitForPeerConnectionClient().then { () -> Void in + Logger.verbose("\(logTag) \(#function) callId: \(callId)") + + guard let callData = self.callData else { + OWSProdError(OWSAnalyticsEvents.callServiceCallMissing(), file: #file, function: #function, line: #line) + self.handleFailedCurrentCall(error: .obsoleteCall(description: "ignoring remote ice update, since there is no current call.")) + return + } + + callData.peerConnectionClientPromise.then { () -> Void in SwiftAssertIsOnMainThread(#function) guard let call = self.call else { @@ -692,10 +777,11 @@ protocol CallServiceObserver: class { return } + Logger.verbose("\(self.logTag) \(#function) addRemoteIceCandidate") peerConnectionClient.addRemoteIceCandidate(RTCIceCandidate(sdp: sdp, sdpMLineIndex: lineIndex, sdpMid: mid)) }.catch { error in OWSProdInfo(OWSAnalyticsEvents.callServiceErrorHandleRemoteAddedIceCandidate(), file: #file, function: #function, line: #line) - Logger.error("\(self.logTag) in \(#function) waitForPeerConnectionClient failed with error: \(error)") + Logger.error("\(self.logTag) in \(#function) peerConnectionClientPromise failed with error: \(error)") }.retainUntilComplete() } @@ -706,15 +792,16 @@ protocol CallServiceObserver: class { private func handleLocalAddedIceCandidate(_ iceCandidate: RTCIceCandidate) { SwiftAssertIsOnMainThread(#function) - guard let call = self.call else { + guard let callData = self.callData else { OWSProdError(OWSAnalyticsEvents.callServiceCallMissing(), file: #file, function: #function, line: #line) self.handleFailedCurrentCall(error: CallError.assertionError(description: "ignoring local ice candidate, since there is no current call.")) return } + let call = callData.call // Wait until we've sent the CallOffer before sending any ice updates for the call to ensure // intuitive message ordering for other clients. - waitUntilReadyToSendIceUpdates().then { () -> Void in + callData.readyToSendIceUpdatesPromise.then { () -> Void in guard call == self.call else { self.handleFailedCurrentCall(error: .obsoleteCall(description: "current call changed since we became ready to send ice updates")) return @@ -730,7 +817,7 @@ protocol CallServiceObserver: class { let iceUpdateMessage = OWSCallIceUpdateMessage(callId: call.signalingId, sdp: iceCandidate.sdp, sdpMLineIndex: iceCandidate.sdpMLineIndex, sdpMid: iceCandidate.sdpMid) - Logger.info("\(self.logTag) in \(#function) sending ICE Candidate.") + Logger.info("\(self.logTag) in \(#function) sending ICE Candidate \(call.identifiersForLogs).") let callMessage = OWSOutgoingCallMessage(thread: call.thread, iceUpdateMessage: iceUpdateMessage) let sendPromise = self.messageSender.sendPromise(message: callMessage) sendPromise.retainUntilComplete() @@ -876,13 +963,13 @@ protocol CallServiceObserver: class { Logger.debug("\(self.logTag) in \(#function)") - guard let currentCall = self.call else { - OWSProdError(OWSAnalyticsEvents.callServiceCallMissing(), file: #file, function: #function, line: #line) - handleFailedCall(failedCall: call, error: CallError.assertionError(description: "\(self.logTag) ignoring \(#function) since there is no current call")) + guard let currentCallData = self.callData else { + OWSProdError(OWSAnalyticsEvents.callServiceCallDataMissing(), file: #file, function: #function, line: #line) + handleFailedCall(failedCall: call, error: CallError.assertionError(description: "\(self.logTag) callData unexpectedly nil in \(#function)")) return } - guard call == currentCall else { + guard call == currentCallData.call else { // This could conceivably happen if the other party of an old call was slow to send us their answer // and we've subsequently engaged in another call. Don't kill the current call, but just ignore it. Logger.warn("\(self.logTag) ignoring \(#function) for call other than current call") @@ -904,14 +991,14 @@ protocol CallServiceObserver: class { let message = DataChannelMessage.forConnected(callId: call.signalingId) peerConnectionClient.sendDataChannelMessage(data: message.asData(), description: "connected", isCritical: true) - handleConnectedCall(call) + handleConnectedCall(currentCallData) } /** * For outgoing call, when the callee has chosen to accept the call. * For incoming call, when the local user has chosen to accept the call. */ - func handleConnectedCall(_ call: SignalCall) { + private func handleConnectedCall(_ callData: SignalCallData) { Logger.info("\(self.logTag) in \(#function)") SwiftAssertIsOnMainThread(#function) @@ -921,16 +1008,15 @@ protocol CallServiceObserver: class { return } - Logger.info("\(self.logTag) handleConnectedCall: \(call.identifiersForLogs).") + Logger.info("\(self.logTag) handleConnectedCall: \(callData.call.identifiersForLogs).") - assert(self.fulfillCallConnectedPromise != nil) // cancel connection timeout - self.fulfillCallConnectedPromise?() + callData.fulfillCallConnectedPromise() - call.state = .connected + callData.call.state = .connected // We don't risk transmitting any media until the remote client has admitted to being connected. - ensureAudioState(call: call, peerConnectionClient: peerConnectionClient) + ensureAudioState(call: callData.call, peerConnectionClient: peerConnectionClient) peerConnectionClient.setLocalVideoEnabled(enabled: shouldHaveLocalVideoTrack()) } @@ -1195,13 +1281,14 @@ protocol CallServiceObserver: class { private func handleDataChannelMessage(_ message: OWSWebRTCProtosData) { SwiftAssertIsOnMainThread(#function) - guard let call = self.call else { + guard let callData = self.callData else { // This should never happen; return to a known good state. owsFail("\(self.logTag) received data message, but there is no current call. Ignoring.") OWSProdError(OWSAnalyticsEvents.callServiceCallMissing(), file: #file, function: #function, line: #line) handleFailedCurrentCall(error: CallError.assertionError(description: "\(self.logTag) received data message, but there is no current call. Ignoring.")) return } + let call = callData.call if message.hasConnected() { Logger.debug("\(self.logTag) remote participant sent Connected via data channel: \(call.identifiersForLogs).") @@ -1217,7 +1304,7 @@ protocol CallServiceObserver: class { } self.callUIAdapter.recipientAcceptedCall(call) - handleConnectedCall(call) + handleConnectedCall(callData) } else if message.hasHangup() { Logger.debug("\(self.logTag) remote participant sent Hangup via data channel: \(call.identifiersForLogs).") @@ -1236,7 +1323,8 @@ protocol CallServiceObserver: class { } else if message.hasVideoStreamingStatus() { Logger.debug("\(self.logTag) remote participant sent VideoStreamingStatus via data channel: \(call.identifiersForLogs).") - self.isRemoteVideoEnabled = message.videoStreamingStatus.enabled() + callData.isRemoteVideoEnabled = message.videoStreamingStatus.enabled() + self.fireDidUpdateVideoTracks() } else { Logger.info("\(self.logTag) received unknown or empty DataChannelMessage: \(call.identifiersForLogs).") } @@ -1321,8 +1409,13 @@ protocol CallServiceObserver: class { Logger.debug("\(self.logTag) \(#function) Ignoring event from obsolete peerConnectionClient") return } + guard let callData = callData else { + Logger.debug("\(self.logTag) \(#function) Ignoring event from obsolete peerConnectionClient") + return + } - self.localVideoTrack = videoTrack + callData.localVideoTrack = videoTrack + fireDidUpdateVideoTracks() } internal func peerConnectionClient(_ peerConnectionClient: PeerConnectionClient, didUpdateRemote videoTrack: RTCVideoTrack?) { @@ -1332,94 +1425,16 @@ protocol CallServiceObserver: class { Logger.debug("\(self.logTag) \(#function) Ignoring event from obsolete peerConnectionClient") return } + guard let callData = callData else { + Logger.debug("\(self.logTag) \(#function) Ignoring event from obsolete peerConnectionClient") + return + } - self.remoteVideoTrack = videoTrack + callData.remoteVideoTrack = videoTrack + fireDidUpdateVideoTracks() } - // MARK: Helpers - - private func waitUntilReadyToSendIceUpdates() -> Promise { - SwiftAssertIsOnMainThread(#function) - - if self.readyToSendIceUpdatesPromise == nil { - createReadyToSendIceUpdatesPromise() - } - - guard let readyToSendIceUpdatesPromise = self.readyToSendIceUpdatesPromise else { - OWSProdError(OWSAnalyticsEvents.callServiceCouldNotCreateReadyToSendIceUpdatesPromise(), file: #file, function: #function, line: #line) - return Promise(error: CallError.assertionError(description: "failed to create readyToSendIceUpdatesPromise")) - } - - return readyToSendIceUpdatesPromise - } - - private func createReadyToSendIceUpdatesPromise() { - SwiftAssertIsOnMainThread(#function) - - guard self.readyToSendIceUpdatesPromise == nil else { - owsFail("expected readyToSendIceUpdatesPromise to be nil") - return - } - - guard self.fulfillReadyToSendIceUpdatesPromise == nil else { - owsFail("expected fulfillReadyToSendIceUpdatesPromise to be nil") - return - } - - guard self.rejectReadyToSendIceUpdatesPromise == nil else { - owsFail("expected rejectReadyToSendIceUpdatesPromise to be nil") - return - } - - let (promise, fulfill, reject) = Promise.pending() - self.fulfillReadyToSendIceUpdatesPromise = fulfill - self.rejectReadyToSendIceUpdatesPromise = reject - self.readyToSendIceUpdatesPromise = promise - } - - private func waitForPeerConnectionClient() -> Promise { - SwiftAssertIsOnMainThread(#function) - - guard self.peerConnectionClient == nil else { - // peerConnectionClient already set - return Promise(value: ()) - } - - if self.peerConnectionClientPromise == nil { - createPeerConnectionClientPromise() - } - - guard let peerConnectionClientPromise = self.peerConnectionClientPromise else { - OWSProdError(OWSAnalyticsEvents.callServiceCouldNotCreatePeerConnectionClientPromise(), file: #file, function: #function, line: #line) - return Promise(error: CallError.assertionError(description: "failed to create peerConnectionClientPromise")) - } - - return peerConnectionClientPromise - } - - private func createPeerConnectionClientPromise() { - SwiftAssertIsOnMainThread(#function) - - guard self.peerConnectionClientPromise == nil else { - owsFail("expected peerConnectionClientPromise to be nil") - return - } - - guard self.fulfillPeerConnectionClientPromise == nil else { - owsFail("expected fulfillPeerConnectionClientPromise to be nil") - return - } - - guard self.rejectPeerConnectionClientPromise == nil else { - owsFail("expected rejectPeerConnectionClientPromise to be nil") - return - } - - let (promise, fulfill, reject) = Promise.pending() - self.fulfillPeerConnectionClientPromise = fulfill - self.rejectPeerConnectionClientPromise = reject - self.peerConnectionClientPromise = promise - } + // MARK: - /** * RTCIceServers are used when attempting to establish an optimal connection to the other party. SignalService supplies @@ -1511,44 +1526,14 @@ protocol CallServiceObserver: class { Logger.debug("\(self.logTag) in \(#function)") - self.localVideoTrack = nil - self.remoteVideoTrack = nil - self.isRemoteVideoEnabled = false + let currentCallData = self.callData + self.callData = nil - self.peerConnectionClient?.terminate() - Logger.debug("\(self.logTag) setting peerConnectionClient in \(#function)") - self.peerConnectionClient = nil + currentCallData?.terminate() - self.call?.removeAllObservers() self.callUIAdapter.didTerminateCall(self.call) - self.call = nil - self.fulfillCallConnectedPromise = nil - - // In case we're still waiting on this promise somewhere, we need to reject it to avoid a memory leak. - // There is no harm in rejecting a previously fulfilled promise. - if let rejectCallConnectedPromise = self.rejectCallConnectedPromise { - rejectCallConnectedPromise(CallError.obsoleteCall(description: "Terminating call")) - } - self.rejectCallConnectedPromise = nil - - // In case we're still waiting on the peer connection setup somewhere, we need to reject it to avoid a memory leak. - // There is no harm in rejecting a previously fulfilled promise. - if let rejectPeerConnectionClientPromise = self.rejectPeerConnectionClientPromise { - rejectPeerConnectionClientPromise(CallError.obsoleteCall(description: "Terminating call")) - } - self.rejectPeerConnectionClientPromise = nil - self.fulfillPeerConnectionClientPromise = nil - self.peerConnectionClientPromise = nil - - // In case we're still waiting on this promise somewhere, we need to reject it to avoid a memory leak. - // There is no harm in rejecting a previously fulfilled promise. - if let rejectReadyToSendIceUpdatesPromise = self.rejectReadyToSendIceUpdatesPromise { - rejectReadyToSendIceUpdatesPromise(CallError.obsoleteCall(description: "Terminating call")) - } - self.fulfillReadyToSendIceUpdatesPromise = nil - self.rejectReadyToSendIceUpdatesPromise = nil - self.readyToSendIceUpdatesPromise = nil + fireDidUpdateVideoTracks() } // MARK: - CallObserver diff --git a/Signal/src/call/PeerConnectionClient.swift b/Signal/src/call/PeerConnectionClient.swift index 99ae3208ab..2a03883b4d 100644 --- a/Signal/src/call/PeerConnectionClient.swift +++ b/Signal/src/call/PeerConnectionClient.swift @@ -166,6 +166,10 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD } } + deinit { + Logger.debug("[PeerConnectionClient] deinit") + } + // MARK: - Media Streams private func createSignalingDataChannel() { diff --git a/SignalServiceKit/src/Util/OWSAnalyticsEvents.h b/SignalServiceKit/src/Util/OWSAnalyticsEvents.h index 759d9f576d..3d9daf59b1 100755 --- a/SignalServiceKit/src/Util/OWSAnalyticsEvents.h +++ b/SignalServiceKit/src/Util/OWSAnalyticsEvents.h @@ -76,6 +76,8 @@ NS_ASSUME_NONNULL_BEGIN + (NSString *)callServicePeerConnectionMissing; ++ (NSString *)callServiceCallDataMissing; + + (NSString *)contactsErrorContactsIntersectionFailed; + (NSString *)errorAttachmentRequestFailed; diff --git a/SignalServiceKit/src/Util/OWSAnalyticsEvents.m b/SignalServiceKit/src/Util/OWSAnalyticsEvents.m index 0e8b807221..635efac879 100755 --- a/SignalServiceKit/src/Util/OWSAnalyticsEvents.m +++ b/SignalServiceKit/src/Util/OWSAnalyticsEvents.m @@ -157,6 +157,11 @@ NS_ASSUME_NONNULL_BEGIN return @"call_service_peer_connection_missing"; } ++ (NSString *)callServiceCallDataMissing +{ + return @"call_service_call_data_missing"; +} + + (NSString *)contactsErrorContactsIntersectionFailed { return @"contacts_error_contacts_intersection_failed";