Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3373182
D9144.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
7 KB
Referenced Files
None
Subscribers
None
D9144.diff
View Options
diff --git a/native/ios/NotificationService/NotificationService.mm b/native/ios/NotificationService/NotificationService.mm
--- a/native/ios/NotificationService/NotificationService.mm
+++ b/native/ios/NotificationService/NotificationService.mm
@@ -9,10 +9,15 @@
NSString *const messageInfosKey = @"messageInfos";
NSString *const encryptedPayloadKey = @"encryptedPayload";
NSString *const encryptionFailureKey = @"encryptionFailure";
+NSString *const collapseIDKey = @"collapseID";
const std::string callingProcessName = "NSE";
// The context for this constant can be found here:
// https://linear.app/comm/issue/ENG-3074#comment-bd2f5e28
int64_t const notificationRemovalDelay = (int64_t)(0.1 * NSEC_PER_SEC);
+// Apple gives us about 30 seconds to process single notification,
+// se we let any semaphore wait for at most 20 seconds
+int64_t const semaphoreAwaitTimeLimit = (int64_t)(20 * NSEC_PER_SEC);
+
CFStringRef newMessageInfosDarwinNotification =
CFSTR("app.comm.darwin_new_message_infos");
@@ -130,9 +135,11 @@
std::string rescindErrorMessage;
try {
@try {
- [self
- removeNotificationWithIdentifier:content
- .userInfo[@"notificationId"]];
+ [self removeNotificationsWithCondition:^BOOL(
+ UNNotification *_Nonnull notif) {
+ return [content.userInfo[@"notificationId"]
+ isEqualToString:notif.request.content.userInfo[@"id"]];
+ }];
} @catch (NSException *e) {
rescindErrorMessage =
"Obj-C exception: " + std::string([e.name UTF8String]) +
@@ -152,6 +159,38 @@
publicUserContent = [[UNNotificationContent alloc] init];
}
+ // Step 4: (optional) execute notification coalescing
+ if ([self isCollapsible:content.userInfo]) {
+ std::string coalescingErrorMessage;
+ try {
+ @try {
+ [self displayLocalNotificationFromContent:content
+ forCollapseKey:content
+ .userInfo[collapseIDKey]];
+ } @catch (NSException *e) {
+ coalescingErrorMessage =
+ "Obj-C exception: " + std::string([e.name UTF8String]) +
+ " during notification coalescing.";
+ }
+ } catch (const std::exception &e) {
+ coalescingErrorMessage = "C++ exception: " + std::string(e.what()) +
+ " during notification coalescing.";
+ }
+
+ if (coalescingErrorMessage.size()) {
+ [errorMessages
+ addObject:[NSString
+ stringWithUTF8String:coalescingErrorMessage.c_str()]];
+ // Even if we fail to execute coalescing then public users
+ // should still see the original message.
+ publicUserContent = content;
+ } else {
+ publicUserContent = [[UNNotificationContent alloc] init];
+ }
+ }
+
+ // Step 5: (optional) create empty notification that
+ // only provides badge count.
if ([self isBadgeOnly:content.userInfo]) {
UNMutableNotificationContent *badgeOnlyContent =
[[UNMutableNotificationContent alloc] init];
@@ -222,6 +261,23 @@
continue;
}
+ if ([self isCollapsible:content.userInfo]) {
+ // If we get to this place it means we were unable to
+ // execute notification coalescing with local notification
+ // mechanism in time given to NSE to process notification.
+ if (!comm::StaffUtils::isStaffRelease()) {
+ handler(content);
+ continue;
+ }
+
+ NSString *errorMessage =
+ @"NSE: Exceeded time limit to collapse a notitication.";
+ UNNotificationContent *errorContent =
+ [self buildContentForError:errorMessage];
+ handler(errorContent);
+ continue;
+ }
+
if ([self shouldBeDecrypted:content.userInfo] &&
!content.userInfo[@"succesfullyDecrypted"]) {
// If we get to this place it means we were unable to
@@ -254,7 +310,8 @@
}
}
-- (void)removeNotificationWithIdentifier:(NSString *)identifier {
+- (void)removeNotificationsWithCondition:
+ (BOOL (^)(UNNotification *_Nonnull))condition {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
void (^delayedSemaphorePostCallback)() = ^() {
@@ -268,18 +325,62 @@
[UNUserNotificationCenter.currentNotificationCenter
getDeliveredNotificationsWithCompletionHandler:^(
NSArray<UNNotification *> *_Nonnull notifications) {
+ NSMutableArray<NSString *> *notificationsToRemove =
+ [[NSMutableArray alloc] init];
for (UNNotification *notif in notifications) {
- if ([identifier isEqual:notif.request.content.userInfo[@"id"]]) {
- [UNUserNotificationCenter.currentNotificationCenter
- removeDeliveredNotificationsWithIdentifiers:@[
- notif.request.identifier
- ]];
+ if (condition(notif)) {
+ [notificationsToRemove addObject:notif.request.identifier];
}
}
+ [UNUserNotificationCenter.currentNotificationCenter
+ removeDeliveredNotificationsWithIdentifiers:notificationsToRemove];
delayedSemaphorePostCallback();
}];
- dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
+ dispatch_semaphore_wait(
+ semaphore, dispatch_time(DISPATCH_TIME_NOW, semaphoreAwaitTimeLimit));
+}
+
+- (void)displayLocalNotificationFromContent:(UNNotificationContent *)content
+ forCollapseKey:(NSString *)collapseKey {
+ UNMutableNotificationContent *localNotifContent =
+ [[UNMutableNotificationContent alloc] init];
+
+ localNotifContent.title = content.title;
+ localNotifContent.body = content.body;
+ localNotifContent.badge = content.badge;
+ localNotifContent.userInfo = content.userInfo;
+
+ UNNotificationRequest *localNotifRequest =
+ [UNNotificationRequest requestWithIdentifier:collapseKey
+ content:localNotifContent
+ trigger:nil];
+
+ // We must wait until local notif display completion
+ // handler returns. Context:
+ // https://developer.apple.com/forums/thread/108340?answerId=331640022#331640022
+
+ dispatch_semaphore_t localNotifDisplaySemaphore =
+ dispatch_semaphore_create(0);
+
+ __block NSError *localNotifDisplayError = nil;
+ [UNUserNotificationCenter.currentNotificationCenter
+ addNotificationRequest:localNotifRequest
+ withCompletionHandler:^(NSError *_Nullable error) {
+ if (error) {
+ localNotifDisplayError = error;
+ }
+ dispatch_semaphore_signal(localNotifDisplaySemaphore);
+ }];
+
+ dispatch_semaphore_wait(
+ localNotifDisplaySemaphore,
+ dispatch_time(DISPATCH_TIME_NOW, semaphoreAwaitTimeLimit));
+
+ if (localNotifDisplayError) {
+ throw std::runtime_error(
+ std::string([localNotifDisplayError.localizedDescription UTF8String]));
+ }
}
- (void)persistMessagePayload:(NSDictionary *)payload {
@@ -326,6 +427,10 @@
return !payload[@"threadID"];
}
+- (BOOL)isCollapsible:(NSDictionary *)payload {
+ return payload[collapseIDKey];
+}
+
- (UNNotificationContent *)getBadgeOnlyContentFor:
(UNNotificationContent *)content {
UNMutableNotificationContent *badgeOnlyContent =
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Nov 27, 8:31 AM (21 h, 53 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2589838
Default Alt Text
D9144.diff (7 KB)
Attached To
Mode
D9144: Enable encrypted notification coalescing in NSE on iOS
Attached
Detach File
Event Timeline
Log In to Comment