Exploring Android Q: Location Permissions

location

Last week we saw the announcement of the Android Q beta release 🎉 With this version of Android comes a collection of exciting changes which we need to get our apps ready for. In this set of articles I’m going to be diving into each one of these so that we are fully prepared for getting our apps ready!

Note: You can find the code for this article here.

As outlined in the beta release notes for Android Q, one of the changes we are seeing introduced is the way in which we work with user locations inside of our applications – these changes affect the access of the location in both the foreground and background. This gives our users more control of when they wish apps to be able to access their location – allowing them to restricting it to only when the app is currently in use. In this post I want to take a quick dive into how these changes will effect apps, along with what we need to do to adapt to these changes.

Foreground Location Permission

There may be cases where an application requires access to the users location whilst there is a continuation of user-initiated actions. For example, maybe your app delivers navigation to the user – once the user navigates to their home screen from your app, you’ll want to continue having access to their location in-order to continue serving navigation to them. If you’re utilising a foreground service to handle this functionality then we’ll only need foreground access to the users location, as there’s no reason for us to access the users location from the background (we’ll cover the case where we may not be using a foreground service in the next section.). When your application requires access to this location data, it’s important to let your user know why when accessing it.

As we mentioned above, any service of this kind that makes use of the users location needs to be declared as a location foreground service. This can be done by making use of the foregroundServiceType when declaring your service within your manifest file.

<service
    android:name="ForegroundService"
    android:foregroundServiceType="location"/>

Before we try to kick off our foreground service we need to ensure that we have the required permission from the user to do so. We can do this by checking for the ACCESS_COARSE_LOCATION permission. Now, this isn’t a new permission – in fact, it’s been around since API level 1. However, we only previously needed to define within our application manifest file – now we must request this permission at runtime. You can see how this now gives our user much more control over how this permission is used.

val hasLocationPermission = ActivityCompat.checkSelfPermission(this, 
    Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED

if (hasLocationPermission) {
    // handle location update
} else {
    ActivityCompat.requestPermissions(this,
        arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION), REQUEST_CODE_FOREGROUND)
}

In this code above you can see that we begin by checking whether we have the location permission. If so, we can continue to handle the location flow or otherwise, we must request the permission from the user. If this is the case, then we will receive the permission state within the onRequestPermissionsResult() callback within our calling class.

When we request this permission, our user will be displayed the following dialog:

device-2019-03-14-075606

As you can see, the intent of the permission has been made quite clear – our app will only have access to their location whilst in the foreground (as in, the app is being used). If the user has denied this permission at any point and we request it again, then they will be shown a slight variation of the dialog:

device-2019-03-14-075617

Similar to how the runtime permissions in android usually work, we know are shown an option where the user can request not to be asked again. Because of this functionality, it’s important that you only ask for this foreground location access at the point it is required. This guides your user as to why the permission is required in the current context, rather than give the feeling that it is being asked for no reason.

Background location permission

When it comes to accessing the user location when our app is backgrounded, things work a little bit differently. We first need to add a new permission to our manifest file, this is the ACCESS_BACKGROUND_LOCATION permission. Although this is declared in the manifest, it can still be revoked at any time by the user.

<manifest>
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
</manifest>

Note that here we aren’t required to add the foregroundServiceType service type to our service declaration, this is because we don’t need momentary permission to run outside of our app – this background permission already gives our application the ability to do this.

As well as the above, the permission needs to be granted by the user at runtime. So before we try and access the users location from the background, we need to ensure that we have the required permission from the user to do so. We can do this by checking for the ACCESS_BACKGROUND_LOCATION permission. You can see again how this now gives our user much more control over how this permission is used and avoids background location access being abused.

Our code for checking the permission may look something like this:

val hasForegroundLocationPermission = ActivityCompat.checkSelfPermission(this, 
    Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED

if (hasForegroundLocationPermission) {
    val hasBackgroundLocationPermission = ActivityCompat.checkSelfPermission(this, 
        Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED

    if (hasBackgroundLocationPermission) {
        // handle location update
    } else {
        ActivityCompat.requestPermissions(this,
            arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION), REQUEST_CODE_BACKGROUND)
    }
} else {
    ActivityCompat.requestPermissions(this,
        arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION,
            Manifest.permission.ACCESS_BACKGROUND_LOCATION), REQUEST_CODE_BACKGROUND)
}

You’ll notice that this code is pretty similar to the checks that we defined for the foreground location permission. Here we check for both permissions, giving us a fallback in location retrieval should the user deny us background access for some reason.

When we request this permission, our user will be displayed the following dialog:

device-2019-03-14-075519

The intent of the permission has been made quite clear – our app will be able to access their location whilst in the background. If the user has denied this permission at any point and we request it again, then they will be shown a slight variation of the dialog:

device-2019-03-14-075633

Similar to how the runtime permissions in android usually work, we know are shown an option where the user can request not to be asked again. Because of this functionality, it’s important that you only ask for this background location access at the point it is required. This guides your user as to why the permission is required in the current context, rather than give the feeling that it is being asked for no reason.


We can see from this article that Android Q changes the way in which our applications works with location permissions. Our apps will no longer be able to freely access the user locations from services, whether in the foreground or background. To support backward compatibility, if your application doesn’t target Q then the ACCESS_BACKGROUND_LOCATION permission will be added if the COARSE or FINE permissions are declared. Similarly, requesting either of the two at runtime will also grant the permission.

These changes give our users more control over how applications access their location, allowing us to create apps with the users privacy and safety in mind. Any questions about these location changes? Feel free to reach out in the comments!