본문 바로가기

개발/PLUG-IN

[NATIVE, ANDROID] Android 6.0 (마쉬멜로우) 이상에서 Permission (시스템 권한) 체크 및 유니티 플러그인

개요

Android 6.0 (마쉬멜로우) 버전 이상의 디바이스에서는 앱에서 요구하는 시스템 권한을 사용자가 언제든 끄고 켤 수 있도록 변경되었어요.
이 기능은 개인정보 유출이 민감한 사항이기 때문에 추가된 기능인것 같습니다.

이 기능이 추가됨으로써 앱 구동에 필요한 필수 시스템 권한까지 끌 수 있게 되어, 앱 구동 중 시스템 권한을 획득하지 못하여 에러가 나는 경우가 발생할 수 있어요.
이러한 문제들을 해결하기 위해 구글 쪽에서도 인 게임에서 요구되는 시스템 권한을 체크하길 권고하고 있습니다.

어떤 권한들을 체크해야 할까?

안드로이드에서 앱을 사용하는데 필요한 시스템 권한을 AndroidManifest.xml 파일에 아래와 같은 방법으로 선언합니다.


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


AndroidMenifest.xml에 선언한 모든 시스템 권한을 체크하고 허가를 받아야 하는것은 아닙니다.

구글에서는 정상 권한과 위험 권한을 구별하고 있습니다.

저희는 위험 권한에 대해서만 체크하고 허가를 요청하면 되는거죠.


그럼 권한의 종류는 어떻게 될까요?

아래 링크(안드로이드 레퍼런스)를 통해 권한을 확인 할 수 있습니다.


https://developer.android.com/guide/topics/security/permissions?hl=ko#normal-dangerous


위 레퍼런스에서 확인한 바와 같이 개인정보를 침해하는 권한들이 위험 권한으로 구별되었네요.

예를 들면 주소록, 단말기 정보 등이 될 수 있겠네요.




권한을 체크하기 위한 라이브러리 및 메서드


1. 라이브러리

import com.android.support.v4.*;

권한을 체크하기 위해서 ActivityCompat, CotextCompat 등의 클래스들을 이용해야하는데 이 클래스들은 안드로이드 서포트 라이브러리에 포함되어 있습니다.


2. 권한 허가 상태 체크

ContextCompat.checkSelfPermission(Context, String)

게임 혹은 앱에서 필요로 하는 권한이 현재 허가가 되어 있는 상태인지 아닌지를 파악해야 권한 요청을 할 것인지에 대한 유무가 결정됩니다.
이때 사용하는 메서드는 ContextCompat.checkSelfPermission(Context, String) 메서드 입니다.

예제) 주소록 권한 허가 상태 체크
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACT) != PackageManager.PERMISSION_GRANTED) {
    // 주소록 권한 사용이 허가되지 않음
} else {
    // 주소록 권한 사용이 허가됨
}

3. 권한 사용 목적 설명이 필요한지 체크

ActivityCompat.shouldShowRequestPermissionRationale(Context, String)

권한을 요청할 때 모든 권한이 사용 목적에 대한 설명이 필요한 것은 아닙니다.
어떤 권한이 설명을 요구하는 지 체크할 때 ActivityCompat.shouldShowRequestPermissionRationale(Context, String) 메서드를 사용하면 됩니다.


예제) 주소록 접근 권한 체크

if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_CONTACT)) {
    // 이 곳에 사용 목적 설명 UI(다이얼로그) 출력
} else {
    // 설명이 필요 없음
}


4. 권한 요청 및 결과

ActivityCompat.requestPermission (Context, String[], int)

권한 허가 상태 체크와 설명 유무 체크가 완료되면 다음 단계로 제일 중요한 권한 요청 단계에 들어갑니다.
권한이 허가가 된 상태에서는 권한 요청을 할 필요없지만, 거부된 상태라면 사용자에게 권한을 허용할 수 있도록 권한 요청을 해야합니다.
이때 사용되는 메서드가 ActivityCompat.requestPermission(Context. String[], int) 입니다.

이 메서드의 인자 중 String[] 이 배열로 들어간 이유는 한 번에 여러개의 권한을 요청할 수 있기 때문입니다.
그리고 마지막 int 형의 인자는 결과를 반환 받는 메서드에서 구분용으로 사용되는 requestCode 입니다.

예제) 주소록 접근 권한 요청
ActivityCompat.requestPermission(this. new String[1] { Manifest.permission.READ_CONTACT }, 0);


