Search
Recommended for You

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");
}

Update: Thanks August, for pointing out the extraneous UIKit header, which is now removed

AddThis Social Bookmark Button
Comments (15)

15 Comments

Optimo said:

true genius.. gets me thinking in new and exciting ways
you are such a great supporter of development
thanks erica

indiekiduk said:

It would be a great help to see the commands needed to generate the comprehensive notification list. Pretty please :-)

lknight said:

Thanks for helping us so much.But it wont work with XCODE.
XCODE think that methods are just errors.
Is it "normal behaviour (and I should try it without XCODE) or I miss something?
===
"_CTTelephonyCenterAddObserver", referenced from:
"_CTTelephonyCenterGetDefault", referenced from:
===

Plz, help! :)

Erica Sadun said:

Core Telephony is a private framework. You cannot link to it in Xcode/SDK.

shawn said:

how does one compile this if one cannot use xcode to link the private framework?

Cheers,
Shawn

Erica Sadun said:

You need to use the open source OpenSDK

shizu said:

It's great!!

By the way, Can it get UITextFieldTextDidChangeNotification?
It seems to get only ^com.apple notifications.
(I tried, but no callback...)

I want to get them.
Please help?

Josh said:

Would there be a way to use something like this to intercept the USSD balance notifications? I know apps like iBlacklist and PYSL can intercept the SMS and Call notifications so they must be doing something similar.

Erica Sadun said:

USSD notifications are part of the core telephony framework. Read the post I linked to in the first paragraph.

Josh said:

So I should be able to build a app that subscribes to the updates. If I see a USSD message come in I need to prevent the popup. Got any ideas? If it can be done on iBlacklist for SMS there has to be a way to do it.

AdoubleU said:

Could you provide the makefile for compiling the code above?

David Morris said:

Erica, just wanted to thank you for posting this approach concept and sample code. We are finding some very useful information related to iPhone network connectivity status.

Compiling using a 2.0 Toolchain built as per Saurik required fishing the CoreTelephony/CoreTelephony.h header out of the 1.1.4 tool chain and adding the appropriate -F value to the compile to reference the PrivateFrameworks.

Senthil ACS said:

XCode allows you to add your Private Frameworks. Its simple. Right click on Project, Add Frameworks. Now go to your iPhoneOS SDK installation path and there you find a directory named "PrivateFrameworks". Select CoreTelephony and add it to your project.

You are done. Now try to link it.. it works fine and i wrote a SMS listener application that listens for a message and also deletes the message...

Cole said:

Erica fantastic blogs, thank you so much for sharing your wisdom with us all! Faced with the no-background Apps delima, I am trying to work around the problem that the App must always be ACTIVE to "listen" for CoreTelephony Notifications. Any tips, ideas, or clues?

Anonymous said:

Is there a list of notifications to listen too?

Leave a comment


Type the characters you see in the picture above.