---
title: Android Foreground Services for Location in Capacitor
description: Set up the Android location foreground service for a Capacitor app, from the service type and ACCESS_BACKGROUND_LOCATION to the two Google Play declarations.
date:
  created: 2026-10-23
  updated: 2026-10-23
authors:
  - robingenz
categories:
  - Capacitor
  - Guides
  - SDKs
links:
  - Capacitor Background Geolocation: sdks/capacitor/background-geolocation.md
faq: true
---

# Android Foreground Services for Location in Capacitor

An Android foreground service for location is the only way a Capacitor app keeps receiving positions after the user switches to another app, and Android attaches four requirements to it: a declared service type, a permission for that type, a visible notification, and a review form in the Google Play Console, with a second form when the app also requests `ACCESS_BACKGROUND_LOCATION`. The [Capacitor Background Geolocation plugin](../../sdks/capacitor/background-geolocation.md) ships the service, its type and the notification, and leaves that one optional manifest permission and the Play forms to your app. This post walks through each requirement, shows which part the plugin covers and which part is yours, when the background location permission is needed and when it is not, and why the `androidNotification` option is mandatory. The plugin is part of [Capawesome Insiders](../../insiders/index.md).

<!-- more -->

<div class="capawesome-z29o10a">
  <a href="https://capawesome.io/" target="_blank">
    <img alt="Try Capawesome Cloud free for 14 days — iOS builds without a Mac" src="https://capawesome.io/assets/banners/cloud-free-trial-capacitor-apps.png" />
  </a>
</div>

**Key takeaways:**

- Apps that target Android 14 (API level 34) must declare a type for every foreground service; the plugin declares the `location` type, `FOREGROUND_SERVICE` and `FOREGROUND_SERVICE_LOCATION` in its own manifest.
- Android counts a running location foreground service as foreground access, so a session started from a visible screen keeps receiving positions under the while-in-use grant; `ACCESS_BACKGROUND_LOCATION` is only needed to start one from the background.
- Request `location` and `notifications` first and `backgroundLocation` in a second call; on Android 11 and newer the second call opens the app's settings page, and a combined request grants nothing.
- Every foreground service shows a notification, so `startWatching(...)` rejects without `androidNotification`; on Android 13 and newer the user can stop the whole app from the Active apps list.
- Google Play asks for a foreground service type declaration, and for a background location declaration with a prominent in-app disclosure and a video when the manifest contains `ACCESS_BACKGROUND_LOCATION`.

## Why a foreground service

Android only keeps delivering positions to an app in the background when that app runs a foreground service, and `watchPosition` from `@capacitor/geolocation` runs in the web view, which is the first thing Android pauses when your app leaves the screen. Android's [foreground service types page](https://developer.android.com/develop/background-work/services/fgs/service-types){:target="_blank"} names the `location` type for "long-running use cases that require location access, such as navigation and location sharing", which covers a courier shift, a run tracker or a shared live location.

