Skip to content

PendingIntent Mutability in Android 12+ ​

When targeting Android 12 (API level 31) or higher, you'll encounter the "Missing PendingIntent mutability flag" lint warning or a runtime crash. This change enhances security by requiring explicit declaration of whether your PendingIntent can be modified by other apps.

Why This Change Matters ​

Starting with Android 12, PendingIntents must explicitly declare their mutability using either:

  • FLAG_IMMUTABLE: The PendingIntent cannot be modified by other apps (recommended)
  • FLAG_MUTABLE: The PendingIntent can be modified by other apps (use only when necessary)

Solution: Add Mutability Flags ​

For most use cases, you should use FLAG_IMMUTABLE:

Kotlin ​

kotlin
val pendingIntent = PendingIntent.getActivity(
    context,
    REQUEST_CODE,
    intent,
    PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)

Java ​

java
PendingIntent pendingIntent = PendingIntent.getActivity(
    context,
    REQUEST_CODE,
    intent,
    PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT
);

Handling Different Android Versions ​

To maintain backward compatibility with older Android versions:

Kotlin ​

kotlin
fun getPendingIntentFlags(isMutable: Boolean = false): Int {
    return when {
        isMutable && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ->
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
        
        !isMutable && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ->
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        
        else -> PendingIntent.FLAG_UPDATE_CURRENT
    }
}

Java ​

java
int getPendingIntentFlags(boolean isMutable) {
    if (isMutable && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        return PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE;
    } else if (!isMutable && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        return PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE;
    } else {
        return PendingIntent.FLAG_UPDATE_CURRENT;
    }
}

When to Use Mutable vs Immutable ​

TIP

Use FLAG_IMMUTABLE in most cases where the PendingIntent doesn't need to be modified by other apps.

WARNING

Use FLAG_MUTABLE only when functionality requires modifying the underlying intent, such as:

  • Notification inline replies
  • Notification bubbles
  • Other cases where the intent content needs to be updated by the system

Common Implementation Patterns ​

Utility Class Approach ​

Create a utility class to handle PendingIntent creation consistently:

kotlin
object PendingIntentCompat {
    
    @JvmStatic
    fun getActivity(
        context: Context,
        requestCode: Int,
        intent: Intent,
        flags: Int,
        isMutable: Boolean = false
    ): PendingIntent {
        val finalFlags = if (isMutable) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                flags or PendingIntent.FLAG_MUTABLE
            } else {
                flags
            }
        } else {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                flags or PendingIntent.FLAG_IMMUTABLE
            } else {
                flags
            }
        }
        
        return PendingIntent.getActivity(context, requestCode, intent, finalFlags)
    }
    
    // Similar methods for getService(), getBroadcast(), etc.
}

Third-Party Library Considerations ​

Many crashes related to PendingIntent mutability come from outdated dependencies. Update these common libraries:

WorkManager ​

gradle
dependencies {
    def work_version = "2.8.1"
    implementation "androidx.work:work-runtime:$work_version"
    // For Kotlin:
    implementation "androidx.work:work-runtime-ktx:$work_version"
}

Firebase ​

gradle
implementation 'com.google.firebase:firebase-messaging:23.4.0'
gradle
implementation 'androidx.navigation:navigation-ui-ktx:2.5.3'
implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3'

Force Library Updates ​

If third-party libraries use outdated WorkManager versions, force an update:

gradle
allprojects {
    configurations.all {
        resolutionStrategy {
            force 'androidx.work:work-runtime:2.8.1'
        }
    }
}

Firebase Cloud Messaging Specifics ​

If you're experiencing issues specifically with FCM:

WARNING

Update from deprecated FirebaseInstanceId to FirebaseMessaging:

Before (deprecated):

kotlin
FirebaseInstanceId.getInstance().instanceId
    .addOnSuccessListener { result ->
        val token = result.token
    }

After:

kotlin
FirebaseMessaging.getInstance().token
    .addOnCompleteListener { task ->
        val token = task.result
    }

Manifest Requirements for Android 12+ ​

Ensure all components in your AndroidManifest.xml declare the android:exported attribute:

xml
<activity
    android:name=".MainActivity"
    android:exported="true" />
    
<service
    android:name=".MyService"
    android:exported="false" />
    
<receiver
    android:name=".MyReceiver"
    android:exported="true" />

Testing Your Implementation ​

After implementing these changes, test your app on:

  • Android 11 and lower devices to ensure backward compatibility
  • Android 12+ devices to verify the PendingIntent mutability requirement is satisfied
  • Both foreground and background scenarios for notification handling

Conclusion ​

The PendingIntent mutability requirement in Android 12+ enhances security while maintaining functionality. By:

  1. Adding appropriate mutability flags (FLAG_IMMUTABLE or FLAG_MUTABLE)
  2. Updating third-party dependencies
  3. Ensuring proper manifest configuration
  4. Testing across Android versions

You can resolve the lint warning and ensure your app works correctly on all supported Android versions.