iPhone Notifications
Several months ago, I wrote about the iPhone's onboard Core Telephony notification system. That post explored how to catch and respond to iPhone interruptions. After writing that, I remained curious about the standard iPhone notification system. I didn't get far. And this is why: The iPhone relies primarily on Darwin Notifications to communicate between applications.
Listening to notifications isn't hard. Just call CFNotificationCenterAddObserver
and add your application as a listener. Unfortunately, unlike standard notification centers, the Darwin forbids eavesdropping. You must specify exactly which notification token you want to listen for.
With that roadblock seemingly insurmountable, I moved onto other projects. This week, I stumbled once again across Daniel "Pumpkin" Peebles who encouraged me to try again. So I did.
What I did was this. I went to /System/Library and ran the strings command on just about every file I could find, particularly the frameworks and Core Services folders. I then grepped through those strings for "^com.apple", the pattern that identifies most inter-application notifications.
I then repeated this for /Applications. Finally, I sorted and uniq'ed my results to create as comprehensive a list as possible of Apple notifications. It took many hours but once I got it automated, it ran pretty quickly.
My list contained quite a number of tokens. These included power notifications (fully charged, etc), sync notifications (sync begun, canceled, etc), and other system events. I was going to look for events using a hammer rather than a paintbrush.
I built the following tool using the open SDK toolchain. In this, I feed notifications into a loop, creating an observer for each notification token type. The source code (omitting the actual notification list) follows below.
The results were gratifying in that they allowed me to listen in to my system in a way I hadn't before. I could sense when apps launched and quit, when the battery charger came on line. I could detect when system notifications appeared on-screen, and more. No, this didn't provide a full or exhaustive view -- the notification list is still a work in progress -- but it was at least a step forward in interacting with the iPhone at a deeper level.
#include <stdio.h> #include <notify.h> #include <unistd.h> #include <stdarg.h> static void callback(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) { fprintf(stderr, "Notification intercepted: %s/n", [name UTF8String]); if (userInfo) CFShow(userInfo); return; } static void signalHandler(int sigraised) { printf("/nInterrupted./n"); _exit(0); } void usage(char *appname) { printf("%s : options /n", appname); printf("-s listen to standard notifications/n"); printf("-t listen to core telephony notifications/n"); } int main(int argc, char **argv) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; if (argc < 2) {usage(argv[0]); exit(0);} NSString *readNotifications = NOTSTRING; NSArray *notifications = [readNotifications componentsSeparatedByString:@"BOUNDARY"]; opterr = 0; int c; id results; BOOL isTelephony = NO; BOOL isStandard = NO; while ((c = getopt (argc, argv, "ts")) != -1) switch (c) { case 's': isStandard = YES; break; case 't': isTelephony = YES; break; case '?': if (isprint (optopt)) fprintf (stderr, "Unknown option `-%c'./n", optopt); else fprintf (stderr, "Unknown option character `//x%x'./n", optopt); return 1; default: return 0; } if ((!isStandard) && (!isTelephony)) { printf("Listen either to standard (-s), telephony (-t) notifications or both/n"); printf("No notification type selected. Exiting./n"); exit(-1); } if (isStandard) { printf("Watching for Standard Notifications (%d notifications)/n", [notifications count]); for (id *notification in notifications) { CFNotificationCenterAddObserver( CFNotificationCenterGetDarwinNotifyCenter(), //center NULL, // observer callback, // callback notification, // name NULL, // object CFNotificationSuspensionBehaviorHold ); } } if (isTelephony) { printf("Watching for Core Telephony notifications/n"); id ct = CTTelephonyCenterGetDefault(); CTTelephonyCenterAddObserver( ct, NULL, callback, NULL, NULL, CFNotificationSuspensionBehaviorHold); } // Set up a signal handler so we can clean up when we're interrupted from the command line sig_t oldHandler = signal(SIGINT, signalHandler); if (oldHandler == SIG_ERR) { printf("Could not establish new signal handler"); exit(1); } // Start the run loop. Now we'll receive notifications. printf("Starting Notification Scan. ^C to quit./n"); CFRunLoopRun(); printf("Unexpectedly back from CFRunLoopRun()!/n"); }