Requesting Permissions

In the late 1990’s, a wave of viruses spread through the Internet, delivered via email, using contact information culled from Microsoft Outlook. A virus would simply email copies of itself to each of the Outlook contacts that had an email address. This was possible because, at the time, Outlook did not take any steps to protect data from programs using the Outlook API, since that API was designed for ordinary developers, not virus authors.

Nowadays, many applications that hold onto contact data secure that data by requiring that a user explicitly grant rights for other programs to access the contact information. Those rights could be granted on a case-by-case basis or all at once at install time.

Android is no different, in that it requires permissions for applications to read or write contact data. Android’s permission system is useful well beyond contact data, and for content providers and services beyond those supplied by the Android framework.

You, as an Android developer, will frequently need to ensure your applications have the appropriate permissions to do what you want to do with other applications’ data. This chapter covers this topic, both the classic approach used for all permissions prior to Android 6.0 and the new runtime permission system used for certain permissions in Android 6.0+.

You may also elect to require permissions for other applications to use your data or services, if you make those available to other Android components. This will be discussed later in this book.

Frequently-Asked Questions About Permissions

Permissions are occasionally a confusing topic in Android app development, more so now that Android 6.0 has arrived and has changed the permission system a fair bit. Here are some common questions about permissions to help get us started.

What Is a Permission?

A permission is a way for Android (or, sometimes, a third-party app) to require an app developer to notify the user about something that the app will do that might raise concerns with the user. Only if an app holds a certain permission can the app do certain things that are defended by that permission.

Mechanically, permissions take the form of elements in the manifest. Right now, we are focusing on requesting and holding permissions, and so we will be working with the <uses-permission> element.

When Will I Need a Permission?

Most permissions that you will deal with come from Android itself. Usually, the documentation will tell you when you need to request and hold one of these permissions.

However, occasionally the documentation has gaps.

If you are trying out some code and you crash with a SecurityException the description of the exception may tell you that you need to hold a certain permission — that means you need to add the corresponding <uses-permission> element to your manifest.

Third-party code, including Google’s own Play Services SDK, may define their own custom permissions. Once again, ideally, you find out that you need to request a permission through documentation, and otherwise you find out through crashing during testing.

What Are Some Common Permissions, and What Do They Defend?

There are dozens upon dozens of permissions in Android. Here are some of the permissions we will see in this book:

In this book and in casual conversation, we refer to the permissions using the unique portion of their name (e.g., INTERNET). Really, the full name of the permission will usually have android.permission. as a prefix (e.g., android.permission.INTERNET), for Android-defined permissions. Custom permissions from third-party apps should use a different prefix. You will need the full permission name, including the prefix, in your manifest entries.

How Do I Request a Permission?

Put a <uses-permission> element in your manifest, as a direct child of the root <manifest> element (i.e., as a peer element of <application>), with an android:name attribute identifying the permission that you are interested in.

For example, here is a sample manifest, with a request to hold the WRITE_EXTERNAL_STORAGE permission:

<?xml version="1.0"?>
<manifest package="com.commonsware.android.fileseditor"
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:versionCode="1"
  android:versionName="1.0">

  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

  <supports-screens
    android:anyDensity="true"
    android:largeScreens="true"
    android:normalScreens="true"
    android:smallScreens="true" />

  <application
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/Theme.Apptheme">
    <activity
      android:name=".MainActivity"
      android:label="@string/app_name">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
  </application>

</manifest>
(from Files/FilesEditor/app/src/main/AndroidManifest.xml)

This is sufficient for most permissions and most devices. Permissions considered to be dangerous need special attention on Android 6.0+, and we will cover that in grand detail later in this chapter.

Note that you are welcome to have zero, one, or several such <uses-permission> elements. Also note that some libraries that you elect to use might add their own <uses-permission> elements to your manifest, through a process called “manifest merger”.

When Is the User Informed About These Permissions?

Well, that gets complicated. It depends on the permission, the version of Android the user is using, from where the user is installing the app, and the phase of the moon.

(well, OK, not really that last one)

Installing Through SDK Tools

Anyone who installs an app using Android Studio will not be prompted for permissions. The same holds true for anyone using anything else based on the Android SDK tools — while the app may request permissions, the user is not prompted for them, and the permissions are granted.

(Android 6.0+ and dangerous permissions change this up a bit – more on that later in this chapter)

