Android 6.0 (Marshmallow) READ_CONTACTS 权限允许在权限被拒绝时读取联系人的姓名

Posted

技术标签:

【中文标题】Android 6.0 (Marshmallow) READ_CONTACTS 权限允许在权限被拒绝时读取联系人的姓名【英文标题】:Android 6.0 (Marshmallow) READ_CONTACTS permission allows to read Contact's name when permission is denied 【发布时间】:2016-06-05 00:05:47 【问题描述】:

我想检查新的权限模型是如何工作的,所以在应用程序的设置中我禁用了Contacts。然后我去应用程序并尝试阅读Contacts 并且......它有点工作:

try 
    Uri result = data.getData();
    int contentIdx;
    cursor = getContentResolver().query(result, null, null, null, null);
    contentIdx = cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER);
    if(cursor.moveToFirst()) 
        content = cursor.getInt(contentIdx);
    

    if(content > 0) 
        contentIdx = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
        if(cursor.moveToFirst()) 
            name = cursor.getString(contentIdx);
        
        contentIdx = cursor.getColumnIndex(BaseColumns._ID);
        if(cursor.moveToFirst()) 
            content = cursor.getLong(contentIdx);
        
        cursor = managedQuery(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, new String[]  Phone.NUMBER , Data.CONTACT_ID + "=?", new String[]  String.valueOf(content) , null);
        if(cursor.moveToFirst()) 
            number = cursor.getString(cursor.getColumnIndex(Phone.NUMBER));
        
    
 catch (Exception e) 
    //SecurityException

我可以读取联系人姓名 当我尝试读取联系人号码时,SecurityException 被抛出

java.lang.SecurityException: Permission Denial: 从 pid=20123 读取 com.android.providers.contacts.HtcContactsProvider2 uri content://com.android.contacts/data/phones,uid=10593 需要 android.permission.READ_CONTACTS , 或 grantUriPermission()

为什么会这样?

相关资料:Contact data leakage via pick activities

【问题讨论】:

请注意,您没有使用谷歌的 ContactsContract,如您所见,我们看到您正在访问 com.android.providers.contacts.HtcContractsProvider,可能 HTC 存在验证权限问题。我的建议是检查 AOSP 设备,看看是否同样如此。如果它不是真的,那么这意味着它是 HTC 内部的权限泄漏问题。如果它们相同,则听起来像是 AOSP 问题。 它也在 Nexus 上重现。 【参考方案1】:

Android marshmallow 有新的权限系统。 您需要在运行时请求权限。 http://developer.android.com/training/permissions/requesting.html.

例子:

@Override
public void onClick(View v) 
    switch (v.getId())
        case R.id.tv_contact:
            askForContactPermission();
            break;
        
    
 

private void getContact()
    Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
    startActivityForResult(intent, PICK_CONTACT);


 public void askForContactPermission()
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) 
        if (ContextCompat.checkSelfPermission(getActivity(),Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) 

            // Should we show an explanation?
            if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(),
                    Manifest.permission.READ_CONTACTS)) 
                AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
                builder.setTitle("Contacts access needed");
                builder.setPositiveButton(android.R.string.ok, null);
                builder.setMessage("please confirm Contacts access");//TODO put real question
                builder.setOnDismissListener(new DialogInterface.OnDismissListener() 
                    @TargetApi(Build.VERSION_CODES.M)
                    @Override
                    public void onDismiss(DialogInterface dialog) 
                        requestPermissions(
                                new String[]
                                        Manifest.permission.READ_CONTACTS
                                , PERMISSION_REQUEST_CONTACT);
                    
                );
                builder.show();
                // 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 

                // No explanation needed, we can request the permission.

                ActivityCompat.requestPermissions(getActivity(),
                        new String[]Manifest.permission.READ_CONTACTS,
                        PERMISSION_REQUEST_CONTACT);

                // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
                // app-defined int constant. The callback method gets the
                // result of the request.
            
        else
            getContact();
        
    
    else
        getContact();
    


@Override
public void onRequestPermissionsResult(int requestCode,
                                       String permissions[], int[] grantResults) 
    switch (requestCode) 
        case PERMISSION_REQUEST_CONTACT: 
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) 
                getContact();
                // permission was granted, yay! Do the
                // contacts-related task you need to do.

             else 
                ToastMaster.showMessage(getActivity(),"No permission for contacts");
                // permission denied, boo! Disable the
                // functionality that depends on this permission.
            
            return;
        

        // other 'case' lines to check for other
        // permissions this app might request
    

【讨论】:

// Show an expanation ... 注释后面应该加一些代码吗? 我可以用 HTML5 和 javascript 做这个吗??【参考方案2】:
@Override
    public void onClick(View v) 
        switch (v.getId())
            case R.id.button:
                askForContactPermission();
                break;
            
        
    

    private void getContact()
        Cursor phones = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,null,null, null);
        while (phones.moveToNext())
        
            String name=phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
            String phoneNumber = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
            Toast.makeText(getApplicationContext(),name+" "+phoneNumber, Toast.LENGTH_LONG).show();

        
        phones.close();
    

    public void askForContactPermission()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) 
            if (ContextCompat.checkSelfPermission(this,Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) 

                // Should we show an explanation?
                if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                        Manifest.permission.READ_CONTACTS)) 
                    AlertDialog.Builder builder = new AlertDialog.Builder(this);
                    builder.setTitle("Contacts access needed");
                    builder.setPositiveButton(android.R.string.ok, null);
                    builder.setMessage("please confirm Contacts access");//TODO put real question
                    builder.setOnDismissListener(new DialogInterface.OnDismissListener() 
                        @TargetApi(Build.VERSION_CODES.M)
                        @Override
                        public void onDismiss(DialogInterface dialog) 
                            requestPermissions(
                                    new String[]
                                            Manifest.permission.READ_CONTACTS
                                    , PERMISSION_REQUEST_CONTACT);
                        
                    );
                    builder.show();
                    // 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 

                    // No explanation needed, we can request the permission.

                    ActivityCompat.requestPermissions(this,
                            new String[]Manifest.permission.READ_CONTACTS,
                            PERMISSION_REQUEST_CONTACT);

                    // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
                    // app-defined int constant. The callback method gets the
                    // result of the request.
                
            else
                getContact();
            
        
        else
            getContact();
        
    

    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           String permissions[], int[] grantResults) 
        switch (requestCode) 
            case PERMISSION_REQUEST_CONTACT: 
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) 
                    getContact();
                    // permission was granted, yay! Do the
                    // contacts-related task you need to do.

                 else 
                    Toast.makeText(this, "No Permissions ", Toast.LENGTH_SHORT).show();
                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.
                
                return;
            

            // other 'case' lines to check for other
            // permissions this app might request
        
    

【讨论】:

总是去 "Toast.makeText(this, "No Permissions ", Toast.LENGTH_SHORT).show();"并且不要询问权限弹出窗口 我可以用 HTML5 和 javascript 做这个吗??

以上是关于Android 6.0 (Marshmallow) READ_CONTACTS 权限允许在权限被拒绝时读取联系人的姓名的主要内容,如果未能解决你的问题,请参考以下文章

Android 6.0 (MarshMallow) 上的 HttpURLConnnection 请求失败

如何在 Android 6.0 Marshmallow 中访问相机?

如何以编程方式打开 Android 6.0 (Marshmallow) 上特定应用的权限屏幕?

Android 6.0 Marshmallow BLE 连接问题

何时在运行时请求 Android Marshmallow 6.0 的权限?

在 Android 6.0 Marshmallow (API 23) 上不推荐使用 getColor(int id)