diff --git a/CHANGES.rst b/CHANGES.rst index bd72b4413..47c0dbe11 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -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 * diff --git a/Riot/Managers/PushNotification/PushNotificationService.m b/Riot/Managers/PushNotification/PushNotificationService.m index 350cd564f..bef062452 100644 --- a/Riot/Managers/PushNotification/PushNotificationService.m +++ b/Riot/Managers/PushNotification/PushNotificationService.m @@ -178,16 +178,17 @@ Matrix session observer used to detect new opened sessions. { [[UNUserNotificationCenter currentNotificationCenter] removeUnwantedNotifications]; [[UNUserNotificationCenter currentNotificationCenter] removeCallNotificationsFor:nil]; -} - -- (void)applicationDidEnterBackground -{ if (_pushNotificationStore.pushKitToken) { self.shouldReceiveVoIPPushes = YES; } } +- (void)applicationDidEnterBackground +{ + +} + - (void)applicationDidBecomeActive { [[UNUserNotificationCenter currentNotificationCenter] removeUnwantedNotifications]; @@ -227,6 +228,8 @@ Matrix session observer used to detect new opened sessions. { _shouldReceiveVoIPPushes = shouldReceiveVoIPPushes; + MXLogDebug(@"[PushNotificationService] setShouldReceiveVoIPPushes: %u", _shouldReceiveVoIPPushes) + if (_shouldReceiveVoIPPushes && _pushNotificationStore.pushKitToken) { MXSession *session = [AppDelegate theDelegate].mxSessions.firstObject; @@ -256,16 +259,25 @@ Matrix session observer used to detect new opened sessions. } 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); @@ -547,83 +559,83 @@ Matrix session observer used to detect new opened sessions. [[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) { - // 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]) - { - [[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(); diff --git a/Riot/Modules/Application/LegacyAppDelegate.m b/Riot/Modules/Application/LegacyAppDelegate.m index f01d3a668..ff81e5f57 100644 --- a/Riot/Modules/Application/LegacyAppDelegate.m +++ b/Riot/Modules/Application/LegacyAppDelegate.m @@ -369,12 +369,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni 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;