티스토리 뷰

지금 현재 개발중인 부분은 글쓰기 창에서 카메라 버튼을 클릭했을때 카메라 앱이 실행되게끔 하는 부분이다. 

암시적 인텐트와 파일 프로바이더 및 Content uri 개념등등을 공부하고 실제 구현에 들어갔다.


그런데 개발 도중 다음과 같은 에러메세지가 발생하였다.



정확한 에러 메시지명 : java.lang.SecurityException: Permission Denial: starting Intent { act=android.media.action.IMAGE_CAPTURE flg=0x3 cmp=com.sec.android.app.camera/.Camera launchParam=MultiScreenLaunchParams { mDisplayId=0 mFlags=0 } clip={text/uri-list U:content://com.sharewith.smartudy.fileprovider/my_images/20180723_100437.png} (has extras) } from ProcessRecord{2379257 28710:com.sharewith.smartudy/u0a290} (pid=28710, uid=10290) with revoked permission android.permission.CAMERA


원인을 검색해본 결과 안드로이드 M(안드로이드 6.0)이라고 불리우는 마시멜로 SDK 23이상 버전 부터는 구글 플레이스토어에서 앱을 다운받을때 사용자에게 권한을 요청하는것이 아니라, 실행중에 권한이 필요하면 동적으로 요청하게끔 변경 되었다고 한다. 이렇게 만든 이유는 앱 설치를 좀 더 빠르고 간편하게 만들기 위해서라고 한다.


내 핸드폰은 갤럭시 S7 엣지(안드로이드 7.0)를 사용중인데 이 갤럭시가 마침 안드로이드 마시멜로 이상의 버전을 사용중이라고 한다. 내 폰이 이것보다 더 낮은 버전이었다면 이 에러를 발견하지 못했을 수도 있는데 다행이라는 생각도 든다.


물론 마시멜로 이상 버전인 누가버전에서도 이 변경사항이 적용된다.


스택 오버플로우 및 안드로이드 공홈에서 원인을 분석해본 결과 실행중에 동적으로 권한을 요청하는 코드에 대해서 알 수 있게 되었다.


https://developer.android.com/training/permissions/requesting 


위의 링크를 따라가보자. 요약을 해보면,

안드로이드의 권한은 개인정보 유출 가능성에 따라서 정상 권한과 위험 권한 두가지로 나뉜다. 위험 권한의 경우 매니페스트 파일에 권한을 요청해야하는것은 당연하고, 실행중에 동적으로 사용자에게 권한 사용을 허락 받아야 한다. 정상 권한의 경우 매니페스트에 적기만 해도 시스템이 자동적으로 그 권한을 부여 하게 된다.


1
2
3
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.CAMERA"/>
cs


위 처럼 매니페스트 파일에 권한을 명시해야 카메라 권한을 획득의 첫단계를 진행할수있다. 카메라 권한은 개인정보 누출 위험이 있으므로 위험권한으로 분류된것같다.


안드로이드 5.1이하 (SDK 22이하) 버전에서는 앱을 설치할때 권한을 부여 한다.

안드로이드 6.0이상 (SDK 23이상) 버전에서는 앱이 실행되는 도중에 위험 권한을 요청해야 하고, 사용자가 위험 권한을 거부하더라도 앱이 제한된 성능으로 계속 구동될 수 있다.


가장 중요한것은 메인 액티비티 (맨처음 실행되는 액티비티)에서 권한을 체크해야 하는 점이다.


앱이 처음 실행될때마다 이 앱에서 필요한 권한들이 현재 사용자의 기기에 부여 되었는지를 검사하고 없으면 요구하는 식으로 개발해야 한다.


// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
       
Manifest.permission.WRITE_CALENDAR);

앱에 권한이 있는 경우 이 메서드는 PackageManager.PERMISSION_GRANTED를 반환하고, 앱이 작업을 계속 진행할 수 있습니다. 앱에 권한이 없는 경우 이 메서드는 PERMISSION_DENIED를 반환하고, 앱이 사용자에게 명시적으로 권한을 요청해야 합니다.


ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,Manifest.permission.READ_CONTACTS);

안드로이드에서는 위와 같은 메소드를 통해서 권한 요구를 해야할지 말아야할지를 결정한다.


위와 같은 화면을 본적이 있을것이다. 만약 이때 사용자가 Dont ask me again을 체크하고 권한을 DENY하게 되면 저 메소드는 false를 리턴한다. 즉, 다시 권한 요구를 할 필요가 없다는 의미이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
                Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) {
 //권한이 부여되면 PERMISSION_GRANTED 거부되면 PERMISSION_DENIED 리턴

//권한 요청 할 필요가 있는가?
    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
            Manifest.permission.READ_CONTACTS)) {
 
        // Show an expanation to the user *asynchronously* -- don't block
        // this thread waiting for the user's response! After the user
        // sees the explanation, try again to request the permission.
 
    } else {
  //권한 요청을 해야할 필요가 있는 경우(사용자가 DONT ASK ME AGIAN CHECK + DENY 선택)
 
        ActivityCompat.requestPermissions(thisActivity,
                new String[]{Manifest.permission.READ_CONTACTS},
                MY_PERMISSIONS_REQUEST_READ_CONTACTS);
 
//requestPermissions 메소드는 비동기적으로 동작한다. 왜냐면 이 권한 검사 및 요청 메소드는
//메인 액티비티에서 동작하기떄문에(메인쓰레드) 사용자 반응성이 굉장히 중요한 파트이다. 여기서 시간을
//오래 끌어버리면 사람들이 답답함을 느끼게 된다. requestPermissions의 결과로 콜백 메소드인
//onRequestPermissionsResult()가 호출된다. 오버라이딩 메소드이다. Ctrl+O

        // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
        // app-defined int constant. The callback method gets the
        // result of the request.
    }
}


cs



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Override
public void onRequestPermissionsResult(int requestCode,
        String permissions[], int[] grantResults) {
    switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0
                && grantResults[0== PackageManager.PERMISSION_GRANTED) {
 
                // permission was granted, yay! Do the
                // contacts-related task you need to do.
 
            } else {
 
                // permission denied, boo! Disable the
                // functionality that depends on this permission.
            }
            return;
        }
 
        // other 'case' lines to check for other
        // permissions this app might request
    }
}
cs


권한 요청 코드에 따라 내가 요청한 권한이 적절히 승인 되었는가 거절되었는가에 따라 코드를 작성하면 된다.


예를 들어, 여러분이 READ_CONTACTS 및 WRITE_CONTACTS를 둘 다 앱 매니페스트에 나열한다고 가정해 봅시다. 여러분이 READ_CONTACTS를 요청하고 사용자가 권한을 부여했는데 여러분이 다시 WRITE_CONTACTS를 요청하면, 시스템은 사용자와의 상호작용 없이 즉시 여러분에게 해당 권한을 부여합니다.

즉, 같은 권한 그룹에 속한 그룹에 대해서 사용자가 권한을 부여 했다면 그 그룹에 속한 다른 권한에 대해서는 사용자에게 한번더 묻지 않고 바로 권한이 부여된다는 얘기 같다.





이제 정상적으로 앱이 처음 실행될때 권한을 요구하는것을 볼 수 있다 허용을 누를경우 정상적으로 카메라 앱이 실행된다!!

댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함