Installing from the Play Store, Android 5.1 and Older

If the user is running an Android 5.1 or older device, and the user goes to install your app from the Play Store, the user will be presented with a roster of permission groups that contain permissions that you are requesting and that are considered to be dangerous:

Permission Confirmation Screen, on Play Store Web Site
Figure 255: Permission Confirmation Screen, on Play Store Web Site

We will discuss more about permission groups and this dangerous concept later in this chapter.

Installing from the Play Store, Android 6.0+

On Android 6.0 and higher, when the user installs your app from the Play Store, what happens depends upon the value of targetSdkVersion for your app.

If your targetSdkVersion is 22 or lower, you get the same behavior as is described above, where the user sees the list of permission groups which contain permissions that you are requesting and that are considered to be dangerous.

If your targetSdkVersion is 23 or higher, the user is not prompted about permissions at install time. Instead these prompts will occur when the user runs your app and when you ask the user for the permissions, as we will see later in this chapter.

Installing by Other Means, Android 5.1 and Older

If you install an app on Android 5.1 or older, by any means (e.g., downloading from a Web site), you will be prompted with a list of all requested permissions:

Permission Confirmation Screen, on Android 4.4
Figure 256: Permission Confirmation Screen, on Android 4.4

Note that this prompt will not appear until you actually have downloaded the app and have begun the installation process. Before then, the device cannot examine the manifest inside the APK file to find the permissions.

Installing by Other Means, Android 6.0+

If your app’s targetSdkVersion is 23 or higher, and you install the app on an Android 6.0+ device by other means than the Play Store, you will not be prompted about any permissions at install time:

Permission Confirmation Screen, on Android 6.0
Figure 257: Permission Confirmation Screen, on Android 6.0

Characteristics of Permissions

Several bits of information make up a permission, and some of those affect app developers or users.

Name

We have already seen that permissions have names, and you use them in the android:name attribute of the <uses-permission> element to identify a permission that you would like your app to hold.

Android framework-defined permissions will begin with android.permission. Permissions from libraries or third-party apps will have some other prefix. Make sure that when you create your <uses-permission> element that you are using the fully-qualified permission name, including android.permission or any other prefix.

Also note that Android is case-sensitive, so make sure you use the case of the permission as documented (e.g., android.permission.INTERNET). Some versions of Android Studio had a bug where if you let the IDE auto-complete a <uses-permission> element for you, sometimes it would have the android:name value appear IN ALL CAPS. This is a bug that has since been fixed, so hopefully it will not affect you in the future.

Protection Level

The definition of a permission, in the framework or in third-party code, will have a “protection level”. This describes how the permission itself should be validated. The two protection levels that you will encounter most often are normal and dangerous.

Normal

A normal permission is something that the user might care about, but probably not. So, while we need to request the permission in the manifest via <uses-permission>, the user will not be bothered about this permission at install time.

The classic example is the INTERNET permission. Most Android apps wind up requesting this permission, either for functionality written by the developers or functionality pulled in from libraries (e.g., ad banners). INTERNET is considered normal, so while we need to request the INTERNET permission in the manifest, the user is not informed about this permission anymore at install time.

(the “anymore” note is because in the early days of Android, users were informed about all permissions, regardless of protection level)

Users can see normal permissions, though, in other places:

Dangerous

A dangerous permission is one that the definers of the permission (e.g., Google) wants to ensure that the user is aware of and has agreed to.

Classically, this meant that the user would be prompted for this permission at install time. On old versions of Android and the Play Store, dangerous permissions would be listed before normal permissions.

With Android 6.0+, while dangerous permissions are not displayed at install time (for apps with a targetSdkVersion of 23 or higher), they will be displayed to the user while the app is running, before the app tries doing something that requires one of those permissions. This is a significant behavior change, so we will be covering it in depth later in this chapter.

Permission Group

Permissions are collected into permission groups.

In the early days of Android, app developers were oblivious to this, as permission groups had no effect on app development, runtime behavior, or user experience.

In the past few years, the “permission” prompts at install time have really been prompting about permission groups. The user is told that the app is requesting permissions from certain groups. Moreover, the blessing that the user gives — by virtue of continuing to install the app — is by group, not by permission. If some future update to the app would ask for a new permission, but one from a group that the user agreed to previously, the user would not be informed about this new permission request.

