Page MenuHomePhabricator

D8795.id30209.diff
No OneTemporary

D8795.id30209.diff

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
@@ -37,9 +37,8 @@
@interface NotificationService ()
-@property(nonatomic, strong) void (^contentHandler)
- (UNNotificationContent *contentToDeliver);
-@property(nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;
+@property(strong) NSMutableDictionary *contentHandlers;
+@property(strong) NSMutableDictionary *contents;
@end
@@ -49,17 +48,20 @@
withContentHandler:
(void (^)(UNNotificationContent *_Nonnull))
contentHandler {
+ // Set-up methods are idempotent
[NotificationService setUpNSEProcess];
+ [self setUpNSEInstance];
- self.contentHandler = contentHandler;
- self.bestAttemptContent = [request.content mutableCopy];
+ NSString *contentHandlerKey = [request.identifier copy];
+ UNMutableNotificationContent *content = [request.content mutableCopy];
+ [self putContent:content withHandler:contentHandler forKey:contentHandlerKey];
// Step 1: notification decryption.
- if ([self shouldBeDecrypted:self.bestAttemptContent.userInfo]) {
+ if ([self shouldBeDecrypted:content.userInfo]) {
std::string decryptErrorMessage;
try {
@try {
- [self decryptBestAttemptContent];
+ content = [self decryptContent:content];
} @catch (NSException *e) {
decryptErrorMessage = "NSE: Received Obj-C exception: " +
std::string([e.name UTF8String]) +
@@ -74,11 +76,11 @@
if (decryptErrorMessage.size()) {
NSString *errorMessage =
[NSString stringWithUTF8String:decryptErrorMessage.c_str()];
- [self callContentHandlerOnErrorMessage:errorMessage];
+ [self callContentHandlerForKey:contentHandlerKey
+ onErrorMessage:errorMessage];
return;
}
- } else if ([self shouldAlertUnencryptedNotification:self.bestAttemptContent
- .userInfo]) {
+ } else if ([self shouldAlertUnencryptedNotification:content.userInfo]) {
// In future this will be replaced by notification content
// modification for DEV environment and staff members
comm::Logger::log("NSE: Received erroneously unencrypted notitication.");
@@ -88,7 +90,7 @@
std::string persistErrorMessage;
try {
@try {
- [self persistMessagePayload:self.bestAttemptContent.userInfo];
+ [self persistMessagePayload:content.userInfo];
} @catch (NSException *e) {
persistErrorMessage =
"NSE: Received Obj-C exception: " + std::string([e.name UTF8String]) +
@@ -103,7 +105,8 @@
if (persistErrorMessage.size()) {
NSString *errorMessage =
[NSString stringWithUTF8String:persistErrorMessage.c_str()];
- [self callContentHandlerOnErrorMessage:errorMessage];
+ [self callContentHandlerForKey:contentHandlerKey
+ onErrorMessage:errorMessage];
return;
}
@@ -111,12 +114,12 @@
// Message payload persistence is a higher priority task, so it has
// to happen prior to potential notification center clearing.
- if ([self isRescind:self.bestAttemptContent.userInfo]) {
+ if ([self isRescind:content.userInfo]) {
std::string rescindErrorMessage;
try {
@try {
[self
- removeNotificationWithIdentifier:self.bestAttemptContent
+ removeNotificationWithIdentifier:content
.userInfo[@"notificationId"]];
} @catch (NSException *e) {
rescindErrorMessage = "NSE: Received Obj-C exception: " +
@@ -131,55 +134,97 @@
if (rescindErrorMessage.size()) {
NSString *errorMessage =
[NSString stringWithUTF8String:rescindErrorMessage.c_str()];
- [self callContentHandlerOnErrorMessage:errorMessage];
+ [self callContentHandlerForKey:contentHandlerKey
+ onErrorMessage:errorMessage];
return;
}
- self.contentHandler([[UNNotificationContent alloc] init]);
+ [self callContentHandlerForKey:contentHandlerKey
+ withContent:[[UNNotificationContent alloc] init]];
return;
}
- if ([self isBadgeOnly:self.bestAttemptContent.userInfo]) {
- UNMutableNotificationContent *badgeOnlyContent =
- [[UNMutableNotificationContent alloc] init];
- badgeOnlyContent.badge = self.bestAttemptContent.badge;
- self.contentHandler(badgeOnlyContent);
+ // Step 4: (optional) update badge count for badge only notif
+ if ([self isBadgeOnly:content.userInfo]) {
+ UNNotificationContent *badgeOnlyContent =
+ [self getBadgeOnlyContentFor:content];
+ [self callContentHandlerForKey:contentHandlerKey
+ withContent:badgeOnlyContent];
return;
}
+ // Step 5: notify main app that there is data
+ // to transfer to SQLite and redux.
[self sendNewMessageInfosNotification];
if (NSString *currentMemoryEventMessage =
[NotificationService getAndSetMemoryEventMessage:nil]) {
- [self callContentHandlerOnErrorMessage:currentMemoryEventMessage];
+ [self callContentHandlerForKey:contentHandlerKey
+ onErrorMessage:currentMemoryEventMessage];
return;
}
- self.contentHandler(self.bestAttemptContent);
+ [self callContentHandlerForKey:contentHandlerKey withContent:content];
}
- (void)serviceExtensionTimeWillExpire {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified
// content, otherwise the original push payload will be used.
- if ([self isRescind:self.bestAttemptContent.userInfo]) {
- // If we get to this place it means we were unable to
- // remove relevant notification from notification center in
- // in time given to NSE to process notification.
- // It is an extremely unlikely to happen.
- NSString *errorMessage =
- @"NSE: Exceeded time limit to rescind a notification.";
- [self callContentHandlerOnErrorMessage:errorMessage];
- return;
+ NSMutableArray<void (^)(UNNotificationContent *_Nonnull)> *allHandlers =
+ [[NSMutableArray alloc] init];
+ NSMutableArray<UNNotificationContent *> *allContents =
+ [[NSMutableArray alloc] init];
+
+ @synchronized(self.contentHandlers) {
+ for (NSString *key in self.contentHandlers) {
+ [allHandlers addObject:self.contentHandlers[key]];
+ [allContents addObject:self.contents[key]];
+ }
+
+ [self.contentHandlers removeAllObjects];
+ [self.contents removeAllObjects];
}
- if ([self shouldBeDecrypted:self.bestAttemptContent.userInfo] &&
- !self.bestAttemptContent.userInfo[@"succesfullyDecrypted"]) {
- // If we get to this place it means we were unable to
- // decrypt encrypted notification content in time
- // given to NSE to process notification.
- NSString *errorMessage =
- @"NSE: Exceeded time limit to decrypt a notification.";
- [self callContentHandlerOnErrorMessage:errorMessage];
- return;
+
+ for (int i = 0; i < allContents.count; i++) {
+ UNNotificationContent *content = allContents[i];
+ void (^handler)(UNNotificationContent *_Nonnull) = allHandlers[i];
+
+ if ([self isRescind:content.userInfo]) {
+ // If we get to this place it means we were unable to
+ // remove relevant notification from notification center in
+ // in time given to NSE to process notification.
+ // It is an extremely unlikely to happen.
+ NSString *errorMessage =
+ @"NSE: Exceeded time limit to rescind a notification.";
+ 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
+ // decrypt encrypted notification content in time
+ // given to NSE to process notification.
+ NSString *errorMessage =
+ @"NSE: Exceeded time limit to decrypt a notification.";
+ UNNotificationContent *errorContent =
+ [self buildContentForError:errorMessage];
+ handler(errorContent);
+ continue;
+ }
+
+ // At this point we know that the content is at least
+ // correctly decrypted so we can display it to the user.
+ // Another operation, like persistence, had failed.
+ if ([self isBadgeOnly:content.userInfo]) {
+ UNNotificationContent *badgeOnlyContent =
+ [self getBadgeOnlyContentFor:content];
+ handler(badgeOnlyContent);
+ continue;
+ }
+
+ handler(content);
}
- self.contentHandler(self.bestAttemptContent);
}
- (void)removeNotificationWithIdentifier:(NSString *)identifier {
@@ -254,6 +299,14 @@
return !payload[@"threadID"];
}
+- (UNNotificationContent *)getBadgeOnlyContentFor:
+ (UNNotificationContent *)content {
+ UNMutableNotificationContent *badgeOnlyContent =
+ [[UNMutableNotificationContent alloc] init];
+ badgeOnlyContent.badge = content.badge;
+ return badgeOnlyContent;
+}
+
- (void)sendNewMessageInfosNotification {
CFNotificationCenterPostNotification(
CFNotificationCenterGetDarwinNotifyCenter(),
@@ -283,17 +336,17 @@
.c_str()];
}
-- (void)decryptBestAttemptContent {
- NSString *decryptedSerializedPayload = [self
- singleDecrypt:self.bestAttemptContent.userInfo[encryptedPayloadKey]];
+- (UNMutableNotificationContent *)decryptContent:
+ (UNMutableNotificationContent *)content {
+ NSString *decryptedSerializedPayload =
+ [self singleDecrypt:content.userInfo[encryptedPayloadKey]];
NSDictionary *decryptedPayload = [NSJSONSerialization
JSONObjectWithData:[decryptedSerializedPayload
dataUsingEncoding:NSUTF8StringEncoding]
options:0
error:nil];
- NSMutableDictionary *mutableUserInfo =
- [self.bestAttemptContent.userInfo mutableCopy];
+ NSMutableDictionary *mutableUserInfo = [content.userInfo mutableCopy];
NSMutableDictionary *mutableAps = nil;
if (mutableUserInfo[@"aps"]) {
@@ -302,7 +355,7 @@
NSString *body = decryptedPayload[@"merged"];
if (body) {
- self.bestAttemptContent.body = body;
+ content.body = body;
if (mutableAps && mutableAps[@"alert"]) {
mutableAps[@"alert"] = body;
}
@@ -310,7 +363,7 @@
NSString *threadID = decryptedPayload[@"threadID"];
if (threadID) {
- self.bestAttemptContent.threadIdentifier = threadID;
+ content.threadIdentifier = threadID;
mutableUserInfo[@"threadID"] = threadID;
if (mutableAps) {
mutableAps[@"thread-id"] = threadID;
@@ -320,7 +373,7 @@
NSString *badgeStr = decryptedPayload[@"badge"];
if (badgeStr) {
NSNumber *badge = @([badgeStr intValue]);
- self.bestAttemptContent.badge = badge;
+ content.badge = badge;
if (mutableAps) {
mutableAps[@"badge"] = badge;
}
@@ -341,9 +394,52 @@
mutableUserInfo[@"aps"] = mutableAps;
}
[mutableUserInfo removeObjectForKey:encryptedPayloadKey];
- self.bestAttemptContent.userInfo = mutableUserInfo;
+ content.userInfo = mutableUserInfo;
+ return content;
+}
+
+// Apple documentation for NSE does not explicitely state
+// that single NSE instance will be used by only one thread
+// at a time. Even though UNNotificationServiceExtension API
+// suggests that it could be the case we don't trust it
+// and keep a synchronized collection of handlers and contents.
+
+- (void)setUpNSEInstance {
+ @synchronized(self) {
+ if (self.contentHandlers) {
+ return;
+ }
+ self.contentHandlers = [[NSMutableDictionary alloc] init];
+ self.contents = [[NSMutableDictionary alloc] init];
+ }
+}
+
+- (void)putContent:(UNNotificationContent *)content
+ withHandler:(void (^)(UNNotificationContent *_Nonnull))handler
+ forKey:(NSString *)key {
+ @synchronized(self.contentHandlers) {
+ [self.contentHandlers setObject:handler forKey:key];
+ [self.contents setObject:content forKey:key];
+ }
+}
+
+- (void)callContentHandlerForKey:(NSString *)key
+ withContent:(UNNotificationContent *)content {
+ void (^handler)(UNNotificationContent *_Nonnull);
+
+ @synchronized(self.contentHandlers) {
+ handler = [self.contentHandlers objectForKey:key];
+ [self.contentHandlers removeObjectForKey:key];
+ [self.contents removeObjectForKey:key];
+ }
+
+ if (!handler) {
+ return;
+ }
+ handler(content);
}
+
- (UNNotificationContent *)buildContentForError:(NSString *)error {
UNMutableNotificationContent *content =
[[UNMutableNotificationContent alloc] init];
@@ -351,16 +447,18 @@
return content;
}
-- (void)callContentHandlerOnErrorMessage:(NSString *)errorMessage {
+- (void)callContentHandlerForKey:(NSString *)key
+ onErrorMessage:(NSString *)errorMessage {
comm::Logger::log(std::string([errorMessage UTF8String]));
if (!comm::StaffUtils::isStaffRelease()) {
- self.contentHandler([[UNNotificationContent alloc] init]);
+ [self callContentHandlerForKey:key
+ withContent:[[UNNotificationContent alloc] init]];
return;
}
UNNotificationContent *content = [self buildContentForError:errorMessage];
- self.contentHandler(content);
+ [self callContentHandlerForKey:key withContent:content];
}
// Monitor memory usage

File Metadata

Mime Type
text/plain
Expires
Wed, Nov 27, 12:13 AM (12 h, 19 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2587017
Default Alt Text
D8795.id30209.diff (13 KB)

Event Timeline