XPCNotificationDispatcher.m [plain text]
//
// XPCNotificationDispatcher.m
// Security
//
// Created by Mitch Adler on 11/1/16.
//
//
#import "XPCNotificationDispatcher.h"
#include <dispatch/dispatch.h>
#include <utilities/debugging.h>
#include <xpc/xpc.h>
//
// PointerArray helpers
@interface NSPointerArray (Removal)
- (void) removePointer: (nullable void *)pointer;
@end
@implementation NSPointerArray (Removal)
- (void) removePointer: (nullable void *)pointer {
NSUInteger pos = 0;
while(pos < [self count]) {
if (pointer == [self pointerAtIndex:pos]) {
[self removePointerAtIndex:pos];
} else {
pos += 1;
}
}
}
@end
//
//
static const char *kXPCNotificationStreamName = "com.apple.notifyd.matching";
static const char *kXPCNotificationNameKey = "Notification";
@interface XPCNotificationDispatcher ()
@property dispatch_queue_t queue;
@property NSPointerArray* listeners;
- (void) notification: (const char *) value;
@end
@implementation XPCNotificationDispatcher
+ (instancetype) dispatcher {
static dispatch_once_t onceToken;
static XPCNotificationDispatcher* sDispactcher;
dispatch_once(&onceToken, ^{
sDispactcher = [[XPCNotificationDispatcher alloc] init];
});
return sDispactcher;
}
- (instancetype) init {
self = [super init];
if (self) {
self.queue = dispatch_queue_create("XPC Notification Dispatch", DISPATCH_QUEUE_SERIAL);
self.listeners = [NSPointerArray weakObjectsPointerArray];
__weak typeof(self) weakSelf = self;
xpc_set_event_stream_handler(kXPCNotificationStreamName, self.queue, ^(xpc_object_t event){
const char *notificationName = xpc_dictionary_get_string(event, kXPCNotificationNameKey);
if (notificationName) {
[weakSelf notification:notificationName];
}
});
}
return self;
}
- (void) notification:(const char *)name {
[self.listeners compact];
[[self.listeners allObjects] enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[obj handleNotification: name];
}];
}
- (void) addListener: (NSObject<XPCNotificationListener>*) newHandler {
dispatch_sync(self.queue, ^{
[self.listeners compact];
[self.listeners addPointer:(__bridge void * _Nullable)(newHandler)];
});
}
- (void) removeListener: (NSObject<XPCNotificationListener>*) existingHandler {
dispatch_sync(self.queue, ^{
[self.listeners removePointer:(__bridge void * _Nullable)existingHandler];
});
}
@end