With Android 6.0, permission groups also extend to the runtime permission UX, as while we developers will still request individual permissions, the user will be asked to grant rights with respect to permission groups.

Maximum SDK Version

<uses-permission> can have an android:maxSdkVersion attribute. This indicates the highest API level for which we need the permission. If the app is running on newer versions of Android, skip the permission.

This is for cases where Android relaxes restrictions over time. We will see an example of this, in the form of the WRITE_EXTERNAL_STORAGE permission, in an upcoming chapter.

Minimum SDK Version

You might think that <uses-permission> would have an android:minSdkVersion attribute to serve as the counterpart to android:maxSdkVersion. The minSdkVersion would indicate the lowest API level for which to request a permission; older devices would skip the permission.

Alas, this is not available.

However, there is the awkwardly-named [uses-permission-sdk-23] element.

This element functions identically to <uses-permission> on Android 6.0+ devices. On older devices, it is ignored.

This element illustrates a problem with the permission system in Android: you have to put all permissions that you want in the manifest. Prior to the runtime permission system in Android 6.0, this would mean that developers who need some controversial permission (e.g., READ_CONTACTS) for some fringe feature would need to request the permission from everyone, not just those who use the feature. As we will see, the runtime permission system lets us not bother the user until they try using the secured feature. [uses-permission-sdk-23] would allow us to not bother with the permission at all on older devices, where its presence might scare away potential users.

New Permissions in Old Applications

Sometimes, Android introduces new permissions that govern behavior that formerly did not require permissions. WRITE_EXTERNAL_STORAGE is one example – originally, applications could write to external storage without any permission at all. Android 1.6 introduced WRITE_EXTERNAL_STORAGE, required before you can write to external storage. However, applications that were written before Android 1.6 could not possibly request that permission, since it did not exist at the time. Breaking those applications would seem to be a harsh price for progress.

What Android does is “grandfather” in certain permissions for applications supporting earlier SDK versions.

For example, if your minSdkVersion is 3 or lower, saying that you support Android 1.5, your application will automatically request WRITE_EXTERNAL_STORAGE and READ_PHONE_STATE, even if you do not explicitly request those permissions. People installing your application on an Android 1.5 device will see these requests.

Eventually, when you drop support for the older version (e.g., switch to minSdkVersion of 4 or higher), Android will no longer automatically request those permissions. Hence, if your code really does need those permissions, you will need to ask for them yourself.

Android 6.0+ Runtime Permission System

In Android 6.0 and higher devices, permissions that are considered to be dangerous not only have to be requested via <uses-permission> elements, but you also have to ask the user to grant you those permissions at runtime. What you gain, though, is that users are not bothered with these permissions at install time, and you can elect to delay asking for certain permissions until such time as the user actually does something that needs them.

This section will occasionally point out snippets of code from the Permissions/PermissionMonger sample project.

Let’s explore the runtime permissions system via a new series of questions.

What Permissions Are Affected By This?

There are nine permission groups that Android 6.0 manages as user-controllable permissions:

Permission Group Permission
CALENDAR READ_CALENDAR, WRITE_CALENDAR
CAMERA CAMERA
CONTACTS GET_ACCOUNTS, READ_CONTACTS, WRITE_CONTACTS
LOCATION ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION
MICROPHONE RECORD_AUDIO
PHONE ADD_VOICEMAIL, CALL_PHONE, PROCESS_OUTGOING_CALLS, READ_CALL_LOG, READ_PHONE_STATE, USE_SIP, WRITE_CALL_LOG
SENSORS BODY_SENSORS
SMS READ_CELL_BROADCASTS, READ_SMS, RECEIVE_SMS, RECEIVE_MMS, RECEIVE_WAP_PUSH, SEND_SMS
STORAGE READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE

Users will be able to revoke permissions by group, through the Settings app. They can go into the page for your app, click on Permissions, and see a list of the permission groups for which you are requesting permissions:

Settings Screen for Permission Monger, Showing Permissions
Figure 258: Settings Screen for Permission Monger, Showing Permissions

What Goes in the Manifest?

The same <uses-permission> elements as before. These declare the superset of all possible permissions that you can have. If you do not have a <uses-permission> element for a particular permission, you cannot ask for it at runtime, and the user cannot grant it to you.

How Do I Know If I Have Permission?

