Android

[안드로이드12(S)] 알람매니저(AlarmManager) 등록 오류 java.lang.SecurityException: Caller com.test.sample needs to hold android.permission.SCHEDULE_EXACT_ALARM to set exact alarms.

2022년 11월부터는 안드로이드 12 (SDK 31)을 타겟팅해야 업데이트 할 수 있다.  

android {
    compileSdkVersion 31
    buildToolsVersion '30.0.3'
    defaultConfig {
        applicationId "com.test.sample"
        minSdkVersion 19    //API 16 (Android 4.1-4.1.1) – Jelly Bean
        targetSdkVersion 31
        versionCode 363
        versionName "3.6.3"
        vectorDrawables.useSupportLibrary = true   //벡터이미지 사용 유무를 설정
        .......
        ...

버전 업 후 앱 테스트를 하는데 권한 오류가 발생하면서 앱이 종료되었다.

구글 개발자 가이드 문서를 보니 변경사항이 있었다.

Process: com.test.sample, PID: 12028
java.lang.SecurityException: Caller com.test.sample needs to hold android.permission.SCHEDULE_EXACT_ALARM to set exact alarms.
    at android.os.Parcel.createExceptionOrNull(Parcel.java:2438)
    at android.os.Parcel.createException(Parcel.java:2422)
    at android.os.Parcel.readException(Parcel.java:2405)
    at android.os.Parcel.readException(Parcel.java:2347)
    at android.app.IAlarmManager$Stub$Proxy.set(IAlarmManager.java:395)
    at android.app.AlarmManager.setImpl(AlarmManager.java:976)
    at android.app.AlarmManager.setImpl(AlarmManager.java:936)
    at android.app.AlarmManager.setExactAndAllowWhileIdle(AlarmManager.java:1204)
    at com.test.sample.MainActivity.alarmManagerSetting(MainActivity.java:690)
    at com.test.sample.MainActivity.access$000(MainActivity.java:132)
    at com.test.sample.MainActivity$4.run(MainActivity.java:663)
    at android.os.Handler.handleCallback(Handler.java:938)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loopOnce(Looper.java:226)
    at android.os.Looper.loop(Looper.java:313)
    at android.app.ActivityThread.main(ActivityThread.java:8751)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)
 Caused by: android.os.RemoteException: Remote stack trace:
    at com.android.server.alarm.AlarmManagerService$5.set(AlarmManagerService.java:2997)
    at android.app.IAlarmManager$Stub.onTransact(IAlarmManager.java:211)
    at android.os.Binder.execTransactInternal(Binder.java:1215)
    at android.os.Binder.execTransact(Binder.java:1179)

 

Android 12를 타겟팅하는 앱 동작 변경사항

 

[오류 해결 방법]

안드로이드 12 이상을 타겟팅 하는 경우 Manifest.xml 파일에 다음 권한을 추가해야한다.

자동으로 승인처리되어 알람매니저에 등록할 수 있다.

<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />

 

안드로이드 13을 타겟팅 하는 경우

그러나 안드로이드 13을 타켓팅 하는 경우에는 사용자에게 권한 부여 요청을 해야한다.

Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM 인텐트를 바로 호출하면 사용자는 이게 뭐지? 라는 상황이 발생할 수 있음으로 별도의 안내 다이얼로그 노출 후 호출해야할 필요가 있다.

 

[자바]

private boolean checkAlarmRemainderPermission() {
    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S) {
        AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
        //안드로이드 13 대응
        if(!alarmManager.canScheduleExactAlarms()) {
            Intent appDetail = new Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM,
                    Uri.parse("package:" + getPackageName()));
            appDetail.addCategory(Intent.CATEGORY_DEFAULT);
            appDetail.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(appDetail);
        } else {
            return true;
        }
    } else {
        return true;
    }
    return false;
}

private void  alarmManagerSetting(){
    try {
        AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);

        Intent intent = new Intent(MainActivity.this, ServiceAlarmReceiver.class);
        //android 12
        PendingIntent sender;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            sender = PendingIntent.getBroadcast(MainActivity.this, REQUEST_CODE_SERVICE_ALARM_MANAGER, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
        }else {
            sender = PendingIntent.getBroadcast(MainActivity.this, REQUEST_CODE_SERVICE_ALARM_MANAGER, intent, 0);
        }
        long currentTime = System.currentTimeMillis();
        //int interval = 15000;//15초
        //int interval = 10000*6;//1분
        int interval = 10000 * 6 * 30;//30분
        if (alarmManager != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) //api level 23 (Marshmallow 6.0)
                alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, currentTime + interval, sender);
            else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
                alarmManager.setExact(AlarmManager.RTC_WAKEUP, currentTime + interval, sender);
            else
                alarmManager.set(AlarmManager.RTC_WAKEUP, currentTime + interval, sender);
        }
    }catch (NullPointerException e){

    }

}
try{
    Handler handler = new Handler();
    handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            if (checkAlarmRemainderPermission()) {
                alarmManagerSetting();
            }
        }
    }, 60000); //1분뒤 실행

}catch (Exception e){
    //e.printStackTrace();
   // Crashlytics.log("메인화면: alarmManager호출 오류n"+ e.getStackTrace());
}

[코틀린]

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    val alarmManager = ContextCompat.getSystemService(context, AlarmManager::class.java)
    if (alarmManager?.canScheduleExactAlarms() == false) {
        Intent().also { intent ->
            intent.action = Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM
            context.startActivity(intent)
        }
    }
}

 

[REFERENCE]

 

 

Leave a Reply

error: Content is protected !!