4

Note: further down in the edits there's simple code that generates the problem without the full complexity of my original program.

I'm trying to code an alarm-clock app for jailbroken iOS. I have a UI set up as a standalone application for scheduling the alarms, which then saves the alarm information to disk. The save file is read by a launch daemon that's always running, which deals with actually scheduling the alarms.

I'm scheduling the alarms as so (EDIT: in the daemon) (NSDate *fireDate is calculated earlier):

NSTimer *singleTimer = [[NSTimer alloc] initWithFireDate:fireDate
                                                interval:0
                                                  target:self
                                                selector:@selector(soundAlarm:)
                                                userInfo:alarm
                                                 repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:singleTimer
                             forMode:NSRunLoopCommonModes];
[self.timers addObject:singleTimer];
[singleTimer release];

EDIT: the above code runs in a method called createTimers, which gets called by reloadData. reloadData reads information about the timers from the shared save file, and it gets called in AMMQRDaemonManager's init function, as well as whenever the manager gets a notification (with notify_post) that the UI app has updated the save file.

The soundAlarm: method (EDIT: also in the daemon) is:

- (void)soundAlarm:(NSTimer *)theTimer {
    NSLog(@"qralarmdaemon: sounding alarm");

    extern CFStringRef kCFUserNotificationAlertTopMostKey;

    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 3, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    CFDictionaryAddValue(dict, kCFUserNotificationAlertTopMostKey, kCFBooleanTrue);
    CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, CFSTR("Title"));
    CFDictionaryAddValue(dict,kCFUserNotificationDefaultButtonTitleKey, CFSTR("OK"));

    SInt32 err = 0;
    CFUserNotificationRef notif = CFUserNotificationCreate(NULL,
              0, kCFUserNotificationPlainAlertLevel, &err, dict);

    CFOptionFlags response;
    if((err) || (CFUserNotificationReceiveResponse(notif, 0, &response))) {
        // do stuff
    } else if((response & 0x3) == kCFUserNotificationDefaultResponse) {
        // do stuff
    }
    CFRelease(dict);
    CFRelease(notif);

    // Do some other stuff
}

This works great, and shows the alert whether the phone is unlocked or locked. But if the phone is locked for a sufficient period of time to enter deep sleep then the timer just fails to fire.

I don't need it to necessarily turn the screen on (though that would be nice) since I'll also be playing sound in addition to displaying the alert, but I do need the timer to fire so that I know when to start the sound.

Any ideas?


EDIT: Here is the main function for the daemon.

int main(int argc, char **argv, char **envp) {

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    NSLog(@"qralarmdaemon: launched");

    AMMQRDaemonManager *manager = [[AMMQRDaemonManager alloc] init];

    NSTimer *keepRunningTimer = [[NSTimer alloc] initWithFireDate:[NSDate distantFuture]
                                                         interval:1000
                                                           target:manager
                                                         selector:@selector(keepRunning:)
                                                         userInfo:nil
                                                          repeats:YES];

    [[NSRunLoop currentRunLoop] addTimer:keepRunningTimer
                                 forMode:NSRunLoopCommonModes];

    // Execute run loop
    NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
    [runLoop run];

    [manager release];

    NSLog(@"qralarmdaemon: exiting");

    [pool release];

    return 0;
}

(Not included is the code that registers for notifications from the main app to know when to read in the save file, etc, but I don't think that's relevant).


EDIT (again): I've added a timer to the run loop that fires at [NSDate distantFuture]. This seems to preserve the timers longer (a timer scheduled 1 min 45 secs after the phone was locked went off, and woke up the phone) but not indefinitely (a timer scheduled 7 min, 30 seconds after the phone was locked did not go off).


EDIT: I've constructed the following toy example that illustrates the problem, without having to worry about interactions with other parts of my code.

I compiled this code, SSH'd in, and ran it, then locked my phone. If I change the dateByAddingTimeInterval:480 to dateByAddingTimeInterval:30, I get the following output:

2013-03-31 12:21:25.555 daemontimertest[6160:707] daemon-timer-test: launched
2013-03-31 12:21:56.265 daemontimertest[6160:707] daemon-timer-test: timer fired

