Ios – Battery drain when using CoreLocation Significant Location Monitoring & CoreBluetooth

cllocationmanagercore-bluetoothcore-locationiospower-management

We've released an application which runs in the background and uses CoreBluetooth & CoreLocation to automatically save your parking location.

At a high level our app just looks for a CoreBluetooth disconnect event and turns on GPS until we get an location fix (accuracy <=10m) or 3 minutes max time (this could occur when you park in an underground parking lot with no GPS coverage). We then use Significant Location Monitoring to automatically re-launch our application in event of the system terminating our app.

During our development we never saw a battery drain issue ourselves, however 75% of our users say that they see a significant battery drain. 10% of our backers responded to the poll so it's a difficult to determine how representative the breakdown is, but it's a large percentage of our users. http://www.findmycarsmarter.com/forum/viewtopic.php?f=4&t=30

We then released an update that allowed users to disable Significant Location Monitoring and 60% say that by disabling Significant Location Monitoring the drain goes away. http://www.findmycarsmarter.com/forum/viewtopic.php?f=4&t=42

Initially we could not duplicate the drain issue ourselves, but we found that when we installed a simple app that just turned on Significant Location Monitoring in conjunction with Find My Car Smarter, we intermittently saw the drain reproduce. In the drain state the phone does not enter hibernate. This is indicated by the usage time in (Settings->Usage->Time since last full charge) continuing to increment even though the phone had been put to sleep and the display is off. Something prevents the system from entering hibernate. The battery drains about 15% per hour in this stage. This drain shows up intermittently and seems to clear itself out after an hour or two and come again randomly. We have not found a way to reliability reproduce the drain.

We believe the issue is caused by multiple clients calling into CoreLocation. We asked a few users who experienced the issue to wipe their phone and only install our Find My Car Smarter app. Alone with just this app installed, the drain did not exhibit. We have had other reports that when our app is used with Google Latitude or Facebook, etc is when they see the drain occur. Or if they go and kill other applications the drain goes away. We've seen the drain persist through a power cycle, with no apps launched. This implies that it has to be a system level service that prevents the OS from sleeping.

Even though we think the issue is caused by some race condition of multiple clients calling into CoreLocation, we never saw the issue reproduce with apps that only used CoreLocation. We even created 4 or 5 different apps that would simultaneously access CoreLocation and we didn't see the drain occur. We did however see the issue when we had an app with CoreLocation and a second app with CoreLocation + CoreBluetooth. There are probably very few apps that use the CoreLocation + CoreBluetooth combination, so potentially that's why more developers haven't hit this issue. Although we're at a loss to explain how CoreLocation & CoreBluetooth interact to cause this drain and how the second app with CoreLocation comes into the equation. Since the drain was intermittent it's possible that it's just a fluke that the issue only happened when we were testing with CoreLocation + CoreBluetooth.

On a wiped 5.0.1 iPhone 4S with only these two apps CTM1 & FMC installed we were able to intermittently get into the drain state. Interestingly, the drain issue seemed to occur much less frequently on a wiped device then our normal device. Unfortunately we only saw the drain state a few times and without being able to reliably reproduce the drain we don't have a good control state to work from.

We've filed a bug report with Apple and opened up a Technical Support Incident, but maybe the Stackover community can also provide some insight. We've seen this issue both in 5.0.1 & in 5.1 Beta 3.

CTM1
http://www.findmycarsmarter.com/files/CTM1.zip

On Going into the Background
    [locationManager stopUpdatingLocation];
    [locationManager stopUpdatingHeading];
    [locationManager startMonitoringSignificantLocationChanges];

On Re-entering Foreground
    [locationManager stopMonitoringSignificantLocationChanges];
    [locationManager startUpdatingLocation];
    [locationManager startUpdatingHeading];
On didUpdateToLocation
    //do nothing
On didUpdateHeading
    //do nothing

FMC
http://www.findmycarsmarter.com/files/FMC.zip

On Going into the Background
    [btleManager stopScan];
    [locationManager stopUpdatingLocation];
    [locationManager stopUpdatingHeading];
    [locationManager startMonitoringSignificantLocationChanges];

On Re-entering Foreground
    [locationManager stopMonitoringSignificantLocationChanges];
    [locationManager startUpdatingLocation];
    [locationManager startUpdatingHeading];        
    [btleManager scanForPeripheralsWithServices:nil options:nil];
On didUpdateToLocation
    //do nothing
On didUpdateHeading
    //do nothing
On centralManagerDidUpdateState
    [btleManager scanForPeripheralsWithServices:nil options:nil];
On didDiscoverPeripheral
    [btleManager connectPeripheral:device options:nil];
On didConnectPeripheral
    //update log
On didDisconnectPeripheral
    //initiate reconnect
    [btleManager connectPeripheral:device options:nil];

If you see any coding mistakes that might account for the drain please let us know.

One other question we did have, if we're using both GPS & Significant Location Monitoring, is there a reason to call stopMonitoringSignificantLocationChanges? Looking at the Regions sample code they call stopMonitoringSignificantLocationChanges & startLocationUpdate on entering foreground and stopLocationUpdate & startMonitoringSignificantLocationChanges on entering background, but I'm wondering if this is necessary/recommended/required?

Update:

We've confirmed with Apple Developer Technical Support that for applications using both GPS & Significant Location Monitoring that our sequence of turning off Significant Location Monitoring before enabling GPS update is correct.

We've also confirmed that the drain issue can still be seen in the GM 5.1 & with a re-compiled Find My Car Smarter application against the 5.1 Frameworks.

Update:

It looks like the issue is triggered when our app is launched from the background in response to a Significant Location Monitoring event. We actually don't handle this scenario properly in our sample code but we do in our actual app.

In the sample code, on a background re-launch we'll turn on location update and since there is not a applicationDidEnterBackground call, the GPS will be left on.

In our app we check to see if we were launched from the background by looking for the UIApplicationLaunchOptionsLocationKey flag, if so we start Significant Location Monitoring, otherwise we were launched in the foreground and we start Updating Location.

Apple got back to us and stated that usage of Significant Location Monitoring does not require the location set in UIBackgroundModes array in the Info.plist. We removed this entry and it appears that the battery drain state is no longer hit. We still have bluetooth-central in the UIBackgroundModes list. At the moment we're unclear as to why this helps. We're going to be running some more experiments to help us understand this better. If anyone has any suggestions please let us know.

Best Solution

At the end of the day, Apple's suggestion of removing location from UIBackgroundModes fixed our battery drain issue.

In order to still get locations in the background we had to wrap the [locationManager startLocationUpdates] & [locationManager stopLocationUpdates] calls with:

[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler];
[[UIApplication sharedApplication] endBackgroundTask:];
Related Question