My crash monitor (ACRA with Tracepot hosting) recently caught a crash saying something along those lines: “Permission Denial: opening provider com.android.providers.contacts.ContactsProvider2 […] requires android.permission.READ_CONTACTS or android.permission.WRITE_CONTACTS”.
Turns out the app wasn’t checking for permission before trying to read contacts and just assumed it was granted, causing a crash when it wasn’t upon calling context.getContentResolver().query( ContactsContract.CommonDataKinds.Phone.CONTENT_URI, PROJECTION, null, null, null);
This issue became obvious with Android 6, but it actually already exists in lower custom versions, provided they have an improve permission system (like CyanogenMod of OxygenOS) and that the user used it to remove the app’s read contact permission.
I found the core solution in this post on Stackoverflow, still I thought I’d share my slightly different implementation. The function to call is tryReadContact(), which will then handle the permission asking, etc
protected void myReadContactFunction() { // this is my function which needs to have READ_CONTACTS permission // I use a try catch so that it can always run (if permission denied, it returns an empty result instead of crashing) MapcontactsMap = new HashMap<>(); ContentResolver cr = c.getContentResolver(); Cursor cursor=null; try { cursor = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, PROJECTION, null, null, null); } catch(Exception e) { // we land here if no permission to read contacts Log.v("myReadContactFunction","reading phone contacts denied"); return contactsMap; } if (cursor != null) { // do read contacts } return contactsMap; } protected void tryReadContact() { if(ActivityCompat.checkSelfPermission(this,Manifest.permission.READ_CONTACTS)==PackageManager.PERMISSION_DENIED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS}, READ_CONTACT_REQUEST_CODE); } else { myReadContactFunction(); } } @Override public void onRequestPermissionsResult(int requestCode,@NonNull String[] permissions,@NonNull int[] grantResults) { // NB: the switch is here to demonstrate support for multiple types of permission requests switch (requestCode){ case READ_CONTACT_REQUEST_CODE: if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { Log.v("PeopleListActivity", "Contact permission has now been granted."); } else { Log.v("PeopleListActivity", "Contact permission was NOT granted."); } // in both cases we call myReadContactFunction, since the function reads more than phone contacts and can deal with non-granted permissions myReadContactFunction(); break; } }
The camera permissions are annoying too now. Got this error “android M java.lang.SecurityException: Permission Denial: starting Intent […] android.media.action.IMAGE_CAPTURE […] with revoked permission android.permission.CAMERA”.
We could just do the very same thing as above, but the Android documentation recommends to use intents rather than asking for permission whenever it is appropriate, and it was the case for me there. In such case, you must NOT declare the permission related to the intent, or if you do then you still have to request the permission: “if you app targets M and above and declares as using the CAMERA permission which is not granted, then attempting to use [ACTION_IMAGE_CAPTURE] will result in a SecurityException” (source ACTION_IMAGE_CAPTURE documentation). The reason for this makes sense, this is to avoid user confusion on an app appearing to be able to use the camera even after being denied the camera permission (source).
So basically, in my case all I had to do was to remove <uses-permission android:name="android.permission.CAMERA"/>
from AndroidManifest.xml, and just call my new Intent(MediaStore.ACTION_IMAGE_CAPTURE)
as I did before.
0 Responses
Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.