But when it's set to 480, I wait more than 8 minutes and only see the first line:

2013-03-31 12:08:09.331 daemontimertest[6049:707] daemon-timer-test: launched

main.m:

#import "MyClass.h"

int main(int argc, char **argv, char **envp) {

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    NSLog(@"daemon-timer-test: launched");

    MyClass *obj = [[MyClass alloc] init];

    NSTimer *singleTimer = [[NSTimer alloc] initWithFireDate:[[NSDate date] dateByAddingTimeInterval:480]
                                                    interval:0
                                                      target:obj
                                                    selector:@selector(fireTimer:)
                                                    userInfo:nil
                                                     repeats:NO];

    [[NSRunLoop currentRunLoop] addTimer:singleTimer
                                 forMode:NSRunLoopCommonModes];

    // Execute run loop
    [[NSRunLoop currentRunLoop] run];

    [pool release];

    return 0;
}

MyClass.m:

#import "MyClass.h"

@implementation MyClass

- (void)fireTimer:(NSTimer *)theTimer {
    NSLog(@"daemon-timer-test: timer fired");
}

@end

EDIT (3/31/13 5:50 EDT): I've added the following code the toy app code to incorporate Nate's suggestion of using GCD's dispatch_after functionality, but it appears subject to the same time constraints. As an additional note, the main UI app is installed in /Applications and the daemon is installed in /usr/bin.

    double delayInSeconds = 10.0;
    NSLog(@"daemon-timer-test: delay is %f",delayInSeconds);
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        NSLog(@"daemon-timer-test: time has passed.");
    });

EDIT (3/31 5:54 PM): Another quick note. The following lines show up (not consecutively) in syslog right before it appears to go into deep sleep and there are no more messages before I wake the phone up. I've selected the ones that look like they may be relevant; the last message is the very last one sent to syslog before deep sleep.

Mar 31 17:34:23 Andrew-MacKie-Masons-iPhone lockdownd[50]: 002c1000 -[hostWatcher handleSleepNotification:service:messageArgument:]: <hostWatcher: 0x1cd59890> [CC535EDB-0413-4E5E-A844-4DA035E7217C 169.254.2.141:54757] [fd=13]: kIOMessageCanSystemSleep
Mar 31 17:34:23 Andrew-MacKie-Masons-iPhone lockdownd[50]: 002c1000 -[hostWatcher handleSleepNotification:service:messageArgument:]: <hostWatcher: 0x1cd59890> [CC535EDB-0413-4E5E-A844-4DA035E7217C 169.254.2.141:54757] [fd=13]: kIOMessageSystemWillSleep
...
Mar 31 17:34:29 Andrew-MacKie-Masons-iPhone lockdownd[50]: 00343000 __63-[hostWatcher handleSleepNotification:service:messageArgument:]_block_invoke_0: Allowing Sleep
Mar 31 17:34:29 Andrew-MacKie-Masons-iPhone powerd[42]: PM scheduled RTC wake event: WakeImmediate inDelta=645.40
Mar 31 17:34:29 Andrew-MacKie-Masons-iPhone powerd[42]: Idle Sleep Sleep: Using BATT (Charge:76%)
...
Mar 31 17:34:29 Andrew-MacKie-Masons-iPhone kernel[0]: en0::stopOutputQueues
...
Mar 31 17:34:29 Andrew-MacKie-Masons-iPhone kernel[0]: pmu wake events: menu
Nate
  • 31,017
  • 13
  • 83
  • 207