On Android 6.0+, you can call a checkSelfPermission() method, available on any Context (e.g., your Activity). This will return either PERMISSION_GRANTED or PERMISSION_DENIED, depending on whether or not the user granted you permission or you were automatically given permission (e.g., for normal permissions).

For a simpler boolean check to see if you have the permission, you could have your own hasPermission() method:

  private boolean hasPermission(String perm) {
    return(PackageManager.PERMISSION_GRANTED==checkSelfPermission(perm));
  }
(from Permissions/PermissionMonger/app/src/main/java/com/commonsware/android/permmonger/MainActivity.java)

Then you can use that hasPermission() call where you need it.

For example, the PermissionMonger app requests five permissions in the manifest:

  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
  <uses-permission android:name="android.permission.CAMERA"/>
  <uses-permission android:name="android.permission.INTERNET"/>
  <uses-permission android:name="android.permission.READ_CONTACTS"/>
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
(from Permissions/PermissionMonger/app/src/main/AndroidManifest.xml)

The UI is then a table showing the current status of those five permissions:

  private void updateTable() {
    location.setText(String.valueOf(canAccessLocation()));
    camera.setText(String.valueOf(canAccessCamera()));
    internet.setText(String.valueOf(hasPermission(Manifest.permission.INTERNET)));
    contacts.setText(String.valueOf(canAccessContacts()));
    storage.setText(String.valueOf(hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)));
  }

  private boolean canAccessLocation() {
    return(hasPermission(Manifest.permission.ACCESS_FINE_LOCATION));
  }

  private boolean canAccessCamera() {
    return(hasPermission(Manifest.permission.CAMERA));
  }

  private boolean canAccessContacts() {
    return(hasPermission(Manifest.permission.READ_CONTACTS));
  }
(from Permissions/PermissionMonger/app/src/main/java/com/commonsware/android/permmonger/MainActivity.java)

At the outset, we only have the one “normal” permission: INTERNET:

Permission Monger, Showing Initial Permissions
Figure 259: Permission Monger, Showing Initial Permissions

The checkSelfPermission() method on Context is only available on API Level 23. You can, if you wish, wrap your call to checkSelfPermission() in a check of the API level of the device you are running on:


if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.M) {
  if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)==
    PackageManager.PERMISSION_GRANTED) {
    // do something cool
  }
}

A simpler approach is to use ContextCompat, from the support-v4 library. This has a static implementation of checkSelfPermission() that takes a Context and your permission string as parameters. It returns the same value (e.g., PackageManager.PERMISSION_GRANTED) as does the checkSelfPermission() that ships with Android 6.0. But, if you are running on an older device, it checks the version for you and returns PackageManager.PERMISSION_GRANTED for older devices. So, the above code snippet turns into:


if (ContextCompat.checkSelfPermission(this,
  Manifest.permission.WRITE_EXTERNAL_STORAGE)==
    PackageManager.PERMISSION_GRANTED) {
  // do something cool
}

(assuming that this is a subclass of Context, like Activity)

How Do I Know If the User Takes Permissions Away From Me?

If the user grants you access to some permission group, the only way the user can revoke that is via the Settings app. If the user does revoke access to a permission group, your process is terminated.

Hence, while your code is running, you will have all permissions that you started with, plus any new ones that the user grants on the fly based upon your request. There should be no circumstance where your process is running yet you lose a permission.

That being said, your app is not specifically notified about losing the permission. You should be calling checkSelfPermission() to determine what you can and cannot do, at least for every process invocation. And, since the call appears to be reasonably cheap, you should just call it whenever you need to know whether you can perform a particular operation.

Note that usually your app will be in the background, if it is running at all, at the time when a runtime permission is revoked. Particularly in an Android 7.0+ multi-window environment, though, it is possible that you will still be visible when the user revokes a runtime permission. Your process is terminated in any case, and if you were visible, your UI is removed from the screen.

How Do I Ask the User For Permission?

To ask the user for one of the runtime permissions, call requestPermissions() on your Activity. This takes a String array of the permissions that you are requesting and a locally-unique integer to identify this request from any other similar requests that you may be making. This int serves in much the same role as does the int passed into startActivityForResult(), though you should keep the value to 8 bits (0 to 255) for maximum compatibility.

For example, PermissionMonger will check in onCreate() to see if we can access locations or access contacts, and if not, it will request access to those two permissions:

    if (!canAccessLocation() || !canAccessContacts()) {
      requestPermissions(INITIAL_PERMS, INITIAL_REQUEST);
    }
