Section 4 · Background Execution Limits — A Version-by-Version History
How Android has progressively restricted background work from Android 6 through Android 14, and what each change means for your code
Why This History Matters
Understanding why the current background execution model looks the way it does requires tracing the history. Each Android release introduced new restrictions in response to real problems — battery drain, memory pressure, performance issues — and each restriction invalidated previously acceptable engineering patterns. Code that worked perfectly on Android 5 may be silently broken on Android 8 and crash on Android 14.
This section is a chronological reference. Use it to understand why the APIs exist in their current form and what migration path each restriction required.
Android Developers — Background optimizations
Android 5.0 — Lollipop (API 21) — The Baseline
Before API 21, background work was largely unconstrained. Apps could:
- Start background
Services freely from any state - Register any number of manifest-declared
BroadcastReceivers for any implicit broadcast - Use
AlarmManager.setRepeating()for frequent repeating alarms - Start
IntentServiceworkers freely
JobScheduler was introduced in API 21 as the first system-managed background work scheduler, but adoption was low and it was not yet required.
Status: No meaningful restrictions. Anything goes.
Android 6.0 — Marshmallow (API 23) — Doze and App Standby
What Changed
Two major systems introduced:
1. Doze Mode
When the device is unplugged, stationary, and has its screen off for a period, the system enters Doze. In Doze:
- Network access is blocked
- Wake locks are ignored
AlarmManageralarms (exceptsetAlarmClock) are deferred to maintenance windowsJobSchedulerjobs are deferred
Apps can use setAlarmClock() to guarantee exact alarm delivery, or setExactAndAllowWhileIdle() which fires but is rate-limited to one alarm per 9 minutes in deep Doze.
2. App Standby
If the user has not interacted with an app recently and has not pinned it, the app enters standby and its background jobs and network access are deferred.
Impact
- Repeating
AlarmManageralarms break in Doze unless they usesetExactAndAllowWhileIdle. - Background network calls in a
Servicemay fail silently when the device is idle. - FCM high-priority messages become the standard way to wake apps from Doze.
Migration
Switch from AlarmManager.setRepeating() to JobScheduler for periodic work, or use setExactAndAllowWhileIdle() for time-critical alarms. Add Doze awareness to any service that performs network operations.
Android 7.0 — Nougat (API 24) — Light Doze and Broadcast Reduction
What Changed
1. Light Doze
Doze was split into two levels: Light Doze activates as soon as the screen turns off and the device is unplugged (even if it is not stationary). Deep Doze activates later when the device has been stationary for an extended period. Light Doze has more frequent maintenance windows; Deep Doze has windows every few hours.
2. Three implicit broadcasts removed from manifest receivers
The following broadcasts could no longer be received by manifest-declared receivers (apps had to register dynamically):
CONNECTIVITY_ACTION(android.net.conn.CONNECTIVITY_CHANGE)ACTION_NEW_PICTUREACTION_NEW_VIDEO
This was the first explicit restriction on implicit broadcasts, foreshadowing the much larger restriction in API 26.
Migration
- Remove manifest receivers for
CONNECTIVITY_ACTION; register dynamically in foreground components - Use
ConnectivityManager.NetworkCallbackfor network state monitoring - Use
JobSchedulerwithsetRequiredNetworkType()for deferred network-dependent work
Android 8.0 — Oreo (API 26) — The Background Revolution
What Changed
Android 8.0 introduced the most significant background execution restrictions in Android’s history. Two sweeping changes:
1. Background Service Limits
An app that is not in the foreground cannot start a background Service with startService(). Calling startService() from the background throws IllegalStateException. The system allows a grace period immediately after the app moves to the background, but this window is short (~1 minute) and unreliable.
Exceptions — the following can still start services from background:
- Apps receiving a high-priority FCM message
- Apps receiving a broadcast in a manifest-declared receiver (for the brief duration of
onReceive) - Apps visible to the user (foreground)
startForegroundService()— which must then callstartForeground()within 5 seconds
2. Implicit Broadcast Restrictions
Nearly all implicit broadcasts can no longer be received by manifest-declared receivers. The system silently drops them. Only a short list of explicit exceptions (statutory broadcasts) still works with manifest receivers:
ACTION_BOOT_COMPLETEDACTION_LOCKED_BOOT_COMPLETEDACTION_MY_PACKAGE_REPLACEDACTION_LOCALE_CHANGEDACTION_TIMEZONE_CHANGED/ACTION_TIME_CHANGEDACTION_NEW_OUTGOING_CALL- (and ~20 others listed in the official docs)
3. Notification Channels
All notifications must now be assigned to a NotificationChannel. Notifications posted without a channel are silently dropped on API 26+.
Impact
This release broke a large fraction of background service patterns that had worked for years:
IntentServicestarted from background →IllegalStateException- Manifest receivers for connectivity, power, storage → silently never fire
- Notifications without channels → silently dropped
Migration
- Replace background
ServicewithWorkManageror usestartForegroundService()+startForeground() - Replace manifest broadcast receivers with dynamic receivers or
WorkManagerconstraints - Add
NotificationChannelcreation to app startup
Android 9.0 — Pie (API 28) — App Standby Buckets and Foreground Service Permission
What Changed
1. App Standby Buckets
The binary App Standby system from API 23 was replaced with five usage buckets: Active, Working Set, Frequent, Rare, and Never. The system automatically moves apps between buckets based on usage patterns and applies different job and alarm deferral policies to each.
Apps in the RARE bucket may see their PeriodicWorkRequest only execute once per day, regardless of the declared interval.
2. FOREGROUND_SERVICE Permission
Starting a Foreground Service now requires declaring:
1
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
This is a normal permission (auto-granted at install) but the manifest declaration is required. Apps that omit it and call startForeground() receive a SecurityException.
3. Power-Saving Mode Enhancements
Manufacturers gained more official hooks for their own power-saving modes. Background work in Battery Saver mode became even more restricted — no background jobs, no location, no network.
Migration
- Add
FOREGROUND_SERVICEpermission to manifest - Test WorkManager in all five standby buckets using
adb shell am set-standby-bucket
Android 10.0 — Q (API 29) — Background Activity Launch Restrictions and Location
What Changed
1. Background Activity Launches Blocked
Apps can no longer start an Activity from the background. Starting an Activity requires the app to be in the foreground or to have been granted a specific exemption (e.g., the app received a high-priority FCM message, the app has a PendingIntent from the system, the activity being started is a Notification full-screen intent).
This broke the common pattern of launching an Activity from a background service or BroadcastReceiver.
2. Foreground Service Type Introduced (Location)
Apps that access location from a Foreground Service must declare foregroundServiceType="location" in the manifest. Without it, accessing fine or coarse location from a foreground service triggers a SecurityException.
3. Background Location Access Restricted
Apps must now request ACCESS_BACKGROUND_LOCATION (a runtime permission) to access location in the background. Users see a separate permission prompt. Background location is categorized as a sensitive permission and Play Store policy governs when apps can request it.
4. READ_CALL_LOG and PROCESS_OUTGOING_CALLS moved
Access to call metadata is further restricted.
Migration
- Replace Activity-from-background patterns with high-priority notifications with
fullScreenIntent - Declare
foregroundServiceType="location"for services that use location - Add
ACCESS_BACKGROUND_LOCATIONpermission requests where necessary
Android 11.0 — R (API 30) — Package Visibility and One-Time Permissions
What Changed
1. Package Visibility (query filter)
Apps can no longer query arbitrary installed packages. The PackageManager.getInstalledApplications() method returns a limited set by default. To query specific packages, the app must declare them in <queries> in the manifest.
2. One-Time Permissions
Location, microphone, and camera permissions can now be granted as one-time grants — they are revoked automatically when the app leaves the foreground.
3. Background location permission on separate screen
Play Store now strictly enforces the policy that apps requesting ACCESS_BACKGROUND_LOCATION must have a core feature that requires it. The permission is moved to a separate settings page — users cannot grant it from a dialog.
Migration
- Declare required
<queries>in manifest for inter-app communication - Handle one-time permission grants gracefully — re-request when the feature is used
Android 12.0 — S (API 31) — Exact Alarms and Foreground Service Restrictions
What Changed
1. Exact Alarm Permission Required
AlarmManager.setExact() and setExactAndAllowWhileIdle() now require apps targeting API 31+ to hold SCHEDULE_EXACT_ALARM. Without it, the alarm is silently made inexact. The permission must be granted by the user in Special App Access settings.
Exception: setAlarmClock() remains unrestricted (it is the designated API for clock apps).
2. Background Activity Launch Restrictions Tightened
Additional restrictions on when background apps can launch Activities or start Foreground Services. Apps in full-background state need explicit exemptions from specific system events (high-priority FCM, recent app usage, PendingIntent from a notification).
3. Custom Notifications Must Use System-Provided Templates
Custom RemoteViews for standard notifications are deprecated. Apps must use NotificationCompat.DecoratedCustomViewStyle or the system standard templates. This prevents abuse of notification space for ads and misleading content.
4. Pending Intent Mutability
All PendingIntent objects must explicitly declare FLAG_MUTABLE or FLAG_IMMUTABLE. Omitting both throws IllegalArgumentException. Use FLAG_IMMUTABLE unless the system must modify the intent (inline reply, direct share).
5. Splash Screen API
Mandatory splash screen for all apps — SplashScreen API. Unrelated to background execution but widely noticed.
Migration
- Declare
SCHEDULE_EXACT_ALARMpermission; addcanScheduleExactAlarms()guard - Add explicit mutability flags to all
PendingIntentcalls - Replace content-wide custom notifications with
DecoratedCustomViewStyle
Android 13.0 — Tiramisu (API 33) — Notification Permission and Receiver Flags
What Changed
1. POST_NOTIFICATIONS Runtime Permission
Posting any notification now requires the user to grant POST_NOTIFICATIONS — a runtime permission. On upgrade from older APIs, the system automatically grants the permission to existing installs during a transition period, but new installs must request it.
1
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
2. Dynamic Receiver Flags Required
Context.registerReceiver() now requires either RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED for receivers that handle implicit intents. Omitting the flag on API 34+ (enforced starting 34, introduced at 33) causes a crash.
3. USE_EXACT_ALARM Permission
A new permission for apps where exact alarms are a core feature (clock, calendar, timer apps) — auto-granted at install without user interaction:
1
<uses-permission android:name="android.permission.USE_EXACT_ALARM" />
Regular apps continue to use SCHEDULE_EXACT_ALARM with user approval.
4. Media Permission Split
READ_EXTERNAL_STORAGE is replaced with granular permissions: READ_MEDIA_IMAGES, READ_MEDIA_VIDEO, READ_MEDIA_AUDIO.
Migration
- Add
POST_NOTIFICATIONSpermission and request flow - Add
RECEIVER_EXPORTED/RECEIVER_NOT_EXPORTEDflags to allregisterReceivercalls - Replace
READ_EXTERNAL_STORAGEwith granular media permissions
Android 14.0 — Upside Down Cake (API 34) — Foreground Service Enforcement
What Changed
1. Foreground Service Types Mandatory
Every Foreground Service must now declare android:foregroundServiceType in the manifest AND pass the type flag to startForeground(). Omitting either throws:
MissingForegroundServiceTypeExceptionif no type is declared in manifestInvalidForegroundServiceTypeExceptionif the type does not match a valid constant
Apps that had foregroundServiceType declared before API 34 remain compatible — the enforcement applies to apps targeting API 34+.
2. Type-Specific Foreground Service Permissions
Each foreground service type now has a corresponding normal permission that must be declared in the manifest:
1
2
3
4
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<!-- etc. -->
3. Short Service Type
New shortService foreground service type — runs for a maximum of 3 minutes, requires no additional permissions, and stops automatically when the time limit is reached.
4. Minimum Exact Alarm Interval
The minimum interval between exact alarms is now enforced more strictly in some scenarios to prevent battery abuse.
5. Implicit Intent Restrictions
Implicit intents can no longer start components that are declared with android:exported="false". Previously this had inconsistent enforcement; API 34 makes it strict.
6. SCHEDULE_EXACT_ALARM Revoked on Update
Apps that upgrade their target SDK to 34 lose the SCHEDULE_EXACT_ALARM grant by default and must re-request it.
Migration
- Add
foregroundServiceTypeto every<service>in the manifest - Add type-specific
FOREGROUND_SERVICE_*permissions - Add
startForeground(ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_*)call - Re-handle
SCHEDULE_EXACT_ALARMgrants on update
Complete Restriction Timeline
| Android Version | API | Key restriction |
|---|---|---|
| 6.0 Marshmallow | 23 | Doze Mode; App Standby |
| 7.0 Nougat | 24 | Light Doze; CONNECTIVITY_ACTION blocked from manifest receivers |
| 8.0 Oreo | 26 | Background Service limits; implicit broadcast restrictions; Notification Channels required |
| 9.0 Pie | 28 | App Standby Buckets; FOREGROUND_SERVICE permission required |
| 10.0 Q | 29 | Background Activity Launch blocked; foregroundServiceType for location; background location permission |
| 11.0 R | 30 | Package Visibility; one-time permissions |
| 12.0 S | 31 | Exact Alarm permission; PendingIntent mutability flags required |
| 13.0 Tiramisu | 33 | POST_NOTIFICATIONS runtime permission; receiver flags required; USE_EXACT_ALARM |
| 14.0 UpsideDownCake | 34 | Foreground Service type mandatory; type-specific FOREGROUND_SERVICE permissions; Short Service |
The Migration Pattern Each Restriction Follows
Every Android background restriction follows the same general shape: Google observed a class of battery or performance abuse, introduced a new API that is exempt from the restriction, and deprecated or blocked the old approach. The canonical migrations are:
| Old pattern (broken by restriction) | New pattern |
|---|---|
Background Service via startService() |
WorkManager, or startForegroundService() + Foreground Service |
Manifest receiver for CONNECTIVITY_ACTION |
ConnectivityManager.NetworkCallback (foreground) or WorkManager constraint (background) |
AlarmManager.setRepeating() |
WorkManager PeriodicWorkRequest |
| Untyped Foreground Service | Typed Foreground Service with foregroundServiceType |
| Notification without channel | NotificationChannel + NotificationCompat.Builder |
Any PendingIntent without mutability |
FLAG_IMMUTABLE or FLAG_MUTABLE explicit flag |
startActivity() from background |
fullScreenIntent in notification, or high-priority FCM handler |
AlarmManager.setExact() without permission |
canScheduleExactAlarms() + SCHEDULE_EXACT_ALARM permission request |
Interview Tip:
A common interview question is “How has Android restricted background work over the years?” The expected answer traces: Doze in API 23 → broadcast restrictions in API 26 + background service ban → Standby Buckets in API 28 → Activity launch ban in API 29 → exact alarm permission in API 31 → notification permission in API 33 → foreground service type enforcement in API 34. Each restriction pairs with the correct modern API that replaced it.