Prompting for location permission when NOT monitoring in background?
Asked Answered
A

2

8

I'm using the Android Beacon Library. Since Marshmallow, I am seeing the following error, as expected and documented.

Permission denial: Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission to get scan results

I have my ranging code in a Fragment, and the RuntimeException is thrown when the Fragment is on screen, as expected as I'm not prompting for permission. If I hit the home button, Pause is called, and I'm unbinding beaconManager, and the exception is no longer thrown, again, as expected. Coming back to Activity from previous app list, the RuntimeException is thrown again each scan.

Why is the exception thrown when in the foreground even though I have added the permission to the AndroidManifest?

According to the documentation HERE, you only need to prompt the user for location permission if you are performing a scan in the background?

you'll get the following error in LogCat when you try to do a bluetooth scan in the background, and no beacons will be detected

Does this mean ONLY a background scan, or am I misinterpreting the documentation, and you must prompt regardless? I want to avoid the extra (off-putting perhaps to nervous users) prompt for permission inside the app if avoidable!!

My Manifest contains -

<uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION"/>

My snipped code from the Fragment is -

public class NearMeNowFragment extends Fragment implements BeaconConsumer {

//Beacon manager stuff
private BeaconManager beaconManager;

<SNIP>


@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    beaconManager = BeaconManager.getInstanceForApplication(getActivity());
    beaconManager.getBeaconParsers().add(new BeaconParser().
            setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"));
}


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View rootView = inflater.inflate(R.layout.near_me_now, container, false);

    //Set up beacon stuff if Bluetooth available
    if (verifyBluetooth(false)) {
        beaconManager.bind(this);
    }

    <SNIP>

    return rootView;
}


/***************************
Beacon config methods
****************************
*/

@Override
public void onBeaconServiceConnect() {
    //update all scan periods
    beaconManager.setForegroundScanPeriod(1100l);
    beaconManager.setForegroundBetweenScanPeriod(8900l);
    beaconManager.setAndroidLScanningDisabled(true);
    try {
        beaconManager.updateScanPeriods();
    } catch (RemoteException e) {
        e.printStackTrace();
    }

    beaconManager.setRangeNotifier(new RangeNotifier() {
        @Override
        public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
            if (beacons.size() > 0) {
                <SNIP>
                //handling beacons here
                ...
            } else {
                Log.i(TAG, "ZERO BEACONS IN SCAN");
            }
        }
    });

    try {
        beaconManager.startRangingBeaconsInRegion(new Region(TAG, null, null, null));
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}

@Override
public Context getApplicationContext() {
    return getActivity().getApplicationContext();
}

@Override
public void unbindService(ServiceConnection serviceConnection) {
    getActivity().unbindService(serviceConnection);
}

@Override
public boolean bindService(Intent intent, ServiceConnection serviceConnection, int mode) {
    return getActivity().bindService(intent, serviceConnection, mode);
}


@Override
public void onPause() {
    super.onPause();

    if(beaconManager.isBound(this)){
        beaconManager.unbind(this);
    }
}

@Override
public void onResume() {
    super.onResume();

    beaconManager.bind(this);
}

@Override
public void onDestroy() {
    super.onDestroy();
    if(beaconManager.isBound(this)){
        beaconManager.unbind(this);
    }
}

}

My actual Logcat error is

W/Binder﹕ Caught a RuntimeException from the binder stub implementation. java.lang.SecurityException: Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission to get scan results

Allowedly answered 15/10, 2015 at 13:25 Comment(0)
T
23

To clarify the requirement is different depending on whether you set targetSdkVersion 23 in your build.gradle file.

If you target SDK 23 (Marshmallow) or higher:

  • You must declare android.permission.ACCESS_COARSE_LOCATION or android.permission.ACCESS_FINE_LOCATION in your manifest.
  • You must prompt a user for permission as described in my blog post here.
  • The user must accept this permission and not turn it off.
  • If any of the above requirements are not met, you won't be able to detect beacons in the foreground or the background.

If you target SDK 22 or lower:

  • You may optionally declare android.permission.ACCESS_COARSE_LOCATION or android.permission.ACCESS_FINE_LOCATION in your manifest.
  • The user may turn on or off the permission by going to settings.
  • If one of the permissions is not granted, you will still be able to detect beacons in the foreground, but not in the background.

See here and here for more details.

Townie answered 15/10, 2015 at 14:0 Comment(6)
Excellent! I am now using the prompt and getting permission. I just wanted to avoid this if I was missing something obvious. It might be worth updating the first paragraph of "requesting_permission" documentation to include no beacons in background OR foreground?Allowedly
I just read the conclusion in your Blog post, it contradicts what you have said above?? QUOTE - "If you have a beacon app that only needs to work in the foreground, no changes are needed. If you have a beacon app that needs to work in the background, you need to update it to request proper location permissions as described in the first section.". My app is only working in the foreground right??Allowedly
I must not be explaining myself well. :) If I change the statement to "If you have an existing beacon app that does not yet target SDK 23 and only needs to work in the foreground, no changes are needed." does this make the perceived contradiction go away?Townie
Yes .. I just wanted to clarify and hopefully avoid anyone in future making the same mistake I did :) The solution was obvious really, but my interpretation of it led me up the garden path. So any scanning on SDK 23 must prompt at run time for permission. Full stop, the end :)Allowedly
@Townie i followed your tutorial but when i grant to location access on my device it stops to scan beacons.Lepper
Please open a new question showing your logcat output and permissions in settings for the appTownie
S
1

I had the same issue with my app when I tested it on a Marshmallow device. To avoid prompting the user for permission, I just changed the targetSdkVersion to 22. The compileSdkVersion can remain at 23. After you change build.gradle, you may need to run Tools > Android > Sync Project with Gradle Files. If you use Eclipse, the targetSdkVersion is in the manifest file.

Sandon answered 5/4, 2016 at 17:33 Comment(2)
Yes you can target a lower SDK, but this isn't a solution for those of us who want to be at the bleeding edge. To remain up to date with latest version / devices, it will be necessary to prompt for access.Allowedly
I just want to provide an option to avoid prompting the user. Some apps do not need to be targeted to the latest SDK. You have to use your judgment.Sandon

© 2022 - 2024 — McMap. All rights reserved.