/ KVSKeychainSyncingProxy / XPCNotificationDispatcher.m
XPCNotificationDispatcher.m
1 // 2 // XPCNotificationDispatcher.m 3 // Security 4 // 5 // Created by Mitch Adler on 11/1/16. 6 // 7 // 8 9 #import "XPCNotificationDispatcher.h" 10 #include <dispatch/dispatch.h> 11 12 #include <utilities/debugging.h> 13 14 #include <xpc/xpc.h> 15 16 // 17 // PointerArray helpers 18 19 @interface NSPointerArray (Removal) 20 - (void) removePointer: (nullable void *)pointer; 21 @end 22 23 @implementation NSPointerArray (Removal) 24 - (void) removePointer: (nullable void *)pointer { 25 NSUInteger pos = 0; 26 while(pos < [self count]) { 27 if (pointer == [self pointerAtIndex:pos]) { 28 [self removePointerAtIndex:pos]; 29 } else { 30 pos += 1; 31 } 32 } 33 } 34 @end 35 36 // 37 // 38 39 static const char *kXPCNotificationStreamName = "com.apple.notifyd.matching"; 40 static const char *kXPCNotificationNameKey = "Notification"; 41 42 @interface XPCNotificationDispatcher () 43 @property dispatch_queue_t queue; 44 @property NSPointerArray* listeners; 45 46 - (void) notification: (const char *) value; 47 48 @end 49 50 51 @implementation XPCNotificationDispatcher 52 53 + (instancetype) dispatcher { 54 static dispatch_once_t onceToken; 55 static XPCNotificationDispatcher* sDispactcher; 56 dispatch_once(&onceToken, ^{ 57 sDispactcher = [[XPCNotificationDispatcher alloc] init]; 58 }); 59 60 return sDispactcher; 61 } 62 63 - (instancetype) init { 64 if ((self = [super init])) { 65 self.queue = dispatch_queue_create("XPC Notification Dispatch", DISPATCH_QUEUE_SERIAL); 66 self.listeners = [NSPointerArray weakObjectsPointerArray]; 67 __weak typeof(self) weakSelf = self; 68 69 xpc_set_event_stream_handler(kXPCNotificationStreamName, self.queue, ^(xpc_object_t event){ 70 const char *notificationName = xpc_dictionary_get_string(event, kXPCNotificationNameKey); 71 if (notificationName) { 72 [weakSelf notification:notificationName]; 73 } 74 }); 75 } 76 77 return self; 78 } 79 80 - (void) notification:(const char *)name { 81 [self.listeners compact]; 82 [[self.listeners allObjects] enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 83 [obj handleNotification: name]; 84 }]; 85 86 } 87 88 - (void) addListener: (NSObject<XPCNotificationListener>*) newHandler { 89 dispatch_sync(self.queue, ^{ 90 [self.listeners compact]; 91 [self.listeners addPointer:(__bridge void * _Nullable)(newHandler)]; 92 }); 93 } 94 95 - (void) removeListener: (NSObject<XPCNotificationListener>*) existingHandler { 96 dispatch_sync(self.queue, ^{ 97 [self.listeners removePointer:(__bridge void * _Nullable)existingHandler]; 98 }); 99 } 100 101 @end