diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index ca903eb09..52851aa24 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -334,8 +334,55 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; - (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray * _Nullable))restorationHandler { if ([userActivity.activityType isEqualToString:@"INStartVideoCallIntent"]) { + if (!SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(10, 0)) { + DDLogError(@"%@ unexpectedly received INStartVideoCallIntent pre iOS10", self.tag); + return NO; + } + DDLogInfo(@"%@ got start video call intent", self.tag); - [[Environment getCurrent].callService handleCallKitStartVideo]; + + INInteraction *interaction = [userActivity interaction]; + INIntent *intent = interaction.intent; + + if (![intent isKindOfClass:[INStartVideoCallIntent class]]) { + DDLogError(@"%@ unexpected class for start call video: %@", self.tag, intent); + return NO; + } + INStartVideoCallIntent *startCallIntent = (INStartVideoCallIntent *)intent; + NSString *_Nullable handle = startCallIntent.contacts.firstObject.personHandle.value; + if (!handle) { + DDLogWarn(@"%@ unable to find handle in startCallIntent: %@", self.tag, startCallIntent); + return NO; + } + + if ([Environment getCurrent].phoneManager.hasOngoingCall) { + DDLogWarn(@"%@ ignoring INStartVideoCallIntent due to ongoing RedPhone call.", self.tag); + return NO; + } + + // This intent can be received from more than one user interaction. + // + // * It can be received if the user taps the "video" button in the CallKit UI for an + // an ongoing call. If so, the correct response is to try to activate the local + // video for that call. + // * It can be received if the user taps the "video" button for a contact in the + // contacts app. If so, the correct response is to try to initiate a new call + // to that user - unless there already is another call in progress. + if ([Environment getCurrent].callService.call != nil) { + if ([handle isEqualToString:[Environment getCurrent].callService.call.remotePhoneNumber]) { + DDLogWarn(@"%@ trying to upgrade ongoing call to video.", self.tag); + [[Environment getCurrent].callService handleCallKitStartVideo]; + return YES; + } else { + DDLogWarn( + @"%@ ignoring INStartVideoCallIntent due to ongoing WebRTC call with another party.", self.tag); + return NO; + } + } + + OutboundCallInitiator *outboundCallInitiator = [Environment getCurrent].outboundCallInitiator; + OWSAssert(outboundCallInitiator); + return [outboundCallInitiator initiateCallWithHandle:handle]; } else if ([userActivity.activityType isEqualToString:@"INStartAudioCallIntent"]) { if (!SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(10, 0)) { @@ -359,6 +406,15 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; return NO; } + if ([Environment getCurrent].phoneManager.hasOngoingCall) { + DDLogWarn(@"%@ ignoring INStartAudioCallIntent due to ongoing RedPhone call.", self.tag); + return NO; + } + if ([Environment getCurrent].callService.call != nil) { + DDLogWarn(@"%@ ignoring INStartAudioCallIntent due to ongoing WebRTC call.", self.tag); + return NO; + } + OutboundCallInitiator *outboundCallInitiator = [Environment getCurrent].outboundCallInitiator; OWSAssert(outboundCallInitiator); return [outboundCallInitiator initiateCallWithHandle:handle]; diff --git a/Signal/src/call/OutboundCallInitiator.swift b/Signal/src/call/OutboundCallInitiator.swift index 547357ab5..f62854bb0 100644 --- a/Signal/src/call/OutboundCallInitiator.swift +++ b/Signal/src/call/OutboundCallInitiator.swift @@ -52,6 +52,16 @@ import Foundation // SignalRecipient *recipient = [SignalRecipient recipientWithTextSecureIdentifier:self.thread.contactIdentifier]; self.contactsUpdater.lookupIdentifier(recipientId, success: { recipient in + + guard !Environment.getCurrent().phoneManager.hasOngoingCall() else { + Logger.error("\(self.TAG) OutboundCallInitiator aborting due to ongoing RedPhone call.") + return + } + guard Environment.getCurrent().callService.call != nil else { + Logger.error("\(self.TAG) OutboundCallInitiator aborting due to ongoing WebRTC call.") + return + } + let remoteWantsWebRTC = recipient.supportsWebRTC Logger.debug("\(self.TAG) localWantsWebRTC: \(localWantsWebRTC), remoteWantsWebRTC: \(remoteWantsWebRTC)") diff --git a/Signal/src/phone/PhoneManager.h b/Signal/src/phone/PhoneManager.h index 168f6580f..fb793bc3b 100644 --- a/Signal/src/phone/PhoneManager.h +++ b/Signal/src/phone/PhoneManager.h @@ -1,3 +1,7 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + #import #import "CallAudioManager.h" #import "CallConnectUtil.h" @@ -36,6 +40,9 @@ - (BOOL)toggleMute; - (void)backgroundTimeExpired; +// Returns YES IFF there is an ongoing RedPhone call. +- (BOOL)hasOngoingCall; + - (ObservableValue *)currentCallObservable; + (PhoneManager *)phoneManagerWithErrorHandler:(ErrorHandlerBlock)errorHandler; diff --git a/Signal/src/phone/PhoneManager.m b/Signal/src/phone/PhoneManager.m index 120a51bd4..d8caa2a96 100644 --- a/Signal/src/phone/PhoneManager.m +++ b/Signal/src/phone/PhoneManager.m @@ -1,3 +1,7 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + #import "AppAudioManager.h" #import "CallAudioManager.h" #import "PhoneManager.h" @@ -132,6 +136,12 @@ } incoming:YES]; } + +- (BOOL)hasOngoingCall +{ + return self.curCallController != nil; +} + - (CallController *)curCallController { return currentCallControllerObservable.currentValue; }