drewmm
  • 737
  • 6
  • 17
  • All the code you show is in your *daemon* process, right, and **not** in the UI app? Also, can you show the `main` program for your daemon, so I can see how you're kicking it off, and keeping it running? Thanks. – Nate Mar 30 '13 at 21:33
  • 1
    @Nate: yep, those are both methods in the `AMMQRDaemonManager` class, which is in the daemon. I've added the main function as well. I just have it on a `NSRunLoop`, and I use `notify_post` and `notify_register_dispatch` to communicate between the UI app and the daemon. – drewmm Mar 30 '13 at 21:40
  • Are you sure that your daemon process is still running? I can't see what you do in `[AMMQRDaemonManager init]`, but I'm wondering if your daemon isn't dying. At the time you expect your alarm to go off, can you `ssh` into your phone, and at the command line do `ps -Aef | grep MyDaemon`, where you should replace *MyDaemon* with whatever the executable name of your daemon is. – Nate Mar 30 '13 at 22:56
  • @Nate: `ps` confirms that the daemon is still running (I executed it several times around when I expected the alarm to go off, and it did not, but the daemon showed up in the process list.) – drewmm Mar 31 '13 at 03:14
  • BTW, I would recommend putting your **edits** in the question at the bottom, chronologically. For other people just coming into the question, the actual question is kind of buried in the middle. Just a thought. – Nate Mar 31 '13 at 22:30
  • Thanks for the advice. I was trying to sort it by the most important information, but there were so many edits that I think it was just getting confusing. I've reorganized it. – drewmm Apr 01 '13 at 19:30

2 Answers2

9

Short Answer

Yes, it's possible (I've done it).

I tried a few different ways, and I was not able to get my daemon/NSTimer to fail in the way you're describing. However, I haven't seen all the files/code that defines your app, so there's at least one more thing I'm concerned about.

Keeping the Daemon Alive

If you look in the Apple docs for NSRunLoop run:

If no input sources or timers are attached to the run loop, this method exits immediately; otherwise, it runs the receiver in the NSDefaultRunLoopMode by repeatedly invoking runMode:beforeDate:. In other words, this method effectively begins an infinite loop that processes data from the run loop’s input sources and timers.

Manually removing all known input sources and timers from the run loop is not a guarantee that the run loop will exit. OS X can install and remove additional input sources as needed to process requests targeted at the receiver’s thread. Those sources could therefore prevent the run loop from exiting.

In the code you show for you daemon's main program, you don't (directly) create any timers. Of course, I don't know what you do in [[AMMQRDaemonManager alloc] init], so maybe I'm wrong. You then use:

NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop run];

to start the run loop. The problem is, if there are no timers at this point, I'm not sure your daemon is going to stay alive. If you look at the second paragraph above, it also indicates that it might stay alive, so maybe that's why I don't see my daemon die when I attempt to use your code.

Your comment says that you see the daemon process alive, when the alarm should go off. However, I'm wondering if maybe your daemon process did die, and then was restarted. Maybe you could also show us the .plist file you use for your Launch Daemon (that goes in /System/Library/LaunchDaemons).

One quick experiment, might be to not start your daemon automatically. Just uninstall the plist file from the LaunchDaemons folder, and make sure you kill the process. Then, start it manually from the command line, ssh'd into the phone:

$ /Applications/MyApp.app/MyDaemon

then, watch the command line. You'll see if it dies or not, and since it's not actually being run by launchd, it won't get restarted if it does die.

Solution?

If it turns out that you do have problems with it dying, then I would try adding a timer that always starts when you daemon does. If you look at my other example, or Chris Alvares' daemon tutorial, it shows this. In the daemon main(), you set one NSTimer to fire a run: method. In that run: method, you could use a while loop and a sleep() call. Or just schedule the timer to repeat at some slow interval.

I'm also not sure how your entire app works. Is it only a tool for scheduling (NSTimer) alarms? If so, it's possible that at any time, there might be no alarms set. Maybe another solution, instead of using the UIApplication to notify_post() to communicate a new timer to the daemon, you could configure the daemon to simply watch a data file. The UIApplication would write out the data file, whenever there is a new timer. Then, iOS could wake your daemon to schedule the NSTimer.

Anyway, this may be a separate issue from your original problem, but it also might be a more efficient way to build an alarm clock daemon, since it doesn't really need to run if there's no alarms active.

Post more if these ideas don't help you fix it (the body of [AMMQRDaemonManager init] might help).

Update

Two more suggestions:

  • make sure your app (daemon and UI) are installed in /Applications. This is the normal location for jailbreak apps, but I just wanted to make sure you weren't installing it in the sandbox area.

  • try replacing your NSTimer implementation (for the alarms, you can leave the main() daemon keepalive timer as is) with GCD blocks:

   // you have used notify_post() to tell the daemon to schedule a new alarm:
   double delayInSeconds = 1000.0;
   dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
   dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
      // put timer expiration code here
   });

