Foreground services
A foreground service is a contract with the user: you may keep running, and in exchange you must say what you are doing. Since Android 14 the contract has a type, a permission and, for some types, a deadline.
The foreground service is Android's most honest background API. It does not pretend the work is free: it makes the app show a notification for as long as it runs, and it makes the user able to see and stop it.
TL;DR#
- A foreground service is a service with an ongoing notification. The notification is the point, not an inconvenience.
- Since API 28 it needs the
FOREGROUND_SERVICEpermission. Since API 34 it must declare a type, and each type has its own permission.1 - Since API 31 an app in the background cannot start a foreground service without an exemption.2
- Some types have additional restrictions:
cameraandmicrophonecannot run from the background, anddataSynchas a time limit from API 35. - The notification still appears even if
POST_NOTIFICATIONSis denied, but the app's other notifications do not.3
Mental model#
Think of a construction permit.
A normal service is a worker who shows up and starts building. Nobody necessarily knows they are there; the city finds out when something goes wrong.
A foreground service is a permitted construction site. The permit is displayed at the entrance — the notification — and it says what kind of work is happening. Since the last round of rules, the permit has a category (demolition, excavation, electrical), and each category has its own insurance requirement. Some categories are not allowed to start at night.
The notification is not paperwork you do to satisfy the city. It is the sign that lets a passer-by know what is happening and complain.
Where the analogy breaks#
A construction permit is granted before work starts and is valid for a duration. An Android foreground service can be started by the app itself, and the "permit" is checked at start time — which is why the start-from-background rule matters so much. The app is both applicant and builder, and the platform is the inspector who may simply refuse.
The big picture#
Text description of this diagram
Four gates a foreground service passes before it may keep running, each added by a different release. Gate one, since API 34, requires a declared foregroundServiceType in the manifest plus the matching FOREGROUND_SERVICE permission; without it the service cannot start at all. Gate two, since API 28, requires the FOREGROUND_SERVICE normal permission, and the type-specific permission from API 34; failing it throws a SecurityException. Gate three, since API 31, requires that the start is allowed — a visible activity or one of a short list of exemptions — or foregroundServiceStartNotAllowedException is thrown; this is the gate that stopped the foreground service from being a universal workaround. Gate four, since API 35, requires the service to stay within its type's time budget, chiefly dataSync and mediaProcessing, and the system stops it and calls onTimeout when the budget expires. A list of start-from-background exemptions follows: a visible activity or existing foreground service, a high-priority FCM message for user-visible work, a user action on a notification or tile, and a privileged role or device owner.
The arc is consistent: each release moved the foreground service from "a service that happens to show a notification" toward "a declared, typed, time-bounded activity".
Deep dive#
The mechanics#
Three things must be true for a foreground service to start:
- The service is declared in the manifest with
android:foregroundServiceType. - The app holds
FOREGROUND_SERVICEand the type-specific permission. - The start is permitted — either the app is visible, or an exemption applies.
<service
android:name=".sync.SyncService"
android:exported="false"
android:foregroundServiceType="dataSync" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />class SyncService : Service() {
override fun onBind(intent: Intent?): IBinder? = null
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val notification = buildNotification()
// The type argument was added in API 29 and is required from API 34.
startForeground(
NOTIFICATION_ID,
notification,
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC,
)
scope.launch {
try {
repository.sync()
} finally {
stopForeground(STOP_FOREGROUND_REMOVE)
stopSelf()
}
}
return START_NOT_STICKY
}
}The types#
The type is not decoration. Each one maps to a permission and to a set of start rules:4
| Type | Permission | Notes |
|---|---|---|
dataSync | FOREGROUND_SERVICE_DATA_SYNC | Time-limited from API 35 |
mediaPlayback | FOREGROUND_SERVICE_MEDIA_PLAYBACK | The canonical "user is listening" case |
location | FOREGROUND_SERVICE_LOCATION | Also needs a location permission |
camera | FOREGROUND_SERVICE_CAMERA | Cannot start from the background |
microphone | FOREGROUND_SERVICE_MICROPHONE | Cannot start from the background |
mediaProjection | FOREGROUND_SERVICE_MEDIA_PROJECTION | Screen capture; needs the projection consent |
phoneCall | FOREGROUND_SERVICE_PHONE_CALL | Calling apps |
connectedDevice | FOREGROUND_SERVICE_CONNECTED_DEVICE | Bluetooth, USB, companion devices |
specialUse | FOREGROUND_SERVICE_SPECIAL_USE | For cases the others do not cover; needs justification |
The specialUse type exists because the list cannot be complete. It is deliberately awkward: Play
review asks what the use case is, and using it to avoid picking a real type is a policy violation.
Starting from the background#
Since API 31, startForegroundService() from the background throws
ForegroundServiceStartNotAllowedException unless an exemption applies.2 The exemptions are narrow and specific:
- The app has a visible activity or a foreground service already.
- The app received a high-priority FCM message intended for user-visible work.
- The user performed an action on a notification, widget or tile.
- The app holds a role such as
SYSTEM_ALARM_CALLor is a device owner. - The app is in the middle of a
BOOT_COMPLETEDbroadcast for certain types.
The practical implication: start the service while the app is visible. A button press is a permission. A timer firing an hour later is not.
Time limits#
Some types are bounded:5
dataSynchas a time budget from API 35. When it expires, the system stops the service and callsService.onTimeout()on API 35+; the app is expected to stop cleanly.mediaProcessing(API 35) has a similar budget.- Short service was introduced as a general mechanism for "run for a couple of minutes and finish", without a type.
// The service is told it is about to be stopped.
override fun onTimeout(startId: Int) {
// Persist progress, then stop.
stopSelf()
}Short service and the dataSync timeout behaviour were refined across API 34 and 35.
unverified(the exact timeout durations and the onTimeout signature vary by API level; confirm against the current reference for the levels you support)The notification#
The notification is required, and it must be a real one:
- A small icon is mandatory.
- The channel's importance determines whether it makes a sound. A low-importance channel is the right choice for an ongoing sync.
- The notification should say what is happening. "Syncing 3 of 12 photos" is a contract; "App is running" is noise.
If POST_NOTIFICATIONS is denied on API 33+, the foreground service notification is still shown —
the system makes an exception — but the app's other notifications are silently dropped. That
asymmetry is worth knowing: you cannot rely on notification permission for anything except the
foreground service's own notification.
What Play policy requires#
Google Play restricts the USE_EXACT_ALARM permission to alarm-clock and calendar apps, and reviews
foreground service usage for the declared type. The policy language is about user-visible work:
if the user cannot tell the service is running and would not want it to be, the type is wrong.
The decision that precedes the code#
Before writing a foreground service, answer three questions in writing. They are the same three the platform will ask, and the same three a Play reviewer may ask.
What is the user watching? If the answer is "nothing", the service should be WorkManager. A foreground service exists to make work visible; if visibility adds nothing, the notification is pure cost.
Which type describes the work? Not the one that is easiest to start. The type determines the permission, the start rules and the time budget, and picking the wrong one means fighting restrictions designed for a different activity.
When does it stop? Every foreground service should have a termination condition that is not "the user force-stops the app". A sync ends when the sync finishes. A call ends when the call ends. A service with no natural end is a design problem, and on API 35 the platform will eventually end it for you.
The migration away from a foreground service#
The most common refactor in modern Android is replacing a long-running foreground service with a WorkManager job that shows progress through a notification it updates itself. The user-visible result is similar; the system-visible result is very different.
The exception is media playback, and it is a genuine one. The user must be able to pause, and the
service must survive the app being backgrounded indefinitely. That is what mediaPlayback is for,
and why it has no time limit.
Reading the exception#
ForegroundServiceStartNotAllowedException is not a generic "denied". It carries a message naming
the reason, and the reason maps directly to a gate.
The three messages you will actually see are: the app is in the background, the app is in a restricted bucket, or the type is not permitted from the background. Each has a different fix — the first means "start it while visible", the second means "the user has not opened this app in weeks", and the third means "this type cannot start from here at all, use WorkManager".
Logging the exception message rather than a generic failure is the difference between a one-minute diagnosis and an afternoon of guessing.
How it evolved#
| API | Version | What changed |
|---|---|---|
| 1 | 1.0 |
|
| 17 | 4.2 Jelly Bean |
|
| 19 | 4.4 KitKat |
|
| 21 | 5.0 Lollipop |
|
| 26 | 8.0 Oreo |
|
| 28 | 9 Pie |
|
| 29 | 10 |
|
| 30 | 11 |
|
| 31 | 12 |
|
| 34 | 14 Upside Down Cake |
|
| 35 | 15 Vanilla Ice Cream |
|
| 36 | 16 Baklava |
|
Under the hood#
- Service.javastartForeground and the type argument
- ServiceInfo.javathe FOREGROUND_SERVICE_TYPE constants
- ActiveServices.javawhere the start is allowed or refused
- ForegroundServiceManager.javathe tracking and timeout logic
ActiveServices is where the exception is thrown. Reading setFgsRestrictionLocked in that file is
the fastest way to see the actual list of exemptions the platform enforces.
Curiosity corner#
- A foreground service is not a thread. It runs on the main thread unless you move work off it,
and
startForegroundreturning does not mean the work has started. stopForeground(STOP_FOREGROUND_REMOVE)removes the notification;STOP_FOREGROUND_DETACHleaves it behind. Removing is almost always what you want.- The media playback type has no time limit, because stopping a user's music is worse than the battery it costs. That is a policy decision, not a technical one.
Try it#
adb shell dumpsys activity services com.example.app
Example output
isForeground=true foregroundId=1001 types=0x00000001adb shell dumpsys package com.example.app | grep FOREGROUND_SERVICE
Example output
android.permission.FOREGROUND_SERVICE_DATA_SYNC: granted=trueadb shell am start-foreground-service com.example.app/.sync.SyncService
Example output
java.lang.ForegroundServiceStartNotAllowedExceptionBuild it#
Write a service that declares dataSync, start it from a button, and watch the notification. Then
try to start it from a BroadcastReceiver while the app is in the background, and read the
exception. The two observations together are the whole rule.
Check yourself#
Check yourself
Put these background restrictions in the order they were introduced.
Drag, or use the arrows to reorder.
- 1Background services are stopped after the app leaves the foreground (API 26)
- 2Alarms become inexact by default (API 19)
- 3Doze defers network, jobs and alarms (API 23)
- 4Foreground service types become mandatory (API 34)
- 5Background activity starts are blocked (API 29)
Sources#
Sources
Every version number, API level, class name and date on this page is traced to one of these. Pages are re-verified against them; last_verified above says when.
- docsBehavior changes: Apps targeting Android 14developer.android.comaccessed 2026-10-27↩
- docsBehavior changes: Apps targeting Android 12developer.android.comaccessed 2026-10-27↩
- docsBehavior changes: Apps targeting Android 13 or higherdeveloper.android.comaccessed 2026-10-27↩
- docsForeground services overview | Background workdeveloper.android.comaccessed 2026-10-27↩
- docsBehavior changes: Apps targeting Android 15 or higherdeveloper.android.comaccessed 2026-10-27↩
- docsBehavior changes: Apps targeting Android 11developer.android.comaccessed 2026-10-27↩
- docsNotifications overviewdeveloper.android.comaccessed 2026-10-27↩
- docsPermissions best practicesdeveloper.android.comaccessed 2026-10-27↩