Introduction: The "Broken" Player on Android 12 and Above
\nImagine: you proudly publish an update to your player or podcast client app. A few days pass, and angry comments appear in the reviews section: "After the update, the music stopped playing!", "Buttons on the lock screen don't work!", "The app crashes when trying to start a track!". Sounds familiar? You've most likely encountered that very error that plagues thousands of developers worldwide:
\nMediaSessionCompat: Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent\nThis problem is one of the most popular questions on Stack Overflow (tens of thousands of views), and it is directly related to changes in Android 12 (API level 31). The good news: the fix is simple and will take you no more than 10 minutes. In this article, we will break down the causes of the error, the correct solution in Kotlin, and best practices to make your app work like clockwork again.
\n\nWhy Does the Error Occur? Understanding PendingIntent
\nTo understand the essence of the problem, we need to recall how MediaSessionCompat works. This is a component that allows your app to interact with system media controls: headset buttons, the lock screen, the notification panel. To do this, it creates a PendingIntent — a kind of "deferred call" for system services. The system uses this Intent to tell your app: "The user pressed Play/Pause/Next".
Starting with Android 12 (API 31), Google tightened the security requirements for PendingIntent. Previously, developers could omit the FLAG_IMMUTABLE or FLAG_MUTABLE flag, and the system would use the mutable variant by default. Now, you are required to explicitly specify whether the system can modify the contents of your Intent after its creation. If you don't do this, your app will crash. Android 12+ simply won't allow creating such a PendingIntent.
FLAG_IMMUTABLE vs FLAG_MUTABLE: Which to Choose?
\n- \n
- FLAG_IMMUTABLE (recommended): The Intent cannot be changed after creation. This is safer because an attacker cannot substitute the data. Use this flag in 90% of cases, including for
MediaSessionCompat. \n - FLAG_MUTABLE: The Intent can be changed. Only needed in special cases, for example, if you use
NotificationCompatwith actions that require dynamic data, or when working withAlarmManager. \n
For MediaSessionCompat and media notifications, always use FLAG_IMMUTABLE. This improves the security and stability of your app.
Step-by-Step Fix in Kotlin
\nLet's look at what typical code that causes the error looks like, and how to fix it. Most often, the problem occurs in the onCreate() method of your service or media browser.
Incorrect Code (Will Crash on Android 12+)
\n// Old way — without specifying a flag\nval pendingIntent = PendingIntent.getActivity(\n this,\n 0,\n Intent(this, MainActivity::class.java),\n 0 // <-- Error! No FLAG_IMMUTABLE or FLAG_MUTABLE\n)\n\nCorrect Code in Kotlin (Fix)
\nSimply add PendingIntent.FLAG_IMMUTABLE as the last parameter:
val pendingIntent = PendingIntent.getActivity(\n this,\n 0,\n Intent(this, MainActivity::class.java),\n PendingIntent.FLAG_IMMUTABLE // <-- Solution!\n)\nIf your app supports older versions of Android (below API 31), use a conditional check to avoid crashes on older devices:
\nval flags = if (Build.VERS