Android 6.0 SecurityException: Permission Denial

在写一个群发短信APP时涉及到获取系统的联系人信息,添加联系人的事件为下:

mBtAdd.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
                startActivityForResult(intent,CODE_REQUEST);
            }
        });

重写onActivityResult:

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == CODE_REQUEST) {
            if (resultCode == RESULT_OK) {
                    Uri contactUri = data.getData();
                    Cursor cursor = getContentResolver().query(contactUri, null, null, null, null);
                    cursor.moveToFirst();
                    String contactName = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
                    String number = getContactNumber(cursor);
                    if (!TextUtils.isEmpty(number)) {
                        mContactNums.add(number);
                        mContactNames.add(contactName);
                        addTag(contactName);
                    }              
            }
        }
    }

运行,选择Android 6.0的AVD,错误如下:

java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1, result=-1, data=Intent { dat=content://com.android.contacts/contacts/lookup/0r1-BFADB1C1/1 flg=0x1 }} to activity {com.example.festival_sms/com.example.festival_sms.SendMsgActivity}: java.lang.SecurityException: Permission Denial: reading com.android.providers.contacts.ContactsProvider2 uri content://com.android.contacts/data/phones from pid=2377, uid=10064 requires android.permission.READ_CONTACTS, or grantUriPermission()
                                                                            at android.app.ActivityThread.deliverResults(ActivityThread.java:3699)
                                                                            at android.app.ActivityThread.handleSendResult(ActivityThread.java:3742)
                                                                            at android.app.ActivityThread.-wrap16(ActivityThread.java)
                                                                            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1393)
                                                                            at android.os.Handler.dispatchMessage(Handler.java:102)
                                                                            at android.os.Looper.loop(Looper.java:148)
                                                                            at android.app.ActivityThread.main(ActivityThread.java:5417)
                                                                            at java.lang.reflect.Method.invoke(Native Method)
                                                                            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                                                                            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
                                                                         Caused by: java.lang.SecurityException: Permission Denial: reading com.android.providers.contacts.ContactsProvider2 uri content://com.android.contacts/data/phones from pid=2377, uid=10064 requires android.permission.READ_CONTACTS, or grantUriPermission()
                                                                            at android.os.Parcel.readException(Parcel.java:1599)
                                                                            at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:183)
                                                                            at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:135)
                                                                            at android.content.ContentProviderProxy.query(ContentProviderNative.java:421)
                                                                            at android.content.ContentResolver.query(ContentResolver.java:491)
                                                                            at android.content.ContentResolver.query(ContentResolver.java:434)
                                                                            at com.example.festival_sms.SendMsgActivity.getContactNumber(SendMsgActivity.java:205)
                                                                            at com.example.festival_sms.SendMsgActivity.onActivityResult(SendMsgActivity.java:180)
                                                                            at android.app.Activity.dispatchActivityResult(Activity.java:6428)
                                                                            at android.app.ActivityThread.deliverResults(ActivityThread.java:3695)
                                                                            at android.app.ActivityThread.handleSendResult(ActivityThread.java:3742) 
                                                                            at android.app.ActivityThread.-wrap16(ActivityThread.java) 
                                                                            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1393) 
                                                                            at android.os.Handler.dispatchMessage(Handler.java:102) 
                                                                            at android.os.Looper.loop(Looper.java:148) 
                                                                            at android.app.ActivityThread.main(ActivityThread.java:5417) 
                                                                            at java.lang.reflect.Method.invoke(Native Method) 
                                                                            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
                                                                            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 

关键错误:

Caused by: java.lang.SecurityException: Permission Denial

检查AndroidManifest.xml:

<uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.WRITE_CONTACTS" />
    <uses-permission android:name="android.permission.SEND_SMS" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />

分析:
虽然在清单文件中已经定义了所需的权限,但是系统提示该权限为denied,想起之前看过某篇文章在Android 6.0 将权限分类处理。

为了更加一步确定我的错误原因在此,换用低于 6.0 的Android系统重新运行,运行成功,没再出现这个错误。

由此说明这确实是Android 6.0 权限管理的改动引发的问题,然后我查阅相关资料,以下为关键信息:

app的安装与获取权限的变化

对于6.0以下: 在安装的时候,根据权限声明产生一个权限列表,用户只有在同意之后才能完成app的安装,造成了我们想要使用某个app,就要默默忍受其一些不必要的权限。

而在6.0以后: 我们可以直接安装,当app需要我们授予不恰当的权限的时候,我们可以予以拒绝,也可以在设置界面对每个app的权限进行查看,以及对单个权限进行授权或者解除授权。

Android 6.0 权限分类

在Android 6.0中,权限被分类为正常权限和危险权限:

  • Normal Permissions

    需要访问的数据一般不涉及用户的隐私,并且对其他应用程序风险很小。例如,允许打开手电筒是正常权限。如果一个应用程序声明它需要一个正常的权限,系统会自动授予应用程序的权限。

  • Dangerous Permission

    需要数据或资源涉及到用户的私人信息,或可能会影响用户的存储数据或其他应用程序的操作。例如,读取用户的联系人是一个危险的权限。如果一个应用程序声明它需要一个危险的权限,用户必须显式地授予应用程序的权限。

关于read_contacts

因为read_contacts是一个危险的许可,我们需要请求用户在运行时授予它权限。那么如何请求用户给予我们的应用程序的明确权限呢?在我目前遇到这个问题之上改正的步骤如下:

对危险权限的处理步骤

  1. 添加一个新的实例变量permissions_request_read_contacts

    这个变量用作申请用户授权权限时的匹配码,用于匹配onRequestPermissionsResult方法中的对权限的操作。
    
  2. 将对涉及到read_contacts权限的操作封装到一个方法中,然后对该方法添加if 条件,条件主要是为了判断当前系统是否为Android 6.0 以及 是否授予权限 ,修改后如下:

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == CODE_REQUEST) { if (resultCode == RESULT_OK) { //如果当前版本大于等于Android 6.0,且该权限未被授予,则申请授权 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) { //申请授权,第一个参数为要申请用户授权的权限;第二个参数为requestCode 必须大于等于0,主要用于回调的时候检测,匹配特定的onRequestPermissionsResult。 //可以从方法名requestPermissions以及第二个参数看出,是支持一次性申请多个权限的,系统会通过对话框逐一询问用户是否授权。 requestPermissions(new String[]{Manifest.permission.READ_CONTACTS}, PERMISSIONS_REQUEST_READ_CONTACTS);
    
                }else{
                    //如果该版本低于6.0,或者该权限已被授予,它则可以继续读取联系人。
                    mIntent = data;
                    getContacts(data);
                }
            }
        }
    }

    3.重写onRequestPermissionsResult 方法 ,请求用户授予权限

@Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == PERMISSIONS_REQUEST_READ_CONTACTS) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 用户成功授予权限
                getContacts(mIntent);
            } else {
                Toast.makeText(this, "你拒绝了此应用对读取联系人权限的申请!", Toast.LENGTH_SHORT).show();
            }
        }
    }

再次运行,问题解决!
Android 6.0 SecurityException: Permission Denial_第1张图片

你可能感兴趣的:(android)