2

I have an issue where an app is registering for background location updates which it receives until the device hibernates, but then after the device resumes from hibernation the location updates do not resume.

The app is very very simple as its a test to measure the effect of location updates on battery life.

I created the app using the single view application template and:

  • Added the code posted below to the app delegate (that's it, no other code)
  • Added the location updates background mode
  • Added coreLocation.framework

When I launch the app then move it to the background then didUpdateLocations: continues to get called continuously for about 15-20 minutes and getDataUsage() executes every few seconds.

Then after about 15-20 minutes the device hibernates (I'm monitoring activity on Organizer's console). However after waking the device up the app never receives any didUpdateLocations: calls.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.locationManager = [[CLLocationManager alloc] init];
    self.locationManager.delegate = self;
    self.timeThatDataMonitoringStarted = [NSDate date];
    self.initialDataValuesSet = NO;

    CLLocationDegrees degreesFilter = 0.1;
    [self.locationManager setHeadingFilter: degreesFilter];
    self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
    self.locationManager.distanceFilter = 10;
    [self.locationManager startUpdatingLocation];

    [self getDataUsage];

    return YES;
}

- (void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    NSLog(@"******************** LOCATION didUpdateLocations ***************");
    static NSDate* previousDate;
    static int count = 0;
    if (previousDate)
    {
        NSTimeInterval timeInterval = [[NSDate date] timeIntervalSinceDate:previousDate];
        NSLog(@" LOCATION: count: %d time since last update: %f", ++count, timeInterval);
    }
    previousDate = [NSDate date];
}

- (void) getDataUsage
{
    NSTimeInterval elapsedTime = [[NSDate date] timeIntervalSinceDate: self.timeThatDataMonitoringStarted];
    NSLog(@"********* GETTING DATA USAGE. Elapsed time: %f **************",elapsedTime);
    NSArray *data = [self getDataCounters];

    NSNumber *wifiSentSinceBoot = (NSNumber*)data[0];
    NSNumber *wifiReceivedSinceBoot = (NSNumber*)data[1];
    NSNumber *wwanSentSinceBoot = (NSNumber*)data[2];
    NSNumber *wwanReceivedSinceBoot = (NSNumber*)data[3];

    int wifiSentSinceBootAsInt      = [wifiSentSinceBoot intValue];
    int wifiReceivedSinceBootAsInt  = [wifiReceivedSinceBoot intValue];
    int wWanSentSinceBootAsInt      = [wwanSentSinceBoot intValue];
    int wWanReceivedSinceBootAsInt  = [wwanReceivedSinceBoot intValue];

    static int initialWifiSent;
    static int initialWifiReceived;
    static int initialWWanSent;
    static int initialWWanReceived;
    if (!self.initialDataValuesSet)
    {
        self.initialDataValuesSet    = YES;
        initialWifiSent     = wifiSentSinceBootAsInt;
        initialWifiReceived = wifiReceivedSinceBootAsInt;
        initialWWanSent     = wWanSentSinceBootAsInt;
        initialWWanReceived = wWanReceivedSinceBootAsInt;
    }

    int wifiSentSinceLastRetrieval     = wifiSentSinceBootAsInt - initialWifiSent;
    int wifiReceivedSinceLastRetrieval = wifiReceivedSinceBootAsInt - initialWifiReceived;
    int wWanSentSinceLastRetrieval     = wWanSentSinceBootAsInt - initialWWanSent;
    int wWanReceivedSinceLastRetrieval  = wWanReceivedSinceBootAsInt - initialWWanReceived;

    uint dataUsed = wifiSentSinceLastRetrieval + wifiReceivedSinceLastRetrieval + wWanSentSinceLastRetrieval + wWanReceivedSinceLastRetrieval;
    NSLog(@"Total data: %d", dataUsed);
    [self performSelector:@selector(getDataUsage) withObject:nil afterDelay:3];
}


