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]
Recommended Posts:
- Android - 진동, Vibrator, VibrationEffect 예제
- Android - FusedLocationProviderClient으로 위치 정보 얻기
- Android - 시간, 날짜 변경 이벤트 받기
- Android - ACTION_BOOT_COMPLETED 이벤트 받기
- Android - Foreground Service 실행
- Android - GPS, Network 위치 정보 얻기 (LocationManager)
- Android - Screen On/Off 이벤트 수신, 상태 확인
- Android - 파일 입출력 예제 (Read, Write, 내부, 외부 저장소)
- Android - currentTimeMillis(), elapsedRealtime(), uptimeMillis()
- Android - PowerManager WakeLock
- Android - 다른 앱의 Service에 바인딩
- Android - Handler vs Executor
- Android - Darkmode 활성화하는 방법