본문 바로가기

책 요약하기/이것이 안드로이드다

#6. 권한 2021-03-18

1.1 권한 명세와 기능 명세

설정 파일에 작성하는 명세에는 권한 명세와 기능 명세 두 가지가 있습니다. 권한 명세는 해당 데이터나 기능의 사용여부를 설정하고, 기능 명세는 해당 기능이 있는 안드로이드폰에서만 내려받을 수 있도록 플레이 스토어에서 내려받는 것을 방지합니다.

 

권한 명세

권한 명세를 설정하는 AndroidManifest.xml 파일은 [app] - [manifests] 디렉터리 밑에 있습니다.
예시로 와이파이 권한과 인터넷 접근 권한을 부여하는 코드입니다.

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

 

기능 명세

권한 이외에도 기능에 대한 명세가 필요할 때가 있는데 기능 명세는 AndroidManifest.xml파일에 따로 추가하지 않아도 해당 기능을 사용할 때 시스템이 자동으로 부여합니다. 이때 사용하는 태그가 <uses-feature/>태그입니다.
예를 들어 카메라를 사용하는 앱을 개발하면 카메라 기능을 사용하는 순간 자동으로 <uses feature android:name="android.hardware.camera" android:required="true"/>가 AndroidManifest.xml 파일에 명세됩니다.
이렇게 개발한 앱을 플레이 스토어에 올렸을 때 카메라가 없는 스마트폰에서 플레이 스토어에 접속했을 때는 이 앱이 검색되지 않습니다. 
그렇다면 카메라 기능이 없어도 해당 앱을 다운받으려면 기능 명세에서 required 옵션을 false로 작성하면 카메라가 없어도 검색되고 설치도 할 수 있습니다. 단, 예외 처리를 해주어야 합니다.(특별한 경우가 아니라면 false x)

 

1.2 권한의 보호 수준

권한은 일반 권한(Normal Permission), 위험 권한(Dangerous Permission), 서명 권한(Signature Permission) 세 가지의 보호 수준으로 나뉩니다.

일반 권한(Normal Permission)

앱 설치 시 사용자에게 권한 승인을 묻는 팝업창을 보여줍니다. 인터넷 사용, 알람 설정 등이 일반권한에 포함됩니다.

권한 설명
ACCESS_NETWORK_STATE 네트워크 연결 상태 확인
ACCESS_WIFI_STATE 와이파이 상태 확인
BLUETOOTH 블루투스 상태 확인
INTERNET 네트워크 및 인터넷 사용
NFC 기기 간 근거리 통신 사용
SET_ALARM 알람 설정
VIBRATE 진동 설정

 

위험 권한(Dangerous Permission)

앱이 사용자의 개인정보와 관련된 데이터나 기능을 액세스하거나 다른 앱 및 기기의 작동에 영향을 줄 우려가 있는 권한입니다. 위험 권한은 [Gradle Scripts]의 build.gradle (Module: app) 파일의 targetSdkVersion이 23(API 23) 이상으로 설정되어야지 정상으로 동작합니다.

권한 그룹 권한 설명
CALENDAR READ_CALENDAR
WRITE_CALENDAR
캘린더 읽기
캘린더 쓰기
CAMERA CAMERA 카메라
CONTACTS READ_CONTACTS
WRITE_CONTACTS
GET_ACCOUNTS
주소록 읽기
주소록 쓰기
계정 정보 가져오기
LOCATION ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
위치 정보 사용
위치 정보 사용
MICROPHONE RECORD_AUDIO 마이크 녹음
PHONE READ_PHONE_STATE
READ_PHONE_NUMBERS
CALL_PHONE
ANSWER_PHONE_CALLS
READ_CALL_LOG
WRITE_CALL_LOG
ADD_VOICEMAIL
USE_SIP
PROCESS_OUTGOING_CALLS
폰 상태 정보
전화번호 가져오기
발신하기
응답하기
전화 로그 읽기
전화 로그 쓰기
음성메일 추가
SIP 사용
통화 관련 Brodcast 수신
SENSORS BODY_SENSORS 바디센서
SMS SEND_SMS
RECEIVE_SMS
READ_SMS
RECEIVE_WAP_PUSH
RECEIVE_MMS
SMS 보내기
SMS 받기
SMS 읽기
WAP 수신
MMS 받기
STORAGE READ_EXTERNAL_STORAGE
WRITE_EXTERNAL_STORAGE
안드로이드 공용 저장소 읽기
안드로이드 공용 저장소 쓰기

 

