πŸš€ UllrichLumina

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

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

πŸ“… | πŸ“‚ Category: Java

Navigating the complexities of Android development requires a keen eye on evolving API changes, especially when it comes to system-level interactions and security. A critical update for developers targeting Android S+ (API level 31 and above) concerns how PendingIntent objects are created, particularly within the context of media playback. Specifically, MediaSessionCompat:Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent. This isn’t just a compliance hurdle; it’s a fundamental shift designed to enhance app security and prevent potential vulnerabilities stemming from mutable PendingIntents. Understanding this requirement is essential for ensuring your media applications function seamlessly and securely on the latest Android versions, providing a robust user experience without unexpected crashes or security warnings.

The Core of the Change: Understanding PendingIntent Mutability

At its heart, a PendingIntent is a token that you give to another application (like the system’s NotificationManager, AlarmManager, or MediaSession) allowing it to perform an operation on your application’s behalf at a later time. Think of it as a pre-authorized action. Before Android S (API 31), PendingIntents were implicitly mutable by default, meaning their underlying intent could be modified by the receiving application before execution. While offering flexibility, this mutability presented a significant security risk. Malicious applications could potentially inject extra data into the Intent or even change its core components, leading to privilege escalation or data theft.

To mitigate these risks, Android S+ mandated that developers explicitly declare whether a PendingIntent is FLAG_IMMUTABLE or FLAG_MUTABLE. This is a crucial security enhancement. When you specify FLAG_IMMUTABLE, the PendingIntent cannot be modified by any external party once created. This provides a strong guarantee about the integrity of the action your application intends to perform. Conversely, FLAG_MUTABLE explicitly allows modifications, but its use cases are now more restricted and must be carefully considered. For most standard operations, FLAG_IMMUTABLE is the recommended and safer choice.

The core reason for this change is to strengthen the security posture of the Android ecosystem. By requiring explicit mutability flags, the system prevents unauthorized intent injection and ensures that sensitive operations, particularly those initiated by system components or other apps, are executed exactly as the originating app intended. This helps protect user data and maintain the integrity of app interactions. For a deeper dive into the technical rationale behind this, developers can consult the official Android documentation on behavior changes in Android 12 regarding PendingIntents.

MediaSessionCompat: A Key Player in Android Media

MediaSessionCompat is a vital component for any Android application that handles media playback. It provides a unified way for your app to interact with media controls from external sources, such as lock screen controls, notification panels, Bluetooth devices, and Android Auto. It acts as a bridge, allowing your app to publish metadata about the currently playing media (like song title, artist, album art) and receive commands (play, pause, skip, stop) from these external controllers. Without a properly configured MediaSessionCompat, your media app would offer a disjointed user experience on modern Android devices.

The connection to PendingIntent comes into play because MediaSessionCompat often uses them to communicate actions back to your app. For instance, when a user taps the “play” button on the lock screen, the system dispatches a PendingIntent associated with that action to your MediaSessionCompat callback. Similarly, if your app’s media notification includes custom actions (e.g., a “Like” button), these actions are typically implemented using PendingIntents. It is in these scenarios, particularly when the system or other apps create or manage these PendingIntents on behalf of your MediaSessionCompat, that the new mutability requirement becomes critical.

The specific challenge for developers is that MediaSessionCompat (and its underlying framework) may internally create PendingIntents for media control actions. If your application targets Android S+ and these internally generated PendingIntents do not explicitly declare FLAG_IMMUTABLE or FLAG_MUTABLE, the system will throw a SecurityException. This is precisely why MediaSessionCompat:Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent. Developers must ensure their MediaSessionCompat setup, and any related notification builders, are updated to conform to this new security standard, preventing app crashes and ensuring a smooth media experience for users on Android 12 and beyond.

Implementing the Fix: Best Practices for Android S+

Addressing the PendingIntent mutability requirement for MediaSessionCompat on Android S+ involves updating how you construct your PendingIntent objects, particularly those passed to the system or used within media notifications. The core principle is to always specify either FLAG_IMMUTABLE or FLAG_MUTABLE explicitly. For most MediaSessionCompat actions, where the intent is simply to trigger a specific callback in your app (like play/pause), FLAG_IMMUTABLE is the preferred and safest choice.

