MediaSessionCompat
PendingIntent
FLAG_IMMUTABLE
FLAG_MUTABLE
Android Development

MediaSessionCompatTargeting S version 31 and above requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

On Android 12 and later, a PendingIntent must declare whether it is mutable or immutable. Media apps often hit this through MediaSessionCompat, media notifications, and media button actions because those features create or consume PendingIntent objects constantly. The fix is usually small, but choosing the right flag matters for both correctness and security.

Why the Crash Happens

When your app targets API level 31 or above, Android rejects PendingIntent creation if neither FLAG_IMMUTABLE nor FLAG_MUTABLE is present. The platform now requires the app to state whether the intent contents may be modified after creation.

For most media use cases, the intent content is fixed. A play, pause, or open-player action should not be rewritten later, so immutable is the safer default.

Use FLAG_IMMUTABLE for Fixed Media Actions

If the action, extras, and target component are known up front, create the PendingIntent with FLAG_IMMUTABLE.

kotlin
1val playIntent = Intent(context, MediaActionReceiver::class.java).apply {
2    action = "com.example.player.PLAY"
3}
4
5val playPendingIntent = PendingIntent.getBroadcast(
6    context,
7    100,
8    playIntent,
9    PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
10)

The same rule applies when setting the session activity:

kotlin
1val openPlayerIntent = Intent(context, PlayerActivity::class.java)
2
3val sessionActivity = PendingIntent.getActivity(
4    context,
5    0,
6    openPlayerIntent,
7    PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
8)
9
10mediaSession.setSessionActivity(sessionActivity)

If you are just launching an activity or sending a fixed media control broadcast, immutable is almost always correct.

Use FLAG_MUTABLE Only When the Framework Requires It

Mutable pending intents are needed only when some later API must add or alter data before delivery. A common example is RemoteInput in a notification reply flow. That is not the normal case for media transport buttons.

The decision rule is simple:

  • if no later mutation is required, use immutable
  • if documented framework behavior needs mutation, use mutable

Do not choose mutable just to make the crash disappear. It broadens the attack surface for no benefit.

Create a Small Compatibility Helper

In a mixed-minSdk project, a helper keeps the flag logic consistent across the app.

kotlin
1fun immutableUpdateCurrentFlags(): Int {
2    return PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
3}
4
5fun mutableUpdateCurrentFlags(): Int {
6    return PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
7}

Then use the helper at every creation site:

kotlin
1val pausePendingIntent = PendingIntent.getBroadcast(
2    context,
3    101,
4    Intent(context, MediaActionReceiver::class.java).apply {
5        action = "com.example.player.PAUSE"
6    },
7    immutableUpdateCurrentFlags()
8)

That reduces the chance of fixing one call site and forgetting three others in notifications or playback helpers.

Where to Look in a Media App

The exception message often appears near MediaSessionCompat, but the real bug may live elsewhere. Search for all PendingIntent factory calls:

  • 'PendingIntent.getActivity'
  • 'PendingIntent.getBroadcast'
  • 'PendingIntent.getService'
  • notification action builders
  • session activity configuration

In older codebases the problem is often in a notification utility class, not the media session itself.

Common Pitfalls

The biggest mistake is setting FLAG_MUTABLE everywhere without asking whether mutation is actually required. That fixes the symptom but ignores the reason Android made the requirement explicit.

Another common issue is adding FLAG_IMMUTABLE and accidentally removing FLAG_UPDATE_CURRENT. If the app relied on refreshed extras for the same request code, behavior can change in subtle ways.

Developers also often patch the main notification action and miss a second PendingIntent used for the session activity, a custom action, or a media button receiver. Test the full playback flow, not just app startup.

Finally, remember that library code can also create pending intents. If the crash stack trace points outside your own class, inspect the call site that passes the intent into the library rather than assuming the support library itself is defective.

Summary

  • Android 12 and later require every PendingIntent to be immutable or mutable.
  • Use FLAG_IMMUTABLE as the default for media actions and session activity intents.
  • Reserve FLAG_MUTABLE for documented cases that truly need post-creation mutation.
  • Search all PendingIntent creation sites in the media flow, not just the first one in the stack trace.
  • Retest notification actions, lock-screen controls, and activity launch paths on API 31 and above.

Course illustration
Course illustration

All Rights Reserved.