(from Permissions/PermissionMonger/app/src/main/java/com/commonsware/android/permmonger/MainActivity.java)

INITIAL_PERMS and INITIAL_REQUEST are just static final data members:

  private static final String[] INITIAL_PERMS={
    Manifest.permission.ACCESS_FINE_LOCATION,
    Manifest.permission.READ_CONTACTS
  };
(from Permissions/PermissionMonger/app/src/main/java/com/commonsware/android/permmonger/MainActivity.java)

  private static final int INITIAL_REQUEST=1337;
(from Permissions/PermissionMonger/app/src/main/java/com/commonsware/android/permmonger/MainActivity.java)

When the app is first launched, dialogs will appear, one per permission that you requested, asking the user if they would be so kind as to allow your app to do the things that you requested:

Permission Monger, Requesting READ_CONTACTS Permission
Figure 260: Permission Monger, Requesting READ_CONTACTS Permission

Permission Monger, Requesting ACCESS_FINE_LOCATION Permission
Figure 261: Permission Monger, Requesting ACCESS_FINE_LOCATION Permission

When the user has proceeded through the dialogs, you will be called with onRequestPermissionsResult(). You are passed three parameters:

Whether you use those latter two parameters or simply call checkSelfPermission() again is up to you. Regardless, at this point, you should determine what you got, so you know how to react, such as disabling things that the user cannot use given the lack of permission.

Just as ContextCompat offers a backwards-compatible implementation of checkSelfPermission(), ActivityCompat offers a backwards-compatible implementation of requestPermissions() that you can use. Otherwise, you will want to take other steps to ensure that you only call requestPermissions() on API Level 23+ devices.

When Do I Ask the User For Permission?

That depends a bit on the nature of the permission.

In an ideal world, your app can function without any of the revocable permissions granted to you, albeit perhaps in a limited fashion. In that case, you might ask for permission only when the user tries to do something (e.g., taps on an action bar item) for which you definitely need the permission.

However, sometimes you will need permission to be at all useful to the user. In that case, you will need to ask for permission when the app opens.

In either case, though, bear in mind that while the user will see the dialog asking for permission, the user may not understand why you are asking for this permission. You need to make sure that the user understands the cost/benefit trade-off in granting the permission — in other words, what does the user get out of the deal?

For permissions that you are requesting based on user input, you might pop your own dialog or other UI explaining what you want and why you want it, before calling requestPermissions(). For permissions that you would want to ask for when the app starts up, make sure that you clearly explain the need for the permissions and what the user gets in exchange as part of a one-time introductory tutorial, one that might also be accessed via an overflow item or nav drawer entry as part of your app’s help facility.

When Do I Not Ask the User For Permission?

One limitation with the requestPermissions() implementation is that it is oblivious to configuration changes.

For example, suppose that in onCreate() of your activity, you check to see if you have been granted a runtime permission (via checkSelfPermission()), and if you have not, you call requestPermissions() to request it from the user. This displays the dialog. Now the user rotates the screen. If the user denies the permission, by default, the user will immediately see the permission dialog again… because your activity will have been destroyed and recreated, and your onCreate() will see that you do not have the permission, and so you ask for it again.

In cases like this, you will need to track whether you are in the permission-request flow (e.g., via a boolean saved in the instance state Bundle) and skip requesting the permission if you have been recreated in the middle of that flow.

What Do I Do If the User Says “No”?

If you were requesting permission as a direct response to some bit of user input (e.g., user tapped on an action bar item), and the user rejects the permission you need to do the work, obviously you cannot do the work. Depending on overall flow, showing a dialog or something to explain why you cannot do what the user asked for may be needed. In some cases, you may deem it to be obvious, by virtue of the fact that the user saw the permission-request dialog and said “deny”.

If you were requesting permission pre-emptively, such as when the activity starts, you will need to decide whether that decision needs to be reflected in the current UI (e.g., “no data available” messages, disabled action bar items).

One thing you can do to help here is to detect when this has occurred before you request permissions again. Before you call requestPermissions(), you can call shouldShowRequestPermissionRationale(), supplying the name of a permission. This will return true if the user had previously declined to grant you permission, in cases where Android thinks that the user might benefit from learning a bit more about why you need the permission. You can use this to determine whether you should show some explanatory UI of your own first, before continuing with the permission request, or if you should just go ahead and call requestPermissions().