Here’s a simplified approach to ensure compliance:

  1. Identify PendingIntent Creation: Review your code for all instances where PendingIntent.getActivity(), PendingIntent.getService(), or PendingIntent.getBroadcast() are called, especially in the context of MediaSessionCompat or notification builders.
  2. Add Mutability Flag: Append either PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_MUTABLE to the flags parameter. For example, PendingIntent.getService(context, requestCode, intent, flags | PendingIntent.FLAG_IMMUTABLE).
  3. Choose Wisely: Unless you specifically need the PendingIntent to be modifiable by another application (e.g., for direct reply actions in notifications), always opt for FLAG_IMMUTABLE. This significantly reduces potential security vulnerabilities.
  4. Update Support Libraries: Ensure your androidx.media and androidx.core libraries are updated to their latest stable versions. These libraries often include internal fixes and adjustments to comply with new Android behavior changes, potentially handling some of the PendingIntent flag requirements automatically for common MediaSessionCompat use cases.

It’s also crucial to remember that this requirement extends beyond just MediaSessionCompat. Any PendingIntent created by your application that targets Android S+ must include one of these flags. This includes PendingIntents for alarms, widgets, and custom notification actions. Adopting this practice across your entire codebase is a fundamental step towards future-proofing your application against further Android security enhancements. For more general guidelines on Android application security, consider exploring resources like this article on secure Android development practices.

Question & Answer :
I’m trying to update my application to Android SDK 31 but I’m having an issue with MediaSessionCompat.

I have a MediaService that extends the MediaBrowserServiceCompat() and in method onCreate of that service I initialise the MediaSessionCompat.

override fun onCreate() { super.onCreate() mediaSession = MediaSessionCompat(this, TAG).apply { setCallback(mediaSessionCallback) isActive = true } ... 

But I’m having the following error

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. at android.app.ActivityThread.handleCreateService(ActivityThread.java:4498) at android.app.ActivityThread.access$1500(ActivityThread.java:250) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2064) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loopOnce(Looper.java:201) at android.os.Looper.loop(Looper.java:288) at android.app.ActivityThread.main(ActivityThread.java:7829) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:982) Caused by: 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. at android.app.PendingIntent.checkFlags(PendingIntent.java:375) at android.app.PendingIntent.getBroadcastAsUser(PendingIntent.java:645) at android.app.PendingIntent.getBroadcast(PendingIntent.java:632) at android.support.v4.media.session.MediaSessionCompat.<init>(MediaSessionCompat.java:567) at android.support.v4.media.session.MediaSessionCompat.<init>(MediaSessionCompat.java:537) at android.support.v4.media.session.MediaSessionCompat.<init>(MediaSessionCompat.java:501) at android.support.v4.media.session.MediaSessionCompat.<init>(MediaSessionCompat.java:475) at com.radio.core.service.MediaService.onCreate(MediaService.kt:63) at android.app.ActivityThread.handleCreateService(ActivityThread.java:4485) ... 9 more 

I’m using the most recent version of media library (“androidx.media:media:1.4.0”) that is able to handle the this requirement from the Andriod “S”". As it’s possible to see in the MediaSessionCompact.java class.

// TODO(b/182513352): Use PendingIntent.FLAG_MUTABLE instead from S. /** * @hide */ @RestrictTo(LIBRARY) public static final int PENDING_INTENT_FLAG_MUTABLE = Build.VERSION.CODENAME.equals("S") ? 0x02000000 : 0; ... if (mbrComponent != null && mbrIntent == null) { // construct a PendingIntent for the media button Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); // the associated intent will be handled by the component being registered mediaButtonIntent.setComponent(mbrComponent); mbrIntent = PendingIntent.getBroadcast(context, 0/* requestCode, ignored */, mediaButtonIntent, PENDING_INTENT_FLAG_MUTABLE); } 

Source code demonstrating the problem - https://github.com/adelinolobao/issue-media-session-compat

Do you guys have any idea how can I fix the error?

If you are NOT USING PendingIntent anywhere. The issue might be resolved by adding or updating this dependency

// required to avoid crash on Android 12 API 31 implementation 'androidx.work:work-runtime-ktx:2.7.1' 

This fixed my problem.

You can execute ./gradlew app:dependencies in the terminal in your project and discover what dependency is including work-runtime with and older version. Then you can try to upgrade that dependency in an effort of do the things right