Register broadcast receiver dynamically does not work - BluetoothDevice.ACTION_FOUND Register broadcast receiver dynamically does not work - BluetoothDevice.ACTION_FOUND android android

Register broadcast receiver dynamically does not work - BluetoothDevice.ACTION_FOUND


Apart from the fact that starting with Android 6.0 you have to have the ACCESS_COARSE_LOCATION permission to receive ACTION_FOUND (as @siniux already mentioned), there's another related thing:

ACCESS_COARSE_LOCATION is among dangerous permissions that you have to explicitly request from user at run time (another security improvement that came in 6.0).

To diagnose, you can run adb logcat | grep BroadcastQueue, and see something like this:

W/BroadcastQueue: Permission Denial: receiving Intent {     act=android.bluetooth.device.action.FOUND flg=0x10 (has extras) }     to ProcessRecord{9007:com.examplepackage} (pid=9007, uid=10492)     requires android.permission.ACCESS_COARSE_LOCATION due to sender    com.android.bluetooth (uid 1002)

So, the correct procedure for BT device discovery on Marshmallow is as follows:

  1. Have ACCESS_COARSE_LOCATION permission requirement in manifest along with usual bluetooth permissions:

    <uses-permission android:name="android.permission.BLUETOOTH" /><uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /><uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  2. Ensure you have run-time permission for ACCESS_COARSE_LOCATION

    protected void checkLocationPermission() {    if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)            != PackageManager.PERMISSION_GRANTED) {        ActivityCompat.requestPermissions(this,                new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},                REQUEST_COARSE_LOCATION);    }}@Overridepublic void onRequestPermissionsResult(int requestCode,                                   String permissions[], int[] grantResults) {    switch (requestCode) {    case REQUEST_COARSE_LOCATION: {        if (grantResults.length > 0                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {            proceedDiscovery(); // --->        } else {            //TODO re-request        }        break;    }}

    }

  3. Register a broadcast receiver for ACTION_FOUND and call BluetoothAdapter.startDiscovery()

    protected void proceedDiscovery() {    IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);    filter.addAction(BluetoothDevice.ACTION_NAME_CHANGED);    registerReceiver(mReceiver, filter);    mBluetoothAdapter.startDiscovery();}

Funny thing about ACTION_NAME_CHANGED. Although 6.0 won't deliver you ACTION_FOUND without the permission for coarse location, you'll still get ACTION_NAME_CHANGED events, which are usually teamed up with ACTION_FOUND when devices are discovered. I.e. you get both events, so without the permission, you can still handle ACTION_NAME_CHANGED for pretty much the same behavior. (Gurus, correct me if I'm wrong)


I was having a similar problem with a Broadcast Receiver.Then I found this:https://developer.android.com/about/versions/marshmallow/android-6.0-changes.html#behavior-hardware-id

Basically, on 6.0 you must use the location permission to scan for Bluetooth devices.


What you missed is that you need to start a device discovery

First, get the bluetooth adapter

BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();

After that, you start the discovery by calling

mBtAdapter.startDiscovery();

You should read the details here as well, e.g. about cancelDiscovery()http://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#startDiscovery%28%29

P.S. Also, it is suggested to use context.getSystemService(Context.BLUETOOTH_SERVICE) to get the BluetoothAdapter on API 18+, according to official doc.

To get a BluetoothAdapter representing the local Bluetooth adapter,when running on JELLY_BEAN_MR1 and below, call the staticgetDefaultAdapter() method; when running on JELLY_BEAN_MR2 and higher,retrieve it through getSystemService(Class) with BLUETOOTH_SERVICE.

Edit:Be reminded that you need BLUETOOTH_ADMIN permission to startDiscovery()