Note that ActivityCompat also has a backwards-compatible implementation of shouldShowRequestPermissionRationale(), so you can avoid your own API level checks.

What Do I Do If the User Says “No, And Please Stop Asking”?

The second time you ask a user for a particular runtime permission, the user will have a “Never ask again” checkbox:

Permission Monger, Requesting ACCESS_FINE_LOCATION Permission (Again)
Figure 262: Permission Monger, Requesting ACCESS_FINE_LOCATION Permission (Again)

If the user checks that and clicks the Deny button, not only will you not get the runtime permission now, but all future requests will immediately call onRequestPermissionsResult() indicating that your request for permission was denied. The only way the user can now grant you this permission is via the Settings app.

You need to handle this situation with grace and aplomb.

Choices include:

For permissions that, when denied, leave your app in a completely useless state, you may wind up just displaying a screen on app startup that says “sorry, but this app is useless to you”, with options for the user to uninstall the app or grant you the desired permissions.

Note that shouldShowRequestPermissionRationale() returns false if the user declined the permission and checked the checkbox to ask you to stop pestering the user.

What Happens When I Ship This to an Older Device?

Older devices behave as they always have. Since you still list the permissions in the manifest, those permissions will be granted to you if the user installs the app, and the user will be notified about those permissions as part of the installation process. If you are checking the API level yourself, or you are using ContextCompat and ActivityCompat as described above, your code should just work.

What Happens When My App Has a Lower Target SDK Version?

Apps with a targetSdkVersion below 23, on the surface, behave on Android 6.0+ as they would on an older device: the user is prompted for all permissions, and the app is granted those permissions if the app is installed.

However, the user will still be able to go into Settings and revoke permissions from these apps, for any permissions the app requests that are in one of the runtime permission groups.

Generally, you will wind up ignoring the issue. All your calls to methods protected by permissions that the user revoke will still “work”, insofar as they will not throw a SecurityException. However, you just will not get any results back or have the intended effects. So, for example, if you try to query() the ContactsContract ContentProvider, and the user revoked your access to contact-related permissions, the query() will return an empty Cursor. This is a completely valid response, even ignoring the permission issue, as it is entirely possible that the user has no contacts. Your app should be handling these cases gracefully anyway. Hence, in theory, even if you do nothing special regarding the lost permissions, your app should survive, albeit with reduced functionality for the user. Dave Smith outlines the expected results for legacy apps calling methods sans permission.

However, all else being equal, you should set your targetSdkVersion to at least 23 and opt into the runtime permission system. After all, the Play Store requires a higher targetSdkVersion, and other app distribution channels might follow suit.

What Happens if the User Clears My App’s Data?

If the user clears your app’s data through the Settings app, the runtime permissions are cleared as well. Behavior at this point will be as if your app had been just installed — checkSelfPermission() will return PERMISSION_DENIED, and you will need to request the permissions.

How Can I Automate Permission Grants?

While the runtime permission system provides a reasonable user-facing UI, having to deal with that UI constantly as a developer can be a significant pain. For testing and debugging purposes, there are some command-line options for granting and revoking permissions that you can use.

Should I Be Using PermissionChecker?

checkSelfPermission() on ContextCompat always returns PERMISSION_GRANTED if either:

PERMISSION_DENIED will be returned only if you have opted into the new runtime permission system (targetSdkVersion of 23 or higher), you are running on Android 6.0 or higher, and the user either never granted the permission or revoked it through the Settings app.

The key is that even if you are running on Android 6.0, with an older targetSdkVersion (so all permissions are requested at install time and are granted to you automatically), checkSelfPermission() still returns PERMISSION_GRANTED even if the user revoked the permission in Settings.

The Android Support libraries — specifically support-v4 — added a PermissionChecker class with a checkPermission() static method. If you are running on Android 6.0+ with an older targetSdkVersion, checkPermission() will return PERMISSION_DENIED_APP_OP if the user revoked the permission in Settings.

Hence, PermissionChecker is useful in cases where you have a really large code base, and you want to try to better handle cases where users revoke permissions, but you are not in position to do a complete implementation of the runtime permissions system. However, it is merely a stopgap — your long-term plan should be to raise your targetSdkVersion to 23 or higher and implement the runtime permissions properly.

