ANDROID/Android 앱 프로그래밍

[Android] 위험 권한(Permission) 부여하기

주 녕 2021. 6. 15. 14:06
반응형

모든 내용은 Do it! 안드로이드 앱 프로그래밍을 바탕으로 정리한 것입니다. 

 

위험 권한

대부분 주요 권한들은 개인 정보가 담겨 있는 정보에 접근하거나 개인정보를 만들어 낼 수 있는 단말의 주요 장치에 접근할 때 부여됨

매니페스트에 넣어준 권한은 앱을 설치할 때 사용자가 허용하면 한꺼번에 권한이 부여되는데 

마시멜로(API 23)부터는 중요한 권한들을 분류하여 설치 시점이 아니라 앱을 실행했을 때 사용자로부터 권한을 부여받도록 변경

 

일반 권한과 위험 권한

*일반 권한(Normal Permission) - 앱을 설치할 때 사용자에게 권한이 부여되어야 함을 알려주고 설치할 것인지 물어봄

 사용자가 부여할 권한들을 보고 수락하면 앱이 설치되고 앱에는 해당 권한들이 부여됨

*위험 권한(Dangerous Permission) - 앱 실행 시 사용자에게 권한을 부여할 것인지 물어봄

→ 사용자가 권한을 부여하지 않으면 해당 기능은 동작하지 않음

→ 앱을 설치했다고 하더라도 권한에 따라 실행할 수 있는 기능에 제약이 생기는 것

 

위험 권한의 세부 권한

위험 권한 그룹 세부 권한
LOCATION
  • ACCESS_FINE_LOCATION : Allows an app to access precise location.
  • ACCESS_COARSE_LOCATION : Allows an app to access approximate location.
CAMERA
  • CAMERA : Required to be able to access the camera device.
MICROPHONE
  • RECORED_AUDIO : Allows an application to record audio.
CONTACTS
  • READ_CONTACTS : Allows an application to read the user's contacts data.
  • WRITE_CONTACTS : Allows an application to write the user's contacts data.
  • GET_ACCOUNTS : Allows access to the list of accounts in the Accounts Service.
PHONE
  • READ_PHONE_STATE : Allows read only access to phone state, including the current cellular network information, the status of any ongoing calls, and a list of any PhoneAccounts registered on the device.
  • CALL_PHONE : Allows an application to initiate a phone call without going through the Dialer user interface for the user to confirm the call.
  • READ_CALL_LOG : Allows an application to read the user's call log.
  • WRITE_CALL_LOG : Allows an application to write (but not read) the user's call log data.
  • ADD_VOICEMAIL : Allows an application to add voicemails into the system.
  • USE_SIP : Allows an application to use SIP service.
  • PROCESS_OUTGOING_CALLS : This constant was deprecated in API level 29. Applications should use CallRedirectionService instead of the Intent.ACTION_NEW_OUTGOING_CALL broadcast.
SMS
  • SEND_SMS : Allows an application to send SMS messages.
  • RECEIVE_SMS : Allows an application to receive SMS messages.
  • READ_SMS : Allows an application to read SMS messages.
  • RECEIVE_WAP_PUSH : Allows an application to receive WAP push messages.
  • RECEIVE_MMS : Allows an application to monitor incoming MMS messages.
CALENDAR
  • READ_CALENDAR : Allows an application to read the user's calendar data.
  • WRITE_CALENDAR : Allows an application to write the user's calendar data.
SENSORS
  • BODY_SENSORS : Allows an application to access data from sensors that the user uses to measure what is happening inside their body, such as heart rate
STORAGE
  • READ_EXTERNAL_STORAGE : Allows an application to read from external storage.
  • WRITE_EXTERNAL_STORAGE : Allows an application to write to external storage

안드로이드 공식문서_Manifest.permission

* 과거에는 build.gradle(Module:app) 파일의 targetSdkVersion 값을 23 미만으로 설정하면 API 23 버전 이후의 플랫폼에서 검증된 앱이 아니라고 인식하여 위험 권한도 자동으로 부여되었음. 하지만 현재는 자동 부여하는 방식은 더 이상 사용할 수 없음

 

 


 

위험 권한 부여하기

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.june.permissiontest">
    
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <application
        ...
    </application>

</manifest>
  • 기본 권한을 부여할 때는 <uses-permission> 태그를 사용함
  • 기본 권한 중에서 SD 카드를 접근하는 권한은 위험 권한이므로 코드로 추가해야 함

 

MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // 위험 권한을 부여할 권한 지정
        String[] permissions = {
                Manifest.permission.READ_EXTERNAL_STORAGE,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
        };
        
        checkPermissions(permissions);
    }
    
    public void checkPermissions(String[] permissions) {
        ArrayList<String> targetList = new ArrayList<String>();
        
        for (int i = 0; i < permissions.length; i++) {
            String curPermission = permissions[i];
            int permissionCheck = ContextCompat.checkSelfPermission(this, curPermission);
            if (permissionCheck == PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(this, curPermission + " 권한 있음", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, curPermission + " 권한 없음", Toast.LENGTH_SHORT).show();
                if (ActivityCompat.shouldShowRequestPermissionRationale(this, curPermission)) {
                    Toast.makeText(this, curPermission + " 권한 설명 필요함.", Toast.LENGTH_SHORT).show();
                } else {
                    targetList.add(curPermission);
                }
            }
        }
        
        String[] targets = new String[targetList.size()];
        targetList.toArray(targets);
        
        // 위험 권한 부여 요청
        ActivityCompat.requestPermissions(this, targets, 101);
    }
}
  • SD 카드의 읽고 쓰는 권한은 위험 권한이므로 다이얼로그로 띄워야 함
  • onCreate() 메서드에서 checkPermissions() 메서드를 호출하는 코드로 작성함
    • checkPermissions() 에서 정해준 권한들에 대해 그 권한이 부여되어 있는지 먼저 확인함
    • 권한이 부여되어 있지 않다면 ArrayList(targetList) 안에 넣었다가 부여되지 않은 권한들만 권한 요청을 함
    • requestPermissions() 메서드를 호출하면 시스템에서 다이얼로그를 띄워주기 때문에 권한 수락/거절 여부를 콜백 메서드로 받아 확인하는 것이 필요함
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        
        switch (requestCode) {
            case 101:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Toast.makeText(this, "첫 번째 권한을 사용자가 승인함", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(this, "첫 번째 권한 거부됨", Toast.LENGTH_SHORT).show();
                }
                
                return;
        }
    }
}
  • onRequestPermissionsResult() 메서드에는 요청 코드와 함께 사용자가 권한을 수락했는지 여부가 파라미터로 전달됨
  • 여러 권한을 한 번에 요청할 수 있기 때문에 grantResults 배열 변수 안에 수락 여부를 넣어 전달
    • 앞에서 2개의 위험 권한을 요청했으므로 grantResults 배열의 길이는 2
    • 사용자가 권한을 수락했다면 PackageManager.PERMISSION_GRANTED 상수가 결과 값으로 저장됨

 

앱을 처음 실행했을 때와 권한 수락 후 앱을 실행했을 때.gif

 

 

❗ [오류] Doit 안드로이드 프로그래밍 7판 380pg-383pg

 

책에 있는 코드를 그대로 작성한 상태로 위험 권한을 Allow하고 다시 앱에 들어가 보니 이런 오류가 발생했다.

java.lang.IllegalArgumentException: permission cannot be null or empty

오류는 아래 드래그한 부분에서 난 것 같고, 현재 permission은 null이 될 수 없다고 하니, 아무래도 이전에 권한 수락을 해서 더 이상 권한이 없는데 다시 요청을 하라고 해서 발생한 오류인 것 같다. 그래서 코드는 현재 요청할 권한이 있는지 배열의 크기를 체크를 하는 코드로 변경하였다.

 

 

 

외부 라이브러리를 이용한 위험 권한 자동 부여하기

build.gradle(Module:app)

Sync Now를 눌러 외부 라이브러리를 다운받음

allprojects {
        repositories {
            maven { url 'https://jitpack.io' }
        }
    }
}

dependencies {
    ...
    implementation 'com.github.pedroSG94:AutoPermissions:1.0.3'
}

 

MainActivity.java

public class MainActivity extends AppCompatActivity implements AutoPermissionsListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        AutoPermissions.Companion.loadAllPermissions(this, 101);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        AutoPermissions.Companion.parsePermissions(this, requestCode, permissions, this);
    }

    @Override
    public void onDenied(int i, String[] permissions) {
        Toast.makeText(this, "permissions denied : " + permissions.length, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onGranted(int i, String[] permissions) {
        Toast.makeText(this, "permissions granted : " + permissions.length, Toast.LENGTH_SHORT).show();
    }
}
  • AndroidManifest.xml에 넣은 권한 중 위험 권한을 자동으로 체크한 후 권한 부여를 요청하는 방식
    • onCreate() 메서드 안에서 loadAllPermissions() 메서드를 호출하면서 자동으로 권한을 부여하도록 요청함
    • 권한 부여 요청 결과가 onRequestPermissionResult() 메서드로 전달됨
      • 이 메서드 안에서 parsePermissions() 메서드를 호출하여 권한 부여 결과가 승인/거부로 나뉘면서 onGranted()onDenied() 메서드를 호출함
      • 현재 코드에서는 승인된 권한의 개수와 거부된 권한의 개수를 각각 토스트 메세지로 띄워주고 있음

 

권한을 거절(Deny)한 경우, 오른쪽 사진과 같이 다시 한번 물어보는 다이얼로그가 생성된다.

이때 Deny&don't ask again 버튼이 생성되는 것을 확인할 수 있다.

 

하지만 첫번째 예시에서는 거절하고 다시 앱을 실행했을 때, 다시 권한 요청을 하는 다이얼로그가 생성되지 않고 거절된 권한이라고 토스트 메세지가 뜬다. 해당 코드를 고치기 위해서 조건문을 수정했다.

 

 

 

 

반응형