- (NSArray *) getDataCounters
{
    BOOL   success;
    struct ifaddrs *addrs;
    const struct ifaddrs *cursor;
    const struct if_data *networkStatisc;

    int WiFiSent = 0;
    int WiFiReceived = 0;
    int WWANSent = 0;
    int WWANReceived = 0;

    NSString *name=[[NSString alloc]init];

    success = getifaddrs(&addrs) == 0;
    if (success)
    {
        cursor = addrs;
        while (cursor != NULL)
        {
            name=[NSString stringWithFormat:@"%s",cursor->ifa_name];
            //   NSLog(@"ifa_name %s == %@\n", cursor->ifa_name,name);
            // names of interfaces: en0 is WiFi ,pdp_ip0 is WWAN

            if (cursor->ifa_addr->sa_family == AF_LINK)
            {
                if ([name hasPrefix:@"en"])
                {
                    networkStatisc = (const struct if_data *) cursor->ifa_data;
                    WiFiSent+=networkStatisc->ifi_obytes;
                    WiFiReceived+=networkStatisc->ifi_ibytes;
                    //  NSLog(@"WiFiSent %d ==%d",WiFiSent,networkStatisc->ifi_obytes);
                    //  NSLog(@"WiFiReceived %d ==%d",WiFiReceived,networkStatisc->ifi_ibytes);
                }

                if ([name hasPrefix:@"pdp_ip"])
                {
                    networkStatisc = (const struct if_data *) cursor->ifa_data;
                    WWANSent+=networkStatisc->ifi_obytes;
                    WWANReceived+=networkStatisc->ifi_ibytes;
                    //  NSLog(@"WWANSent %d ==%d",WWANSent,networkStatisc->ifi_obytes);
                    //  NSLog(@"WWANReceived %d ==%d",WWANReceived,networkStatisc->ifi_ibytes);
                }
            }

            cursor = cursor->ifa_next;
        }

        freeifaddrs(addrs);
    }

    return [NSArray arrayWithObjects:[NSNumber numberWithInt:WiFiSent], [NSNumber numberWithInt:WiFiReceived],[NSNumber numberWithInt:WWANSent],[NSNumber numberWithInt:WWANReceived], nil];
}

Note I'm using the term "hibernation" not to refer to the app state but to the device state. If you observe the device activity in Organizer's console then after a while the device as a whole starts to shut down. I'm calling this hibernation as I don't know what the actual iOS term is, and by hibernation I also don't mean Auto-Lock and the screen sleeping. This is a separate activity after that, I'm presuming the telephony and GPS chips etc. are powering down. Anyway after this "hibernation" phase has been reached, then if you resume activity on the device is when I am not getting the didUpdateLocations.

Gruntcakes
  • 37,738
  • 44
  • 184
  • 378

1 Answers1

-1

You need to move your getDataUsage method call from the App delegate method:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

to the CLLocationManager delegate:

- (void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations

Since everytime a location update happens (above delegate is called), then your getDataUsage would get called.

Trying to use a pseudo "For loop" using performSelector is bad design and will cause unpredictable result when the app is in background mode. So you need to remove that line:

[self performSelector:@selector(getDataUsage) withObject:nil afterDelay:3];

I would also make sure that the app is registered for background mode if you intend to have the app run in the background.

I would also look at some more details in my answer here: iOS7 Core Location not updating

Community
  • 1
  • 1
Khaled Barazi
  • 8,681
  • 6
  • 42
  • 62
  • The primary problem is that didUpdateLocations: is not getting called at all after the device resumes so the location of getDatUsage is irrelevant. However I've removed the performSelector in case it could have some adverse side affect and am experimenting with pausesLocationUpdatesAutomatically – Gruntcakes Apr 09 '14 at 18:02
  • Are you testing on device or simulator? – Khaled Barazi Apr 09 '14 at 18:04
  • Its on the device. So far setting pausesLocationUpdatesAutomatically looks like its preventing (or delaying) the device from hibernating but I need to wait and watch longer to see what happens. – Gruntcakes Apr 09 '14 at 18:10
  • The word "Hibernating" worries me. in iOS, you really only have 2 app modes (foreground and background). I am not sure what you mean by hibernating but I would think on top of the answer above, testing on a real device would help to pinpoint any additional problems. – Khaled Barazi Apr 09 '14 at 18:12
  • I'm not talking about the app and the app modes, I'm taking about the device. If you observe the device activity in Organizer's console then after a while the device *as a whole* starts to shut down. I'm calling this hibernation as I don't know what the actual iOS term is, and I also don't mean Auto-Lock and the screen sleeping. Anyway after this "hibernation" phase has been reached, then if you resume activity on the device is when I was not getting the didUpdateLocations. Looks like pausesLocationUpdatesAutomatically is preventing this device "hibernation" from occurring. – Gruntcakes Apr 09 '14 at 18:21
  • So the hibernation phase is presumably when things like the telephony chip and gps chip etc. are powering down. – Gruntcakes Apr 09 '14 at 18:31
  • @Aminoacids I have similar issue. Question: when the device goes into hibernation, how do you wake up the app? Do you launch the app, or do you keep the app in background and you walk around to wake up the location service? In my case, when the device goes into hibernation, and I start walking around to wake up location services, well location services don't wake up until i launch my app. That is a problem because I am missing a bunch of coordinates. – Van Du Tran May 13 '14 at 17:40
  • If someone is going to downvote 3 of my answers randomly, 3 months after posting; at a minimum, please post a comment with the reason (if there is one). – Khaled Barazi May 20 '14 at 11:25