서명 권한(Signature Permission)

서명 권한은 권한을 사용하려는 앱이 권한을 전의하는 앱과 동일한 인증서로 서명된 경우 시스템은 권한을 자동으로 부여합니다. 쉽게말해 구글에서 만든 앱은 권한이 자동으로 부여되는 것과 같다고 생각하면 됩니다.

 

1.3 권한 그룹

각각의 권한은 그룹 단위로 구성됩니다. 파일에 대해 읽기/쓰기 권한이 있으면 이 2개의 권한은 하나의 그룹에 속합니다. 권한에 대한 요청은 그룹 단위로 처리되며 동일한 권한 그룹 내에서 다른 권한이 이미 부여된 경우 시스템은 즉시 권한을 부여합니다. 예를 들어 앱에서 동일한 권한 그룹 CONTACTS에 있는 READ_CONTACTS와 WRITE_CONTACTS를 사용해야 하는 앱에서 READ_CONTACTS 요청에 대한 승인이 있었다면 , 시스템은 사용자에게 다시 물어보지 않고 WRITE_CONTACTS 권한에 대한 사용을 허가합니다. 권한 그룹으로 묶인 권한은 모두 한 번에 처리한다고 보면 됩니다.

 

- 미니 퀴즈 6-1 -

1. 권한의 유형에는 사용자에게 권한 부여 여부만 물어보고 앱의 매니페스트에 공개하면 자동으로 부여되는 ( ① ) 권한과 권한이 부여되면 사용자 데이터 및 특정 시스템 기능에 액세스 할 수 있는 ( ② ) 권한이 있습니다.

답 : ① 일반권한, ② 위험권한

 

2. 안드로이드 6.0 버전부터는 앱의 매니페스트에서 위험 권한을 공개하고 권한 요청을 코드로도 처리해야 합니다. 액티비티에서 권한을 요청하고 처리하는 메서드는 무엇입니까?

답 :

 

2. 위험한 권한 처리하기

위험한 권한에 대해서는 AndroidManifest.xml 파일을 수정한 다음 소스 코드에도 추가로 처리해야 합니다.

 

예제로 카메라 권한을 추가해보겠습니다.

1. AndroidManifest.xml파일을 열고 해당 코드를 작성합니다.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.hellow.permission_6">

    <uses-permission android:name="android.permission.CAMERA"/> // 카메라 권한

    <application
        android:allowBackup="true"
        ...
    </application>
</manifest>

 

2. 권한 요청을 위한 버튼을 생성합니다.

 

3. 위험 권한을 처리하기 위해 소스 코드 수정 시 다음의 3단계를 거칩니다.

  1. 권한에 대한 사용자 승인 확인(이전에 승인하였는지)
  2. 사용자에게 승인 요청
  3. 사용자 승인 후 처리

 

[1단계] : 권한에 대한 사용자 승인 요청

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        }
    }

    fun checkPermission() {
        // Manifest 클래스에서 카메라 권한 가져옴
        val cameraPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)

        // cameraPermission에 저장된 값의 승인 여부에 따라 분기
        if (cameraPermission == PackageManager.PERMISSION_GRANTED) {
            // 상태가 승인일 경우 코드 진행
            startProcess()
        } else {
            // 승인되지 않았다면 권한 요청 프로세스 진행
            requestPermission()
    }

    fun startProcess() {
        Toast.makeText(this, "카메라를 실행합니다.", Toast.LENGTH_SHORT).show()
    }
}

 

[2단계] : 사용자에게 승인 요청