Update II

I also noticed that in your original alarm: callback, you use CFUserNotificationReceiveResponse() with an infinite timeout. That means that if the user doesn't dismiss the popup, the timer callback won't complete, and I believe that means that no subsequently scheduled timer callbacks can fire. Probably, you should put all the CFUserNotification code into its own method (e.g. showPopup), and then have your timer callback like so:

- (void)soundAlarm:(NSTimer *)theTimer {
   dispatch_async(dispatch_get_main_queue(), ^(void) {
       [self showPopup];
   });
}

Then, there's the main program (in the code you put on Dropbox). I would recommend changing your main timer (that you call directly from main()) to be a repeating timer, with a relatively small interval, instead of using a fire date with distantFuture. If you want, you can do nothing in it. It's just a heartbeat.

main.m:

NSTimer *singleTimer = [[NSTimer alloc] initWithFireDate:[NSDate date]
                                                interval:5*60   // 5 minutes
                                                  target:obj
                                                selector:@selector(heartbeat:)
                                                userInfo:nil
                                                 repeats:YES];

[[NSRunLoop currentRunLoop] addTimer:singleTimer
                             forMode:NSRunLoopCommonModes];

MyClass.m:

- (void)heartbeat:(NSTimer *)theTimer {
   NSLog(@"daemon-timer-test: heartbeat timer fired");
}

My last comment is that I don't use syslogd. I'm wondering if any of your tests are failing, not because timers aren't running, but because NSLog statements aren't showing up in your log file. I've done all tests where I actually run the daemon executable at the command line, ssh'd into the phone, and I just watch the console for NSLog output. Take logging out of the list of possible failure points ...

Community
  • 1
  • 1
