Android 11 users can’t grant background location permission?
Asked Answered
F

3

38

As of Android 11, apps targeting SDK 30+ will not show a user an option to grant background location permission to an app more than once. If not initially granted, it requires users to go to a settings page. How do we bring a user to the proper settings page?

When a feature in your app requests background location on a device that runs Android 11 or higher, the system dialog doesn't include a button to enable background location access. In order to enable background location access, users must set the Allow all the time option for your app's location permission on a settings page, as described in the guide on how to Request background location.

https://developer.android.com/about/versions/11/privacy/location#change-details

The user-visible label of the settings option that grants background location (for example, Allow all the time in figure 3). You can callgetBackgroundPermissionOptionLabel() to get this label. The return value of this method is localized to the user's device language preference.

https://developer.android.com/training/location/permissions#request-location-access-runtime

While Android provides a new API to get this settings page label, there is no documented API to bring up this settings page directly. The closest you can come is to bring up the app-specific settings page as described below. From there, the user must perform at least two taps to drill down to Permissions -> Location to enable background access. This is an onerous process that many users will fail to complete.

The lack of an API to bring up a settings page has been documented for a long time in this question, but is far more important as of the release of Android 11, as there is no other way of granting background permission.

How to programmatically open the Permission Screen for a specific app on Android Marshmallow?

It is possible to bring the user to the proper settings page the very first time the user is asked using code like this: requestPermissions(arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION), PERMISSION_REQUEST_BACKGROUND_LOCATION). This will work only once. If the user denies the permission (or even accidentally hits back or leaves the screen without granting), this will never work again, and the user must manually drill-down in settings as described above.

Is there there really no way for an app to help users grant background location permission after an initial denial other than to instruct them to go hunting for the right page in Settings?

Am I missing something? If not, isn’t this a major Android 11 usability problem?

A full example of code needed to trigger the proper settings page in a first time prompt, but the inability to do it ever again is here:

        if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            if (checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
                != PackageManager.PERMISSION_GRANTED
            ) {
                if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_BACKGROUND_LOCATION)) {
                    val builder =
                        AlertDialog.Builder(this)
                    builder.setTitle("This app needs background location access")
                    builder.setMessage("Please grant location access so this app can detect beacons in the background.")
                    builder.setPositiveButton(android.R.string.ok, null)
                    builder.setOnDismissListener {
                        requestPermissions(
                            arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION),
                            PERMISSION_REQUEST_BACKGROUND_LOCATION
                        )
                    }
                    builder.show()
                } else {
                    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
                        val builder =
                            AlertDialog.Builder(this)
                        builder.setTitle("Functionality limited")
                        builder.setMessage("Since background location access has not been granted, this app will not be able to discover beacons in the background.  Please go to Settings -> Applications -> Permissions and grant background location access to this app.")
                        builder.setPositiveButton(android.R.string.ok, null)
                        builder.setOnDismissListener {
                            val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
                            val uri: Uri = Uri.fromParts("package", packageName, null)
                            intent.data = uri
                            // This will take the user to a page where they have to click twice to drill down to grant the permission
                            startActivity(intent)
                        }
                        builder.show()
                    }
                }
            }
        } else {
            if (!shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)) {
                requestPermissions(
                    arrayOf(
                        Manifest.permission.ACCESS_FINE_LOCATION
                        /*Manifest.permission.ACCESS_BACKGROUND_LOCATION*/
                    ),
                    PERMISSION_REQUEST_FINE_LOCATION
                )
            } else {
                val builder = AlertDialog.Builder(this)
                builder.setTitle("Functionality limited")
                builder.setMessage("Since location access has not been granted, this app will not be able to discover beacons.  Please go to Settings -> Applications -> Permissions and grant location access to this app.")
                builder.setPositiveButton(android.R.string.ok, null)
                builder.setOnDismissListener {
                    val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
                    val uri: Uri = Uri.fromParts("package", packageName, null)
                    intent.data = uri
                    // This will take the user to a page where they have to click twice to drill down to grant the permission
                    startActivity(intent)
                }
                builder.show()
            }
        }


