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.