Greetings, I'm tracking down a severe battery issue that we're experiencing with our app, which occurs while the app is in the background. Focusing on location activity, my methodology is as follows:
- Swizzle several `CLLocationManager` methods.
- Each time this gets called, log a detailed remote log. This log examines the stack trace and places blame on which party was responsible for which call. The full stack trace is also sent.
- Create a helper class that tracks how much a particular `CLLocationManager` instance goes between calling `startUpdatingLocation` & `stopUpdatingLocation`.
In my test over last week, I noticed that the GoogleMobileAdsSDK had an alarming amount of `CLLocationManager` activity in the background, asking for "best accuracy" (-1). It seems that once it got going, every minute it would call start & stop several times. Details below:
SDK version was: 7.28.0
This occurs when the user gives "Always Allow" location permission. This is not the only SDK that uses location in our app, in the background. Though I don't understand why the GoogleMobileAdSDK is using location in the background at all.
Across a 24 hour period:
`startUpdatingLocation` was called: 8238 times (with the string "GAD" present in the stack trace)
`startUpdatingLocation` was called: 13874 times (with the string "GAD" present in the stack trace)
For a calculated cumulative time of 26 mins, 2 secs
This is the stack trace for this call:
0 MyApp 0x0000000102ecb5f8 _T08MyApp15GBLoggingHelperC23logLocationManagerEventySS_So010CLLocationG0C7managerSo8CLRegionCSg6regionSo8NSNumberCSg0L18MonitoringAccuracys10DictionaryVySSypGSg9extraDatatFZ + 2560
1 MyApp 0x0000000102ecbf38 _T08MyApp15GBLoggingHelperC23logLocationManagerEventySS_So010CLLocationG0C7managerSo8CLRegionCSg6regionSo8NSNumberCSg0L18MonitoringAccuracys10DictionaryVySSypGSg9extraDatatFZTo + 324
2 MyApp 0x000000010253b06c -[CLLocationManager(Swizzler) LocationManagerAnalyzer_startUpdatingLocation] + 196
3 MyApp 0x0000000103705588 GADDispatchAsyncSafeMainQueue + 60
4 MyApp 0x0000000103709d9c GADIsContentRenderedInView + 5612
5 MyApp 0x000000010371d528 GADCategories_GADSlot_State + 5832
6 MyApp 0x000000010371ce68 GADCategories_GADSlot_State + 4104
7 MyApp 0x0000000103705588 GADDispatchAsyncSafeMainQueue + 60
8 MyApp 0x000000010371cdc8 GADCategories_GADSlot_State + 3944
9 MyApp 0x000000010371d028 GADCategories_GADSlot_State + 4552
10 Foundation 0x0000000186777d24 __NSFireTimer + 88
11 CoreFoundation 0x0000000185d2e92c <redacted> + 28
12 CoreFoundation 0x0000000185d2e650 <redacted> + 864
13 CoreFoundation 0x0000000185d2de50 <redacted> + 248
14 CoreFoundation 0x0000000185d2ba38 <redacted> + 1928
15 CoreFoundation 0x0000000185c4bfb8 CFRunLoopRunSpecific + 436
16 GraphicsServices 0x0000000187ae3f84 GSEventRunModal + 100
17 UIKit 0x000000018f2202e8 UIApplicationMain + 208
18 MyApp 0x000000010253dbfc main + 76
19 libdyld.dylib 0x000000018576e56c <redacted> + 4",
Details of this location manger settings:
"allowsBackgroundLocationUpdates": false,
"desiredAccuracy": -1,
"party": "google",
"distanceFilter": -1,
"activityType": "other",
"pausesLocationUpdatesAutomatically": true,
"showsBackgroundLocationIndicator": false
My app has the following "background activity modes":
<array>
<string>fetch</string>
<string>location</string>
<string>remote-notification</string>
</array>
For full detail on how time was calculated:
Code from Swizzled Objective C
```
- (void)LocationManagerAnalyzer_startUpdatingLocation {
self.CLLocationManagerSwizzler_isUpdatingLocation = @(YES);
[GBLoggingHelper logLocationManagerEvent:@"startUpdatingLocation"
manager:self
region:nil
regionMonitoringAccuracy:nil
extraData:nil];
[self LocationManagerAnalyzer_startUpdatingLocation];
}
```
Swift code that tracks location manager active time
```
// Tracking start and stops
static var activeLocationManagers = [CLLocationManager: Date]()
@objc static func logLocationManagerEvent(_ name: String,
manager: CLLocationManager,
region: CLRegion?,
regionMonitoringAccuracy: NSNumber?,
extraData: [String: Any]?) {
var data = [String: Any]()
if name.contains("startUpdatingLocation") {
if activeLocationManagers[manager] == nil {
activeLocationManagers[manager] = Date()
}
} else if name.contains("stopUpdatingLocation") {
if let time = activeLocationManagers[manager] {
let timeActive = Date().timeIntervalSince(time)
data["timeActive"] = timeActive
activeLocationManagers[manager] = nil
}
}
// ...
// data gets logged remotely
}
```
This is a snippet of the log for startUpdatingLocation & stopUpdatingLocation:
4/13 11:40:33: [google] start updating, accuracy: -1
4/13 11:40:33: [google] start updating, accuracy: -1
4/13 11:40:33: [google] start updating, accuracy: -1
4/13 11:40:33: [google] stop updating. Time active: 0.13 seconds
4/13 11:40:33: [google] start updating, accuracy: -1
4/13 11:40:33: [google] stop updating. Time active: 0.02 seconds
4/13 11:40:33: [google] start updating, accuracy: -1
4/13 11:40:33: [google] stop updating. Time active: 0.04 seconds
4/13 11:41:33: [google] start updating, accuracy: -1
4/13 11:41:33: [google] start updating, accuracy: -1
4/13 11:41:33: [google] start updating, accuracy: -1
4/13 11:41:33: [google] stop updating. Time active: 0.32 seconds
4/13 11:41:33: [google] start updating, accuracy: -1
4/13 11:41:33: [google] start updating, accuracy: -1
4/13 11:41:33: [google] stop updating. Time active: 0.08 seconds
4/13 11:42:33: [google] start updating, accuracy: -1
4/13 11:42:33: [google] start updating, accuracy: -1
4/13 11:42:33: [google] stop updating. Time active: 0.09 seconds
4/13 11:42:33: [google] start updating, accuracy: -1
4/13 11:42:33: [google] stop updating. Time active: 0.03 seconds
4/13 11:42:33: [google] start updating, accuracy: -1
4/13 11:42:33: [google] start updating, accuracy: -1
4/13 11:42:33: [google] stop updating. Time active: 0.06 seconds
4/13 11:43:33: [google] start updating, accuracy: -1
4/13 11:43:33: [google] start updating, accuracy: -1
4/13 11:43:33: [google] stop updating. Time active: 0.13 seconds
4/13 11:43:33: [google] start updating, accuracy: -1
4/13 11:43:33: [google] start updating, accuracy: -1
4/13 11:43:33: [google] start updating, accuracy: -1
4/13 11:43:33: [google] stop updating. Time active: 0.12 seconds
4/13 11:44:33: [google] start updating, accuracy: -1
4/13 11:44:33: [google] start updating, accuracy: -1
4/13 11:44:33: [google] stop updating. Time active: 0.11 seconds
4/13 11:44:33: [google] start updating, accuracy: -1
4/13 11:44:33: [google] start updating, accuracy: -1
4/13 11:44:33: [google] start updating, accuracy: -1
4/13 11:44:33: [google] stop updating. Time active: 0.11 seconds
4/13 11:45:33: [google] start updating, accuracy: -1
4/13 11:45:33: [google] start updating, accuracy: -1
4/13 11:45:33: [google] stop updating. Time active: 1.07 seconds
4/13 11:45:33: [google] stop updating. Time active: ??
4/13 11:45:33: [google] start updating, accuracy: -1
4/13 11:45:33: [google] start updating, accuracy: -1
4/13 11:45:33: [google] start updating, accuracy: -1
4/13 11:45:33: [google] stop updating. Time active: 1.49 seconds
4/13 11:45:33: [google] stop updating. Time active: ??
4/13 11:45:33: [google] stop updating. Time active: ??
4/13 11:46:36: [google] start updating, accuracy: -1
4/13 11:46:36: [google] stop updating. Time active: 0.17 seconds
4/13 11:46:36: [google] start updating, accuracy: -1
4/13 11:46:36: [google] stop updating. Time active: 0.05 seconds
4/13 11:46:36: [google] start updating, accuracy: -1
4/13 11:46:36: [google] stop updating. Time active: 0.05 seconds
4/13 11:46:36: [google] start updating, accuracy: -1
4/13 11:46:36: [google] stop updating. Time active: 0.05 seconds
4/13 11:46:36: [google] start updating, accuracy: -1
4/13 11:46:36: [google] stop updating. Time active: 0.03 seconds
4/13 11:47:33: [google] start updating, accuracy: -1
4/13 11:47:33: [google] stop updating. Time active: 0.16 seconds
4/13 11:47:33: [google] start updating, accuracy: -1
4/13 11:47:33: [google] stop updating. Time active: 0.15 seconds
4/13 11:47:33: [google] start updating, accuracy: -1
4/13 11:47:33: [google] stop updating. Time active: 0.15 seconds
4/13 11:47:33: [google] start updating, accuracy: -1
4/13 11:47:33: [google] stop updating. Time active: 0.30 seconds
4/13 11:47:33: [google] start updating, accuracy: -1
4/13 11:47:33: [google] stop updating. Time active: 0.16 seconds
4/13 11:48:33: [google] start updating, accuracy: -1
4/13 11:48:33: [google] stop updating. Time active: 0.15 seconds
4/13 11:48:33: [google] start updating, accuracy: -1
4/13 11:48:33: [google] stop updating. Time active: 0.15 seconds
4/13 11:48:33: [google] start updating, accuracy: -1
4/13 11:48:33: [google] stop updating. Time active: 0.17 seconds
4/13 11:48:33: [google] stop updating. Time active: ??
4/13 11:48:33: [google] start updating, accuracy: -1
4/13 11:48:33: [google] stop updating. Time active: 0.16 seconds
4/13 11:48:33: [google] start updating, accuracy: -1
4/13 11:48:43: [google] stop updating. Time active: 0.15 seconds
I'm running another test with the latest version 7.30.0. I couldn't find any mention of this bug in the release notes.
Please advise, this is the highest priority item for our app, and as you can imagine is causing all sorts of frustrated users.