A Simple Runtime Permission Abstraction

Occasionally, our need for runtime permissions is fairly straightforward: we need them to do anything meaningful in the app, and so we need to request them up front and exit the app if the user declines to grant one or more of them. Some of the samples in this book fit this model, in part because many of the samples in this book are fairly small apps.

Some of those apps will use a variation of an AbstractPermissionActivity to hide all the runtime permission work, allowing the sample’s “real” activity to focus on demonstrating the portion of the Android SDK that the sample is tied to.

One such AbstractPermissionActivity can be found in the Files/FilesEditor sample project. Later in the book, we will look at this sample to see how to do file I/O on Android. Here, though, let’s take a look at AbstractPermissionActivity.

Examining the Protocol

The idea is that AbstractPermissionActivity will handle the runtime permissions, in its onCreate() implementation. Subclasses will need to implement three methods to make this work:

Requesting the Permission

onCreate() will see if we have the desired permissions, and if not, it will call requestPermissions() to ask for those that we do not already hold:

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    this.state=savedInstanceState;

    if (state!=null) {
      isInPermission=state.getBoolean(STATE_IN_PERMISSION, false);
    }

    if (hasAllPermissions(getDesiredPermissions())) {
      onReady(state);
    }
    else if (!isInPermission) {
      isInPermission=true;

      ActivityCompat
        .requestPermissions(this,
          netPermissions(getDesiredPermissions()),
          REQUEST_PERMISSION);
    }
  }
(from Files/FilesEditor/app/src/main/java/com/commonsware/android/fileseditor/AbstractPermissionActivity.java)

hasAllPermissions() iterates over the permission array from getDesiredPermissions() and returns true if we hold them all, false otherwise:

  private boolean hasAllPermissions(String[] perms) {
    for (String perm : perms) {
      if (!hasPermission(perm)) {
        return(false);
      }
    }

    return(true);
  }

  protected boolean hasPermission(String perm) {
    return(ContextCompat.checkSelfPermission(this, perm)==
      PackageManager.PERMISSION_GRANTED);
  }
(from Files/FilesEditor/app/src/main/java/com/commonsware/android/fileseditor/AbstractPermissionActivity.java)

If we hold all of the permissions, we go ahead and call onReady(), so the activity can start its real work. Otherwise, we call requestPermissions() on ActivityCompat, using a netPermissions() method to identify those permissions that we do not already hold:

  private String[] netPermissions(String[] wanted) {
    ArrayList<String> result=new ArrayList<String>();

    for (String perm : wanted) {
      if (!hasPermission(perm)) {
        result.add(perm);
      }
    }

    return(result.toArray(new String[result.size()]));
  }
(from Files/FilesEditor/app/src/main/java/com/commonsware/android/fileseditor/AbstractPermissionActivity.java)

Handling the Result

In onRequestPermissionResult(), depending on whether we now hold all the desired permissions, we call onReady() or onPermissionDenied():

  @Override
  public void onRequestPermissionsResult(int requestCode,
                                         String[] permissions,
                                         int[] grantResults) {
    isInPermission=false;

    if (requestCode==REQUEST_PERMISSION) {
      if (hasAllPermissions(getDesiredPermissions())) {
        onReady(state);
      }
      else {
        onPermissionDenied();
      }
    }
  }
(from Files/FilesEditor/app/src/main/java/com/commonsware/android/fileseditor/AbstractPermissionActivity.java)

Dealing with (Configuration) Change

It is possible that the user will rotate the device or otherwise trigger a configuration change while our permission dialog is in the foreground. Since our activity is still visible behind that dialog, we get destroyed and recreated… but we do not want to re-raise the permission dialog again.

That is why we have a boolean, named isInPermission, that tracks whether or not we are in the middle of requesting permissions. We hold onto that value in onSaveInstanceState():

  @Override
  protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    outState.putBoolean(STATE_IN_PERMISSION, isInPermission);
  }
(from Files/FilesEditor/app/src/main/java/com/commonsware/android/fileseditor/AbstractPermissionActivity.java)

We restore it in onCreate(). If we do not hold all of the desired permissions, but isInPermission is true, we skip requesting the permissions, since we are in the middle of doing so already.

As noted earlier, later in the book, we will look at how a subclass of AbstractPermissionActivity works, and you will see AbstractPermissionActivity used in other sample apps as well.