Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VoIP: Fixes for PushKit Crashes #4424

Merged
merged 7 commits into from
Jun 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Changes to be released in next version
* RoomDirectCallStatusBubbleCell: Fix crash when entering a DM after a call is hung-up/rejected while being answered (#4403).
* ContactsDataSource: iPad Crashes when you select a contact in search and then collapse a section or clear the query text (#4414).
* SettingsViewController: Fix "auto" theme message to clarify that it matches the system theme on iOS 13+ (#2860).
* VoIP: Handle application inactive state too for VoIP pushes (#4269).
* VoIP: Do not terminate the app if protected data not available (#4419).

⚠️ API Changes
*
Expand Down
130 changes: 71 additions & 59 deletions Riot/Managers/PushNotification/PushNotificationService.m
Original file line number Diff line number Diff line change
Expand Up @@ -178,16 +178,17 @@ - (void)applicationWillResignActive
{
[[UNUserNotificationCenter currentNotificationCenter] removeUnwantedNotifications];
[[UNUserNotificationCenter currentNotificationCenter] removeCallNotificationsFor:nil];
}

- (void)applicationDidEnterBackground
{
if (_pushNotificationStore.pushKitToken)
{
self.shouldReceiveVoIPPushes = YES;
}
}

- (void)applicationDidEnterBackground
{

}

- (void)applicationDidBecomeActive
{
[[UNUserNotificationCenter currentNotificationCenter] removeUnwantedNotifications];
Expand Down Expand Up @@ -227,6 +228,8 @@ - (void)setShouldReceiveVoIPPushes:(BOOL)shouldReceiveVoIPPushes
{
_shouldReceiveVoIPPushes = shouldReceiveVoIPPushes;

MXLogDebug(@"[PushNotificationService] setShouldReceiveVoIPPushes: %u", _shouldReceiveVoIPPushes)

if (_shouldReceiveVoIPPushes && _pushNotificationStore.pushKitToken)
{
MXSession *session = [AppDelegate theDelegate].mxSessions.firstObject;
Expand Down Expand Up @@ -256,16 +259,25 @@ - (void)setShouldReceiveVoIPPushes:(BOOL)shouldReceiveVoIPPushes
}
else
{
_pushRegistry.delegate = nil;
[self deconfigurePushKit];
}
}

- (void)configurePushKit
{
MXLogDebug(@"[PushNotificationService] configurePushKit")

_pushRegistry.delegate = self;
_pushRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];
}

- (void)deconfigurePushKit
{
MXLogDebug(@"[PushNotificationService] deconfigurePushKit")

_pushRegistry.delegate = nil;
}

- (void)removePusher:(MXPusher*)pusher inSession:(MXSession*)session
{
MXLogDebug(@"[PushNotificationService][Push] removePusher: %@", pusher.appId);
Expand Down Expand Up @@ -547,83 +559,83 @@ - (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayloa
[[UNUserNotificationCenter currentNotificationCenter] removeUnwantedNotifications];
[[UNUserNotificationCenter currentNotificationCenter] removeCallNotificationsFor:roomId];

if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground)
if (@available(iOS 13.0, *))
{
MXLogDebug(@"[PushNotificationService] didReceiveIncomingPushWithPayload: application is in bg");
// for iOS 13, we'll just report the incoming call in the same runloop. It means we cannot call an async API here.
MXEvent *callInvite = [_pushNotificationStore callInviteForEventId:eventId];
// remove event
[_pushNotificationStore removeCallInviteWithEventId:eventId];
MXSession *session = [AppDelegate theDelegate].mxSessions.firstObject;
// when we have a VoIP push while the application is killed, session.callManager will not be ready yet. Configure it.
[[AppDelegate theDelegate] configureCallManagerIfRequiredForSession:session];

if (@available(iOS 13.0, *))
MXLogDebug(@"[PushNotificationService] didReceiveIncomingPushWithPayload: iOS 13+, callInvite: %@", callInvite);

if (callInvite)
{
// for iOS 13, we'll just report the incoming call in the same runloop. It means we cannot call an async API here.
MXEvent *callInvite = [_pushNotificationStore callInviteForEventId:eventId];
// remove event
[_pushNotificationStore removeCallInviteWithEventId:eventId];
MXSession *session = [AppDelegate theDelegate].mxSessions.firstObject;
// when we have a VoIP push while the application is killed, session.callManager will not be ready yet. Configure it.
[[AppDelegate theDelegate] configureCallManagerIfRequiredForSession:session];
// We're using this dispatch_group to continue event stream after cache fully processed.
dispatch_group_t dispatchGroup = dispatch_group_create();

MXLogDebug(@"[PushNotificationService] didReceiveIncomingPushWithPayload: callInvite: %@", callInvite);
dispatch_group_enter(dispatchGroup);
// Not continuing in completion block here, because PushKit mandates reporting a new call in the same run loop.
// 'handleBackgroundSyncCacheIfRequiredWithCompletion' is processing to-device events synchronously.
[session handleBackgroundSyncCacheIfRequiredWithCompletion:^{
dispatch_group_leave(dispatchGroup);
}];

if (callInvite)
if (callInvite.eventType == MXEventTypeCallInvite)
{
// We're using this dispatch_group to continue event stream after cache fully processed.
dispatch_group_t dispatchGroup = dispatch_group_create();

dispatch_group_enter(dispatchGroup);
// Not continuing in completion block here, because PushKit mandates reporting a new call in the same run loop.
// 'handleBackgroundSyncCacheIfRequiredWithCompletion' is processing to-device events synchronously.
[session handleBackgroundSyncCacheIfRequiredWithCompletion:^{
dispatch_group_leave(dispatchGroup);
}];

if (callInvite.eventType == MXEventTypeCallInvite)
{
// process the call invite synchronously
[session.callManager handleCallEvent:callInvite];
MXCallInviteEventContent *content = [MXCallInviteEventContent modelFromJSON:callInvite.content];
MXCall *call = [session.callManager callWithCallId:content.callId];
if (call)
{
[session.callManager.callKitAdapter reportIncomingCall:call];
MXLogDebug(@"[PushNotificationService] didReceiveIncomingPushWithPayload: Reporting new call in room %@ for the event: %@", roomId, eventId);

// Wait for the sync response in cache to be processed for data integrity.
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{
// After reporting the call, we can continue async. Launch a background sync to handle call answers/declines on other devices of the user.
[self launchBackgroundSync];
});
}
else
{
MXLogDebug(@"[PushNotificationService] didReceiveIncomingPushWithPayload: Error on call object on room %@ for the event: %@", roomId, eventId);
}
}
else if ([callInvite.type isEqualToString:kWidgetMatrixEventTypeString] ||
[callInvite.type isEqualToString:kWidgetModularEventTypeString])
// process the call invite synchronously
[session.callManager handleCallEvent:callInvite];
MXCallInviteEventContent *content = [MXCallInviteEventContent modelFromJSON:callInvite.content];
MXCall *call = [session.callManager callWithCallId:content.callId];
if (call)
{
[[AppDelegate theDelegate].callPresenter processWidgetEvent:callInvite
inSession:session];
[session.callManager.callKitAdapter reportIncomingCall:call];
MXLogDebug(@"[PushNotificationService] didReceiveIncomingPushWithPayload: Reporting new call in room %@ for the event: %@", roomId, eventId);

// Wait for the sync response in cache to be processed for data integrity.
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{
// After reporting the call, we can continue async. Launch a background sync to handle call answers/declines on other devices of the user.
[self launchBackgroundSync];
});
}
else
{
// It's a serious error. There is nothing to avoid iOS to kill us here.
MXLogDebug(@"[PushNotificationService] didReceiveIncomingPushWithPayload: We have an unknown type of event for %@. There is something wrong.", eventId);
MXLogDebug(@"[PushNotificationService] didReceiveIncomingPushWithPayload: Error on call object on room %@ for the event: %@", roomId, eventId);
}
}
else if ([callInvite.type isEqualToString:kWidgetMatrixEventTypeString] ||
[callInvite.type isEqualToString:kWidgetModularEventTypeString])
{
[[AppDelegate theDelegate].callPresenter processWidgetEvent:callInvite
inSession:session];
}
else
{
// It's a serious error. There is nothing to avoid iOS to kill us here.
MXLogDebug(@"[PushNotificationService] didReceiveIncomingPushWithPayload: iOS 13 and in bg, but we don't have the callInvite event for the eventId: %@. There is something wrong.", eventId);
MXLogDebug(@"[PushNotificationService] didReceiveIncomingPushWithPayload: We have an unknown type of event for %@. There is something wrong.", eventId);
}
}
else
{
// below iOS 13, we can call an async API. After background sync, we'll hopefully fetch the call invite and report a new call to the CallKit.
[self launchBackgroundSync];
// It's a serious error. There is nothing to avoid iOS to kill us here.
MXLogDebug(@"[PushNotificationService] didReceiveIncomingPushWithPayload: iOS 13+, but we don't have the callInvite event for the eventId: %@.", eventId);
}
}
else
{
MXLogDebug(@"[PushNotificationService] didReceiveIncomingPushWithPayload: application is not in bg. There is something wrong.");
if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive)
{
// below iOS 13, we don't have to report a call immediately.
// We can wait for a call invite from event stream and process.
MXLogDebug(@"[PushNotificationService] didReceiveIncomingPushWithPayload: Below iOS 13 and active app. Do nothing.");
completion();
return;
}

// below iOS 13, we can call an async API. After background sync, we'll hopefully fetch the call invite and report a new call to the CallKit.
[self launchBackgroundSync];
}

completion();
Expand Down
6 changes: 0 additions & 6 deletions Riot/Modules/Application/LegacyAppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -369,12 +369,6 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(

MXLogDebug(@"[AppDelegate] didFinishLaunchingWithOptions: isProtectedDataAvailable: %@", @([application isProtectedDataAvailable]));

if (![application isProtectedDataAvailable])
{
MXLogDebug(@"[AppDelegate] didFinishLaunchingWithOptions: Terminating the app because protected data not available");
exit(0);
}

_configuration = [AppConfiguration new];

self.appInfo = AppInfo.current;
Expand Down