안드로이드 시스템 앱 권한 관리 및 아키텍처 분석
시스템 앱(Privileged App) 개발 시 가장 빈번하게 발생하는 이슈는 /system/priv-app/ 경로에 APK를 배치했음에도 불구하고, 특정 기능 실행 시 SecurityException이 발생하는 상황입니다. 많은 개발자가 시스템 앱은 'God Mode'로 동작한다고 오해하지만, 안드로이드 보안 모델(Android Security Model)은 권한의 명시적 선언과 승인을 엄격하게 요구합니다.
// 일반적인 런타임 에러 로그
java.lang.SecurityException: Permission Denial: reading com.android.providers.telephony.TelephonyProvider uri content://telephony/carriers from pid=1422, uid=1001 requires android.permission.WRITE_APN_SETTINGS, or grantUriPermission()
시스템 앱 권한의 계층 구조 (Privileged vs Signature)
안드로이드 권한 시스템은 protectionLevel에 따라 엄격하게 구분됩니다. 시스템 앱이 높은 권한을 사용하기 위해서는 단순히 AndroidManifest.xml에 선언하는 것만으로는 부족하며, AOSP 빌드 시스템 내의 화이트리스트(Whitelist) 등록 또는 서명 일치(Signature Match)가 필요합니다.
Privileged Permission이란?
protectionLevel="privileged"로 선언된 권한은 오직 /system/priv-app 디렉토리에 위치한 앱만이 획득할 수 있습니다. 안드로이드 8.0(Oreo) 이후부터는 이러한 권한을 사용하기 위해 반드시 priv-app allowlist XML 파일에 명시적으로 선언해야 합니다.
아키텍처 솔루션 1: priv-app 화이트리스트 설정 (Pre-granting)
OEM 제조사나 ROM 개발자가 시스템 앱에 권한을 사전 부여(Pre-grant)하기 위해서는 /system/etc/permissions/ 경로에 XML 설정 파일을 배치해야 합니다. 만약 이 설정이 누락된 상태에서 시스템 앱이 Privileged Permission을 요청하면, 부팅 시 시스템이 해당 앱의 실행을 차단하거나 부트루프(Bootloop)가 발생할 수 있습니다.
<!-- /system/etc/permissions/priv-app-permissions-myapp.xml -->
<permissions>
<privapp-permissions package="com.example.systemapp">
<!-- Dangerous 권한도 시스템 앱은 사전 승인 가능 -->
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
<permission name="android.permission.STATUS_BAR"/>
<permission name="android.permission.REBOOT"/>
</privapp-permissions>
</permissions>
주의: AOSP 빌드 시 Enforce 모드
ro.control_privapp_permissions=enforce 속성이 설정된 최신 안드로이드 빌드에서는, 화이트리스트에 없는 권한을 Manifest에 선언만 해도 빌드 실패 혹은 런타임 크래시가 발생합니다. 불필요한 권한은 Manifest에서 제거해야 합니다.
아키텍처 솔루션 2: 런타임 권한 방어 코드 구현
시스템 앱이라 할지라도 dangerous 레벨의 권한(예: READ_CONTACTS, ACCESS_FINE_LOCATION)은 사용자 동의가 필요할 수 있습니다. 화이트리스트를 통해 사전 승인(Pre-grant)되지 않은 경우, 일반 앱과 동일하게 런타임에 권한을 체크하고 요청하는 로직이 필수적입니다. 이는 시스템 무결성을 유지하고 예외 상황을 방어하기 위함입니다.
// Kotlin: 시스템 앱을 위한 권한 체크 및 요청 방어 로직
fun checkAndRequestSystemPermission(context: Context) {
val permission = Manifest.permission.WRITE_SECURE_SETTINGS
// 1. 권한 보유 여부 확인 (Binder IPC 비용 발생 가능)
if (ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED) {
executePrivilegedAction()
} else {
// 2. 시스템 앱은 일반적으로 UI를 띄우지 않고 Silent Grant 되는 경우가 많으나
// 명시적으로 거부된 경우 로그를 남기거나 Fallback 처리 필요
Log.e("SystemApp", "Critical: Permission $permission not granted via whitelist.")
// 3. 필요한 경우 명시적 요청 (단, 시스템 앱 UX에 따라 생략 가능)
// ActivityCompat.requestPermissions(...)
}
}
private fun executePrivilegedAction() {
// 권한이 보장된 상태에서 안전하게 API 호출
try {
Settings.Global.putInt(contentResolver, "adb_enabled", 1)
} catch (e: SecurityException) {
Log.e("SystemApp", "Access Denied despite check: ${e.message}")
}
}
시스템 앱 vs 사용자 앱 권한 처리 비교
시스템 앱 개발 시 고려해야 할 권한 획득 전략의 차이를 비교 분석합니다. 시스템 앱은 사용자 개입을 최소화하는 방향으로 설계되어야 합니다.
| 구분 | 시스템 앱 (Privileged) | 사용자 앱 (User) |
|---|---|---|
| 권한 획득 시점 | 주로 부팅 시 (XML Whitelist 기반 사전 승인) | 앱 실행 중 (Runtime Request) |
| Signature 권한 | 플랫폼 서명이 일치하면 자동 획득 가능 | 획득 불가 (일반적으로) |
| 사용자 거부 | 사용자가 설정을 통해 권한을 회수할 수 있음 (방어 코드 필요) | 사용자가 거부하면 기능 사용 불가 |
| SELinux Context | platform_app 또는 priv_app 도메인 적용 |
untrusted_app 도메인 적용 |
Best Practice:
가능하다면 protectionLevel="signature"를 활용하여 플랫폼 키로 서명된 앱끼리만 권한을 공유하도록 설계하십시오. 이는 privileged 권한보다 보안상 훨씬 강력하며, 화이트리스트 관리가 필요 없습니다.
결론 및 요약
안드로이드 시스템 앱 개발에서 권한 설정은 단순한 매니페스트 선언을 넘어, 빌드 시스템의 priv-app 화이트리스트 구성과 런타임 방어 로직이 결합된 아키텍처 관점에서 접근해야 합니다. 특히 안드로이드 버전이 올라갈수록 보안 정책(SELinux, Permission Enforcement)이 강화되므로, 개발 단계에서부터 권한 획득 실패에 대한 예외 처리를 철저히 구현하는 것이 안정성을 보장하는 핵심입니다.

Post a Comment