Nate
  • 31,017
  • 13
  • 83
  • 207
  • I don't think this is what's going on. I've updated the main function in the question above to show some `NSLog` commands I had at the beginning and end to notify me when the daemon is launching and exiting; I'm seeing the launch but no exit when this problem happens, and no relaunch if I wake the phone up (and the alarm subsequently goes off). I'll try some of your suggestions when I'm back with my code, but I think the problem is something else. I've also updated the question to describe in more detail when timers get set. – drewmm Mar 31 '13 at 14:43
  • Interestingly, a toy example I just made to test some of these ideas seemed to work. I'm going to tinker with my real code and see if a similar idea fixes it. Thanks for the ideas, and I'll let you know if they work! – drewmm Mar 31 '13 at 15:13
  • I ended up fixing it by adding a timer in main that was set to fire at `[NSDate distantFuture]`. I'm still a little confused about why it worked when the phone was open, but my best guess is that since I had it configured in the plist to be always running, when the phone was unlocked it was continuously cycling through starting/stopping, in such a way that it could fire the alarms, but that when the phone was locked launchd didn't do that automatic cycle. I'll also look into your suggestion of having the daemon watch the file: that seems more efficient. Thanks! – drewmm Mar 31 '13 at 15:40
  • Sorry for the repeated comments, but see my edits above. It turns out that while this trick lets the alarms work after longer periods of sleep than before, it doesn't work indefinitely. If the phone is locked for several minutes, the timer doesn't fire at all. I've posted the code for a toy example that illustrates the problem, rather than trying to post all of the potentially relevant bits of my code. – drewmm Mar 31 '13 at 16:26
  • @drewmm, I added two more things to check/try in my answer above. – Nate Mar 31 '13 at 20:43
  • thanks for all the help you've been giving me. I've added an edit to the top of the original question that addresses your latest suggestions. The app is in /Applications, the daemon is in /usr/bin. The GCD code works with a short delay, but seems to be subject to the same time constraints. – drewmm Mar 31 '13 at 21:52
  • @drewmm, I'm running out of suggestions. Just curious, why's your daemon in `/usr/bin`? Normally, a daemon that's part of an application gets installed with the app (e.g. `/Applications/MyApp.app/MyAppDaemon`). I'm not saying that's the problem, I'm just trying to figure out why I can do this, and you can't. I've successfully scheduled `NSTimer` objects to fire 15 minutes into the future, then immediately closed the UI app, and locked the phone, and then 15 minutes later, I see the timer fire. Is it possible to zip up a sample project, and add a link to download? I could try to run it. – Nate Mar 31 '13 at 22:01
  • The daemon's in `/usr/bin` because I made it with a theos template and that was the default. I tried moving the test daemon to `/Applications`, but it didn't make a difference. I've zipped the directory for the test project I've been using here: https://www.dropbox.com/s/vtlidpe512jywhg/daemontimertest.zip. The Makefile requires various theos configuration things, but all the source files are there. – drewmm Mar 31 '13 at 22:18
  • In case it ends up being a system config thing, I'm testing this on an iPhone 5 running iOS 6.1 with the evasi0n jailbreak. – drewmm Mar 31 '13 at 22:23
  • @drewmm, thanks. I've got the same device. I don't use theos for such things, so I'm now suspicious that its template is configuring the daemon in a way that's causing you problems. Hopefully, I'll have time tonight to run your exact code. Just to be clear, if you change main.m and bump the GCD block delay up to 10 minutes, you won't see it fire? – Nate Mar 31 '13 at 22:27
  • Even with the log at 2 minutes, I don't see it fire, I believe. My exact procedure was to ssh in and start it from the command line, then pull /var/log/syslog off the phone after it should have fired and check for the fire message. – drewmm Mar 31 '13 at 23:42
  • I'm considering the possibility of moving the main app to `/var/mobile/Applications` so that I can use it to schedule UILocalNotifications that will wake the phone up, and then using the daemon's timers (set, perhaps, two seconds after the UILocalNotification so that the phone is guaranteed to be awake) to control the alarm in ways that a UILocalNotification can't be. That's less than ideal for various time-zone related reasons, though, so if you have any other ideas I'd love to hear them. Thanks again! – drewmm Apr 01 '13 at 20:29
  • Got it. No rush at all. Just trying to think about other options. – drewmm Apr 01 '13 at 20:57
  • Out of curiosity, have you had the time to try the code I gave you? I'm really confused about why this isn't working, and I've been banging my head against it for the last few days. I'm not trying to pressure you into spending more time on it, of course, just sort of wondering what the status is. I'll probably start a bounty on this question soon, regardless. – drewmm Apr 05 '13 at 00:40
  • @drewmm, So, I ran your **exact** code in the Dropbox zip file, and at the command line, it worked (it printed out `It's been 2 minutes.`. I waited several minutes after the phone locked to do so, via ssh. I updated my answer with some things that didn't look quite right to me, but again, your smallest Hello World program worked for me, on a 6.1 iPhone 5 (evasion). I don't install much on my JB phones, except Cydia and SBSettings. Maybe run `dpkg -l` at the command line on your phone, and post a list (Dropbox) of all your installed packages. Maybe something else has hosed your device? – Nate Apr 05 '13 at 10:51
  • Nate: I'm working through packages on my end that may be conflicting. I'll post a list once I've narrowed it down some (there's a lot on there). In the meantime do you think you could send me the full output of syslog from the start of the test until it successfully completes on your phone? If you don't have syslogd installed on your phone that's fine, but it might help narrow down what's happening differently under the hood. By the way, I've now confirmed that if I wake the phone up, it will display the success message after two minutes of wake time, not two minutes of real time. – drewmm Apr 05 '13 at 15:26
  • @drewmm, [here is an excerpt from my sysylog](http://pastebin.com/qXDG8reJ) ... I installed syslogd just for this, but it didn't affect anything. You can see that I ssh into the phone, and then I lock the phone, go away for more than an hour, then go back to my ssh session, and run `/Applications/daemontimertest` as root. The GCD "timer" goes off after 2 minutes, as it should. The `MyClass` timer callback never fires, but your main program has it scheduled for `distantFuture`, so that's not an error. The whole time, I never unlocked the phone, or touched anything to wake the screen. – Nate Apr 06 '13 at 08:21
  • Just for privacy's sake, I did remove a few lines where the mail app was trying to connect to one of my email accounts ... which shouldn't have anything to do with your issue. Otherwise, the full syslog is there. – Nate Apr 06 '13 at 08:21
  • Thanks, this seems like it might be extremely helpful. I'm getting a lot of syslog messages that you aren't, that look like it might be the phone going to sleep. Do you by any chance not have `/System/Library/LaunchDaemons/com.apple.mobile.lockdown.plist` or `/System/Library/LaunchDaemons/com.apple.powerd.plist`? – drewmm Apr 06 '13 at 20:12
  • @drewmm, I have both of those, and they're running. If you want to see their configuration options in my .plist files, [see this pastebin](http://pastebin.com/h69MAfTr). – Nate Apr 06 '13 at 20:38
  • So it really seems like something is going on with `powerd`. Disabling it temporarily fixed the problem (and made my syslog look much more like yours), but I'm hesitant to do something that might hurt battery life that much. I'm looking into options involving `CPScheduleWakeUpAtDateWithIdentifier()`, but not having much success so far. I'll post again if I make more progress. – drewmm Apr 07 '13 at 01:52
  • Is it possible that you have some tweak which is hooking into powerd? – drewmm Apr 07 '13 at 01:52
  • @drewmm, all i normally install is Cydia and SBSettings (and dependencies). i'll try to run dpkg -l and post later. hmmm, maybe sshd .... do you have that? (openssh) – Nate Apr 07 '13 at 02:02
  • `sshd` doesn't show up as a package, but `openssh` does and `ps aux` shows an `sshd` process. – drewmm Apr 07 '13 at 02:59
  • Thank you *so* much for all your help with this. I've posted the way I ended up solving it, which involves scheduling power events to wake the phone shortly before firing the alarm. I'm still not sure why we were getting such different results on our devices, but I know I wouldn't have been able to figure this out without comparing our different syslogs. So thank you again for all the time you put into this. – drewmm Apr 09 '13 at 05:27
4

I've worked out a method that works for me. As per my long exchange with Nate (and I definitely wouldn't have been able to work out what was going on without his help), this seems to happen automatically on some systems, but not on others. The problem on my phone seemed to be that powerd was putting the phone into some sort of deep sleep that paused the NSTimers and didn't allow them to fire properly.