The Capacitor Background Geolocation plugin starts that service when you call [`startWatching(...)`](../../sdks/capacitor/background-geolocation.md#startwatching). On the native side, the service checks that location services are enabled, builds the notification from your options, calls `startForeground()` with the `location` type on Android 10 and newer, takes a partial wake lock and then subscribes to the fused location provider, or to the platform location manager on devices without Google Play services. [`stopWatching()`](../../sdks/capacitor/background-geolocation.md#stopwatching) reverses all of it and removes the notification. Swiping the app away from the recent apps list stops the service too, which is why tracking ends when the user force-quits the app.

iOS has no foreground service. It keeps the app alive under the `location` background mode with its own set of rules, which [Why iOS Stops Sending Background Location Updates](./why-ios-stops-background-location-updates.md) covers. The rest of this post is Android only.

## The location service type

Since Android 14, every foreground service needs a declared type, and the plugin's own manifest carries it. Android's [list of changes for Android 14](https://developer.android.com/about/versions/14/changes/fgs-types-required){:target="_blank"} states that an app targeting Android 14 "must specify appropriate foreground service types", and that calling `startForeground()` on a service without a type raises `MissingForegroundServiceTypeException`. Each type also needs its own permission, and a missing one ends in a `SecurityException`. The plugin declares the service and both permissions, so none of this lands in your manifest:

```xml
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />

<service
    android:name=".BackgroundGeolocationService"
    android:exported="false"
    android:foregroundServiceType="location" />
```

`FOREGROUND_SERVICE` exists since API level 28 and `FOREGROUND_SERVICE_LOCATION` since API level 34; both have the `normal` protection level, so the user never sees a prompt for them. The plugin's manifest also declares `ACCESS_COARSE_LOCATION`, `ACCESS_FINE_LOCATION`, `POST_NOTIFICATIONS` and `WAKE_LOCK`, which Gradle merges into your app's manifest during the build.

The `location` type comes with two runtime prerequisites, and the plugin reports each one as a distinct error. Android requires that the user has enabled location services and that the app holds `ACCESS_COARSE_LOCATION` or `ACCESS_FINE_LOCATION` at the moment the service starts, otherwise `startForeground()` throws a `SecurityException`. The plugin checks location services before it starts the service and rejects `startWatching(...)` with `LOCATION_SERVICES_DISABLED`, and it requests the location permission itself when it has not been granted yet. If the permission request is refused, the call rejects with `PERMISSION_DENIED`. A failure inside `startForeground()` itself surfaces as the message "Failed to start the foreground service. Ensure that all required permissions are granted.", which points at a manifest problem, such as a merge rule that removed one of the declared permissions or another plugin that overrides the service entry.

## Background location permission

`ACCESS_BACKGROUND_LOCATION` is the one Android permission your app declares itself, and whether you need it depends on when the session starts, not on whether the app stays on screen. Android's [location permission guide](https://developer.android.com/develop/sensors-and-location/location/permissions){:target="_blank"} counts an app as using foreground location while "your app is running a foreground service", and states that the app "retains access when it's placed in the background". A watch session that starts while the app is visible therefore keeps receiving positions after the user switches apps, under the while-in-use grant alone. The permission exists since Android 10 (API level 29) with the `dangerous` protection level, and you add it to `android/app/src/main/AndroidManifest.xml` next to the `application` tag when your app needs location in the cases below:

```xml
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
```

The plugin leaves it out on purpose. Google Play requires a [policy declaration and review](https://support.google.com/googleplay/android-developer/answer/9799150){:target="_blank"} for every app that requests background location, and a plugin that declared it would drag every app into that review, including apps that only need the position while they are open. A delivery app that tracks a shift adds the permission; a store finder that only reads the position on screen does not.

The cases that need the permission are the ones Android does not count as foreground access, and they all involve starting the service while the app is already in the background. The foreground service types page states that an app in the background cannot start a location foreground service unless it holds `ACCESS_BACKGROUND_LOCATION`, and the Android 11 [foreground service changes](https://developer.android.com/about/versions/11/privacy/foreground-services){:target="_blank"} add that a service started from the background gets no location access without background location access. A session that a geofence transition, a push message or a notification tap starts after the user left the app needs the permission; a session the user starts with a button does not. The plugin's README is more conservative: it asks for the permission whenever you want position updates in the background and notes that updates may be suspended without it. Treat that as the plugin's documented requirement and verify the while-in-use case on your target devices before you ship without the permission. The queue and the upload keep working for whatever positions arrive either way.

One more rule shapes where you call `startWatching(...)`. Apps that target Android 12 (API level 31) or higher can no longer start a foreground service while they run in the background, except in a [list of exemptions](https://developer.android.com/develop/background-work/services/fgs/restrictions-bg-start){:target="_blank"} such as a tap on a notification, a geofence transition or a high-priority FCM message; a start outside those cases throws `ForegroundServiceStartNotAllowedException`. Start the watch session from a visible screen, for example when the user taps "Start shift", and never from a timer that fires after the app has been backgrounded.

## Requesting permissions in order

Request the location permission first and, when your app needs it, the background permission in a separate call, because Android 11 enforces that order. Android's [runtime permission guide](https://developer.android.com/develop/sensors-and-location/location/permissions/runtime){:target="_blank"} recommends "incremental requests" and states that for apps targeting Android 11 or higher a request that contains both foreground and background location at once is ignored, so the app gets neither. The plugin's [`requestPermissions(...)`](../../sdks/capacitor/background-geolocation.md#requestpermissions) mirrors that with three permission names, `location`, `notifications` and `backgroundLocation`:

```typescript
import { BackgroundGeolocation } from '@capawesome-team/capacitor-background-geolocation';

const requestForegroundPermissions = async () => {
  const status = await BackgroundGeolocation.requestPermissions({
    permissions: ['location', 'notifications'],
  });
  return status.location === 'granted';
};

const requestBackgroundPermission = async () => {
  const status = await BackgroundGeolocation.requestPermissions({
    permissions: ['backgroundLocation'],
  });
  return status.backgroundLocation === 'granted';
};
```

What the second call shows depends on the Android version. On Android 10, the system dialog offers "Allow all the time" directly. On Android 11 and newer, the [background location guide](https://developer.android.com/develop/sensors-and-location/location/permissions/background){:target="_blank"} states that the dialog no longer contains that option and "users must enable background location on a settings page", so the plugin takes the user to the app's location settings, where they pick "Allow all the time" and return to your app. Google recommends an in-app explanation before that jump, and the Play policy requires one, so show your disclosure screen first and call `requestBackgroundPermission()` from its button.

[`checkPermissions()`](../../sdks/capacitor/background-geolocation.md#checkpermissions) returns the state of all three names, with two version-specific rules. On Android 9 and older, `backgroundLocation` mirrors `location`, because the separate permission did not exist yet, and the plugin skips the background request on those versions. On Android 12 and older, `notifications` is always `granted`. If a user denied a permission permanently, [`openSettings()`](../../sdks/capacitor/background-geolocation.md#opensettings) opens the app's details page in the system settings, which is the only place left to change it.

## The mandatory notification

`startWatching(...)` rejects on Android when the `androidNotification` option is missing, because Android shows a notification for every foreground service and the plugin builds that notification from your options instead of inventing text in your app's name. The native options parser throws "notification must be provided." before anything starts, and `title` and `text` are required inside the object:

```typescript
import { Accuracy, BackgroundGeolocation } from '@capawesome-team/capacitor-background-geolocation';

const startShift = async () => {
  await BackgroundGeolocation.startWatching({
    accuracy: Accuracy.High,
    distanceFilter: 25,
    androidInterval: 10000,
    androidNotification: {
      title: 'Shift in progress',
      text: 'Your route is being recorded.',
      channelName: 'Shift tracking',
      icon: 'ic_stat_route',
      color: '#1E88E5',
    },
  });
};
```

The plugin posts the notification as ongoing with low priority on a channel named by `channelName` (default "Background Geolocation"), so it sits in the drawer without a sound. Tapping it opens your app's launcher activity. `icon` names a drawable resource in `android/app/src/main/res`; without it, the plugin falls back to the launcher icon, which Android renders as a white shape in the status bar, so a dedicated monochrome icon such as `ic_stat_route` looks better. `color` tints the small icon.

Android 13 (API level 33) turned posting notifications into a runtime permission, and the plugin declares `POST_NOTIFICATIONS` and exposes it as the `notifications` permission name. The [notification permission guide](https://developer.android.com/develop/ui/views/notifications/notification-permission){:target="_blank"} states that users who deny it still see foreground service notices in the Task Manager but not in the notification drawer, so the service keeps running either way. Request it in the first permission call anyway: a user who sees no notification has no idea that tracking is on, and that is the situation the Play reviewer looks for.

The same Task Manager gives users a way to end the session. Android 13 lists every app with a running foreground service under "Active apps" in the notification drawer with a Stop button, and the [Android 13 changes page](https://developer.android.com/about/versions/13/changes/fgs-manager){:target="_blank"} states that tapping it removes the app from memory, so "your entire app stops, not just the running foreground service", and that the app receives no callback. Treat it like a force-quit: [`isWatching()`](../../sdks/capacitor/background-geolocation.md#iswatching) returns `false` on the next launch, and the positions collected before the stop are still in the native queue when the queue is enabled. [How to Track Location in the Background with Capacitor](./how-to-track-location-in-the-background-with-capacitor.md) shows how to restore the session from that state.

## Google Play declarations

Google Play reviews up to two things about a location foreground service, in two separate forms under App content in the Play Console.

The first form is the [foreground service types declaration](https://support.google.com/googleplay/android-developer/answer/13392821){:target="_blank"}, which Google requires from every app that targets Android 14 and newer and uses a foreground service. For each type, the form asks for a description of the feature that uses the service, the impact on the user if the system defers or interrupts the task, a link to a video that shows how the user triggers the feature, and a use case from a preset list. For the `location` type, the preset use cases are user-initiated location sharing (a find-a-friend feature or vehicle tracking), navigation (driving navigation or ride tracking) and geofencing. The video has to show the user starting the sharing, the navigation or the geofence setup inside your app.

The second form is the [location permissions declaration](https://support.google.com/googleplay/android-developer/answer/9799150){:target="_blank"}, which every app with `ACCESS_BACKGROUND_LOCATION` in its manifest has to complete, and it has stricter rules:

- **Core functionality**: background location must be required for the main purpose of the app and provide a significant benefit. Google lists physical safety, perceived safety and health and fitness as significant, and ads, analytics, personalization and convenience as minimal. Requests made only for ads are denied.
- **One feature**: the form evaluates one feature at a time, and listing several features leads to rejection. Approval then covers the whole app.
- **Prominent disclosure**: the app has to show an in-app disclosure before the runtime permission prompt, during normal use rather than behind a settings menu. It must contain the word "location", say that collection happens "even when the app is closed or not in use", and name every feature that uses background location. Google's suggested wording is "collects location data to enable" followed by the features. The same statement goes into the store listing and your website; a privacy policy alone does not count.
- **Video**: a recording of at most 30 seconds that shows the feature, the disclosure and the runtime prompt, preferably on YouTube.
- **Consequences**: without approval, updates can be blocked and the app removed. An ineligible feature means removing the permission from the manifest and the related code on every track, including closed tests.

A disclosure screen for the shift example above reads: "Courier Pro collects location data to enable live delivery tracking for your dispatcher, even when the app is closed or not in use." Show it once, with a button that calls `requestBackgroundPermission()`, and record exactly that flow for the video. The plugin's two-step permission API fits this requirement without extra code, because the background request only runs when your button triggers it.

## FAQ

### Which permissions does a Capacitor app need for background location on Android?

Six manifest permissions from the plugin, one optional permission from your app, and one runtime grant the service cannot run without. The Capacitor Background Geolocation plugin declares `ACCESS_COARSE_LOCATION`, `ACCESS_FINE_LOCATION`, `FOREGROUND_SERVICE`, `FOREGROUND_SERVICE_LOCATION`, `POST_NOTIFICATIONS` and `WAKE_LOCK`; your app adds `ACCESS_BACKGROUND_LOCATION` only when a session has to start while the app is in the background. At runtime, the service needs the `location` grant; `notifications` decides whether the user sees the service notification in the drawer, and `backgroundLocation` comes in a second call through [`requestPermissions(...)`](../../sdks/capacitor/background-geolocation.md#requestpermissions) when your app needs it. Google Play needs the foreground service type declaration, and the location permissions declaration when the manifest contains `ACCESS_BACKGROUND_LOCATION`.

### Does the plugin work without `ACCESS_BACKGROUND_LOCATION`?

Yes. A session started from a visible screen runs as a location foreground service, which Android counts as foreground access, so it keeps receiving positions after an app switch with the "While using the app" grant. The permission is needed when the session has to start while the app is already in the background, for example from a geofence transition or a push message. The plugin README asks for it whenever you want background updates, so verify the while-in-use case on your devices before you rely on it.

### Why does `startWatching(...)` fail with "Failed to start the foreground service"?

Because `startForeground()` threw, which Android does when the service type, the `FOREGROUND_SERVICE_LOCATION` permission or a runtime prerequisite is missing. Check the merged manifest in `android/app/build/intermediates/merged_manifest` for the service entry with `foregroundServiceType="location"` and both foreground service permissions, and make sure no other plugin or `tools:node="remove"` rule strips them. Disabled location services and a denied location permission are reported separately as `LOCATION_SERVICES_DISABLED` and `PERMISSION_DENIED`.

### Can I hide the foreground service notification?

No. Android shows a notification for every foreground service, and the plugin only controls its content. You can make it less intrusive: it already uses low priority and a silent channel, and a small monochrome `icon` plus a short `text` keeps it to one line in the drawer. On Android 13 and newer, a user who denies the notification permission no longer sees it in the drawer, but the service keeps running.

### What happens when the user taps Stop under Active apps?

Android removes the whole app from memory without a callback, so the watch session ends like after a force-quit. On the next launch, `isWatching()` returns `false`, and positions collected before the stop are still in the native queue if you enabled it with `setConfig(...)`. Start a new session from a visible screen; a background timer cannot restart the foreground service on Android 12 and newer.

### Is the Capacitor Background Geolocation plugin free?

No. The plugin is part of the [Capawesome Insiders](../../insiders/index.md) subscription and is installed from the Capawesome npm registry with the license key that comes with it.

## Build in the Cloud

Permission and manifest changes like these need a fresh native build before you can test them on a device. Capawesome Cloud builds your Android and iOS apps from your Git repository, so a change to `AndroidManifest.xml` becomes a signed APK or AAB without a local Android Studio setup.

[Try Capawesome Cloud Free](https://capawesome.io){ .md-button .md-button--primary }

## Conclusion

Add `ACCESS_BACKGROUND_LOCATION` only when a session has to start while the app is in the background, because a session started on screen keeps its location access without it, and if you add it, build the disclosure screen and record the video before you submit, because without an approved location declaration Google can block updates and remove the app. Everything else about the foreground service, from the `location` type to the notification, is the plugin's job, and the two-step permission API keeps the runtime flow within Google's rules. For the full tracking build around this setup, including the queue and the upload, read [How to Track Location in the Background with Capacitor](./how-to-track-location-in-the-background-with-capacitor.md).

If you have any questions, join us on the [Capawesome Discord server](https://discord.gg/VCXxSVjefW){:target="_blank"}. To stay updated on the latest news, subscribe to the [Capawesome newsletter](https://capawesome.io/newsletter/){:target="_blank"}.
