Android - 앱 권한 확인(Permission check)

JS · 14 Nov 2020

App이 어떤 권한을 갖고 있는지 확인하는 방법을 소개합니다.

  • 자신의 앱이 어떤 권한을 갖고 있는지 확인
  • 다른 앱이 어떤 퍼미션을 갖고 있는지 확인

Sample App

Sample App을 만들고 이 앱의 권한을 확인하는 코드를 구현할 것입니다.

다음과 같이 Sample app의 Manifest에 두개의 퍼미션을 요청했습니다.

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

그리고 앱은 INTERNET 퍼미션은 갖고 있고, READ_EXTERNAL_STORAGE 퍼미션은 갖고 있지 않은 상태입니다.

Context.checkSelfPermission()으로 권한 확인

checkSelfPermission()는 인자로 permisison 이름을 받습니다.

public abstract int checkSelfPermission(@NonNull String permission);

Context의 App이 인자로 전달된 퍼미션을 갖고 있다면 PackageManager.PERMISSION_GRANTED를 리턴합니다. 그렇지 않다면 PackageManager.PERMISSION_DENIED을 리턴합니다. 즉, 이 API는 자신의 앱의 퍼미션 상태만 확인할 수 있습니다.

PackageManager.PERMISSION_GRANTED = 0;
PackageManager.PERMISSION_DENIED = -1;

Activity에서 다음과 같이 사용할 수 있습니다.

if (checkSelfPermission(Manifest.permission.INTERNET)
        == PackageManager.PERMISSION_GRANTED) {
    Log.d("Test", "Has INTERNET permission")
}

if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
        == PackageManager.PERMISSION_DENIED) {
    Log.d("Test", "Doesn't have READ_EXTERNAL_STORAGE permission")
}

Output:

11-14 22:27:15.014  5483  5483 D Test    : Has INTERNET permission
11-14 22:27:15.014  5483  5483 D Test    : Doesn't have READ_EXTERNAL_STORAGE permission

ActivityCompat API

Compatibility API를 사용하고 싶다면 ActivityCompat.checkSelfPermission(Context, Permission)을 사용하시면 됩니다. 리턴 값은 위와 동일합니다.

if (ActivityCompat.checkSelfPermission(this, Manifest.permission.INTERNET)
        == PackageManager.PERMISSION_GRANTED) {
    Log.d("Test", "Has INTERNET permission")
}

if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
        == PackageManager.PERMISSION_DENIED) {
    Log.d("Test", "Doesn't have READ_EXTERNAL_STORAGE")
}

PackageManager.checkPermisison()으로 퍼미션 확인

checkPermisison()은 다음과 같이 퍼미션 이름과 패키지 이름을 인자로 받습니다.

public abstract int checkPermission(@NonNull String permName, NonNull String packageName);

인자로 받은 패키지에 인자로 받은 퍼미션을 갖고 있는지 확인하고 결과를 리턴합니다. 즉, 이 API는 다른 패키지의 퍼미션 상태를 확인할 수 있습니다.

다음과 같이 App에서 사용할 수 있습니다.

if (packageManager.checkPermission(Manifest.permission.INTERNET, packageName)
        == PackageManager.PERMISSION_GRANTED) {
    Log.d("Test", "Has INTERNET permission")
}
if (packageManager.checkPermission(Manifest.permission.READ_EXTERNAL_STORAGE, packageName)
        == PackageManager.PERMISSION_DENIED) {
    Log.d("Test", "Doesn't have READ_EXTERNAL_STORAGE permission")
}

Output:

11-14 22:30:54.999  6407  6407 D Test    : Has INTERNET permission
11-14 22:30:55.000  6407  6407 D Test    : Doesn't have READ_EXTERNAL_STORAGE permission

다른 패키지의 퍼미션 확인

다음과 같이 다른 앱이 특정 퍼미션을 갖고 있는지 확인할 수 있습니다. 아래 예제는 Youtube가 NFC 퍼미션을 갖고 있는지 확인하는 예제입니다.

