[안드로이드] 퍼미션(Permission)

hee09·2021년 10월 18일

안드로이드

목록 보기
6/20
post-thumbnail

깡쌤의 안드로이드 프로그래밍 책을 보며 작성하였습니다.

퍼미션

1. 퍼미션이란?

퍼미션(Permission)이란, 개발자 코드의 알고리즘이 아니라 AndroidManifest.xml에 들어가는 설정입니다. 안드로이드 컴포넌트를 이용한 앱과 앱 사이의 연동이 빈번한데, 이러한 연동에서 어떤 앱이 <permission>을 부여했다면 그 앱을 이용하는 앱은 <uses-permission>을 선언해야 합니다. 이는 앱과 앱 간의 문제일 수도 있고, 시스템에서 특정 기능에 퍼미션을 부여하는 경우도 있습니다.

1.1 <permission> 이용

<permission>은 자신의 앱을 외부에서 이용할 때 권한을 부여하여 해당 권한을 가지고 들어올 때만 실행되게 하기 위한 설정입니다. 즉, <permission>으로 선언된 앱을 이용하는 앱이 <uses-permission>을 선언하지 않으면 실행 시 에러가 발생합니다.


예시와 함께 보겠습니다.


TestApp에서 TargetApp의 컴포넌트를 실행한다고 가정하겠습니다. 이 때, TargetApp에서 해당 컴포넌트에 <permission>을 부여하여 보호하고 있는데 TestApp에서 오류가 발생하지 않고 해당 컴포넌트를 실행하기 위해서는 꼭 <uses-permission>을 선언해서 사용해야 합니다.

정리하자면 앱의 컴포넌트를 보호하고 싶을 때 <permission>을 선언하고, 그렇게 선언된 앱을 이용하려면 <uses-permission>을 선언하는 구조입니다.


AndroidManifest.xml의 소스코드는 다음과 같습니다.
<!-- TargetApp의 Permission 생성 코드 -->
<!-- name : 퍼미션 레벨 -->
<!-- label, description : 퍼미션에 대한 설명 -->
<!-- protectionLevel : 보호 수준 -->
<permission
    android:name="kr.co.lee.permission.TargetPermission"
    android:description="Permission 예제입니다"
    android:label="Target Permission"
    android:protectionLevel="normal" />

<!-- 컴포넌트에 정의한 퍼미션 적용 -->
<activity
    android:name=".TargetActivity"
    android:permission="kr.co.lee.permission.TargetPermission">
    <intent-filter>
        <action android:name="Target" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

<!-- TestApp에서 TargetApp의 컴포넌트를 실행하기 위한 uses-permisson 선언 -->
<!-- permission의 이름으로 설정 -->
<uses-permission android:name="kr.co.lee.permission.TargetPermission" />

우선 컴포넌트를 퍼미션으로 보호하고 싶으면 <permission> 태그를 추가합니다.
퍼미션 태그안의 속성은 다음과 같습니다.

  • name : 퍼미션 이름
  • label, description : 퍼미션에 대한 설명(사용자에게 보이는 문자열)
  • protectionLevel : 보호 수준

protectionLevel의 보호 수준은 다음과 같습니다

  • normal : 낮은 수준의 보호, 사용자에게 권한 부여 요청이 필요 없는 경우
  • dangerous : 높은 수준의 보호, 사용자에게 권한 부여 요청이 필요한 경우
  • signature : 동일한 키로 서명된 앱만 실행
  • signatureOrSystem : 안드로이드 시스템 앱이거나 동일 키로 서명된 앱만 실행

1.2 protectionLevel 속성

퍼미션을 선언할 때 protectionLevel 속성을 선언하는데, 보통 "normal", "dangerous", "signature" 중 하나의 값으로 설정합니다. 어떤 값으로 선언하는지에 따라 동작이 달라집니다.
normal로 선언하면 <uses-permission>은 요구하지만 사용자에게 퍼미션 부여를 요구하지는 않습니다.
"dangerous"로 선언하면 <uses-permission>도 요구하고 사용자에게 퍼미션 부여도 요구합니다.
"signature"는 외부 앱이 같은 키로 서명되어 있어야 실행됩니다.


1.3 시스템 퍼미션

위의 링크를 클릭하면 시스템 퍼미션관련 정보가 나오는데 퍼미션 정보와 protectionLevel에 대한 설명이 나와있습니다.

퍼미션이 앱과 앱 사이의 문제일 수도 있지만, 어떤 퍼미션은 시스템에서 선언한 것도 있습니다. 다른 앱을 연동하는 것도 아닌데 특정 기능을 시스템에서 보호하고 있어서 앱에서 그 기능을 이용할 때 <uses-permission>을 선언하지 않으면 에러가 발생합니다.

대표적인 예가 사용자 위치 정보를 사용할 때 입니다. 사용자 위치 정보는 외부 앱 연동이 아님에도 시스템 자체에서 퍼미션으로 보호하고 있어서 <uses-permission>을 선언해 주어야 합니다.

