Post

Priv App에 runtime permission 부여하기

priv-app은 system app의 일부인데 privileged app의 약자다.

system app은 /system/app/에 위치하고 시스템 이미지에 포함되며 시스템 서명이면 일부 시스템 권한만 획득한다. priv-app은 /system/priv-app/에 위치하고 signature+privileged 권한을 갖고 있어서 일반 시스템 앱보다 더 강력한 시스템 API 접근 가능하다. 권한이 priv-app이 더 빡센데, 모든 priv-app이 system app이지만, 모든 system app이 priv-app은 아니다.

안드로이드 시스템에서 /system/priv-app/ 디렉터리에 설치된 앱을 가리키며 일반적인 앱(data/app/에 설치됨)과 다르게 특별한 시스템 권한(Privileged Permission)을 가질 수 있는 앱이다. 커스텀 펌웨어에서 자동 업데이트, 디바이스 관리, 키오스크 등 특수 목적 앱도 priv-app으로 넣는다.

일반 개발에서는 쓸 수 없고, 루팅된 기기를 다루거나 시스템 쪽 권한을 직접 만질 수 있는 경우라면 접근할 수 있다.

시스템 이미지에 의해 포함된 앱들이고, aosp의 플랫폼 signing으로 빌드되어야 권한을 제대로 부여받는다. aosp로 빌드하는 소스의 manifest에 적당히 선언만 해도 자동 부여되는 게 아니다. 반드시 /system/etc/permissions/privapp-permissions-*.xml 파일에 각 권한을 명시해야 한다.

1
2
3
4
5
6
7
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<permissions>
    <privapp-permissions
        package="com.abc.example">
        <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
    </privapp-permissions>
</permissions>

그렇지 않으면 시스템이 설치할 때 “권한이 거부됨”으로 처리한다. 근데 문제는 이게 runtime 권한은 제대로 적용되지않는 다는 점이다.

권한 페이지를 가보면, runtime permission들의 level이 dangerous로 되어있는 걸 볼 수 있는데, 이 등급의 보안을 가진 권한들은 privapp-permissions에 선언한다고 부여해주지않는다.

그럼 runtime 권한은 어떻게 부여해야될까?

/system/에 있는 앱에서 사용하는 dangerous level permission은 system/etc/default-permissions_sample-*.xml로 부여 가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<exceptions>
    <exception
            package="com.google.android.apps.pixelmigrate">
        <!-- External storage -->
        <permission name="android.permission.READ_EXTERNAL_STORAGE" fixed="false"/>
        <permission name="android.permission.WRITE_EXTERNAL_STORAGE" fixed="false"/>
        <!-- Contacts -->
        <permission name="android.permission.READ_CONTACTS" fixed="false"/>
        <permission name="android.permission.WRITE_CONTACTS" fixed="false"/>
        <!-- Call logs -->
        <permission name="android.permission.READ_CALL_LOG" fixed="false"/>
        <permission name="android.permission.WRITE_CALL_LOG" fixed="false"/>
        <!-- SMS -->
        <permission name="android.permission.RECEIVE_SMS" fixed="false"/>
        <permission name="android.permission.READ_PHONE_NUMBERS" fixed="false"/>
    </exception>
</exceptions>

exceptions 태그로 감싸서 runtime권한들을 직접 지정해준다. 권한 보는 건 그냥 dumpsys package [app package] | grep -i permission으로 출력하면 된다.

이렇게 xml파일로 default permission을 적어두면 DefaultPermissionGrantPolicy.java 코드에서 이 xml을 파싱해서 권한을 부여한다.

문제는 이제 auto grant가 제대로 동작하지 않는 경우가 android 12 이후로 발생한다는 건데, 이 경우 DefaultPermissionGrantPolicy부터 수정해서 aosp를 빌드해야된다. 근데 android 정책이 점점 runtime 권한에 대한 관리를 엄격하게 하고 있어서 이거도 막힐 수 있다.

frameworks/base/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.javagrantDefaultSystemHandlerPermissions메서드를 적당히 수정해준다.

1
2
3
4
5
6
7
8
mService.mSettings.onDefaultRuntimePermissionsGrantedLPr(userId);
// 앞에 바로 위 코드가 존재함
PackageParser.Package yourPackage = getSystemPackageLPr("YOUR_APP_PACKAGE_NAME");
if (yourPackage != null
        && doesPackageSupportRuntimePermissions(yourPackage)) {
    grantRuntimePermissionsLPw(yourPackage, CONTACTS_PERMISSIONS, userId);
    grantRuntimePermissionsLPw(yourPackage, CALENDAR_PERMISSIONS, userId);
}

시스템 앱에 대한 권한 프로세스를 mermaid로도 한 번 그려봤다.

flowchart TD
    A(앱이 시스템 이미지에 포함됨)
    B{권한 종류}
    C1[Normal Permission]
    C2[Signature/Privileged Permission]
    C3[Dangerous Permission]

    D1[설치 시 자동 grant]
    D2[privapp-permissions.xml에 명시해야 grant]
    D3{Android 버전}
    D4[default-permissions.xml에 예외 등록]
    D5[DefaultPermissionGrantPolicy.java 수정 필요]
    E1[앱에서 바로 사용 가능]
    E2[grant 실패 or 사용자 수동 허용 필요]

    A --> B
    B --> C1
    B --> C2
    B --> C3

    C1 --> D1 --> E1
    C2 --> D2 --> E1
    C3 --> D3

    D3 -->|Android 11 이하| D4 --> E1
    D3 -->|Android 12 이상| D5 --> E2
This post is licensed under CC BY 4.0 by the author.