Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Android Runtime Permission Model

DZone's Guide to

Android Runtime Permission Model

Learn how to make your Android mobile apps compliant with the runtime permission model that changed in the recent Android Marshmallow release.

· Web Dev Zone ·
Free Resource

Bugsnag monitors application stability, so you can make data-driven decisions on whether you should be building new features, or fixing bugs. Learn more.

This tutorial is about the Android runtime permission model that changed in the Android Marshmallow release:

"Google Play will require that new apps target at least Android 8.0 (API level 26) from August 1, 2018, and that app updates target Android 8.0 from November 1, 2018." - Android Docs

Google’s decision led developers to change development priorities for their applications. Now, a huge number of apps need to be updated and change their permission model. This tutorial guides developers on how to change their current permission model into the new one.

New Runtime Permissions vs. Old Model

Android application permission handling has changed to another dimension with API level 23. In the old model, all permissions were asked to be accepted during installation. But now, after Marshmallow non-normal permissions are asked to be granted during runtime.

Permissions are divided into four protection levels :

  1. Normal
  2. Signature
  3. Dangerous
  4. Special

We will look through normal and dangerous levels. If you are interested, you can jump to the Bonus Section for Special and Signature levels. Here are the differences between normal and dangerous permissions.

Normal Permissions

Normal permissions have nearly no risk to user privacy or to the operation of other applications. For example, permission for connection to the Internet or setting the alarms.

These types of permissions are automatically granted by the system during installation, and the user will not be prompted explicitly to grant the permission. As well users cannot revoke these permissions.

See complete list of normal permissions on Android’s website.

Dangerous Permissions

Dangerous permissions can cause data leaks that involve the user’s private information, change the user’s saved data, or put the operation of other applications at risk. For example, permissions for opening the camera and taking pictures, sending SMS, or recording through the microphone.

To use a dangerous permission, the user should grant the permission vi aa prompt shown at runtime or from system settings. The user can also revoke this type of permissions any time.

See complete list of dangerous permissions on Android’s website.

Requesting Android Runtime Permissions

For implementing the permission model, you will need these four functions :

  • ContextCompat.checkSelfPermission() to check if permission is granted.
  • ActivityCompat.requestPermissions() to request permissions. Multiple permissions can be requested with multiple permission groups.
  • ActivityCompat.shouldShowRequestPermissionRationale() to decide to show the rationale message about why the permission wanted to be granted.
  • onRequestPermissionsResult() to handle the user’s response to the permission prompt.

Let’s say you want to manage call logs in your app. The permissions required are in the dangerous level, so, first, you need to check the required permissions and if it is granted or not.

 private val REQUEST_CODE = 1234
...
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALL_LOG) == PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_CALL_LOG) == PackageManager.PERMISSION_GRANTED) {
  // Run your logic ideally with pre-granted permission.
} else {
  // Here you do not have the permission. So you need to request it.
  ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_CALL_LOG, Manifest.permission.WRITE_CALL_LOG), REQUEST_CODE)
}

The parameter REQUEST_CODE is the identifier of our request action. It will help us to locate our action in the onRequestPermissionsResult function.

In this stage, if the app is not pre-granted permissions, the application should show a prompt to the user. Here in the code, two separate permissions are requested, but only one prompt with the group-generic “make and manage phone calls” text is shown. The reason behind this is that Google has grouped dangerous permissions by in themselves.

See dangerous permission groups on Android’s website.

The grouping is helpful in these ways :

  • If the app doesn’t currently have any permissions in the permission group, the system shows the permission request dialog to the user describing the permission group that the app wants to access.
  • If the app has already been granted another dangerous permission in the same permission group, the system immediately grants the permission without any interaction with the user.

Let’s request the same permissions a second time. Here, the additional “Don’t ask again” checkbox has appeared.

The user has three choices for the prompt:

  1. Allow – Allows the permission grant – the happy path.
  2. Deny – Denies the permission grant but the app can request it again.
  3. Don’t ask again – Denies the permission and blocks the app from requesting the permission again.

On selecting any choice, a prompt will disappear and onRequestPermissionsResult() will be called.

override fun onRequestPermissionsResult(requestCode: Int, @NonNull permissions: Array<String>, @NonNull grantResults: IntArray) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    when (requestCode) {
        REQUEST_CODE -> {
            if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // Run your logic with newly-granted permission.
            } else if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_CALL_LOG)) {
                // Permission denied.
                // User selected "Don't ask again" choice.
                // Change the UI accordingly.
            } else {
                // Permission denied.
                // User selected "Deny".
                // App can re-request permission. Change your logic accordingly.
            }
            // Do not forget to control each permission.
        }
    }
}

In this block, the shouldShowRequestPermissionRationale() method helps us to determine the denial reason. If the user selected the “Don’t ask again” choice, the application should explicitly help the user to understand why the permission is needed; i.e. some graphics, explanation text, or UI changes.

Bonus Section

We need to think of these signature and special permission groups together. Because special permissions are also considered signature permissions by Google.

Signature Permissions

Signature permissions are automatically granted to the application by the system during installation like normal permissions. However, the difference here is that not every application can get these permissions. If the application is signed by the same certificate which signs the permission, then the application can use the permission.

When you define a custom permission in one of your applications, and you set its protection level to “signature” only, the application, which is signed by the same certificate that's been used in permission-defined-application, can grant and use that permission.

Special Permissions

Some of the signature permissions are above the dangerous permissions, which means they can effect and change the user’s whole use of the phone. These permissions are not used by most of the applications. But, in rare cases, you need to use them. Your application can not grant these, but it does ask the user to manually add the permissions to the app via the system settings.

Here is two of the special permissoins:

SYSTEM_ALERT_WINDOW

This permission allows the application to “draw over screen” like Facebook chat heads and Truecaller’s caller card. If your application needs this special permission, you can not use the regular request flow. Here, you should call with the intent to open the system permission management screen so the user can grant permission.

fun checkDrawOverlayPermission() {
  // Check if the app already has the permission
  if (Settings.canDrawOverlays(this)) {
    // Run your logic with pre-granted permission.
  } else {
    // Intent to request the permission
    val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:$packageName"))
    // Start the activity
    startActivityForResult(intent, REQUEST_CODE)
  }
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
  when (requestCode) {
    REQUEST_CODE -> {
      // Check if the app get the permission
      if (Settings.canDrawOverlays(this)) {
        // Run your logic with newly-granted permission.
      } else {
        // Permission not granted. Change your logic accordingly.
        // App can re-request permission anytime.
      }
    }
  }
}


WRITE_SETTINGS

This permission allows the application to “change system settings,” like admin applications and bluetooth manager applications. It is handled in the same way as other special permissions. But, you should use ACTION_MANAGE_WRITE_SETTINGS and Settings.System.canWrite() instead of ACTION_MANAGE_OVERLAY_PERMISSION and Settings.canDrawOverlays().

Happy coding!

Monitor application stability with Bugsnag to decide if your engineering team should be building new features on your roadmap or fixing bugs to stabilize your application.Try it free.

Topics:
kotlin ,marshmallow ,web dev ,mobile app development ,tutorial

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}