Ignore call messages that won't trigger CallKit ring

This commit is contained in:
Michelle Linington 2021-09-21 18:09:08 -07:00
parent 48435eb05d
commit 3f956130c6
6 changed files with 91 additions and 49 deletions

View File

@ -20,6 +20,22 @@ public class NSECallMessageHandler: NSObject, OWSCallMessageHandler {
// MARK: - Call Handlers
public func action(for envelope: SSKProtoEnvelope, callMessage: SSKProtoCallMessage) -> OWSCallMessageAction {
// We should only handoff messages that are likely to cause a ring.
guard Date.ows_millisecondTimestamp() - envelope.timestamp < 5 * kMinuteInMs else {
Logger.info("Discarding very old call message. No longer relevant.")
}
if callMessage.offer != nil {
return .handoff
} else if let opaqueMessage = callMessage.opaque, opaqueMessage.urgency == .handleImmediately {
return .handoff
} else {
Logger.info("Ignoring call message. Not an offer or urgent opaque message.")
return .ignore
}
}
public func externallyHandleCallMessage(
envelope: SSKProtoEnvelope,
plaintextData: Data,
@ -27,48 +43,37 @@ public class NSECallMessageHandler: NSObject, OWSCallMessageHandler {
serverDeliveryTimestamp: UInt64,
transaction: SDSAnyWriteTransaction
) -> Bool {
do {
// There used to be a way to hand off decrypted messages to the main
// app for processing, but it was prone to races. We have ideas to fix
// this but in the meantime, we'll be more aggressive about blocking
// and handoff.
//
// If we successfully wake the main app, we should kill the NSE mid-
// transaction without acking to the service. This will roll back our
// sessions to their prior state and the service will re-deliver
// the encrypted message to the main app.
//
// We have to block on a semaphore because we don't want to continue
// processing other messages and risk this transaction committing until
// we know that the main app isn't going to be launched.
// let payload = try CallMessageRelay.enqueueCallMessageForMainApp(
// envelope: envelope,
// plaintextData: plaintextData,
// wasReceivedByUD: wasReceivedByUD,
// serverDeliveryTimestamp: serverDeliveryTimestamp,
// transaction: transaction
// )
// There used to be a way to hand off decrypted messages to the main
// app for processing, but it was prone to races. We have ideas to fix
// this but in the meantime, we'll be more aggressive about blocking
// and handoff.
//
// If we successfully wake the main app, we should kill the NSE mid-
// transaction without acking to the service. This will roll back our
// sessions to their prior state and the service will re-deliver
// the encrypted message to the main app.
//
// We have to block on a semaphore because we don't want to continue
// processing other messages and risk this transaction committing until
// we know that the main app isn't going to be launched.
// let payload = try CallMessageRelay.enqueueCallMessageForMainApp(
let payload = CallMessageRelay.wakeMainAppPayload()
let payload = CallMessageRelay.wakeMainAppPayload()
// This semaphore should only be signalled if we failed to wake the main app.
// If we successfully wake the main app, we should exit.
let sema = DispatchSemaphore(value: 0)
CXProvider.reportNewIncomingVoIPPushPayload(payload) { error in
if let error = error {
owsFailDebug("Failed to notify main app of call message: \(error)")
sema.signal()
} else {
Logger.info("Successfully notified main app of call message. Quitting...")
exit(0)
}
}
if sema.wait(timeout: .now() + .seconds(30)) == .timedOut {
owsFail("Timed out waiting for response from main app")
// This semaphore should only be signalled if we failed to wake the main app.
// If we successfully wake the main app, we should exit.
let sema = DispatchSemaphore(value: 0)
CXProvider.reportNewIncomingVoIPPushPayload(payload) { error in
if let error = error {
owsFailDebug("Failed to notify main app of call message: \(error)")
sema.signal()
} else {
Logger.info("Successfully notified main app of call message. Quitting...")
exit(0)
}
}
if sema.wait(timeout: .now() + .seconds(30)) == .timedOut {
owsFail("Timed out waiting for response from main app")
}
return true
}
@ -114,6 +119,7 @@ public class NSECallMessageHandler: NSObject, OWSCallMessageHandler {
_ update: SSKProtoDataMessageGroupCallUpdate,
for groupThread: TSGroupThread,
serverReceivedTimestamp: UInt64) {
owsFailDebug("This should never be called, calls are handled externally")
// TODO: Let's fix this soon, but for now let's just ignore to unblock iOS 15
Logger.warn("Ignoring group call update message delivered to NSE")
}
}

View File

@ -20,6 +20,10 @@ public class WebRTCCallMessageHandler: NSObject, OWSCallMessageHandler {
// MARK: - Call Handlers
public func action(for envelope: SSKProtoEnvelope, callMessage: SSKProtoCallMessage) -> OWSCallMessageAction {
.process
}
public func receivedOffer(
_ offer: SSKProtoCallMessageOffer,
from caller: SignalServiceAddress,

View File

@ -7,6 +7,10 @@ import SignalServiceKit
@objc
public class NoopCallMessageHandler: NSObject, OWSCallMessageHandler {
public func action(for envelope: SSKProtoEnvelope, callMessage: SSKProtoCallMessage) -> OWSCallMessageAction {
.process
}
public func receivedOffer(_ offer: SSKProtoCallMessageOffer, from caller: SignalServiceAddress, sourceDevice device: UInt32, sentAtTimestamp: UInt64, serverReceivedTimestamp: UInt64, serverDeliveryTimestamp: UInt64, supportsMultiRing: Bool) {
owsFailDebug("")
}

View File

@ -428,15 +428,25 @@ NS_ASSUME_NONNULL_BEGIN
OWSLogInfo(@"Discarding message with timestamp: %llu", envelope.timestamp);
return;
}
if (![self.callMessageHandler externallyHandleCallMessageWithEnvelope:envelope
plaintextData:plaintextData
wasReceivedByUD:wasReceivedByUD
serverDeliveryTimestamp:serverDeliveryTimestamp
transaction:transaction]) {
[self handleIncomingEnvelope:envelope
withCallMessage:contentProto.callMessage
serverDeliveryTimestamp:serverDeliveryTimestamp
transaction:transaction];
OWSCallMessageAction action = [self.callMessageHandler actionForEnvelope:envelope
callMessage:contentProto.callMessage];
switch (action) {
case OWSCallMessageActionIgnore:
OWSLogInfo(@"Ignoring call message with timestamp: %llu", envelope.timestamp);
break;
case OWSCallMessageActionHandoff:
[self.callMessageHandler externallyHandleCallMessageWithEnvelope:envelope
plaintextData:plaintextData
wasReceivedByUD:wasReceivedByUD
serverDeliveryTimestamp:serverDeliveryTimestamp
transaction:transaction];
break;
case OWSCallMessageActionProcess:
[self handleIncomingEnvelope:envelope
withCallMessage:contentProto.callMessage
serverDeliveryTimestamp:serverDeliveryTimestamp
transaction:transaction];
break;
}
} else if (contentProto.typingMessage) {
[self handleIncomingEnvelope:envelope

View File

@ -5,6 +5,7 @@
NS_ASSUME_NONNULL_BEGIN
@class SDSAnyWriteTransaction;
@class SSKProtoCallMessage;
@class SSKProtoCallMessageAnswer;
@class SSKProtoCallMessageBusy;
@class SSKProtoCallMessageHangup;
@ -16,8 +17,20 @@ NS_ASSUME_NONNULL_BEGIN
@class SignalServiceAddress;
@class TSGroupThread;
typedef NS_ENUM(NSUInteger, OWSCallMessageAction) {
// This message should not be processed
OWSCallMessageActionIgnore,
// Process the message by deferring to -externallyHandleCallMessage...
OWSCallMessageActionHandoff,
// Process the message normally
OWSCallMessageActionProcess,
};
@protocol OWSCallMessageHandler <NSObject>
/// Informs caller of how the handler would like to handle this message
- (OWSCallMessageAction)actionForEnvelope:(SSKProtoEnvelope *)envelope callMessage:(SSKProtoCallMessage *)message;
- (void)receivedOffer:(SSKProtoCallMessageOffer *)offer
fromCaller:(SignalServiceAddress *)caller
sourceDevice:(uint32_t)device

View File

@ -10,6 +10,11 @@ NS_ASSUME_NONNULL_BEGIN
@implementation OWSFakeCallMessageHandler
- (OWSCallMessageAction)actionForEnvelope:(SSKProtoEnvelope *)envelope callMessage:(SSKProtoCallMessage *)message;
{
return OWSCallMessageActionProcess;
}
- (void)receivedOffer:(SSKProtoCallMessageOffer *)offer
fromCaller:(SignalServiceAddress *)caller
sourceDevice:(uint32_t)device