Android Pie (API 28, 9.0) 지원 사항

2018년 11월 기준으로 신규 / 업데이트 앱은 Android Oreo (API 27, 8.0 – 8.1) 을 targetSDKVersion 으로서 지원해야 되므로 최소 2019년 중반 까지는 Andorid Pie 에 대한 지원이 되어야 합니다.

현재 구글 픽셀 시리즈, 에센셜, 엑스페리아 XZ3 에 적용되었으며 LG V40 ThinQ 및 삼성
갤럭시 S10에도 적용 예정으로 발표되었습니다. 삼성 갤럭시 S8의 경우 ‘메이저 OS 2회 업데이트’ 정책에 따라 이번이 마지막
지원이 됩니다. (7.0 -> 8.0 -> 9.0)

참조 문서

Andorid Pie 에서 실행되는 모든 앱에 적용되는 지원 사항

아래 사항은 Android Pie에 구동되는 모든 앱에 해당되며, 이번 지원 사항에는 아래와 같은 주요 포인트를 가지고 있습니다.

  • 전원 관리 – 앱 대기 버킷, 배터리 세이버 개선
  • 백그라운드에서의 센서 (마이크, 카메라, 가속도계, 자이로스코프) 엑세스 제한
  • 통화 로그 액세스 제한 및 CALL_LOG 권한 그룹 신설
  • 전화번호 액세스 제한
  • 비 SDK 인터페이스에 대한 제한

이 중 개발자가 반드시 내용을 파악하고 있어야 하는 것은 전원 관리비 SDK 인터페이스에 대한 제한 이며 아래부터 중요도 순으로 내용을 설명합니다.

비 SDK 인터페이스에 대한 제한

먼저, 비 SDK 인터페이스 란 공식 Android SDK의 일부가 아닌
Java 필드와 메서드로 정의할 수 있습니다. 쉽게 말하지면, private 및 protected 접근자를 가지고 있는 필드 및
메서드라 표현할 수 있습니다. 공식 Andorid SDK의 모든 API는 Android 프레임워크의 SDK 문서에 전부 작성되어있으며, 작성되어있지 않은 API의 경우는 비 SDK 인터페이스라 볼 수 있습니다.

기존까지 비 SDK 인터페이스를 사용하려면 Class 클래스의 getDeclearedFieldgetDeclearedMethod 등의 메서드를 사용하여 각각의 FieldMethod 객체를 찾아 직접적으로 실행하는 방법이 흔히 쓰였습니다.

하지만 Android Pie에서 돌아가는 앱은 이러한 방법을 사용했을 때 정확한 객체를 반환하는 대신 NoSuchFieldException, NoSuchMethodException 을 반환합니다. 또한, getDeclearedFields, getDeclearedMethods 는 이러한 비 SDK 인터페이스가 제외된 결과만 반환합니다. 마찬가지로 Reflection을 제외하고도 JNI의 env->GetFieldID() , env->GetMethodID() 도 같은 결과를 발생시킵니다.

즉, 직접적인 Reflection 및 JNI를 통한 비 SDK 인터페이스의 사용이 모두 제한됩니다.

단, 모든 비 SDK 인터페이스가 사용 불가능한 것이 아닌 동작에 따라 제한을 만들었는데, 그 리스트는 다음과 같습니다.

  • 화이트리스트(모두 허용): SDK
  • 라이트 그레이리스트: 여전히 엑세스가 가능한 비 SDK 메서드 / 필드. 다만 다음 버전에서 이 리스트에 있는 것이 다크 그레이리스트 나 블랙리스트로 이동되지 않는다는 보장이 없습니다.
  • 다크 그레이리스트:

    • targetSDKVersion 가 28 미만일 경우 -> 다크 그레이리스트 인터페이스 사용 허용
    • targetSDKVersion 가 28 이상일 경우 -> 블랙리스트와 같은 동작
  • 블랙리스트: targetSDKVersion 상관 없이 제한되며, 상기된 NoSuchFieldExceptionNoSuchMethodException 의 제한이 적용됩니다. 즉, 플랫폼은 인터페이스가 없는 것 처럼 동작합니다.

여전히 라이트 그레이리스트에 있는 항목은 사용이 가능하지만, 상기된 내용과 같이 다음 버전에서도 작동한다는 보장이 안 되므로 변경할 수 있을 때 모두 변경하는 것이 좋습니다.

현재 앱이 비 SDK 인터페이스를 사용하고 있는지에 대한 판단 방법

StrictMode 클래스의 detectNonSdkApiUse 메서드를 사용하여 검출합니다.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    StrictMode.setVmPolicy(
            StrictMode.VmPolicy
                    .Builder()
                    .detectNonSdkApiUsage()
                    .build())
}

비 SDK 인터페이스 사용시 아래와 같은 로그가 출력됩니다.

Accessing hidden method Landroid/gesture/Gesture;->setID(J)V (blacklist, reflection)

StackTrace도 출력됩니다.