Rather than disabling deep sleep (which I suspect has negative power implications) I scheduled a power event:

NSDate *wakeTime = [[NSDate date] dateByAddingTimeInterval:(delayInSeconds - 10)];
int reply = IOPMSchedulePowerEvent((CFDateRef)wakeTime, CFSTR("com.amm.daemontimertest"), CFSTR(kIOPMAutoWake));

This successfully wakes the phone 10 seconds before the alarm is supposed to go off. (The interval isn't precise. I wanted it to be short enough that the phone wouldn't go back to sleep, but long enough that if the phone takes a moment to wake up the timer can still go at the right time. I'll probably shorten it to just 3 or 4 seconds.

The remaining problem is that the NSTimer for the alarm itself won't update automatically, and so it'll be late by whatever period the phone was asleep for. To fix this you can cancel and reschedule the NSTimer whenever the phone wakes up. I did this by registering for a notification that the power management system posts whenever the power state changes:

int status, notifyToken;
status = notify_register_dispatch("com.apple.powermanagement.systempowerstate",
                                  &notifyToken,
                                  dispatch_get_main_queue(), ^(int t) {
                                      // do stuff to cancel currently running timer and schedule a new one here
                                  });

The inefficiency here is that the notification is posted both on sleeps and wakes, but I haven't been able to find an alternative yet.

I hope this is helpful to anyone else who was struggling with this issue.

drewmm
  • 737
  • 6
  • 17
  • +1. I will add, however, that I don't experience any noticeable battery drain on my phone, so whatever is active on yours that is interfering with your daemon's ability to function, probably isn't saving a ton of processing. I'm really curious as to what's doing this, so if you ever figure it out, please post an update here. Thanks, and good luck! – Nate Apr 09 '13 at 07:58
  • have you [seen this information?](http://www.beijingiphonerepair.com/tag/tweak-iphone-power-profile/) Here is [a paste of a couple plist files on my device](http://pastebin.com/z6Y0eetM). Maybe some of our settings are different? Again, as far as I know, I have no battery-life or high-performance tweaks installed on my phone. – Nate Apr 09 '13 at 22:17