How to Reliably Fetch Location in Background on Newest Androids
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:
<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:
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:
<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.