D/StrictMode(19092): StrictMode policy violation: android.os.strictmode.NonSdkApiUsedViolation: Landroid/widget/Toast;->mDuration:I
D/StrictMode(19092):    at android.os.StrictMode.lambda$static$1(StrictMode.java:428)
D/StrictMode(19092):    at android.os.-$$Lambda$StrictMode$lu9ekkHJ2HMz0jd3F8K8MnhenxQ.accept(Unknown Source:2)
D/StrictMode(19092):    at java.lang.Class.getDeclaredField(Native Method)
D/StrictMode(19092):    at com.os.operando.non_sdkinterfaces.sample.MainActivity$onCreate$3.onClick(MainActivity.kt:49)
D/StrictMode(19092):    at android.view.View.performClick(View.java:6597)
D/StrictMode(19092):    at android.view.View.performClickInternal(View.java:6574)
D/StrictMode(19092):    at android.view.View.access$3100(View.java:778)
D/StrictMode(19092):    at android.view.View$PerformClick.run(View.java:25883)
D/StrictMode(19092):    at android.os.Handler.handleCallback(Handler.java:873)
D/StrictMode(19092):    at android.os.Handler.dispatchMessage(Handler.java:99)
D/StrictMode(19092):    at android.os.Looper.loop(Looper.java:193)
D/StrictMode(19092):    at android.app.ActivityThread.main(ActivityThread.java:6642)
D/StrictMode(19092):    at java.lang.reflect.Method.invoke(Native Method)
D/StrictMode(19092):    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
D/StrictMode(19092):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

StrictMode 는 런타임 상에서의 검출이지만, apk를 대상으로 할 때에는 AOSP가 제공하는 veridex를 사용합니다.

veridex는 shell script 명령으로 실행시킬 수 있습니다. : ./appcompat.sh --dex-file=test.apk

부가적으로 OEM 업체 (삼성, LG) 가 자체 리스트를 추가할 수 있으나, 기본 리스트를 삭제할 수는 없으므로 기본이 되는 AOSP의 리스트에 대한 호환성은 모두 확보해야 합니다.

전원 관리 – 대기 버킷, 배터리 세이버 개선

앱 대기 버킷은 해당 앱이 얼마나 최근에, 자주 사용되는지에 따라 앱의 리소스 요청에 우선순위를 부여합니다. 총 5개의 버킷이 존재합니다.

Active

사용자가 앱을 현재 사용중이면 Active 버킷에 배치됩니다. 예를 들어 액티비티를 시작하거나 포그라운드 서비스를 실행중인 경우, 알림을 클릭한 경우에 해당됩니다.

Working Set

앱이 자주 실행되지만 현재는 활성이 아닌 경우에는 Working Set 버킷에 배치됩니다. 예를 들어,
소셜 미디어 앱은 Working Set에 있을 가능성이 높습니다. 또한 앱이 간접적으로 사용될 경우에도 Working Set
버킷으로 승격됩니다.

Frequent

앱이 매일 실행되는 것은 아니지만 정기적으로 사용되는 경우에는 Frequent 버킷에 배치됩니다.

Rare

앱이 자주 사용되지 않으면 Rare 버킷에 배치됩니다.

Nerver

설치되었지만 한 번도 실행되지 않은 앱은 Never 버킷에 배치됩니다.

시스템은 각 앱을 우선순위 버킷에 동적으로 할당하며, 필요한 경우에는 앱을 재할당합니다. 버킷은 앱이 얼마나 자주 실행되는지, 얼마나 자주 알람을 트리거하는지, 우선순위가 높은 Firebase Cloud Messaging 메세지를 자주 수신하는지에 대해 판별합니다. 이러한 제한은 기기가 배터리 전원을 사용 중인 동안에만 적용되며, 기기가 충전 중일 때는 시스템이 이러한 제한을 적용하지 않습니다.

부가적으로, Doze 허용 목록에 있는 앱은 대기 버킷의 영향을 받지 않습니다.

배터리 세이버 개선에 있어서는 여러 사항이 제한되었는데, 기기 제조업체가 적용되는 정확한 제한 사항을
결정합니다. 예를 들어, AOSP에서는 앱이 유휴 상태가 되기를 기다리는 대신 보다 적극적으로 대기 모드로 전환하며, 백그라운드
실행 제한을 앱의 targetSDKVersion 과 상관 없이 모두 적용하는 등의 행동을 결정합니다.

아래 표는 전원 관리에 대한 제한 표 입니다.

(1) : 작업 제한이 강제적일 경우, 특정 주기마다 10분동안의 작업이 허용됩니다. 10분이 지난 경우에는 모든 작업은 다음 대기열로 미뤄집니다.

(2) : 알람 제한이 강제적인 경우, 모든 알람은 스케쥴된 대기열에 발동되며, 10초동안 작업이 가능합니다.

(3): 네트워크 접근이 제한되었을 경우, 특정 주기마다 10분동안 네트워크를 사용하는 것이 가능합니다.

(4): 우선순위가 높은 FCM 메세지를 수신했을 때에 상한에 도달해있으면 그 이후의 메세지는 보통 우선순위를 가진 메세지로서 처리됩니다.

백그라운드에서의 센서 (마이크, 카메라, 가속도계, 자이로스코프) 엑세스 제한

