Android Runtime Permissions[Updated with Android 11] — Howtodoandroid

Velmurugan Murugesan
Howtodoandroid
Published in
6 min readMar 18, 2022

From the beginning with Android 6.0 Marshmallow, Google has introduced a new runtime permission model, users are not asked for permissions at the time of installation rather developers need to request the permissions at the run time. Only the permissions that are defined in the manifest file can be requested at run time.

This helps in protecting the privacy of an Android Device user. So for example you(your apps) have to request permissions to:

  1. Access sensitive user data (such as contacts and SMS).
  2. Access certain system features (such as camera and internet).

Depending on the feature, the system might grant the permission automatically or might prompt the user to approve the request.

Type of permissions

Android defines basically three types of permissions:

  1. Normal Permissions
  2. Signature Permissions
  3. Dangerous Permissions

Both Normal and Dangerous permissions must be defined in the Manifest file. But only Dangerous permissions are checked at runtime, Normal permissions are not.

Normal Permissions

These are required when your app needs to access data or resources outside the app’s sandbox, but where there’s very little risk to the user’s privacy or the operation of other apps.

If an app declares in its manifest that it needs normal permission, the system automatically grants the app that permission at install time. and users cannot revoke these permissions.

Permissions like INTERNET, BLUETOOTH, ACCESS_WIFI_STATE, ACCESS_NETWORK_STATE, etc are normal permissions.

For more details about the permission check the permission API reference page.

Signature Permissions

A permission that the system grants only if the requesting application is signed with the same certificate as the application that declared the permission. If the certificates match, the system automatically grants permission without notifying the user.

Permissions like BIND_AUTOFILL_SERVICE, BIND_INPUT_METHOD, BIND_NFC_SERVICE, etc are signature permissions.

Dangerous permissions

Some permissions may affect the user’s private information, or could potentially affect his data or the operation of other applications are called Dangerous Permissions. For example, the ability to read the user’s contacts is a dangerous permission.

Permissions like CAMERA, READ_CALL_LOG, ACCESS_FINE_LOCATION, READ_SMS, READ_EXTERNAL_STORAGE, etc aredangerous permissions.

For more details about the permission check the permission API reference page.

How to Check for permissions

If your app needs dangerous permission, you must check whether you have that permission every time you perform an operation that requires that permission.

you do it using the ContextCompat.checkSelfPermission() method with the permission name.

if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) 
== PackageManager.PERMISSION_GRANTED) {
// permission granted
} else {
// permission not granted
}

This will return two types of responses.

  • PackageManager.PERMISSION_GRANTED — This means permission granted. So you can proceed with your operations.
  • PackageManager.PERMISSION_DENIED — This means pemission not granted. So you need to ask the permission again or you can skip that process.

Request permissions

If the permission is not granted, we need to ask for permission to access certain features like camera, storage, etc.

To ask permission, we need to use ActivityCompat.requestPermission() method with the permission name and the request code.

First, we need to add the requested permission into the manifest.xml file.

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

Note: don’t forget to add all the needed permission into the manifest file. otherwise, run time permission won’t work.

ActivityCompat.requestPermissions( this, arrayOf(Manifest.permission.CAMERA), request_code )

This will pop up the permission dialog for the defined permission. Users can able to Allow or deny permission.

Asking Runtime permission

Handle the permission request response

Once the user responded to the permission request, the result will be received in the onRequestPermissionResult. based on the request code, we can check our permission granted or denied.

override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == 201) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "Permission granted", Toast.LENGTH_SHORT).show()
openCamera()
} else {
Toast.makeText(this, "Permission denied", Toast.LENGTH_SHORT).show()
}
}
}

Requesting multiple permissions

Previously, we checked about requesting single permission at a time. In android, it’salso possible to ask for multiple permission at a time. We can use the same ActivityCompat.requestPermissions() method as above by passing anarray of permissions.

ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE),
request_code
)
Runtime permission for camera
Runtime permission to access photos

But, we cannot check the multiple permission at a time. We need to check the permission status individually.

Handle the permission denial

When the user is denied permission for the first time. It means that the user doesn’t understand the purpose of the particular permission. So, we need to show some additional messages to explain the purpose of the permission.

To do that we need to check for the shouldShowRequestPermissionRationale() method with the permission to check, whether the permission is already denied or not. If denied already need to show some additional message using the dialog.

if (checkPermission(Manifest.permission.CAMERA)) {
openCamera()
} else {
if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
AlertDialog.Builder(this)
.setMessage("Need camera permission to capture image. Please provide permission to access your camera.")
.setPositiveButton("OK") { dialogInterface, i ->
dialogInterface.dismiss()
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.CAMERA),
201
)
}
.setNegativeButton("Cancel") { dialogInterface, i ->
dialogInterface.dismiss()
}
.create()
.show();
} else {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.CAMERA,Manifest.permission.READ_EXTERNAL_STORAGE),
201
)
}
}
Additional Permission Message

One-time permissions

Starting in Android 11, whenever your app requests permission related to location, microphone, or camera, the user-facing permissions dialog contains an option called Only this time. If the user selects this option in the dialog, your app is granted temporary one-time permission.

Android one-time permission

checks more about one-time permission.

Android Runtime Permission Example

check out my final code for Runtime permission MainActivity.kt,

class MainActivity : AppCompatActivity() {

lateinit var btnCamera: Button
lateinit var btnLocation: Button

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

btnCamera = findViewById(R.id.btnCamera)
btnLocation = findViewById(R.id.btnLocation)

btnCamera.setOnClickListener {

if (checkPermission(Manifest.permission.CAMERA)) {
//openCamera()
} else {
if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
AlertDialog.Builder(this)
.setMessage("Need camera permission to capture image. Please provide permission to access your camera.")
.setPositiveButton("OK") { dialogInterface, i ->
dialogInterface.dismiss()
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.CAMERA),
201
)
}
.setNegativeButton("Cancel") { dialogInterface, i ->
dialogInterface.dismiss()
}
.create()
.show();
} else {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.CAMERA,Manifest.permission.READ_EXTERNAL_STORAGE),
201
)
}
}


/**/

}

}

private fun checkPermission(permission: String): Boolean {
return ContextCompat.checkSelfPermission(
this,
permission
) == PackageManager.PERMISSION_GRANTED
}

override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == 201) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "Permission granted", Toast.LENGTH_SHORT).show()
//openCamera()
} else {
Toast.makeText(this, "Permission denied", Toast.LENGTH_SHORT).show()
}
}
}

}

also, updated Manifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.velmurugan.runtimepermissionandroid">

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

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.RuntimePermissionAndroid">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

Thanks for reading. let me know your feedback on comments. Also, you can download this example on GITHUB.

Originally published at https://howtodoandroid.com on March 18, 2022.

--

--

Velmurugan Murugesan
Howtodoandroid

Lead Android Engineer @htcindia | @github contributor | Blog writer @howtodoandroid | Quick Learner