# Google Play services and runtime permissions

<br />

Since Android 6.0 Marshmallow, Android uses a
[permissions model](https://developer.android.com/preview/features/runtime-permissions.html) that streamlines the app install and
auto-update process. Permissions are requested at runtime instead of before
app installation. Additionally, users can choose to deny specific permissions.
To give users this flexibility, you need to make sure that your app behaves as
expected when a user enables or disables a specific permission.

Google Play services itself has runtime permissions that users can choose to
deny separately from those permissions specifically requested by your app.
Google Play services automatically obtains all permissions it needs to support
its APIs. However, your app should still check and request runtime permissions
as necessary and appropriately handle errors in cases where a user has denied
Google Play services a permission required for an API your app uses.

It's good practice to manage the user's expectations in setting permissions
that the runtime may require. The following best practices will help you avoid
potential issues.

## Prerequisites

You'll need to declare permissions in your `AndroidManifest.xml` file.
For example:

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

## Guidelines

### Verify permissions before calling APIs

Once you've declared the APIs that you want to use in your `AndroidManifest.xml`
file, verify that you have the required permission before calling an API. This
can be done using the `checkSelfPermission` method of
[`ActivityCompat`](https://developer.android.com/reference/androidx/core/app/ActivityCompat) or [`ContextCompat`](https://developer.android.com/reference/androidx/core/content/ContextCompat).

If the call returns false this means the permissions aren't granted and you
should use `requestPermissions` to request them. The response to this is
returned in a callback which you will see in the next step.

For example:

### Kotlin

```kotlin
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
        != PackageManager.PERMISSION_GRANTED) {
            // Request Permissions Now
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                REQUEST_LOCATION_PERMISSION_CODE)
    } else {
        // permission has been granted, continue as usual
        val locationResult = LocationServices
            .getFusedLocationProviderClient(this /* Context */)
            .lastLocation
    }
```

### Java

```java
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
        != PackageManager.PERMISSION_GRANTED) {
        // Request Permissions Now
        ActivityCompat.requestPermissions(
            this,
            new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
            REQUEST_LOCATION_PERMISSION_CODE);
    } else {
        // permission has been granted, continue as usual
        Task locationResult = LocationServices
            .getFusedLocationProviderClient(this /** Context */)
            .getLastLocation();
    }
```

### Implement the request permission callback

If the permission your app needs hasn't been granted by the user, the
[`requestPermissions`](https://developer.android.com/reference/androidx/core/app/ActivityCompat#requestPermissions(android.app.Activity,java.lang.String%5B%5D,int)) method should be called to ask the
user to grant them. The response from the user is captured in the
[`onRequestPermissionsResult`](https://developer.android.com/reference/android/app/Activity.html#onRequestPermissionsResult(int,%20java.lang.String%5B%5D,%20int%5B%5D)) callback. Your app should
implement this and always check the return values because the request could be
denied or canceled. You can also request and check for multiple permissions at
once--the following sample only checks for a single permission.

### Kotlin

```kotlin
    fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array,
        grantResults: IntArray
    ) {
        if (requestCode == REQUEST_LOCATION_PERMISSION_CODE) {
            if (grantResults.singleOrNull() == PackageManager.PERMISSION_GRANTED) {
               // We can now safely use the API we requested access to
               val locationResult: Task = LocationServices
                    .getFusedLocationProviderClient(this /* Context */)
                    .lastLocation // Request the last known location.
            } else {
                // Permission was denied or request was cancelled
            }
        }
    }
```

### Java

```java
    public void onRequestPermissionsResult(int requestCode,
                                            String[] permissions,
                                            int[] grantResults) {
        if (requestCode == REQUEST_LOCATION_PERMISSION_CODE) {
            if(grantResults.length == 1
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // We can now safely use the API we requested access to
                Task locationResult = LocationServices
                    .getFusedLocationProviderClient(this /** Context */)
                    .getLastLocation(); // Request the last known location.
            } else {
                // Permission was denied or request was cancelled
            }
        }
    }
```

### Show the permission rationale

If the permissions your app requests are necessary for the core features of the
app and the user has previously denied the permission request, your app should
display an additional explanation before requesting the permission again. Users
are more likely to grant permissions when they understand the why the permission
is needed and the immediate benefit for them.

In this case, before the calling `requestPermissions`, you should call
[`shouldShowRequestPermissionRationale`](https://developer.android.com/reference/androidx/core/app/ActivityCompat#shouldShowRequestPermissionRationale(android.app.Activity,java.lang.String)). If it returns
true, you should create some UI to display additional context for the
permission.

For example, your code might look like this:

### Kotlin

```kotlin
    private const val REQUEST_LOCATION_PERMISSION_CODE = 2
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
        != PackageManager.PERMISSION_GRANTED) {
        // Check Permissions Now
        if ActivityCompat.shouldShowRequestPermissionRationale(
            this,
            Manifest.permission.ACCESS_FINE_LOCATION
        ) {
            // Display UI and wait for user interaction
        } else {
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                REQUEST_LOCATION_PERMISSION_CODE)
        }
    } else {
        // Permission has already been granted, continue as usual
        val locationResult: Task = LocationServices
            .getFusedLocationProviderClient(this /* Context */)
            .lastLocation
    }
```

### Java

```java
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
        != PackageManager.PERMISSION_GRANTED) {
        // Check Permissions Now
        private static final int REQUEST_LOCATION_PERMISSION_CODE = 2;
        if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                Manifest.permission.ACCESS_FINE_LOCATION)) {
            // Display UI and wait for user interaction
        } else {
            ActivityCompat.requestPermissions(
                this, new String[]{Manifest.permission.LOCATION_FINE},
                REQUEST_LOCATION_PERMISSION_CODE);
        }
    } else {
        // permission has been granted, continue as usual
        Task locationResult = LocationServices
            .getFusedLocationProviderClient(this /** Context */)
            .getLastLocation();
    }
```

Google Play services API calls will automatically display either a dialog (if
the client is instantiated with an `Activity`) or system tray notification (if
the client is instantiated with a `Context`) that the user can tap to start the
permissions resolution intent. Calls will be enqueued and retried once the
permission is granted.