카테고리 없음

[Flutter/Android] Android 12 PendingIntent Error & How to resolve the error

mark340 2024. 6. 26. 10:20

블루투스 서비스에서 PendingIntent 사용 시 발생하는 문제 및 해결 방법

최근에 Flutter 앱을 디버깅 모드로 빌드하는 과정에서 다음과 같은 에러를 경험했음:

E/AndroidRuntime(11350): FATAL EXCEPTION: main
E/AndroidRuntime(11350): Process: com.sxr.sdk.ble.keepfit.client, PID: 11350
E/AndroidRuntime(11350): java.lang.RuntimeException: Unable to create service com.apposter.smart_device.BleService: java.lang.IllegalArgumentException: com.sxr.sdk.ble.keepfit.client: Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.
E/AndroidRuntime(11350): 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.

이 에러는 Android 12(S) 이상을 타겟으로 하는 앱에서 PendingIntent를 생성할 때 FLAG_IMMUTABLE 또는 FLAG_MUTABLE을 지정하지 않아 발생함. 이 문제를 해결하기 위해 PendingIntent를 생성하는 코드를 수정해야 함.

문제 상황

Flutter 앱에서 BleService를 사용하는 과정에서 다음과 같은 코드를 포함하고 있었음:

val pendingIntent = PendingIntent.getActivity(this, 0, intent, 0)

Android 12(S) 이상을 타겟으로 하는 경우, 위 코드에서는 FLAG_IMMUTABLE 또는 FLAG_MUTABLE을 추가로 지정해야 함. 그렇지 않으면 앱이 실행 중에 크래시가 발생함.

해결 방법

아래는 BleService.kt 파일에서 PendingIntent를 생성하는 부분을 수정한 코드임:

package com.apposter.smart_device

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Intent
import android.os.Build
import androidx.core.app.NotificationCompat
import com.sxr.sdk.ble.keepfit.service.BluetoothLeService

class BleService : BluetoothLeService() {

    companion object {
        const val ECG_SWITCH = "ECG_SWITCH"
        const val ECG_VALUE = "ECG_VALUE"
        const val CHANNEL_ID = "BRingServiceChannel"
    }

    override fun onCreate() {
        super.onCreate()

        createNotificationChannel()

        val notificationIntent = Intent(this, MainActivity::class.java)
        val pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE)

        val notification: Notification =
                NotificationCompat.Builder(this, CHANNEL_ID)
                        .setContentTitle("B.RING")
                        .setContentText("연결됨")
                        .setSmallIcon(R.drawable.ic_fluent_smartwatch_24_regular)
                        .setContentIntent(pendingIntent)
                        .build()

        startForeground(1, notification)
    }

    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val serviceChannel =
                    NotificationChannel(
                            CHANNEL_ID,
                            "B.RING Service Channel",
                            NotificationManager.IMPORTANCE_DEFAULT
                            )
            val manager = getSystemService(NotificationManager::class.java)
            manager.createNotificationChannel(serviceChannel)
        }
    }

    override fun onDestroy() {
        stopForeground(true)
        super.onDestroy()
    }
}

PendingIntent.getActivity 호출 시 PendingIntent.FLAG_IMMUTABLE 플래그를 추가하여 문제를 해결함. 만약 FLAG_MUTABLE이 필요한 경우에는 PendingIntent.FLAG_MUTABLE을 사용하면 됨.

FLAG_IMMUTABLE를 추가해야 하는 이유

Android 12(S) 이상에서는 보안과 안정성을 강화하기 위해 PendingIntent 생성 시 FLAG_IMMUTABLE 또는 FLAG_MUTABLE을 명시적으로 지정해야 함. 이는 PendingIntent의 내용을 변경할 수 없도록 하거나 변경 가능하도록 명시적으로 설정하여, 무단 변경으로 인한 보안 문제를 방지하기 위함임.

  • FLAG_IMMUTABLE: 생성된 PendingIntent의 내용을 변경할 수 없도록 설정함. 대부분의 경우에 이 플래그를 사용함.
  • FLAG_MUTABLE: 특정 상황에서 PendingIntent의 내용을 변경할 수 있도록 허용함. 예를 들어, 인라인 응답이나 버블과 같은 기능을 사용해야 하는 경우에만 사용함.

이 수정 후, 앱을 다시 빌드하고 실행하면 에러 없이 정상적으로 동작하는 것을 확인할 수 있음.

결론

Android 12(S) 이상을 타겟으로 하는 앱에서 PendingIntent를 생성할 때는 반드시 FLAG_IMMUTABLE 또는 FLAG_MUTABLE을 지정해야 함. 이를 통해 발생할 수 있는 런타임 에러를 방지할 수 있음. 코드에서 PendingIntent를 생성하는 부분을 확인하고, 필요한 플래그를 추가하여 문제를 해결하도록 함.