SecurityExceptions, Runtime Permissions, and "Reset app preferences"
Maksim Dmitriev, in a Stack Overflow question, pointed out an obscure but unpleasant bug in Android 6.0 and its runtime permission system.
Most of the time, you will handle permissions yourself within your app:
requestPermissions(), dialogs appear, users grant permissions,
and everyone walks away happy.
Users can go into Settings > Apps > (your app name) > Permissions and
revoke permissions that the user previously granted. However, when this
happens, Android terminates your process. This will force you to call
checkSelfPermission() again, when your code next runs, and you will
find out about the lost permission.
However, there is also Settings > Apps > “Reset app preferences”, where “Reset app preferences” is found in the action bar overflow. Tapping that brings up a dialog that tells the user about wide-ranging effects of resetting app preferences, such as re-enabling disabled apps. One of those effects is to revoke all granted permissions. If the user proceeds with the operation, your app loses its permissions.
However, in this case, Android does not terminate your process.
As a result, if you call some protected method after “Reset app
preferences”, relying on some previous call to
you will fail with a
SecurityException or similar sort of error.
This is not good.
The one saving grace is that “Reset app preferences” is obscure and comes with a fairly scary-looking warning dialog. Few, if any, of your users will elect to reset those preferences. And, while Android will not terminate your process due to those reset permissions, it’s entirely possible that your process will die of “natural causes” while it is in the background anyway.
Personally, while this particular problem should be addressed in Android, it’s not the sort of thing that will keep me awake nights worrying about at the SDK level. However, it is something you should keep in the back of your mind.
If you really want to try to minimize the risk, use
at two levels in your app:
Check if you hold the permission at the point in time where you would need to call
requestPermissions()to get the permission from the user. This could be anywhere from on first run of your app to when the user taps some action bar item that triggers work that will need a
Check right before you call APIs that require that
dangerouspermission. Or, wrap those APIs in
catchblocks to catch
SecurityException, though it’s not guaranteed that
SecurityExceptionwill be the specific exception thrown from all such APIs. In these cases, you know that your permission was revoked behind your back, and you can treat it as you would other sorts of error cases (e.g.,
IOExceptionwhen you can’t reach the REST server) that your app encounters.
Want an expert opinion on your Android app architecture decisions? Perhaps Mark Murphy can help!