Skip to content

PendingIntent mutability flag

Android 12(API レベル 31)以降をターゲットとするアプリケーションでPendingIntentを使用する場合、mutability flag(変更可能性フラグ)の明示的な指定が必要になります。この変更により、「Missing PendingIntent mutability flag」というリンター警告が表示される問題とその解決方法について詳しく説明します。

問題の概要

Android 12(targetSdkVersion 31以上)では、PendingIntentの作成時にFLAG_IMMUTABLEまたはFLAG_MUTABLEのいずれかのフラグを明示的に指定する必要があります。これまでデフォルトで変更可能とされていたPendingIntentの動作が変更され、セキュリティが強化されました。

kotlin
// Android 12以降では以下のコードは警告が発生
val pendingIntent = PendingIntent.getActivity(
    context,
    requestCode,
    intent,
    PendingIntent.FLAG_UPDATE_CURRENT  // 警告: Missing mutability flag
)

解決方法

基本的な解決策

ほとんどの場合、FLAG_IMMUTABLEを使用することを推奨します。これはPendingIntentの内容が変更されないことを保証します。

kotlin
// Kotlinでの実装例
val pendingIntent = PendingIntent.getActivity(
    context,
    requestCode,
    intent,
    PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
java
// Javaでの実装例
int flags = PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT;
PendingIntent pendingIntent = PendingIntent.getActivity(
    context,
    requestCode,
    intent,
    flags
);

バージョン互換性の考慮

Android 12以前のバージョンとの互換性を保つための実装方法:

kotlin
fun createPendingIntent(context: Context, intent: Intent, requestCode: Int): PendingIntent {
    val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
    } else {
        PendingIntent.FLAG_UPDATE_CURRENT
    }
    
    return PendingIntent.getActivity(context, requestCode, intent, flags)
}

Mutable PendingIntentが必要な場合

インライン返信やバブルのような機能でPendingIntentの内容を変更する必要がある場合は、FLAG_MUTABLEを使用します。

kotlin
val mutablePendingIntent = PendingIntent.getActivity(
    context,
    requestCode,
    intent,
    PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)

WARNING

FLAG_MUTABLEは必要な場合にのみ使用してください。セキュリティ上の理由から、可能な限りFLAG_IMMUTABLEを使用することが推奨されます。

ライブラリ関連の問題と解決策

WorkManagerの更新

この問題は多くの場合、サードパーティライブラリ(特にWorkManager)の古いバージョンが原因で発生します。WorkManagerを最新バージョンに更新してください。

gradle
dependencies {
    def work_version = "2.8.1"
    
    // Java
    implementation "androidx.work:work-runtime:$work_version"
    
    // Kotlin + coroutines
    implementation "androidx.work:work-runtime-ktx:$work_version"
    
    // RxJava2サポート
    implementation "androidx.work:work-rxjava2:$work_version"
}

Firebaseの更新

Firebaseを使用している場合、古いバージョンがこの問題を引き起こす可能性があります。

gradle
implementation 'com.google.firebase:firebase-messaging:23.4.0'

Navigationコンポーネントを使用している場合も、最新バージョンに更新してください。

gradle
implementation 'androidx.navigation:navigation-ui-ktx:2.5.3'
implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3'

ユーティリティクラスの作成

さまざまな種類のPendingIntentを扱う場合、以下のようなユーティリティクラスを作成すると便利です。

kotlin
object PendingIntentCompat {

    @JvmStatic
    @JvmOverloads
    fun getActivity(
        context: Context,
        requestCode: Int,
        intent: Intent,
        flags: Int,
        isMutable: Boolean = false
    ): PendingIntent {
        return PendingIntent.getActivity(
            context,
            requestCode,
            intent,
            addMutabilityFlags(isMutable, flags)
        )
    }

    @JvmStatic
    @JvmOverloads
    fun getService(
        context: Context,
        requestCode: Int,
        intent: Intent,
        flags: Int,
        isMutable: Boolean = false
    ): PendingIntent {
        return PendingIntent.getService(
            context,
            requestCode,
            intent,
            addMutabilityFlags(isMutable, flags)
        )
    }

    private fun addMutabilityFlags(isMutable: Boolean, flags: Int): Int {
        var updatedFlags = flags

        if (isMutable) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                updatedFlags = flags or PendingIntent.FLAG_MUTABLE
            }
        } else {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                updatedFlags = flags or PendingIntent.FLAG_IMMUTABLE
            }
        }
        return updatedFlags
    }
}

よくあるエラーと解決策

リモート入力付きアクションでのクラッシュ

通知の返信アクションなどのリモート入力を使用する場合は、FLAG_MUTABLEが必要です。

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
    }
}

// 使用例
val quickReplyPendingIntent = PendingIntent.getBroadcast(
    context, 
    notificationId, 
    replyIntent,
    getPendingIntentFlags(true)  // リモート入力の場合は mutable を true に
)

まとめ

Android 12以降では、PendingIntentの作成時にmutability flagの明示的な指定が必須となりました。ほとんどのケースではFLAG_IMMUTABLEを使用し、必要な場合のみFLAG_MUTABLEを使用してください。また、サードパーティライブラリが古い場合は更新することで、この問題を解決できる場合があります。

TIP

Android 12の変更に対応する際は、アプリのすべてのPendingIntent作成箇所を確認し、適切なmutability flagを追加することを忘れないでください。