/ 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