if (packageManager.checkPermission(Manifest.permission.NFC,
        "com.google.android.youtube") == PackageManager.PERMISSION_DENIED) {
    Log.d("Test", "Youtube has NFC permission")
}

checkSelfPermission()과 checkPermisison()의 차이점

안드로이드의 권한은 크게 PackageManager의 Permission과 AppOps의 권한으로 나눌 수 있습니다. PackageManager의 Permission은 Manifest에 선언하는 퍼미션이고, 사용자에게 권한을 요청하여 받는 퍼미션을 말합니다. AppOps는 Appliction Operation의 의미로, App의 동작을 제한하거나 허용하는 등의 권한을 설정하는 것을 의미합니다.

Context.checkSelfPermission()은 AppOps의 권한과 PackageManager의 Permission을 모두 확인하여 리턴합니다. 즉, PackageManager의 Permission을 갖고 있어도 AppOps로부터 제한되었다면 PERMISSION_DENIED가 리턴될 수 있습니다.

반면에 PackageManager.checkPermisison()은 App이 PackageManager의 Permission을 갖고 있는지만 확인합니다. AppOps로부터 허용되었는지 제한되었는지는 알 수 없습니다.

adb shell로 퍼미션 상태를 확인하는 방법

Settings 앱에서 내 앱의 정보에서 퍼미션 상태를 확인할 수 있습니다.

하지만 adb shell을 사용하면 개발 중에 더 쉽게 퍼미션 상태를 확인할 수 있습니다.

다음 명령어를 입력하면 터미널에 패키지 정보가 출력됩니다.

$ adb shell dumpsys package [package name]

예를 들어, 다음과 같이 youtube에 대한 패키지 정보를 출력할 수 있습니다. 그리고 그 정보에서 퍼미션 상태를 확인할 수 있습니다.

$ adb shell dumpsys package com.google.android.youtube

Packages:
  Package [com.google.android.youtube] (79c836a):
    userId=10121
    pkg=Package{baa705b com.google.android.youtube}
    declared permissions:
      com.google.android.youtube.permission.C2D_MESSAGE: prot=signature, INSTALLED
    requested permissions:
      android.permission.INTERNET
      android.permission.ACCESS_NETWORK_STATE
      android.permission.ACCESS_WIFI_STATE
      android.permission.WRITE_EXTERNAL_STORAGE: restricted=true
      ....
    install permissions:
      com.google.android.c2dm.permission.RECEIVE: granted=true
      android.permission.USE_CREDENTIALS: granted=true
      com.google.android.providers.gsf.permission.READ_GSERVICES: granted=true
      ....
    User 0: ceDataInode=122890 installed=true hidden=false suspended=false distractionFlags=0 stopped=false notLaunched=false enabled=1 instant=false virtual=false
      gids=[3003]
      runtime permissions:
        android.permission.ACCESS_FINE_LOCATION: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]
        android.permission.READ_EXTERNAL_STORAGE: granted=false, flags=[ REVOKE_WHEN_REQUESTED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|RESTRICTION_UPGRADE_EXEMPT]
        android.permission.ACCESS_COARSE_LOCATION: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]
        ....

각각의 항목의 의미는 다음과 같습니다.

  • declared permissions : 앱에서 직접 정의한 퍼미션을 의미합니다.
  • requested permissions : 앱에서 요청하는 퍼미션을 의미합니다. (Manifest에서 uses-permission으로 선언한 퍼미션)
  • install permissions : 앱이 갖고 있는 퍼미션입니다. install permission은 설치될 때 부여되는 퍼미션 종류를 의미합니다.
  • runtime permissions : 앱이 갖고 있는 퍼미션입니다. rumtime permission은 설치될 때 부여되지 않고, 앱이 사용자에게 권한을 요청하여 부여받는 퍼미션을 의미합니다.

다음과 같이 grep을 이용하여 granted 상태를 쉽게 확인할 수 있습니다.

$ adb shell dumpsys package com.google.android.youtube | grep READ_CONTACTS
      android.permission.READ_CONTACTS
        android.permission.READ_CONTACTS: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]
댓글을 보거나 쓰려면 이 버튼을 눌러주세요.
codechachaCopyright ©2019 codechacha