You Cannot Hold Non-Existent Permissions

Stack Overflow user skirix ran into an interesting problem last week with respect to the Android 6.0 runtime permission system. Calling ActivityCompat.requestPermissions() was always returning that the permission was denied, when running on older devices.

It turns out that the permission was READ_EXTERNAL_STORAGE and the devices that ActivityCompat.requestPermissions() was failing on ran Android 4.0 or older.

The reason this does not work is simple: READ_EXTERNAL_STORAGE did not exist back then. It was added to the SDK in Android 4.1 (API Level 16).

ActivityCompat.requestPermissions() will use the real requestPermissions() implementation on Android 6.0+. On older devices, though, rather than hard-coding a response of PERMISSION_GRANTED, ActivityCompat.requestPermissions() will ask PackageManager to see if you hold the permission. That’s fine, but it does mean that you have to pay attention to when permissions you seek were added to Android. PackageManager will not know about READ_EXTERNAL_STORAGE on pre-Android 4.1 devices, and so you will be told that you do not have the permission… even though you do not need the permission back then.

In other words, there are limits to the backwards compatibility offered by ActivityCompat.requestPermissions(). The method can be called going back to API Level 4, but the results may vary based upon when that permission was added to the system.

In this case, since external storage certainly predated API Level 16, the right answer is to only bother calling ActivityCompat.requestPermissions() for READ_EXTERNAL_STORAGE on API Level 16+, and behave as though you have it on older devices. You will probably just handle this case when you call ContextCompat.checkSelfPermission() (which also will suffer from the same problem as ActivityCompat.requestPermissions()):

if (Build.VERSION.SDK_INT<Build.VERSION_CODES.JELLY_BEAN ||
  ContextCompat.checkSelfPermission(READ_EXTERNAL_STORAGE)==PackageManager.PERMISSION_GRANTED) {
  // you're good, go ahead
}
else {
  // do the request-permissions thing
}

This is just another one of those “gotcha” cases that we will run into over the next several months as we work out the application of the Android 6.0 runtime permissions to more and more scenarios.