Question
Fix PendingIntent FLAG_IMMUTABLE or FLAG_MUTABLE Error in MediaSessionCompat on Android 12+
Question
I am updating an Android app to target SDK 31 (Android 12) and I am getting an error when creating a MediaSessionCompat inside a service that extends MediaBrowserServiceCompat.
My service initializes the media session in onCreate() like this:
override fun onCreate() {
super.onCreate()
mediaSession = MediaSessionCompat(this, TAG).apply {
setCallback(mediaSessionCallback)
isActive = true
}
}
At runtime, the app crashes with this exception:
java.lang.RuntimeException: Unable to create service com.radio.core.service.MediaService: java.lang.IllegalArgumentException: com.xxx.xxx: Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.
Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if some functionality depends on the PendingIntent being mutable, e.g. if it needs to be used with inline replies or bubbles.
The stack trace shows the crash happens inside MediaSessionCompat while it creates a PendingIntent internally.
I am already using a recent media dependency:
implementation("androidx.media:media:1.4.0")
In the library source, I can see logic related to mutable PendingIntent flags, for example:
public static final int PENDING_INTENT_FLAG_MUTABLE =
Build.VERSION.CODENAME.equals("S") ? 0x02000000 : 0;
and later:
mbrIntent = PendingIntent.getBroadcast(
context,
0,
mediaButtonIntent,
PENDING_INTENT_FLAG_MUTABLE
);
Why does this still fail when targeting Android 12+, and how can it be fixed correctly?
Short Answer
By the end of this page, you will understand why Android 12+ requires explicit PendingIntent mutability flags, why older MediaSessionCompat versions can still crash even if they appear to support Android S, and how to fix the issue by updating dependencies or creating PendingIntents correctly in Android apps.
Concept
Android 12 (API 31, also called Android S) introduced a stricter rule for PendingIntent: you must explicitly declare whether it is mutable or immutable.
A PendingIntent is a token that lets another part of Android, or another app, perform an action later on your app's behalf. Because this can affect security, Android now requires developers and libraries to state whether that token can be modified.
The two important flags are:
PendingIntent.FLAG_IMMUTABLE— the wrapped intent cannot be changed later.PendingIntent.FLAG_MUTABLE— the wrapped intent can be modified later if required.
In most cases, FLAG_IMMUTABLE is the safer choice.
The problem in this question is slightly tricky because the crash is not coming from your own code directly. It is happening inside MediaSessionCompat, which internally creates a PendingIntent for media button handling.
Why this happens:
- Your app targets API 31 or above.
- Android enforces explicit mutability flags.
- An older library version may try to work around Android S using preview-only checks such as
Build.VERSION.CODENAME.equals("S"). - Once Android 12 became final, that logic became unreliable because production devices report
Build.VERSION.SDK_INT >= 31, not preview codename logic in the same way the library expected.
So even though the library source seems to mention Android S support, that specific version may still not be fully compatible for released Android 12 behavior.
Mental Model
Think of a PendingIntent like a sealed or unsealed envelope that you hand to the Android system.
- Immutable means the envelope is sealed. The system can deliver it, but cannot change what is inside.
- Mutable means the envelope is openable. The system or another component may add or modify details before using it.
Android 12 now says: you must label the envelope clearly.
If you do not say whether it is sealed or unsealed, Android rejects it with an exception.
In this case, MediaSessionCompat is creating the envelope for you. If the library forgets to label it properly, your app still crashes even though your own service code looks correct.
Syntax and Examples
When creating a PendingIntent on Android 12+, always include either FLAG_IMMUTABLE or FLAG_MUTABLE.
Basic syntax
val pendingIntent = PendingIntent.getActivity(
context,
0,
intent,
PendingIntent.FLAG_IMMUTABLE
)
Or, if mutability is required:
val pendingIntent = PendingIntent.getBroadcast(
context,
0,
intent,
PendingIntent.FLAG_MUTABLE
)
Safer cross-version pattern
If your code must support older Android versions too, combine flags carefully:
val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
} else {
PendingIntent.FLAG_UPDATE_CURRENT
}
val pendingIntent = PendingIntent.getActivity(
context,
0,
intent,
flags
)
Example in a notification
val openAppIntent = Intent(this, MainActivity::class.java)
val openAppPendingIntent = PendingIntent.getActivity(
,
,
openAppIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
Step by Step Execution
Consider this example:
val intent = Intent(this, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
this,
100,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
Here is what happens step by step:
Intent(this, MainActivity::class.java)creates an intent describing the action: openMainActivity.PendingIntent.getActivity(...)asks Android to create a reusable token for launching that activity later.- Request code
100helps Android identify this pending intent. FLAG_UPDATE_CURRENTtells Android to update the existing matching pending intent if one already exists.FLAG_IMMUTABLEtells Android that the intent contents must not be modified later.- On Android 12+, the system accepts this because mutability has been explicitly declared.
Now compare that with broken code:
val pendingIntent = PendingIntent.getActivity(
this,
100,
intent,
PendingIntent.FLAG_UPDATE_CURRENT
)
On Android 12+:
- Android sees that a is being created.
Real World Use Cases
PendingIntent mutability matters in many common Android features:
-
Notifications
- Opening an activity when a notification is tapped
- Triggering action buttons like Reply, Pause, or Dismiss
-
Media playback
- Media session controls
- Media button events from headphones, car systems, or lock screen controls
-
Alarms and scheduled work
AlarmManagercallbacks- Deferred actions triggered by the system
-
Broadcast receivers
- Delivering future intents to a receiver
- Background event handling
-
Foreground services
- Notification actions tied to service behavior
In media apps specifically, the support library may create internal PendingIntents for transport controls and media button integration. That is why dependency compatibility is so important when upgrading target SDK versions.
Real Codebase Usage
In real Android projects, developers usually handle this concept in a few practical ways.
1. Prefer immutable by default
Most app-generated PendingIntents do not need to be changed later.
val flags = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
This is the most common pattern for notifications and launch intents.
2. Use mutable only when required
Use FLAG_MUTABLE only for cases where Android or another component must modify the intent.
Examples include:
- inline replies in notifications
- bubbles
- some remote input flows
- framework features that explicitly require mutable intents
3. Centralize flag logic
Many codebases create helper functions so all PendingIntents follow the same rules.
fun defaultPendingIntentFlags(): Int {
return PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
}
This avoids repeated mistakes.
4. Update dependencies when platform rules change
If a crash comes from AndroidX or another library, application code may not be enough. Real projects often fix this by:
Common Mistakes
1. Assuming the crash must be in your own code
In this question, your service code is simple and looks valid:
mediaSession = MediaSessionCompat(this, TAG)
But the real failure happens inside the library when it creates a PendingIntent.
How to avoid it
- Read the stack trace carefully.
- Look at the deepest library call before the exception.
- Check dependency versions.
2. Using no mutability flag on Android 12+
Broken example:
PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
Fix
PendingIntent.getBroadcast(
this,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
3. Using FLAG_MUTABLE everywhere
Some developers fix the crash by always using mutable.
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
This may work, but it is not the safest default.
Better approach
Comparisons
| Topic | What it means | When to use it | Notes |
|---|---|---|---|
FLAG_IMMUTABLE | The PendingIntent cannot be changed after creation | Default choice for most notifications, launches, and broadcasts | Safer and recommended unless mutability is required |
FLAG_MUTABLE | The PendingIntent can be modified later | Inline replies, bubbles, or framework cases that require changes | Use only when necessary |
| No mutability flag | No explicit mutability declared | Never on Android 12+ targets | Causes IllegalArgumentException |
| Approach | Pros |
|---|
Cheat Sheet
PendingIntent rules on Android 12+
- Always specify one of:
PendingIntent.FLAG_IMMUTABLEPendingIntent.FLAG_MUTABLE
- Prefer
FLAG_IMMUTABLEby default. - Use
FLAG_MUTABLEonly when required.
Common patterns
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
Choose the right flag
-
Use immutable for:
- opening an activity
- standard notification taps
- most broadcasts
- fixed service intents
-
Use mutable for:
- inline reply actions
- bubbles
- cases where Android must fill in or modify extras later
For MediaSessionCompat crashes
- Check the stack trace.
- If the crash is inside
MediaSessionCompat, update the media library.
FAQ
Why does this error appear only after targeting SDK 31?
Because Android 12 enforces explicit PendingIntent mutability when your app targets API 31 or higher.
Should I use FLAG_IMMUTABLE or FLAG_MUTABLE?
Use FLAG_IMMUTABLE in most cases. Use FLAG_MUTABLE only when the PendingIntent must be modified later.
Can MediaSessionCompat create a PendingIntent internally?
Yes. That is why the crash can happen even if you did not manually create a PendingIntent in your own service code.
Why didn't androidx.media:media:1.4.0 fix it?
Some earlier support logic relied on preview Android S checks and was not sufficient for final Android 12 behavior. A newer compatible version is needed.
How do I know whether the crash comes from my code or a library?
Read the stack trace carefully. If the exception originates inside android.support.v4.media.session.MediaSessionCompat or androidx classes, the library is involved.
Can I work around this by lowering target SDK?
You could temporarily avoid the enforcement, but this is not a proper fix. The correct solution is to update code and dependencies.
Mini Project
Description
Build a small Android utility object that creates safe PendingIntents for activities and broadcasts. This project demonstrates how to apply Android 12+ mutability rules consistently and avoid crashes caused by missing flags.
Goal
Create reusable helper functions that generate correct immutable or mutable PendingIntents for common app actions.
Requirements
- Create one helper function for launching an activity.
- Create one helper function for sending a broadcast.
- Use
FLAG_IMMUTABLEas the default behavior. - Allow a mutable option only when explicitly requested.
- Demonstrate usage with one activity intent and one broadcast intent.
Keep learning
Related questions
Accessing Kotlin Extension Functions from Java
Learn how Kotlin extension functions are compiled and how to call them correctly from Java with clear examples and common pitfalls.
Allow HTTP and HTTPS in Android 9 Pie with Network Security Configuration
Learn how Android 9 Pie handles cleartext HTTP traffic and how to allow HTTP and HTTPS safely using network security config.
Android AlarmManager Example: Scheduling Tasks with AlarmManager
Learn how to use Android AlarmManager to schedule tasks, set alarms, and handle broadcasts with a simple beginner example.