[안드로이드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]