From 6eb1951ee6d7810eaa69fa48e52b8b96622fa4bf Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Fri, 16 Feb 2018 17:40:37 -0800 Subject: [PATCH 1/2] Don't stop audio until after CallKit audio session is deactivated // FREEBIE --- Signal/src/call/Speakerbox/CallKitCallUIAdaptee.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Signal/src/call/Speakerbox/CallKitCallUIAdaptee.swift b/Signal/src/call/Speakerbox/CallKitCallUIAdaptee.swift index a2ff90b1d..36b9bb230 100644 --- a/Signal/src/call/Speakerbox/CallKitCallUIAdaptee.swift +++ b/Signal/src/call/Speakerbox/CallKitCallUIAdaptee.swift @@ -25,6 +25,7 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate { internal let notificationsAdapter: CallNotificationsAdapter internal let contactsManager: OWSContactsManager private let provider: CXProvider + let audioActivity: AudioActivity // CallKit handles incoming ringer stop/start for us. Yay! let hasManualRinger = false @@ -60,6 +61,7 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate { self.contactsManager = contactsManager self.notificationsAdapter = notificationsAdapter self.provider = CXProvider(configuration: type(of: self).providerConfiguration) + self.audioActivity = AudioActivity(audioDescription: "[CallKitCallUIAdaptee]") super.init() @@ -348,6 +350,7 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate { Logger.debug("\(TAG) Received \(#function)") + OWSAudioSession.shared.startAudioActivity(self.audioActivity) OWSAudioSession.shared.isRTCAudioEnabled = true } @@ -356,6 +359,7 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate { Logger.debug("\(TAG) Received \(#function)") OWSAudioSession.shared.isRTCAudioEnabled = false + OWSAudioSession.shared.endAudioActivity(self.audioActivity) } // MARK: - Util From 8dfe06e3f2195b8b372154a7d5a5ca031457b056 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Fri, 16 Feb 2018 17:41:41 -0800 Subject: [PATCH 2/2] Ensure audio session is default after call is terminated. Previously, we were enabling PlayAndRecord when a call ended due to the CallService observer's "updateVideoTrack" callbacks. // FREEBIE --- Signal/src/ViewControllers/AboutTableViewController.m | 7 +++++++ Signal/src/call/CallAudioService.swift | 4 +++- Signal/src/call/CallService.swift | 3 +++ Signal/src/call/SignalCall.swift | 9 +++++++++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Signal/src/ViewControllers/AboutTableViewController.m b/Signal/src/ViewControllers/AboutTableViewController.m index a0efd3199..4d3000ba2 100644 --- a/Signal/src/ViewControllers/AboutTableViewController.m +++ b/Signal/src/ViewControllers/AboutTableViewController.m @@ -90,6 +90,13 @@ NSString *_Nullable voipToken = [preferences getVoipToken]; [debugSection addItem:[OWSTableItem labelItemWithText:[NSString stringWithFormat:@"Push Token: %@", pushToken ?: @"None" ]]]; [debugSection addItem:[OWSTableItem labelItemWithText:[NSString stringWithFormat:@"VOIP Token: %@", voipToken ?: @"None" ]]]; + + // Strip prefix from category, otherwise it's too long to fit into cell on a small device. + NSString *audioCategory = + [AVAudioSession.sharedInstance.category stringByReplacingOccurrencesOfString:@"AVAudioSessionCategory" + withString:@""]; + [debugSection + addItem:[OWSTableItem labelItemWithText:[NSString stringWithFormat:@"Audio Category: %@", audioCategory]]]; #endif self.contents = contents; diff --git a/Signal/src/call/CallAudioService.swift b/Signal/src/call/CallAudioService.swift index 6617bbe24..43d28884f 100644 --- a/Signal/src/call/CallAudioService.swift +++ b/Signal/src/call/CallAudioService.swift @@ -159,6 +159,7 @@ protocol CallAudioServiceDelegate: class { super.init() + // This fails when someone toggles iOS Call Integration SwiftSingletons.register(self) // Configure audio session so we don't prompt user with Record permission until call is connected. @@ -242,7 +243,8 @@ protocol CallAudioServiceDelegate: class { private func ensureProperAudioSession(call: SignalCall?) { AssertIsOnMainThread() - guard let call = call else { + guard let call = call, !call.isTerminated else { + // Revert to default audio setAudioSession(category: AVAudioSessionCategorySoloAmbient, mode: AVAudioSessionModeDefault) return diff --git a/Signal/src/call/CallService.swift b/Signal/src/call/CallService.swift index 6bed97cb8..e346ae5ee 100644 --- a/Signal/src/call/CallService.swift +++ b/Signal/src/call/CallService.swift @@ -1507,6 +1507,9 @@ protocol CallServiceObserver: class { Logger.info("\(self.logTag) clearing pendingIceUpdateMessages") self.pendingIceUpdateMessages = [] 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")) } diff --git a/Signal/src/call/SignalCall.swift b/Signal/src/call/SignalCall.swift index 23e4f199b..186b99fa6 100644 --- a/Signal/src/call/SignalCall.swift +++ b/Signal/src/call/SignalCall.swift @@ -43,6 +43,15 @@ protocol CallObserver: class { var observers = [Weak]() let remotePhoneNumber: String + var isTerminated: Bool { + switch state { + case .localFailure, .localHangup, .remoteHangup, .remoteBusy: + return true + case .idle, .dialing, .answering, .remoteRinging, .localRinging, .connected: + return false + } + } + // Signal Service identifier for this Call. Used to coordinate the call across remote clients. let signalingId: UInt64