onCreate() 메서드 안에서 checkPermission()을 호출한 후, 앱을 실행하면 애뮬레이터에 권한 승인을 묻는 팝업이 나타나고 두 번째 입력되는 권한 배열의 개수만큼 2개의 권한을 요청하면 두 번의 팝업이 뜹니다.

    fun startProcess() {
        Toast.makeText(this, "카메라를 실행합니다.", Toast.LENGTH_SHORT).show()
    }

    // 미승인일 때 권한 요청하는 메서드
    fun requestPermission() {
        ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), 99)
    }

 

[3단계] : 사용자 승인 후 처리

권한을 묻는 팝업창에 사용자가 DENY(거부) 또는 ALLOW(수락)을 클릭하면 액티비티의 onRequestPermissionResult() 메서드가 호출됩니다. 메서드 안에서 승인 후 처리하면 됩니다.

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    }
onRequestPermissionsResult() 파라미터
requestCode : 요청한 주체를 확인하는 코드, requestPermissions() 메서드의 세 번째 파라미터로 전달됩니다.
permissions : 요청한 권한 목록. requestPermissions() 메서드의 두 번째 파라미터로 전달됩니다.
grantResults : 권한 목록에 대한 승인 미승인 값, 권한 목록의 개수와 같은 수의 결괏값이 전달됩니다.
    override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        when(requestCode) {
            99 -> {
                // 권한 결괏값 확인 후 실행 내용 결정
                // 결괏값을 체크해서 승인 여부를 체크하고 승인이면 startProcess() 메서드 실행
                // 미승인일 경우 finish() 메서드 실행하여 앱 종료
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    startProcess()
                } else {
                    finish()
                }
            }
        }
    }

마지막으로 onCrate()메서드 안에 버튼 클릭 시 checkPermission() 메서드 호출하도록 코드 작성

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

        val btnCamera : Button = findViewById<Button>(R.id.btnCamera)
        btnCamera.setOnClickListener {
            checkPermission()
        }
    }

 

[실행 결과 및 전체 코드]

(참고로 한번 권한을 수락하면 이후 애플리케이션을 동작 할 때마다 일일이 권한 여부를 물어보지 않습니다.)

 

[MainActivity.kt]

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val btnCamera : Button = findViewById<Button>(R.id.btnCamera)
        btnCamera.setOnClickListener {
            checkPermission()
        }
    }

    fun checkPermission() {
        // Manifest 클래스에서 카메라 권한 가져옴
        val cameraPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)

        // cameraPermission에 저장된 값의 승인 여부에 따라 분기
        if (cameraPermission == PackageManager.PERMISSION_GRANTED) {
            // 상태가 승인일 경우 코드 진행
            startProcess()
        } else {
            // 승인되지 않았다면 권한 요청 프로세스 진행
            requestPermission()
        }
    }

    fun startProcess() {
        Toast.makeText(this, "카메라를 실행합니다.", Toast.LENGTH_SHORT).show()
    }

    // 미승인일 때 권한 요청하는 메서드
    fun requestPermission() {
        ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), 99)
    }

    override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        when(requestCode) {
            99 -> {
                // 권한 결괏값 확인 후 실행 내용 결정
                // 결괏값을 체크해서 승인 여부를 체크하고 승인이면 startProcess() 메서드 실행
                // 미승인일 경우 finish() 메서드 실행하여 앱 종료
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    startProcess()
                } else {
                    finish()
                }
            }
        }
    }
}

 

- 미니 퀴즈 6-2 -

1. 이 권한이 부여되면 민감한 개인정보 및 기능에 접근할 수 있으며 안드로이드 6.0 버전부터는 앱 매니패스트에 공개하고 코틀린 코드에 권한 요청 및 처리로 액세스가 가능한 권한은 무엇인가요?

답 : 위험 권한

 

2. 사용자에게 권한을 요청하는 팝업을 표시하는 메서드의 이름은 무엇인가요?

답 : ActivityCompat.requestPermissions(Activity activity, String[] permissions, int requestCode)

 

3. 사용자에게 권한을 요청한 결과를 알려주는 메서드의 이름은 무엇인가요?

답: ContextCompat.checkSelfPermission(Context context, String permission)

 

이상 마치겠습니다. :)