[포그라운드서비스 오류 Android 12] android.app.ForegroundServiceDidNotStartInTimeException: Context.startForegroundService() did not then call Service.startForeground()
갤럭시 S20 휴대폰에서 발생한 오류이다. 앱을 실행 후 백그라운드 서비스를 시작했지만 포그라운드 서비스로 올라오지 못해서 발생하는 오류이다. 갤럭시S20은 안드로이드12 운영체제를 사용하는 휴대전화이다.
Android 12 포그라운드서비스 오류
2022-09-18 14:20:31.179 13864-13864/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: smart.app.battery.mobile.charger, PID: 13864
android.app.ForegroundServiceDidNotStartInTimeException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{db20abe u0 smart.app.test/.StatusService}
at android.app.ActivityThread.throwRemoteServiceException(ActivityThread.java:2147)
at android.app.ActivityThread.access$2900(ActivityThread.java:310)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2376)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8669)
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)
안드로이드 12를 타켓팅하는 경우 포그라운서비스 실행 제한이 시작된다.!! 아 정말 힘들게하는 구글!! 백그라운드 서비스 제한에 이어 이제는 포그라운 서비스도 제한을 시작하였다. 자세한 내용은 아래 글을 참고하면 되고……
그래서 해결책은? 포그라운드 서비스 권장 대안: WorkManager를 사용하라고 하지만
정확한 시간에 알림을 해야하는 경우에는 사용할 수 없다.
포그라운드 서비스 실행이 허용되게 하는 방법은 다음과 같은 케이스들이 있다.
백그라운드에서 포그라운드 서비스 실행이 허용되는 경우
다음 상황에서는 앱이 백그라운드에서 실행되는 동안에도 포그라운드 서비스를 시작할 수 있습니다.
- 앱이 활동과 같은 사용자에게 표시되는 상태에서 전환됩니다.
- 앱은 백그라운드에서 활동을 시작할 수 있습니다. 단, 앱이 기존 작업의 백 스택에 활동을 보유한 경우는 예외입니다.
- 앱이 Firebase 클라우드 메시징을 사용하여 우선순위가 높은 메시지를 수신합니다.
- 사용자가 앱과 관련된 UI 요소에서 작업을 실행합니다. 예를 들어 대화창이나 알림, 위젯, 활동과 상호작용할 수 있습니다.
- 앱이 지오펜싱이나 활동 감지 전환과 관련된 이벤트를 수신합니다.
- 기기가 재부팅되고 broadcast receiver에서 ACTION_BOOT_COMPLETED나 ACTION_LOCKED_BOOT_COMPLETED, ACTION_MY_PACKAGE_REPLACED 인텐트 작업을 수신한 후의 경우입니다.
- 앱이 broadcast receiver에서 ACTION_TIMEZONE_CHANGED나 ACTION_TIME_CHANGED, ACTION_LOCALE_CHANGED 인텐트 작업을 수신합니다.
- 앱이 BLUETOOTH_CONNECT나 BLUETOOTH_SCAN 권한이 필요한 블루투스 브로드캐스트를 수신합니다.
- 앱에 기기 소유자와 프로필 소유자 등 특정 시스템 역할이나 권한이 있습니다.
- 앱에서 호환 기기 관리도구를 사용합니다.
- 호환 기기가 근처에 있을 때마다 시스템이 앱의 절전 모드를 해제하도록 하려면 Android 12에서 호환 기기 서비스를 구현합니다.
- 시스템에서 ‘고정’ 포그라운드 서비스를 다시 시작합니다. 포그라운드 서비스를 고정하려면 onStartCommand()에서 START_STICKY나 START_REDELIVER_INTENT를 반환합니다.
- 사용자가 앱의 배터리 최적화를 사용 중지합니다. 시스템 설정에서 앱의 앱 정보 페이지로 사용자를 보내 사용자가 이 옵션을 찾도록 할 수 있습니다. ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS 인텐트 작업이 포함된 인텐트를 호출하면 됩니다.
위 허용 대상 중에 방법을 찾아야한다.
나의 경우 밑에서 2번째 혹은 마지막 방법으로 적용해야하는데
다음 방법은 적용해도 동일한 오류가 계속 발생되었다.
시스템에서 '고정' 포그라운드 서비스를 다시 시작합니다.
포그라운드 서비스를 고정하려면 onStartCommand()에서
START_STICKY나 START_REDELIVER_INTENT를 반환합니다.
onStartCommand는 서비스가 시스템에 의해서 종료되었을 때 재시작 할지 결정하는 flag이며 종류는 다음과 같다.
START_NOT_STICKY
서비스가 시스템에 의해 종료되어도 재시작 하지 않습니다.
START_STICKY
서비스가 시스템에 의해 종료되면 재시작합니다.
그러나 종료 전에 사용된 마지막 Intent는 Null로 초기화하여 재시작 합니다.
START_REDELIVER_INTENT
서비스가 시스템에 의해 종료되면 재시작하며 종료 전에 보류된 Intent를 모두 순차적으로 전달합니다.
서비스가 지속되어야 하는 파일다운로드에 적합합니다.
마지막 방법인 ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS 인텐트 작업을 이미 사용중인데 오류가 발생한다.
안드로이드 12 테스트폰이 없어서 테스트를 못했는데
이번에 폰 바꾸면서 확인하게 된 계기가 되었다.
나의 경우 사용자에게 알림 다이얼로그를 노출 후 사용자가 확인 버튼을 누르면
배터리 사용량 최적화 리스트로 바로 보내서 사용자가 예외시키도록 해주고 있는데
가이드의 내용은 시스템 설정에서 앱 정보 페이지로 사용자를 보내 사용자가 이 옵션을 찾도록 하라는 것이다.
그렇게 하든 이렇게 하든 찾아가는 방법만 다를뿐
사용자가 설정하는 옵션은 배터리 최적화에서 제외시키는 동작은 동일한다.
그런데 왜 오류는 여전히 발생중인가??
앱 정보 페이지에 가면 기존에 없던 옵션이 보인다. 안드로이드 12부터 추가된 것같다.
앱 정보(애플리케이션 정보) 화면은 다음과 같다.
사용량 > 배터리 메뉴를 클릭하면 다음과 같이 아주 쉽게 사용자가 변경할 수 있는 UI가 나타난다.
배터리 사용량 최적화 리스트 화면으로 보내서 앱을 찾아서 해제 하는 방식은 일반 사용자들이 어려워 할 수 있는데
이건 너무 쉽게 설정할 수 있게 해주고 있다.
위 캡쳐 화면의 내용과 같이 제한없음에 이미 설정되어 있는데
여전히 오류가 발생하는 것이다.
삼성전자서비스 사이트에는 다음과 같이 안내하고 있다
참고로 Android 8.0에 추가된 백그라운드 실행 제한과는 관련이 없다.
이 제한 및 예외는 Android 12/SDK 31 버전에서 새롭게 추가되었다 .
포그라운드 서비스 타입을 지정해보려했으나 해당사항이 없었다.
<service android:description="string resource"
android:directBootAware=["true" | "false"]
android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:foregroundServiceType=["camera" | "connectedDevice" |
"dataSync" | "location" | "mediaPlayback" |
"mediaProjection" | "microphone" | "phoneCall"]
android:icon="drawable resource"
android:isolatedProcess=["true" | "false"]
android:label="string resource"
android:name="string"
android:permission="string"
android:process="string" >
. . .
</service>
참고로 카카오톡 앱은 안드로이드 12에서
아래 퍼미션을 사용중이고 “권한이 필요합니다” 다이얼로그 창을 띄운 후 확인을 누르면
배터리 최적화 리스트로 가지 않고 안내문구와 함께 “거부”, “허용” 버튼이 있는 시스템 대화 상자가 뜨도록 구현되어있다.
시스템 다이얼로그창 구현 방법은 아래 샘플 코드를 보면된다.
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
public class CustomDialog extends DialogFragment
{
public static CustomDialog newInstance() {
return new CustomDialog();
}
@Override
public Dialog onCreateDialog(Bundle a_savedInstanceState) {
String title = "권한이 필요합니다.";
String message = "정상적인 앱 사용을 위해 해당 어플을 "배터리 사용량 최적화" 목록에서 "제외"하는 권한이 필요합니다. nn[확인] 버튼을 누른 후 시스템 알림 대화 상자가 뜨면 [허용] 을 선택해 주세요";
OnClickListener clickListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + getContext().getPackageName()));
startActivity(intent);
}
};
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage(message)
.setPositiveButton("확인", clickListener);
return builder.create();
}
}
그러나 이 역시 권한을 부여하는 방법의 하나일뿐 지금 발생하는 오류와는 무관하다고 생각된다.
이 권한을 사용하여 마켓에 올리면 심사과정에서 거부될 수 있다고 알고 있다.
배터리 최적화 관련 코드들은 아래 링크에서 참고할 수 있다.
내가 원하는 나에게 맞는 해결책이 없는 것인가??
설마 모든 안드로이드 12 운영체제를 사용하는 폰에서 동일하게 발생하는 문제일까?
아니면 삼성전자 휴대전화에서만 발생하는 것일까?
아직 강제 안드로이드 12 적용 구글플레이 등록까지는
한 달 정도의 시간이 남아있다.
2022년 11월 부터는 무조건 안드로이드 12 이상을 타켓팅해야한다.
해결책을 찾아야한다.
삼성전자 개발자 사이트의 개발자 서포트를 활용하여
문의를 해둔 상태이다.
어떻게 하면 해결 가능 한지 문의를 해두었다.
그러나 몇 일이 지나도 답변이 없었다.
그래서 결국 해결책을 찾기 위해 많은 삽질과 구글링을 하였다.
그리고 우연히 오류 원인을 발견했다.
오류의 원인은 PendingIntent의 플래그값을 추가해주지 않아서였다.
포그라운드 서비스 시작시 사용하는 PendingIntent 관련 오류는 전혀 노출되지 않기 때문에 오류원인을 찾기 쉽지 않다.
안드로이드 12를 타켓팅하는 앱이라면 소스코드를 모두 뒤져서 PendingIntent 코드를 찾아 플래그값을 추가 해주면 된다.
자세한 내용은 아래 글 참고!!
[reference]
- https://stackoverflow.com/questions/70044393/fatal-android-12-exception-startforegroundservice-not-allowed-due-to-mallows
- https://stackoverflow.com/questions/69604951/getting-android-app-foregroundservicestartnotallowedexception-in-android-12-sdk
- https://navczydev.medium.com/foreground-service-launch-restrictions-android12-ee00bf8a1674
- https://lightrun.com/answers/tanguyantoine-react-native-music-control-foregroundservicestartnotallowedexception-android-12
- https://stackoverflow.com/questions/70880379/android-remoteserviceexeption-context-startforegroundservice-did-not-then-cal
- https://stackoverflow.com/questions/70711950/android-12-foregroundservicestartnotallowedexception-while-in-foreground
- https://stackoverflow.com/questions/69604951/getting-android-app-foregroundservicestartnotallowedexception-in-android-12-sdk/69604952#69604952
- https://splunktool.com/fatal-android-12-exception-startforegroundservice-not-allowed-due-to-mallowstartforeground-false