Android 6.0 (Marshmallow) READ_CONTACTS permission

2019-04-28 20:39发布

问题:

I want to check how new permission model works so in app's settings I disable Contacts. Then I go to app and try to read Contacts and ... it kinda works:

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
}
  • I'm able to read Contact's Name
  • when I try to read Contact's Number SecurityException is thrown

java.lang.SecurityException: Permission Denial: reading com.android.providers.contacts.HtcContactsProvider2 uri content://com.android.contacts/data/phones from pid=20123, uid=10593 requires android.permission.READ_CONTACTS, or grantUriPermission()

Why is that?

Related stuff: Contact data leakage via pick activities

回答1:

Android marshmallow have new permissions system. You need to request permissions at run time. http://developer.android.com/training/permissions/requesting.html.

Example:

@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
    }
}


回答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
        }
    }