시스템의 퍼미션 중 이용빈도가 높은 퍼미션으로는 다음과 같습니다.

퍼미션 제목내용
ACCESS_FINE_LOCATION정확한 위치 정보 액세스
ACCESS_NETWORK_STATE네트워크에 대한 정보 액세스
ACCESS_WIFI_STATE와이파이 네트워크에 대한 정보 액세스
BATTERY_STATS배터리 통계 수집
BLUETOOTH연결된 블루투스 장치에 연결
BLUETOOTH_ADMIN블루투스 장치를 검색하고 페어링
CALL_PHONE다이얼 UI를 거치치 않고 전화를 시작
CAMERA카메라 장치에 액세스
INTERNET네트워크 연결
READ_CONTACTS사용자의 연락처 데이터 읽기
READ_EXTERNAL_STORAGE외부 저장소에서 파일 읽기
READ_PHONE_STATE장치의 전화번호, 네트워크 정보, 진행 중인 통화 상태 등 전화 상태에 대한 읽기
READ_SMSSMS 메시지 읽기
RECEIVE_BOOT_COMPLETED부팅 완료 시 수행
RECORD_AUDIO오디오 녹음
SEND_SMS메시지 발신
VIBRATE진동 울리기
WRITE_CONTACTS사용자의 연락처 데이터 읽기
WRITE_EXTERNAL_STORAGE외부 저장소에 파일 쓰기

1.4 퍼미션 확인 및 다이얼로그를 통한 퍼미션 요청 코드

만약 시스템 퍼미션 중 protectionLevel이 dangerous(위험)인 것들은 <uses-permission>을 AndroidManifest.xml에 정의한다고 하더라도 끝나는 것이 아닙니다. 코드에서 권한을 확인하고 거부상태이면 다이얼로그를 통한 권한 요청 등을 수행하는 식으로 알고리즘을 작성해야 합니다. 그 상황에서 사용하는 함수들 입니다.

  • checkSelfPermission(Context context, String permission) : 퍼미션의 상태를 확인하는 함수로 두 번째 매개변수가 퍼미션의 이름이며, 함수의 반환값은 PERMISSION_GRANTED(퍼미션 부여), PERMISSION_DENIED(퍼미션 부여되지 않은 상태)입니다.
  • requestPermissions(Activity activity, String[] permissions, int requestCode) : 퍼미션 허용 여부를 묻는 다이얼로그가 뜨는 함수로 사용자가 이 다이얼로그를 통해 거부 혹은 허용을 선택합니다. 두 번째 매개변수는 요청하고자 하는 퍼미션의 배열이고, 세 번째 매개변수는 상태코드 값으로 퍼미션 조정 결과를 확인할 때 이용합니다.
  • onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) : 위의 다이얼로그가 닫히면 자동으로 호출되는 함수로 이 함수에서 사용자의 퍼미션 조정 결과를 확인할 수 있습니다. 첫 번째 매개변수로 requestPermissions에서 사용한 상태코드 값이 넘어오고, 두 번째 매개변수는 요청한 퍼미션 이름, 세 번째 매개변수는 사용자가 조정한 결과값입니다.

다음과 같이 소스코드에서 작성할 수 있습니다(간단한 예시코드 입니다)
class TargetActivity : AppCompatActivity() {

    // 위치 권한 확인용 변수
    var locationFlag: Boolean = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_target)

        // 권한 확인 용 메소드
        // 매개변수 첫 번째로는 Context, 두 번째는 확인 할 권한
        // 반환값으로 PackageMananger.PERMISSION_GRANTED(권한 허용) or PackageManager.PERMISSION_DENIED(권한 거부)가 들어온다
        if(ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // 다이얼로그를 통한 권한 요청하기
            // 첫 번째 매개변수는 Activity, 두 번째 매개변수는 권한목록 배열(들어온 권한의 순서대로 다이얼로그가 차례대로 뜬다
            // 세 번째 매개변수는 다이얼로그가 닫히면 자동으로 호출되는 onRequestPermissionResult에서 퍼미션을 구분하기 위한 값
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 100)
        } else { // 권한이 허용일 경우
            locationFlag = true
        }
    }

    // ActivityCompat.requestPermissions이 끝나면 자동으로 호출되는 메소드
    // 첫 번째 매개변수는 requestPermissions에서 지정한 상태코드 값
    // 두 번째 매개변수는 요청한 퍼미션 이름
    // 세 번째 매개변수는 사용자가 조정한 결과 값
    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        // 요청코드가 100번이고 결과가 있는 경우
        if(requestCode == 100 && grantResults.isNotEmpty()) {
            if(grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // Permission 허용시 코드
                locationFlag = true
            } else if(grantResults[0] == PackageManager.PERMISSION_DENIED) {
                // Permission 거부시 코드
                locationFlag = false
            }
        }

        return super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    }
}
profile
되새기기 위해 기록

0개의 댓글