An Android application (app, for short) runs in its own sandbox, and only has the authority to access its own resources. When it needs to use resources outside of its own sandbox, it must request the appropriate permissions. Before Android 5.1, all the permissions needed are requested when the app is installed. However, the user grants permissions at runtime after Android 6.0, which brings about some changes about the permission acquisition process. This post briefly introduces Android permission system and gives the solutions for different use cases.

Declare the Permissions Needed

An app needs permission if it wants to use some resources that doesn’t belong to it. For example, an app must have the READ_CONTACTS permission before it can read the contacts to find the phone numbers.

To declare the permissions an app needs, use <uses-permission> tag in Manifest file:

1
2
3
4
5
6
7
8
9
10
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.testapp">
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<application ...>
...
</application>
</manifest>

Androids system grants the declared permission automatically if the permission is not very sensitive (e.g., the permission to turn on the device’s flashlight). However, the system will ask the user to approve the request if it is sensitive such as READ_CONTACTS. You can learn more at Normal and Dangerous Permissions.

Requesting Permissions at Runtime

After Android 6.0, all sensitive permissions need the user to grant at runtime. The ContextCompat.checkSelfPermission() method can be used to check whether your app has the permission:

1
2
3
4
5
6
7
8
if (ContextCompat.checkSelfPermission(getActivity(),
android.Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
// not have the permission yet.
...
} else {
// already have the permission.
...
}

If the return value is not PackageManager.PERMISSION_GRANTED, your app needs to request the user to grant the permission.

Depends on the context, there are two different but similar ways to request the permission:

Activity Recipe

If your app requests the permission from an Activity directly, then you can do it as follow:

  1. Define a constant for request code, such as READ_CONTACTS_PERMISSION:

    1
    2
    private static final int READ_CONTACTS_PERMISSION = 0;
  2. Initiate the request by invoking ActivityCompact.requestPermissions():

    1
    2
    3
    4
    ActivityCompat.requestPermissions(thisActivity,
    new String[] { Manifest.permission.READ_CONTACTS },
    READ_CONTACTS_PERMISSION);
  3. Override ActivityCompact.onRequestPermissionResult() method to handle the response:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    @Override
    public void onRequestPermissionsResult(int requestCode,
    String permissions[], int[] grantResults) {
    switch (requestCode) {
    case MY_PERMISSIONS_REQUEST_READ_CONTACTS:
    // If request is cancelled, the result arrays are empty.
    if (grantResults.length > 0
    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
    // permission was granted, do the
    // contacts-related task you need to do.
    } else {
    // permission denied, disable the
    // functionality that depends on this permission.
    }
    return;
    // other 'case' lines to check for other
    // permissions this app might request
    }
    }

Fragment Recipe

If your app requests the permission from a Fragment, you can do it like this:

  1. Also define a constant READ_CONTACTS_PERMISSION for request code, like what we do in an Activity.

  2. Invoking Fragment.requestPermissions() to initiate the request, it’s an instance method, so you can invoke it in your Fragment directly:

    1
    2
    3
    requestPermissions(new String[] { Manifest.permission.READ_CONTACTS },
    READ_CONTACTS_PERMISSION);
  3. Override Fragment.onRequestPermissionsResult() to handle request response, it is also an instance method and you can just override it in your Fragment like what you do in an Activity.

Some Suggestions

Consider Using an Intent

Sometimes you don’t need to bother to request all the permissions by shfiting the burden to other apps using Intent. For example, you can use a camera app to finish all the taking photo work and just handle the result. It relieves you from all the heavy work about designing the UI, implement the logic etc, but you cannot have any control on the user experience about the operation.

It’s better for you to give careful consideration weighing the pros and cons.

Ask the User When Appropriate

Do not overwhelm the user with all the permission requests and you should ask the user to grant it only necessary and in an appropriate manner. For example, it’s better for you to send the permission request right before your app needs to read the contacts than as soon as the user opens your app.

Give Some Explanation

The permission request dialog doesn’t contain any explanations about why the permission is needed and it cannot be customized. In some circumstances, the user will get confused without any explanation. For example, a photo app requests the geo-location permission. It’s better to inform the user that your app needs geo-location permission to geo-tag the photo.

The Android system does provide the API for check whether you should show the explanation to the user: the shouldShowRequestPermissionRationale() method. It returns true if the app has requested this permission previously and the user denied the request. For Activity, it’s ActivityCompat.shouldShowRequestPermissionRationale() static method, and it’s Fragment.shouldShowRequestPermissionRationale() instance method for Fragment.

Add the explanation handling part, the recipe for Fragment permission request is as follow:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if (ContextCompat.checkSelfPermission(getActivity(),
android.Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
if (shouldShowRequestPermissionRationale(android.Manifest.permission.READ_CONTACTS)) {
// show an explanation, here we just make a toast
Toast.makeText(getActivity(),
"We need to read your contacts to find the phone number.",
Toast.LENGTH_SHORT).show();
requestReadContactsPermission();
} else {
// directly ask the user for permission.
requestReadContactsPermission();
}
} else {
// already have the permission.
getPhoneNumber();
}

The requestReadContactsPermission() is as follow:

1
2
3
4
private void requestReadContactsPermission() {
requestPermissions(new String[] { Manifest.permission.READ_CONTACTS },
READ_CONTACTS_PERMISSION);
}

It’s easy to rewrite the code in the Activity context.

Test for Both Permission Models

Android 5.1 and Android 6.0 have different permission models, and you should test both. One important note for Android 6.0 is that you should test different combinations of available permissions to make sure the code path is correct under all conditions.

Wrap-up

This post presents two recipes for handling permission request in Android system, and here are the steps:

  1. Think about all the permissions the app needs.

  2. Add the needed permissions to the Manifest file.

  3. Take the corrensponding recipe depending on whether your app requests permission through Activity or Fragment.

  4. Make sure to do some tests with different combinations of permissions available.

Reference

[1] Declaring Permissions

[2] Requesting Permissions at Run Time

[3] Permissions Best Practices

[4] Android M Permissions: onRequestPermissionsResult() not being called