111
votes

After upgrading to Android version 6.0 Bluetooth Low Energy (BLE) scanning will only work if Location services are enabled on the device. See here for reference: Bluetooth Low Energy startScan on Android 6.0 does not find devices

Basically, you need to have the permission enabled for the app as well as on for the phone. Is this a bug? Is it possible to scan without location services actually enabled? I don't want to have to have location for all my apps.

EDIT I failed to mention that I am using the startScan() method in BluetoothLeScanner provided in API 21. I am okay with the course and fine location permissions in the manifest that this method require. I just don't want the users of my app to have to enable location services on their device (GPS, etc.) to use my app.

Previously, the startScan() method would run and return results with the Location services disabled on the phone. On Marshmallow, however, the same application would "scan" but silently failed and returned no results when location services were not enabled on the phone and course/fine location permissions were still in the manifest.

9
What devices are you using? I ran into the same problem with a Moto G 2nd Generation. Moto G 1st Generation and Nexus 6 run fine with exact the same code without explicitly enabling location services.shadowhorst
I've noticed it on any device running Marshmallow - Nexus 5X, Samsung S6, Samsung S7, LG G4V-PTR

9 Answers

96
votes

No, this is not a bug.

This issue was brought up to Google where they responded saying that this was the intended behavior and they won't fix it. They directed developers to this site where it points out that location permission is now needed for hardware identifier access. It is now the developer's responsibility to make their users aware of the requirement.

In the issue, however, it doesn't address why Location services (GPS, etc.) are required and it doesn't seem like they are going to revisit the issue to explain this since it has been marked as the intended behavior.

To answer the second part of the question: Yes, it is possible to scan without enabling Location services. You can do a Bluetooth classic scan using BluetoothAdapter.getDefaultAdapter().startDiscovery() and that will work with Location services off. This will discover all Bluetooth devices, BLE and otherwise. However, BLE devices won't have a scan record that they would have had if they were seen as a result of startScan().

11
votes

I solved this by setting targetSdkVersion to 22 in the Gradle file. You must declare ACCESS_COARSE_LOCATION in the manifest but, BLE scanning will work even if the user denies this permission from App Settings.

This is just a hack to avoid requesting location permission. It's better to target the latest android versions.

Edit

This solution should no longer be used as Google Play will require that new apps target at least Android 8.0 (API level 26). Apps should request for location permission for BLE scanning.

10
votes

What I found is that after Android 6 you must grant ACCESS_COARSE_LOCATION permission. But on some devices is also necessary your phone location service (GPS) to be switched on, so you can discover peripheral devices. I found that using Nexus 5x, with Android 7.0.

3
votes

I also tried this on manifest but did not request permission, not sure why. Is you app prompting for Location permission on startup? If it's not, we need to request for permission on runtime.

Also you can check this to test if your app is working fine:

Open Settings > Apps > YourApplication > Permissions and enable Location and then try to scan for results.

Location will be listed here only if you have provided ACCESS_COARSE_LOCATION on manifest.

3
votes

You can use BluetoothAdapter.startDiscovery().
It will scan for both Bluetooth Smart and classic Bluetooth devices, but location services do not need to be enabled.
(You still need ACCESS_COARSE_LOCATION permissions on Android 6.)

You can call BluetoothDevice.getType on found devices to filter for Bluetooth Smart / Low Energy devices.

1
votes

You can scan BLE devices without location access using CompanionDeviceManager (API26). https://developer.android.com/reference/android/companion/CompanionDeviceManager.

1
votes

after you add ACCESS_COARSE_LOCATION to Manifest, ask for permission on runtime:

 public void checkPermission() {
            if (Build.VERSION.SDK_INT >= 23) {
                if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && checkSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {

            } else {
                ActivityCompat.requestPermissions(this, new String[]{
                        Manifest.permission.ACCESS_FINE_LOCATION,
                        Manifest.permission.ACCESS_COARSE_LOCATION,}, 1);
            }
        }
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
        } else {
            checkPermission();
        }
    }

worked for me!

0
votes

Well, I have looked at my code written in Eclipse and I use there the startScan (API 21) function without declaring location stuff in manifest file. I still get the proper callback. Have you tried running the code without the location declaration? In the other hand - you can use the deprecated startLeScan (API 18) which does not require these permissions. However, in my opinion searching and reading desired characteristic in service is more complicated with API 18 methods.

0
votes

From what I recently noticed on android 8.0, it is not required to turn on your GPS to do a BLE Scan, but you have to declare it in the manifest, but the user must allow the permission.

Android will prompt the user to allow location permission when you attempt to do a scan with startScan() method. Your scan will fail if the permission is not allowed.