Skip to content
Support the creatorSupport

Android foreground service for location

On Android, an app that reads location after the user leaves it does so from a foreground service: a service the system treats as visible to the user, with a notification that stays up while it runs. Since Android 8, almost every release has added a rule about how that service is declared, started and permitted.

  • Apps in the background get location updates only a few times an hour. A foreground service counts as foreground, which is why tracking apps use one.
  • Apps in the background cannot start ordinary services. They call startForegroundService(), and the service must call startForeground() with a notification within five seconds, or the system stops the app.
  • Notifications need a channel. Android 9 adds the FOREGROUND_SERVICE permission, granted at install.

Android 10 (API 29): service type and background permission

Section titled “Android 10 (API 29): service type and background permission”
  • ACCESS_BACKGROUND_LOCATION is a separate permission. Fine or coarse location alone means “while the app is in use”.
  • A service that uses location declares android:foregroundServiceType="location". Started while the app is in use, it keeps location access after the user leaves the app, even with only while-in-use permission.

Android 11 (API 30): background location is a second step

Section titled “Android 11 (API 30): background location is a second step”
  • Foreground and background location requested together are both refused. Apps ask for foreground location first, then for background location separately.
  • The background request has no dialog option. The system opens the app’s location permission page in Settings, where the user picks “Allow all the time”.
  • A foreground service started from the background gets no location without background permission.

Android 12 (API 31): no starts from the background

Section titled “Android 12 (API 31): no starts from the background”
  • Starting a foreground service from the background throws ForegroundServiceStartNotAllowedException. Exemptions include: an activity of the app is visible, the app has just received BOOT_COMPLETED or MY_PACKAGE_REPLACED, or the user has exempted the app from battery optimization.
  • Users can choose approximate location, as long as the app asks for fine and coarse together.

Android 13 (API 33): notification permission

Section titled “Android 13 (API 33): notification permission”
  • POST_NOTIFICATIONS is a runtime permission. Without it the foreground service still runs, but the user does not see its notification.

Android 14 (API 34): declared types and type permissions

Section titled “Android 14 (API 34): declared types and type permissions”
  • Every foreground service declares a type, and the app holds that type’s permission: FOREGROUND_SERVICE_LOCATION for location.
  • startForeground() with type location throws a SecurityException unless the app holds location permission. With only while-in-use permission, the app has to be in the foreground at that moment.

Gradle merges the library’s manifest into your app:

Long Ping's AndroidManifest.xml (excerpt)
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application>
<service
android:name="com.longping.engine.LongPingService"
android:exported="false"
android:foregroundServiceType="location"
android:stopWithTask="false" />
<receiver
android:name="com.longping.engine.LongPingBootReceiver"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
</intent-filter>
</receiver>
</application>

It also declares WAKE_LOCK, INTERNET and ACCESS_NETWORK_STATE. stopWithTask="false" keeps tracking running when the user swipes the app away; set stopOnTerminate: true to stop it instead.

Long Ping starts the service with startForegroundService(), and the service’s first action is startForeground() with the notification and, on Android 10 and newer, the location type. If that call fails, the service stops itself straight away.

  • On Android 11 and newer, when the app is not visible and “Allow all the time” is not granted, Long Ping does not try to start the service. It emits an error event (PERMISSION_DENIED) and starts the service when the app comes to the foreground again.
  • When Android refuses a start (ForegroundServiceStartNotAllowedException on 12+, SecurityException on 14+), Long Ping catches it and emits an error event instead of crashing your app. A WorkManager check every 15 minutes tries again.
  • After a reboot or an app update, the boot receiver restarts tracking if it was on and startOnBoot is true (the default).

So call LongPing.start() while the user is in your app, for example from an “On duty” button: that works on every Android version, with either permission level. Restarts from the background need “Allow all the time”, and on Android 12+ a battery-optimization exemption helps them through; see Android battery managers.

The only Long Ping call that shows a permission dialog follows the Android 11 rule:

  1. It asks for ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION together, so Android 12+ can offer approximate location. It skips this step when location access is already granted.
  2. On Android 10 and newer, if foreground access was granted, it asks for ACCESS_BACKGROUND_LOCATION in a separate request. On Android 11+ the system opens the permission page in Settings, and the promise resolves when the user comes back. Android 9 and older have no second step.
import { PermissionsAndroid, Platform } from 'react-native';
import LongPing, { AuthorizationStatus } from 'react-native-long-ping';
// From a visible screen, after your own disclosure:
if (Platform.OS === 'android' && Platform.Version >= 33) {
await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS);
}
const status = await LongPing.requestPermission('always');
if (status === AuthorizationStatus.ALWAYS || status === AuthorizationStatus.WHEN_IN_USE) {
await LongPing.start(); // WHEN_IN_USE: runs, but cannot restart from the background
}

The call needs an Activity in the foreground and rejects NO_ACTIVITY without one. Permissions has the full flow and the status values.

Long Ping builds the notification from the notification config:

await LongPing.configure({
// …the rest of your config
notification: {
title: 'On duty',
text: 'Sharing your location with dispatch',
smallIcon: 'ic_stat_location', // a drawable or mipmap in your app; default: the app icon
priority: 'low',
},
});

Each priority gets its own channel, since Android fixes a channel’s importance once it exists. Tapping the notification opens your app; debug: true adds the last fix’s age and the queue size. On Android 8.0, where an adaptive small icon crashes the app, Long Ping uses a system location icon. All keys and defaults are in the config reference.

Long Ping neither declares nor requests POST_NOTIFICATIONS. Request it as in the snippet above, and declare it in your app’s manifest, because Android only prompts for declared permissions:

android/app/src/main/AndroidManifest.xml
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

Play Console asks apps that target Android 14 or newer to declare their foreground service types, and apps with ACCESS_BACKGROUND_LOCATION to declare background location. Long Ping’s merged manifest needs both; see App Store & Google Play review.