Android 14 Support

Transistor Software
4 min readSep 30, 2023

The Background Geolocation SDK for React Native v4.13.2, Cordova v4.13.2, Capacitor v5.1.2 and Flutter v4.12.2 now includes support forAndroid 14 (SDK 34)

Android continues to “turn the screws” with this release, with more limitations imposed on the background operation of foreground-services, but the Background Geolocation SDK follows the rules and continues to walk the path that Android clearly wants developers to follow.

AlarmManager Exact Alarms

The Background Geolocation SDK has traditionally made use of “exact alarms” for many features, including Config.stopTimeout, Config.motionTriggerDelay, Config.schedule. The permission making those “exact alarms” is now denied by default and the usage of permissions granting “exact alarms” will now be more closely scrutinized. For this reason, the SDK will now leave it up to developers to manually add a new permission to their own AndroidManifestas a new step in the Setup Instructions linked at the Github repo.

<manifest>
<uses-permission android:minSdkVersion="34" android:name="android.permission.USE_EXACT_ALARM" />
.
.
.
</manifest>

If you choose to not add this new permission, the SDK will simply fall-back to using “in-exact alarms”, so an alarm scheduled to occur 5 minutes in the future, might fire 7–8 minutes instead. This is not a big deal for operations such as Config.stopTimeout.

However, one major advantage of “exact alarms” is their ability to launch a foreground-service in the background. The SDK had previously used this to bypass foreground-launch restrictions imposed by Android 13. For example, say you had a BackgroundFetch callback which calls .getCurrentPosition(options) while your app was in the background:

BackgroundFetch.configure({
minimumFetchInterval: 15
}, async (taskId) => {
try {
const location = await BackgroundGeolocation.getCurrentPosition({
samples: 3
});
console.log('[getCurrentPosition] success: ', location);
} catch (error) {
console.warn('[getCurrentPosition] ERROR: ', error);
}
});

Executing the code above with your app in the background and the SDK in the stationary state (ie: where not foreground-service is currently running) would result in the following error shown in logcat:

ActivityManager startForegroundService() not allowed due to mAllowStartForeground false

The SDK would catch this error and re-attempt a foreground-service launch by first firing an AlarmManager exact-alarm with an interval of 1ms, (exact-alarms are granted an exception to foreground-service background launch rules), whose receiver would then re-launch the failed foreground-service Intent and voila: it worked! But in Android 14, this clever trick will no longer work without manually adding the permission above.

There is still one more clever trick up my sleeve, but it won’t work all-the-time like AlarmManager exact-alarms. Sometimes, it will simply be impossible to call .getCurrentPosition while your app is in the background with the plugin in the stationary state.

A new Location attribute: Location.Coords.age

This new attribute has been added to both Android and iOS. Since sometimes your .getCurrentPosition request will not always be able to launch a foreground-service to turn location-services on to request a fresh location, .getCurrentPosition will always at least return the last known location. Because this location can be from some time ago, the plugin now attaches a new attribute Location.Coords.age (milliseconds) , showing how old the location’s timestamp was relative to the OS SystemTime at the moment it was received.

Above are two locations recorded from simulated BackgroundFetch events, just like the code above, where the app was in the background.

  • recorded_at : from Location.timestamp recorded on the device.
  • created_at : the time the location was received at the server.

The first record in the table above shows an AGE of 49601ms . Playing a little with the raw UTC timestamp data from the table above, we can see that recorded_at + age =~ created_at :

>recorded_at = new Date('2023-08-30T20:04:42.669Z')
>Wed Aug 30 2023 16:04:42 GMT-0400 (Eastern Daylight Time)

>created_at = new Date('2023-08-30T20:05:30.131Z')
>Wed Aug 30 2023 16:05:30 GMT-0400 (Eastern Daylight Time)

>age = 49601
>49601

>recorded_at_plus_age = recorded_at.getTime() + age
>1693425932270

>new Date(recorded_at_plus_age)
>Wed Aug 30 2023 16:05:32 GMT-0400 (Eastern Daylight Time)

--

--

Transistor Software

Creator of Background Geolocation SDK. Professional plugin developers — geolocation specialists. https://www.transistorsoft.com