Fellmonger answered 7/10, 2020 at 15:3 Comment(6)
I have ran into the exact same problem. I agree that this is an issue for any developer who needs background location permission. I would like to add additional notes for other readers: (1) On API 30+ you will first need basic location permissions before asking for background location permission - otherwise it won't go to the permission screen at all. (2) When you ask for background location permission and it sends them to the permission request screen, it will only 'lock' the user out if they ONLY hit the back button. If they tap any of the options and then back the request will work again.Mcduffie
Facing the same issue, I read whole documentation but still did not figure out how can we use getBackgroundPermissionOptionLabel() Septenary
@StephenRuda you should post this comment as an answer. Thanks!Tetreault
This must be addressed as a bug in Android 11. If this is the intended behavior, then the programmers at Google shouldn't get paid.Anomalistic
medium.com/swlh/…Jook
Above medium article is misleading. It doesn't state that corase/fine location should be requested first before asking for background permission.Resplendence
T
22

Credits for the answer to @Stephen Ruda

I have run into the exact same problem. I agree that this is an issue for any developer who needs background location permission. I would like to add additional notes for other readers:

(1) On API 30+ you will first need basic location permissions before asking for background location permission - otherwise, it won't go to the permission screen at all.

(2) When you ask for background location permission and it sends them to the permission request screen, it will only 'lock' the user out if they ONLY hit the back button. If they tap any of the options and then back the request will work again.

Tetreault answered 6/1, 2021 at 21:13 Comment(7)
This is correct for a first-time prompt. However, my specific question is about what happens after the user fails to grant that permission (on purpose or by accident), and the app wants to help the user find the right permissions screen to grant the permission at a later time. This, as far as I can tell, is impossible to link directly.Fellmonger
What if you save the denied permission in the app as a SharedPreference, and later, if the user need the permissions, you check that preference, and if it was denied before, you open an intent to app permissions, using something like startActivity(new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, ...);Guyer
Yes, Luciano, this is exactly the approach my code takes as shown in the question. However, the settings landing screen for Settings.ACTION_APPLICATION_DETAILS_SETTINGS on stock Android is two levels up from where the background location permission needs to be granted. The problem is that many users never find it even when they want to.Fellmonger
Well, it is not the best solution, I am with this problem too. I have accessibility tools for blind users that need this permission, and now the flow for Android >= 11 has changed (they made it far more complicated by needing to give two permissions). I do not know if there is an alternative.Guyer
I've experience the item (2) and did not understand what was happening... Now I know I'm not the only one! Thank you!... As a side note I really think Google is messing things around lately.Film
I have written a library to solve all of this, hope someone find it useful. Any feedback is welcomed! github.com/warting/permissions-uiBoddie
Ran into the same issue. Google made that insanely complicated. It's unbelievable that the dialog is not shown at all when doing something that was recommended for android 10 (location fine AND background all at once). Code looks like shit now, as the flow is that stupid. I can confirm that direct navigation to location permission only works once. In every following attempt you must show an explanation to the user and hope he follows it (correctly). What a shame. This is ridiculous!Gaitskell
H
0

you cannot request foreground and background permission same time and cannot request background location permission without foreground permission granted. Therefore first you have to request foreground permission and then 'onRequestPermissionsResult' you can request background permission

First in onCreate

 if (ActivityCompat.checkSelfPermission(
            this,
            Manifest.permission.ACCESS_COARSE_LOCATION
    ) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(
            this,
            Manifest.permission.ACCESS_FINE_LOCATION
    ) != PackageManager.PERMISSION_GRANTED
    ) {

        ActivityCompat.requestPermissions(
                this,
                new String[]{
                        Manifest.permission.ACCESS_COARSE_LOCATION,
                        Manifest.permission.ACCESS_FINE_LOCATION
                },
                1
        );


   @Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
        ) {
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED
            ) {
                ActivityCompat.requestPermissions(
                        this,
                        new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION},
                        1
                );
            }
        }
    }
}
Hedwighedwiga answered 15/3, 2023 at 3:36 Comment(0)
K
-3

It shouldn't be any different but this works for me every time (not only once):

ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION}, REQUEST_BACKGROUND_LOCATION_PERMISSION);
Kimmie answered 13/4, 2021 at 11:59 Comment(3)
If you did not give foreground location permission previously, the request for background location permission will fail.Guyer
Obviously, you need to request it first. The question complains about not being able to ask for a background permission more than once. This code seems to work for me every time (after receiving the regular location permission).Kimmie
@SirCodesalot In that case, it is more than once.Dineen

© 2022 - 2024 — McMap. All rights reserved.