Many Android apps need access to the user’s location to provide all of their functionality, the most obvious examples are maps, weather forecast apps or taxi services. While most of the time it’s sufficient to access location information only when using the app, sometimes it is useful to get info about current location even when the user is not using the app or the phone at all. As an example, a launcher widget with a weather forecast should always show a forecast for the user’s current location even when he doesn’t use the app itself.

A few years ago, this was a simple task, but since Android 6, new restrictions and rules for accessing location or processing background tasks have appeared in every new version of Android. There are two reasons for it – the first one is of course privacy, since physical location is considered as sensitive personal information. And the second reason for restricting usage of location is battery, because mobile devices still have limited capacity of power and any background task, especially fetching location with usage of GPS, is battery consuming.

In this article, we’ll take a look at the changes of rules for accessing background location in each version of the system, and then show you how to properly solve this task in the latest version of the system, Android 12.

History of location related restrictions in Android versions

Android 5 and older

Freedom! Just fire up the background service and enjoy unlimited access to the user’s location.

Android 6

Runtime permissions

Starting with Android 6, apps are not granted all requested permissions after install anymore, but “dangerous” ones require explicit consent from the user at runtime. Location is one of these dangerous permissions, so you need to request access to the location first and properly handle the case when the user does not allow it. After the permission is granted, you can access the location even in the background as you wish.

Doze mode

Android 6 is the first version that contained a system-level battery saver called Doze. When the device is unplugged from the charger and it is stationary with screen off for a while, it enters Doze mode. In this mode, pending background tasks for syncing data, accessing network etc. are deferred and batched into short maintenance windows, nothing except very important tasks (such as the alarm clock to wake you up) is performed between these windows. You can read more information about Doze mode in the official documentation.

Reference: Android 6.0 Behavior Changes

Android 7

Improved Doze mode

Doze mode is further tweaked in Android 7, when the device is in Doze mode for some time, a second level of optimizations is now applied. That means bigger delays between maintenance windows and another set of restrictions, one of which is restricted access to location. Good thing is that nothing changes from the developer perspective if you already comply with the previous version of Doze from Android 6.

Reference: Android 7.0 Behavior Changes

Android 8

Background location limits

Since Android 8, apps now have access to the location only a few times each hour. This means that you can still register a callback for location updates either from FusedLocationProvide or LocationManager, but frequency of obtaining Location results is now limited.

Background execution limits

Background services are now limited in their execution time. You are no longer allowed to start background service from the background, they can be started only from the foreground. Even when the background service is started when the app is being used by the user, it will be killed a few minutes after the app goes to background. 

As an alternative to background services, Google recommended using jobs JobScheduler (or WorkManager & JobIntentService that internally uses JobScheduler in Android 8 and newer versions). While it was possible to workaround these limitations by starting foreground service, it is not a good solution nowadays, because randomly appearing notifications annoy the user and foreground services cannot be started from background from Android 12.

Reference: Background Location Limits, Background Execution Limits

Android 9

Foreground service permission

No new location or background tasks related restrictions were introduced in Android 9, but if you use foreground services, you need to include FOREGROUND_SERVICE permission in your manifest. This is a normal permission, so it is automatically granted to the app without requiring consent from the user.

Reference: Behavior changes: apps targeting API level 28+

Android 10

New permission for accessing background location

ACCESS_BACKGROUND_LOCATION permission is now required when accessing location while the app is in the background. Users can now decide if they allow the app access only when it is in the foreground or at any time.

Foreground service type parameter

Foreground services that access the user’s location now must contain the android:foregroundServiceType="location” parameter in the manifest. Your location callback won’t receive location updates without this parameter when you are targeting Android 10 SDK or newer.

Reference: Privacy changes in Android 10

Android 11

Changed interface for background location permission

In Android 10, dialog for requesting location permissions contained the “Allow all the time” option when background location permission is requested. From Android 11, background location permission can be granted only from the app settings. While the permission dialog still contains a link to the settings in this case, it is less convenient for a user to actually grant the permission.

One-time permissions

Users are now able to grant permission only this time. This means that the app has permission granted only when the activity or foreground service started from this activity is visible. When it is closed and launched again, the app has no longer access to the permission and you need to request it again. More info about one-time permissions can be found in the official documentation.

Incremental location permissions requests

System now also enforces a practice called “incremental requests”, which means that you cannot request background location permission along with foreground permissions. You must first request permission for the foreground location, and then, if really necessary, you can request the background location.

Background location permission is needed even in foreground services

Even though an application is considered to be running in the foreground when using the foreground service, it is now required to have ACCESS_BACKGROUND_LOCATION permission when accessing location from foreground service.

Reference: Location updates in Android 11

Android 12

Approximate location

Location permissions dialog now differentiates between fine and approximate location. This means that when you request ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION in one request, the user can decide to give the app only access to an approximate location.

