Android Question How to request background locations with API 29 and above?

Sandman

Well-Known Member
Licensed User
Yesterday I did a lot of tests to figure out how things worked regarding permissions and background location (full report here). It's clear that background permission is more difficult to get approved since API 29.

How do we request permission for background locations in API 29 and above?


I found that API 29 got a new permission: ACCESS_BACKGROUND_LOCATION, but just requesting it doesn't solve the problem. I quote from the documentation:
Allows an app to access location in the background. If you're requesting this permission, you must also request either ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION. Requesting this permission by itself doesn't give you location access.

This is a hard restricted permission which cannot be held by an app until the installer on record whitelists the permission. For more details see PackageInstaller.SessionParams.setWhitelistedRestrictedPermissions(Set).
The documentation mentions setWhitelistedRestrictedPermissions, and looking at that page makes things really complicated for me.

(I also have a very vague memory about me reading somewhere that developers need to list some dangerous permissions when uploading the app in the console, and Google manually need to grant access to permissions if they think your use-case sounds reasonable. I'm not sure I remember that correctly, and I'm not sure that it's relevant to this situation.)
 

Sandman

Well-Known Member
Licensed User
The term "background location" is confusing by itself. The classic B4A background location example, isn't considered background tracking because there is foreground service. It is relevant for geofencing.
Yeah, this whole topic is quite confusing. And I'm sure it doesn't help that I don't use the correct terminology. :-/

I don't think that you need to call setWhitelistedRestrictedPermissions. Try it on an Android 10+ device.
I assume you mean this example? https://www.b4x.com/android/forum/threads/geofence-monitoring-a-region-in-the-background.84767/

In that case, I did try it on Android 11, with API levels 26, 27, 28, 29 and 30. (More info about those tests.) For 29 and 30, there is no option for "Always allow", not even on the settings page for the app.

There is an option "While using the app", which might be applicable for when we have a foreground service? But as far as I can tell your code example doesn't have a foreground service?
 

Sandman

Well-Known Member
Licensed User
After experimenting a bit more, and searching the forum, I found that I should add this to the manifest
B4X:
AddPermission(android.permission.ACCESS_BACKGROUND_LOCATION)
Doing so will add the option "Allow all the time" to the app settings page.
 

Sandman

Well-Known Member
Licensed User
I just found this very interesting thread at Stack Overflow:

How to open location permission settings in Android 30?

The top answer says:
First request permission for ACCESS_FINE_LOCATION (or coarse). Then if you got the permission, request permission for ACCESS_BACKGROUND_LOCATION. It will open directly the app location settings but only if you have Target SDK 30.
I'll investigate some more.
 

Sandman

Well-Known Member
Licensed User
I'll investigate some more.
I've investigated some more. The Stack Overflow post is correct, first request FINE and iff that is given, request BACKGROUND. The latter will open the apps location settings page, iff the app also have the permission from #4 in the manifest.

So there seems to be a happy path to gettin "Always allow", which is absolutely great.

There is also some sad issues to handle, that I don't know the best solution for yet. The worst seem to be the case where the user doesn't select anything else than Deny. If they do so twice, the request doesn't even display anymore. As far as I can tell, there are only two ways to solve this:
  1. Ask the user to manually go into app settings, to location, and pick "Always allow" - something many users would find way too complicated...
  2. Ask the user to uninstall the app and install it again and this time follow the happy path - not much good can come from asking the user to uninstall your app...
The best workaround I can think of at the moment is if we could find an intent to open the location settings. We could then create a user interface with "You need to do this to make the app work, please click here to open the settings". But I've searched the interwebs, and I'm not able to find that intent, so I'm not hopeful it exists.

UPDATE. The best I've been able to find is opening the main settings page for the app:
B4X:
Dim in1 As Intent
in1.Initialize("android.settings.APPLICATION_DETAILS_SETTINGS", "package:" & Application.PackageName)
StartActivity(in1)
Before doing so, one should probably instruct the user how to navigate to Permissions and then Location
 
Last edited:

José J. Aguilar

Well-Known Member
Licensed User
if we could find an intent to open the location settings.
Maybe this way from the GPS tutorial is still valid?
When the program starts we check whether the location features are enabled:

B4X:
Sub Activity_Resume
If Starter.GPS1.GPSEnabled = False Then
ToastMessageShow("Please enable the GPS device.", True)
StartActivity(Starter.GPS1.LocationSettingsIntent) 'Will open the relevant settings screen.
Else
Starter.rp.CheckAndRequest(Starter.rp.PERMISSION_ACCESS_FINE_LOCATION)
Wait For Activity_PermissionResult (Permission As String, Result As Boolean)
If Result Then CallSubDelayed(Starter, "StartGPS")
End If
End Sub
If not then we open the relevant settings screen.
 

OliverA

Expert
Licensed User
There is an option "While using the app", which might be applicable for when we have a foreground service?
Yes
But as far as I can tell your code example doesn't have a foreground service?
ALERT!!!!: I've not done geofencing before, I'm just going by what I see in the source of the example pointed out by you (@Sandman ) in post#3
I don't think it is necessary in this case. When the app sets up the geofence, it sets up an intent to be called when entering/exiting the fence an passes it to the geofencing API. That intent points to the GeofenceService. So after successfully setting up an area through the geofencing API, the system will start the GeofenceService when it detects that the area is entered. It will also call the GeofenceService when one exits the area. No background/foreground service necessary.
 
Last edited:

Sandman

Well-Known Member
Licensed User
I don't think it is necessary in this case. When the app sets up the geofence, it sets up an intent to be called when entering/exiting the fence an passes it to the geofencing API. That intent points to the GeofenceService. So after successfully setting up an area through the geofencing API, the system will start the GeofenceService when it detects that the area is entered. It will also call the GeofenceService when one exits the area. No background/foreground service necessary.
Yeah, that's how my thinking goes also. The only thing that troubles me is that @Erel mentioned foreground service. I'll do some real-world tests tomorrow and see what's what. I'll report back here if I find something solid.
 
Top