Marshmallow Runtime Permissions

One of the most talked about features of Android 6.0 (Marshmallow) was the finer permission controls, known as ‘Runtime Permissions’. Runtime Permissions allow the user to control whether a certain app has access to confidential data such as user’s contacts. Users can also take back permissions that they’ve already granted.

Permission Categories

Before Marshmallow, all permissions were automatically granted during install. If the user did not agree with a certain permission, they would have to stop using the app altogether. In Android 6.0, system permissions are divided into 2 categories: normal and dangerous.

Normal Permissions

Normal permissions pose no direct risk to the users’ privacy or confidential data. Examples of normal permissions are ACCESS_NETWORK_STATE,  INTERNET,  WAKE_LOCK, etc. When these permissions are declared in the app manifest, they’re automatically granted during install.

Dangerous Permissions

Dangerous permission protect the user’s privacy and confidential data. These permissions are not granted during install. Instead, the app has to make an explicit request at runtime, which is either accepted or declined by the user. Examples of dangerous permissions are: ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION, RECORD_AUDIO, READ_SMS, etc.

Challenges

The new permission model poses a few challenges to existing apps, some of which aren’t trivial. The challenges posed are: Changes in flow of control, external permission changes, and backwards compatibility.

Flow of Control

When the permission is automatically granted, the app can make a reasonable assumption that the permission will remain granted (exceptional cases could occur on rooted phones, but we won’t worry about them here). For example, let’s consider an Activity that displays the user’s contacts for the purpose of sending them an invite. Before Marshmallow, once the activity started, it could immediately start reading the contacts from the Contact provider. With the runtime permissions, this is no longer the case. What used to a be a sequential flow of control can now execute asynchronously. Linearity is preserved when the permission is currently granted, but by default the permissions are not granted. What previously executed immediately in your onCreate method now has to wait until you’ve secured the required permission(s).

External Permission Changes

Aside from being able to grant or deny permissions at runtime, users can also open the app details page and remove a previously granted permission (or grant a previously removed permission). This means that the user can pull the carpet from under you by pausing the app, changing permissions, and returning to the app. Your app needs to be able to handle this and similar scenarios.

Permission settings
Permissions can be revoked from settings

Backwards Compatibility

New features add methods and classes that aren’t available in previous versions of the platform. In order to make sure you don’t come across any NoSuchMethodExceptions, you need to use the Compatibility classes available in the support-v4 compatibility library:

The Process

Start by checking the current permission state for each permission that you need. Try to request each permission only when you need it so that you don’t scare the users (they’re frail beings). If the permission is already granted, continue on with accessing the data. If the permission isn’t currently granted there are a few possible scenarios:

  1. This is the first time the user is being asked for permission.
  2. The user has previously declined permission
  3. The user has previously declined permission and selected the ‘Never ask again’ checkbox.

Depending on which one of the above scenarios applies, you may be allowed to display a permission rationale dialog. The purpose of this dialog is to let the user know why you need these permissions. This is your chance to communicate to the user how their confidential data will be used. Scenarions 1 and 3 above will tell you that you shouldn’t display the rationale dialog because either the user has not yet denied you the permission OR the user has already made it clear that they don’t want to be asked again about this permission. You don’t need to keep track of these scenarios. Just call shouldShowRequestPermissionRationale and display the rationale dialog if it returns true otherwise just continue to requesting permission.

Permission rationale dialog
Explain to the user why you need to access their private data

At this point if the user has never been presented with the permission dialog or they’ve never selected the ‘Never ask again’ checkbox, they’re presented with a permission request dialog. Once the user clicks ‘Allow’ or ‘Deny’, the system calls your onRequestPermissionsResult method with the request result(s). If the user has previously selected the ‘Never ask again’ checkbox, the permission request dialog is not displayed. Instead your onRequestPermissionsResult method is called immediately indicating that your permission request was denied.

Permission request dialog
First time permission request dialog
Consequent permission request dialog
Consequent permission request dialog

Slides

Demo Code

The demo code can be found on github. Checkout the old_perms_model branch to see the original pre-Marshmallow code. The master branch (at HEAD) contains the same functionality, but updated for runtime permissions. The two branches are set up to use a different application ID and name so that they can be installed on the same device and easily distinguished.

Read more about runtime permissions on Android Developers reference.