Foreground service cannot be started from background

Apps can now start foreground services only from the foreground. This means that your music player can still use foreground services to show currently playing music, but it is no longer allowed to start these services when it’s just updating album cover photos in background. There are exceptions to this restriction which are listed in the documentation.

Exact alarm permission

When scheduling exact alarms, you are now required to request new SCHEDULE_EXACT_ALARM permission. They should be used only by reminder and alarm clock apps, so you should avoid them for planning data updates and other tasks that do not require exact time of execution. For more info about definition of exact alarms and new permission, refer to the documentation.

Reference: Behavior changes: Apps targeting Android 12, Foreground service launch restrictions

Implementation

This was really exhausting, wasn’t it? Now that you know what the rules for location access and background tasks are, we’ll take a look at some tips and best practices for implementing this feature.

First, think if you really need to access the user’s location periodically and when he doesn’t use the app. Accessing the location only when the app is in foreground is much easier, not just because of the implementation, but there are also additional requirements that you need to be compliant with when releasing the app to the Play Store. We’ll explore this in the next section.

Still not discouraged? All right, then, here we go. Let’s start with adding the permissions declaration to the Android Manifest:

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

If you don’t need access to a precise location, you can remove ACCESS_FINE_LOCATION and use just ACCESS_COARSE_LOCATION, it can make the approval process in the Play Store smoother.

Then request foreground permissions inside your app in appropriate situations, e.g. when the user wants to show his current location. Do not forget that you cannot request background permission at the same time! Ask for background location permission separately only when the user wants to use a feature that cannot fully work without it. Typical example can be the launcher widget, since it is always used in the background.

After that, you need to decide how you will schedule periodic tasks. In general, it is advisable to use a WorkManager, but in the case of a widget implementation it is better to use a JobIntentService that will be periodically triggered from the onUpdate() callback of the AppWidgetProvider. When using the WorkManager and widget together, beware of the problems that can occur, as described in this article. Here is example how to use JobIntentService from widget for accessing the location on background:

kotlin
class WidgetUpdateService : JobIntentService() {

    override fun onHandleWork(intent: Intent) {
        val appWidgetId = intent.extras?.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID) 
?: AppWidgetManager.INVALID_APPWIDGET_ID    
   
    if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
        val locationProvider = LocationServices.getFusedLocationProviderClient(this)
            
    // fetch location and do something with it
        }
    }

    companion object {

        private const val JOB_ID = 1

        fun enqueueWork(context: Context, appWidgetId: Int) {
            val intent = Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
            enqueueWork(context, WidgetUpdateService::class.java, JOB_ID, intent)
        }
    }
}

class LocationWidget : AppWidgetProvider() {

    override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
        super.onUpdate(context, appWidgetManager, appWidgetIds)
        appWidgetIds.forEach { appWidgetId ->
            WidgetUpdateService.enqueueWork(context, appWidgetId)
        }
    }
}

Then register the service in Manifest.xml and add WAKE_LOCK permission, since JobIntentService manages wakelocks automatically for you:

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

<service

    android:name=".WidgetUpdateService"
    android:exported="true
    android:permission="android.permission.BIND_JOB_SERVICE" />

Do not use foreground services or schedule exact alarms to start the services! Foreground services cannot be started from the background since Android 12, and exact alarms cannot be used without special permission either.

And that’s it for the implementation. After all, it wasn’t that hard, right? Just remember all the rules and restrictions, so you won’t face any unexpected issues.

Releasing to Google Play

So, the app is ready, well-tested and you want to deploy it to production. There are a few additional requirements that you need to comply with if you want your app to get approved. 

You need a privacy policy where you clearly describe that you access location data and you need to clarify how you process them. Link to this privacy policy must be present in your app listing inside the Play Store and in the app, along with a note that your app accesses the user’s location. This is not the only requirement, you also need to show prominent disclosure to the user before asking for background location permission. This can be usually implemented by a dialog with an information message that describes how the location information is processed and stored. More details about these requirements can be found in this article from Google

Since ACCESS_BACKGROUND_LOCATION is considered a sensitive permission, you need special approval from Google. You have to fill declaration form inside Play Console, it can be found at App Content > Sensitive app permission > Manage > Location permissions > Manage. Here you need to fill in a description for which feature the background position permission is needed. If your application contains more than one of these features, just describe one. The last step is to upload a video of the application walkthrough showing the feature you are describing. Make sure the video shows that the app meets all the relevant requirements (prominent disclosure, privacy policy link, user consent).

Unfortunately, even if you comply with all these requirements and your use case for background location is perfectly valid, you still have no guarantee that your app will get approved by Google.

Source code

 I hope that the information in the article was useful and that it helped you with solving your problem. You can find a sample app with the full source code on GitHub.

Are you interested in working together? We wanna know more. Let’s discuss it in person!

Get in touch >