Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
215 views
in Technique[技术] by (71.8m points)

ios - pull notification locally on jailbroken device

Since the iOS framework doesn't allow local notifications to execute code before they are posted, I'm looking for a way to achieve it on a jailbroken device.

  • Is there built in functionally on a jailbroken device to schedule code execution with no need for the user to interact?
  • The code should download updates and determine if the user should receive a notification.
  • I don't want to use push notifications, which requires an external server to push them to the user.

Update

Well, I've managed to create a daemon which launches on start-up and keeps itself running. However, posting notifications requires the UIApplication object. According to the documentation this singleton is created by the UIApplicationMain() method which, for a regular application is called by main(). Since I want the notification be posted by a daemon, the singleton is nil.

Can I create an instance of UIApplication? Or post the notification any other way?

I've tried calling UIApplicationMain() and then posting the notification in the app delegate, as well as killing the application, but this shows a black screen for a moment; I guess its launching the application. Moreover, it causes the daemon to crash when app launching is impossible (when the phone is yet to fully boot).

Here is a sketch of the code

int main(){
   if(launchedBySpringBoard || launchedBynotification)
      UIApplicationMain(...);
   else if(launchedByDaeamon)
      StartRunLoop();
}

void triggerdByRunLoopEveryXhours(){
    downloadData();
    if(isNewData())
       postNotification();
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

... Or post the notification any other way?

Yes. You can make this work with a background (launch) daemon that triggers a notification (not necessarily a UILocalNotification). When the notification shows the user an alert, your daemon can then decide to open a normal UI application (or not).

Build a Launch Daemon.

This is the best tutorial I've found. The launch daemon starts when the phone boots, and runs all the time as a non-graphical background process. From there, you can schedule your check for updates. (I have a HelloDaemon class which does all its work in the run: method):

int main(int argc, char *argv[]) {
    @autoreleasepool {
        HelloDaemon* daemon = [[HelloDaemon alloc] init];
        
        // start a timer so that the process does not exit.
        NSTimer* timer = [[NSTimer alloc] initWithFireDate: [NSDate date]
                                                  interval: 1.0
                                                    target: daemon
                                                  selector: @selector(run:)
                                                  userInfo: nil
                                                   repeats: NO];
        
        NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
        [runLoop addTimer: timer forMode: NSDefaultRunLoopMode];
        [runLoop run];
    }    
    return 0;
}

Daemons can use NSTimer normally, so schedule another timer (within run:) to check for updates to download whenever you want.

Notify User from Daemon

If the daemon decides that the user should be notified, then you can either:

1) open the full UI application.

#include <dlfcn.h>
#define SBSERVPATH "/System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices"

-(void) openApp {
  
    // the SpringboardServices.framework private framework can launch apps,
    //  so we open it dynamically and find SBSLaunchApplicationWithIdentifier()
    void* sbServices = dlopen(SBSERVPATH, RTLD_LAZY);
    int (*SBSLaunchApplicationWithIdentifier)(CFStringRef identifier, Boolean suspended) = dlsym(sbServices, "SBSLaunchApplicationWithIdentifier");
    int result = SBSLaunchApplicationWithIdentifier(CFSTR("com.mycompany.AppName"), false);
    dlclose(sbServices);
}

This code requires the com.apple.springboard.launchapplications entitlement for your daemon to use it successfully. See here for adding an entitlement. You'd need an entitlements.xml file for your daemon executable, like this:

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>com.apple.springboard.launchapplications</key>
        <true/>
    </dict>
</plist>

2) show a simple alert window from your daemon, notifying the user of the event, and prompting them to open the UI app

#include "CFUserNotification.h"

-(void) showAlert {
  
    NSMutableDictionary* dict = [NSMutableDictionary dictionary];
    [dict setObject: @"Alert!" forKey: (__bridge NSString*)kCFUserNotificationAlertHeaderKey];
    [dict setObject: @"Updates Ready!" forKey: (__bridge NSString*)kCFUserNotificationAlertMessageKey];
    [dict setObject: @"View" forKey:(__bridge NSString*)kCFUserNotificationDefaultButtonTitleKey];
    [dict setObject: @"Cancel" forKey:(__bridge NSString*)kCFUserNotificationAlternateButtonTitleKey];
    
    SInt32 error = 0;
    CFUserNotificationRef alert =
    CFUserNotificationCreate(NULL, 0, kCFUserNotificationPlainAlertLevel, &error, (__bridge CFDictionaryRef)dict);
    
    CFOptionFlags response;
    // we block, waiting for a response, for up to 10 seconds
    if((error) || (CFUserNotificationReceiveResponse(alert, 10, &response))) {
        NSLog(@"alert error or no user response after 10 seconds");
    } else if((response & 0x3) == kCFUserNotificationAlternateResponse) {
        // user clicked on Cancel ... just do nothing
        NSLog(@"cancel");
    } else if((response & 0x3) == kCFUserNotificationDefaultResponse) {
        // user clicked on View ... so, open the UI App
        NSLog(@"view");
        [self openApp];
    }
    CFRelease(alert);
}

You'll need a CFUserNotification.h header to use the code the way I did above. You can find one by googling, or see one here. This older wiki document also shows some good information for using CFUserNotification from iOS apps.

The answer I linked to from KennyTM above also shows how you can make your alert popup show, even if the device is locked.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...