onRequestPermissionsResult (int, @NonNull String[], @NonNull int[])

권한 요청을 하였다면, 그 결과를 반환 받아야 해당 권한을 이용하여 게임이 구동되겠죠?

이 때 onRequestPermissionsResult (int, @NonNull String[], @NonNull int[]) 라는 오버라이드 메서드로 반환받게 됩니다.


예제) 처리 결과

void onRequestPermissionsResult (int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    /* 이 곳에서 결과값을 처리합니다.
    * requestCode : 권한 허가를 요청시 구분용으로 입력한 int 인자
    * permissions : 권한 허가를 요청했던 권한 종류들

* grantResults :  요청했던 권한들의 결과 값들 (허가 - [0] or 거부 - [-1]) * permissions와 grantResults의 배열 크기가 동일하고, 같은 인덱스를 사용 */ }




Unity용 플러그인(Plug-in)

Last Update : 2018. 11. 08.

Version 1.0.0


permissions-1.0.0.unitypackage


(안드로이드 라이브러리를 요구하기 때문에 구글에서 제공하는 PlayServiceResolver를 사용 및 포함하고 있습니다.)


적용 방법

적용 방법은 어렵지는 않습니다.
일단 위에 첨부된 unitypackage 파일을 다운받고 적용할 프로젝트에 임포트합니다.

권한 체크 기능을 작성할 스크립트 상단에 플러그인 클래스를 참조할 수 있도록 선언합니다.


using Dasony.Libs.Android;


이렇게 선언 후에 아래 설명을 보시고 필요한 기능을 구현하시면 됩니다.


1. 플러그인 초기화

권한 요청 플러그인은 싱글톤으로 제작되어 Permissions.Instance 속성을 불러오면 자동으로 초기화됩니다.

public class Test : MonoBehaviour {
    Permissions checker;

    void Start () {
        checker = Permissions.Instance;
    }
}


2. 권한 허가 상태 체크

Permissions.CheckPermission (string permission, Action<string, bool> resultCallback)

예제) 주소록 읽기 권한 체크

public void CheckPermission () {
    checker.CheckPermission (Permissions.READ_CONTACTS, (permission, granted) => {
        if(!granted) {
            // 이곳에 Rationale 이나 Request 구현
        } else {
            // 권한이 허가된 상태이므로 권한을 이용한 기능 구현 혹은 메서드 호출
        }
    });
}


3. 권한 사용 목적 설명 필요 체크

Permissions.CheckRationale (string permission, Action<string, bool> resultCallback)

예제) 주소록 읽기 권한 설명 체크

public void CheckPermissionRationale () {
    checker.CheckRationale (Permissions.READ_CONTACTS, (permission, isExplain) => {
        if(!isExplain) {
            // 이곳에 설명할 UI 출력
        } else {
            // 설명이 필요 없으므로 권한 요청 구현
        }
    });
}


4. 권한 허가 요청

Permissions.RequestPermissions (string[] permissions, int requestCode, Action<PermissionData[] datas)

예제) 주소록 권한 읽기 허가 요청
public void RequestPermission () {
    checker.RequestPermissions (new string[] { Permissions.READ_CONTACTS }, 0, (permissionDatas) => {
        if(permissionDatas != null) {
            if(permissionDatas[0].granted) { // 1개의 권한을 요청했기에 인덱스 0번 데이터만 비교
                // 권한 허가됨
                // 권한을 이용한 기능 구현 혹은 메서드 호출
            } else {
                // 권한 거부됨
                // 권한이 거부되어 해당 권한이 필요한 기능을 사용할 수 없음을 사용자에게 알려주는 UI 출력
            }
        } else {
            // 권한 허가 요청 실패 
        }
    });
}

권한 허가 요청은 한번에 다수의 권한을 요청 할 수 있으므로, 사용 목적 UI 출력시 필요한 권한 모두를 한번에 설명하는 방법도 사용되고 있습니다.

예제에는 없지만, 조금만 이해하시면 쉽게 구현하실 수 있습니다.



자동 권한 허가 요청 다이얼로그 스킵

유니티에는 아무런 설정이 없는 상태에서 빌드를 하면 허가를 요청해야하는 권한들을 자동으로 체크하여 게임 시작시 다이얼로그를 출력해주는 기능이 적용되어 있습니다.

이 기능을 해제하려면 AndroidManifest.xml 파일에 아래 내용을 추가하면 됩니다.


<meta-data android:name="unityplayer.SkipPermissionsDialog" android:value="true" />