앱이 마이크, 카메라, 가속도계, 자이로스코프 등의 센서를 사용중인 경우 백그라운드 상태에서는 이 센서의 정보를 받지 못합니다. 앱이 센서 이벤트를 감지해야 하는 경우, 포그라운드 서비스를 사용해야 됩니다.

통화 로그 엑세스 제한

기존에 존재하던 READ_CALL_LOG, WRITE_CALL_LOGPROCESS_OUTGOING_CALLS 권한이 위치하던 PHONE 그룹에서 신설되는 CALL_LOG 그룹으로 이동됩니다. 이 CALL_LOG 그룹은 전화 통화 기록을 보고 전화번호를 식별하는 등과 같이 전화 통화와 관련된 민감한 정보를 엑세스하는 데에 필요한 기능을 제공합니다.

앱에서 통화 로그에 엑세스 해야 하거나 발신 통화를 처리해야 할 경우, Runtime
Permissions 요청을 통해 이 권한을 적절히 요청해야 합니다. 이러한 권한은 사용자가 앱에서 전화 통화 기록 정보에
엑세스하지 못하도록 거부할 수 있으므로, 정보에 엑세스하지 못하더라도 이를 적절히 처리할 수 있어야 합니다.

전화번호 엑세스 제한

READ_CALL_LOG 권한을 취득하지 않는 이상, 전화 상태 브로드캐스트 및 PhoneStateListener 클래스에서 전화번호 필드가 비어있게 됩니다. PHONE_STATE 브로드캐스트에서 전화번호를 읽으려면 READ_CALL_LOG 권한과 READ_PHONE_STATE 권한이 모두 필요합니다. 또, onCallStateChanged() 에서 전화번호를 읽으려면 READ_CALL_LOG 권한만 필요로 합니다.

부가 사항들

  • 앱이 Wi-Fi를 스캔하는 데에 필요한 권한 요구사항이 엄격해졌습니다. 정리하면, WifiManager.startScan()ACCESS_FINE_LOCATION 또는 ACCESS_COARSE_LOCATION 권한을 가지고 있고, CHANGE_WIFI_STATE 권한을 가지고 있어야 합니다. WifiManager.getScanResult()ACCESS_FINE_LOCATION 또는 ACCESS_COARSE_LOCATION 권한을 가지고 있고, ACCESS_WIFI_STATE 권한을 가지고 있어야 합니다.
  • UTC는 더 이상 GMT와 동의어가 아닙니다.
  • Android 9의 UTF-8 디코더는 이전 버전보다 더욱 엄격하게 Unicode 표준을 준수합니다.
  • /proc/net/xt_qtaguid 폴더에 있는 파일을 직접 읽을 수 없게 됩니다. 이는 이러한 파일이 전혀 없는 기기와의 일관성을 유지하기 위해서 입니다.
  • Android 9부터 Crypto JCA 제공자가 제거되었습니다. SecureRandom.getInstance("SHA1PRNG", "Crypto")를 호출하면 NoSuchProviderException이 발생합니다.

Andorid Pie를 대상으로 하는 앱에 적용되는 지원 사항

  • 포그라운드 서비스를 생성하기 위해서는 FOREGROUND_SERVICE 권한을 요청해야 합니다. 이 권한은 정상 권한이므로 자동으로 권한을 부여합니다. 이 권한 없이 포그라운드 서비스를 시작하면 SecurityException 가 발생합니다.
  • Android 9에서는 Bouncy Castle 제공자가 제공하는 여러 암호화가 지원 중단되고 Conscrypt 제공자가 제공하는 암호화가 대신 사용됩니다. Bouncy Castle 제공자를 요청하는 getInstance() 호출은 NoSuchAlgorithmException 오류를 생성합니다. 이 오류를 해결하려면 getInstance()에서 제공자를 지정하지 마십시오(즉, 기본 구현 요청).
  • Build.SERIAL 가 UNKNOWN 으로 고정됩니다. 단, 하드웨어 일련번호에 엑세스해야 하는 경우에는 READ_PHONE_STATE 권한을 요청한 다음, getSerial() 을 호출해야 합니다.
  • 앱이 더 이상 여러 프로세스에서 단일 WebView 데이터 디렉토리를 공유할 수 없습니다. 앱에 android.webkit
    패키지의 WebView, CookieManager 또는 기타 API를 사용하는 프로세스가 2개 이상 있는 경우, 두 번째
    프로세스에서 WebView 메서드를 호출하면 앱이 중단됩니다.
  • 시스템은 각 앱의 비공개 데이터 디렉토리 에 대한 앱별 SELinux 샌드박스를 적용합니다. 이제 경로를 통해 다른 앱의
    데이터 디렉토리에 직접 엑세스하는 것은 허용되지 않습니다. 앱이 FD 전달을 포함한 IPC 메커니즘을 사용하여 데이터를 계속
    공유할 수도 있습니다.
  • 0 영역 (너비나 높이가 0) 이 있는 뷰는 포커스가 불가능합니다. 또한 터치모드에서는 액티비티가 더 이상 초기 포커스를 암시적으로 할당하지 않습니다. 그 대신, 원할 경우 초기 포커스를 명시적으로 요청해야 합니다.