Suspend while answering shows "Missed Call"

When awakened by a voip push, we get some arbitrary amount of background
time to connect the call (in practice this is ~30s) before the app is
suspended.

Though we were properly terminating the call upon being suspended, we
were not notifying the user that they had missed a call.

// FREEBIE
pull/1/head
Michael Kirk 8 years ago
parent a434a381f7
commit dd5a19d1fd

@ -79,7 +79,7 @@ enum CallError: Error {
} }
// Should be roughly synced with Android client for consistency // Should be roughly synced with Android client for consistency
private let connectingTimeoutSeconds = 120 private let connectingTimeoutSeconds: TimeInterval = 120
// All Observer methods will be invoked from the main thread. // All Observer methods will be invoked from the main thread.
protocol CallServiceObserver: class { protocol CallServiceObserver: class {
@ -338,7 +338,7 @@ protocol CallServiceObserver: class {
self.rejectCallConnectedPromise = reject self.rejectCallConnectedPromise = reject
// Don't let the outgoing call ring forever. We don't support inbound ringing forever anyway. // Don't let the outgoing call ring forever. We don't support inbound ringing forever anyway.
let timeout: Promise<Void> = after(interval: TimeInterval(connectingTimeoutSeconds)).then { () -> Void in let timeout: Promise<Void> = after(interval: connectingTimeoutSeconds).then { () -> Void in
// rejecting a promise by throwing is safely a no-op if the promise has already been fulfilled // rejecting a promise by throwing is safely a no-op if the promise has already been fulfilled
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorTimeoutWhileConnectingOutgoing(), file:#file, function:#function, line:#line) OWSProdInfo(OWSAnalyticsEvents.callServiceErrorTimeoutWhileConnectingOutgoing(), file:#file, function:#function, line:#line)
throw CallError.timeout(description: "timed out waiting to receive call answer") throw CallError.timeout(description: "timed out waiting to receive call answer")
@ -441,7 +441,7 @@ protocol CallServiceObserver: class {
/** /**
* User didn't answer incoming call * User didn't answer incoming call
*/ */
public func handleMissedCall(_ call: SignalCall, thread: TSContactThread) { public func handleMissedCall(_ call: SignalCall) {
AssertIsOnMainThread() AssertIsOnMainThread()
// Insert missed call record // Insert missed call record
@ -451,9 +451,9 @@ protocol CallServiceObserver: class {
} }
} else { } else {
call.callRecord = TSCall(timestamp: NSDate.ows_millisecondTimeStamp(), call.callRecord = TSCall(timestamp: NSDate.ows_millisecondTimeStamp(),
withCallNumber: thread.contactIdentifier(), withCallNumber: call.thread.contactIdentifier(),
callType: RPRecentCallTypeMissed, callType: RPRecentCallTypeMissed,
in: thread) in: call.thread)
} }
assert(call.callRecord != nil) assert(call.callRecord != nil)
@ -465,16 +465,16 @@ protocol CallServiceObserver: class {
/** /**
* Received a call while already in another call. * Received a call while already in another call.
*/ */
private func handleLocalBusyCall(_ call: SignalCall, thread: TSContactThread) { private func handleLocalBusyCall(_ call: SignalCall) {
Logger.info("\(TAG) \(#function) for call: \(call.identifiersForLogs) thread: \(thread.contactIdentifier())") Logger.info("\(TAG) \(#function) for call: \(call.identifiersForLogs) thread: \(call.thread.contactIdentifier())")
AssertIsOnMainThread() AssertIsOnMainThread()
let busyMessage = OWSCallBusyMessage(callId: call.signalingId) let busyMessage = OWSCallBusyMessage(callId: call.signalingId)
let callMessage = OWSOutgoingCallMessage(thread: thread, busyMessage: busyMessage) let callMessage = OWSOutgoingCallMessage(thread: call.thread, busyMessage: busyMessage)
let sendPromise = messageSender.sendPromise(message: callMessage) let sendPromise = messageSender.sendPromise(message: callMessage)
sendPromise.retainUntilComplete() sendPromise.retainUntilComplete()
handleMissedCall(call, thread: thread) handleMissedCall(call)
} }
/** /**
@ -551,7 +551,7 @@ protocol CallServiceObserver: class {
// TODO on iOS10+ we can use CallKit to swap calls rather than just returning busy immediately. // TODO on iOS10+ we can use CallKit to swap calls rather than just returning busy immediately.
Logger.info("\(TAG) receivedCallOffer: \(newCall.identifiersForLogs) but we're already in call: \(existingCall.identifiersForLogs)") Logger.info("\(TAG) receivedCallOffer: \(newCall.identifiersForLogs) but we're already in call: \(existingCall.identifiersForLogs)")
handleLocalBusyCall(newCall, thread: thread) handleLocalBusyCall(newCall)
if existingCall.remotePhoneNumber == newCall.remotePhoneNumber { if existingCall.remotePhoneNumber == newCall.remotePhoneNumber {
Logger.info("\(TAG) handling call from current call user as remote busy.: \(newCall.identifiersForLogs) but we're already in call: \(existingCall.identifiersForLogs)") Logger.info("\(TAG) handling call from current call user as remote busy.: \(newCall.identifiersForLogs) but we're already in call: \(existingCall.identifiersForLogs)")
@ -642,7 +642,7 @@ protocol CallServiceObserver: class {
let (promise, fulfill, reject) = Promise<Void>.pending() let (promise, fulfill, reject) = Promise<Void>.pending()
let timeout: Promise<Void> = after(interval: TimeInterval(connectingTimeoutSeconds)).then { () -> Void in let timeout: Promise<Void> = after(interval: connectingTimeoutSeconds).then { () -> Void in
// rejecting a promise by throwing is safely a no-op if the promise has already been fulfilled // 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) OWSProdInfo(OWSAnalyticsEvents.callServiceErrorTimeoutWhileConnectingIncoming(), file:#file, function:#function, line:#line)
throw CallError.timeout(description: "timed out waiting for call to connect") throw CallError.timeout(description: "timed out waiting for call to connect")
@ -824,7 +824,7 @@ protocol CallServiceObserver: class {
switch call.state { switch call.state {
case .idle, .dialing, .answering, .localRinging, .localFailure, .remoteBusy, .remoteRinging: case .idle, .dialing, .answering, .localRinging, .localFailure, .remoteBusy, .remoteRinging:
handleMissedCall(call, thread: thread) handleMissedCall(call)
case .connected, .localHangup, .remoteHangup: case .connected, .localHangup, .remoteHangup:
Logger.info("\(TAG) call is finished.") Logger.info("\(TAG) call is finished.")
} }
@ -1403,6 +1403,14 @@ protocol CallServiceObserver: class {
} }
if let failedCall = failedCall { if let failedCall = failedCall {
if failedCall.state == .answering {
assert(failedCall.callRecord == nil)
// call failed before any call record could be created, make one now.
handleMissedCall(failedCall)
}
assert(failedCall.callRecord != nil)
// It's essential to set call.state before terminateCall, because terminateCall nils self.call // It's essential to set call.state before terminateCall, because terminateCall nils self.call
failedCall.error = error failedCall.error = error
failedCall.state = .localFailure failedCall.state = .localFailure

Loading…
Cancel
Save