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

hee09·2021년 10월 18일
0

안드로이드

목록 보기
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개의 댓글