예제) AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="net.dasony.study" xmlns:tools="http://schemas.android.com/tools" android:installLocation="preferExternal"> 
    <supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" android:anyDensity="true" /> 
    <application android:theme="@style/UnityThemeSelector" android:icon="@mipmap/app_icon" android:label="@string/app_name"> 
        <activity android:name="com.unity3d.player.UnityPlayerActivity" android:label="@string/app_name"> 
            <intent-filter> 
                <action android:name="android.intent.action.MAIN" /> 
                <category android:name="android.intent.category.LAUNCHER" /> 
            </intent-filter> 
            <meta-data android:name="unityplayer.UnityActivity" android:value="true" /> 
            <meta-data android:name="unityplayer.SkipPermissionsDialog" android:value="true" />  // 권한 허가 요청 스킵
        </activity> 
    </application> 

    <uses-permission android:name="android.permission.READ_CONTACTS"/>
</manifest>





도움이 되셨나요?

잘못된 내용이나 이해가 잘 안되시면 댓글이나 방명록을 남겨주세요.

  • 플러그인 다운로드 :
    http://lib.dasony.net/attachment/cfile5.uf@9966A24C5BE42C361433CC.unitypackage

  • 초보개발자 2019.01.25 00:01 댓글주소 수정/삭제 댓글쓰기

    안녕하세요 아직 이 블로그를 운영하시는지는 잘 모르겟지만..
    질문남겨봅니다 : 0
    플러그인이랑 다 잘 임포트해서 문제없이 되는것 같은데요,,
    저기 하나가 문제있어서요..
    주소록 권한 읽기 허가 요청 <- 이부분에서
    there is no argument given that corresponds to the required formal parameter " result" 이런 에러메시지가 나와서요..
    추가로 더 해줘야하는 작업이 있나해서요..^^

    • 안녕하세요.
      아직 이 블로그를 운영하고 있습니다ㅎㅎ
      혹시 권한 읽기 허가 요청을 똑같이 구현하셨는지 아니면, 다른 방법으로 사용하셨는지 알고 싶네요.
      다른 방법으로 구현하셨다면 코드를 조금 공개해주시면 문제되는 부분을 알려드리는데 도움이 될 수 있지 않을까 싶은데요.

  • 초보개발자 2019.01.25 19:09 댓글주소 수정/삭제 댓글쓰기

    public void OnCheckPermissionRationale()
    {
    checker.CheckRationale(Permissions.READ_CONTACTS, (permission, isExplain) =>
    {
    if (!isExplain)
    { // 이곳에 설명할 UI 출력
    }
    else
    { // 설명이 필요 없으므로 권한 요청 구현
    }
    });
    }

    public void RequestPermission()
    {
    checker.RequestPermissions(Permissions.READ_CONTACTS, (permissionDatas) =>
    {
    if (permissionDatas != null)
    {
    if (permissionDatas[0].granted)
    { // 1개의 권한을 요청했기에 인덱스 0번 데이터만 비교 // 권한 허가됨 // 권한을 이용한 기능 구현 혹은 메서드 호출
    }
    else
    { // 권한 거부됨 // 권한이 거부되어 해당 권한이 필요한 기능을 사용할 수 없음을 사용자에게 알려주는 UI 출력
    }
    }
    else
    { // 권한 허가 요청 실패
    }
    });
    }

    이렇게 코딩하였는데요..

    checker.RequestPermissions 여기서 RequestPermissions 여기에 버그표시가 나면서 에러메시지는
    there is no argument given that corresponds to the required formal parameter 'result' of Permissions.RequsetPermissions~
    이렇게 나오고있습니당..

    제 생각엔 매개변수가 하나 빠진거 같은데.. 잘 모르겠어요 ㅜㅜ


    • Permissions.RequestPermissions (string[] permissions, int requestCode, Action<PermissionData[]> datas)

      인자는 스트링 형식의 퍼미션 배열, 요청과 결과가 일치하는지 확인할때 사용되는 요청코드, 그리고 결과값을 리턴받을 콜백함수를 입력해야하는데 제가 예제에 실수를 했네요.

      checker.RequestPermissions (new string[] { Permissions.READ_CONTACTS }, 0, (permissionDatas) => {
      // 예제와 같이 처리
      })

      이렇게 사용해야합니다.

      글은 조만간 수정해둘게요ㅠ

  • 초보개발자 2019.01.25 20:09 댓글주소 수정/삭제 댓글쓰기

    이렇게 좋은 정보 얻어가면서 작은 도움이 되어서 기쁩니다 : )