Willi Mentzel
Willi Mentzel

Reputation: 29844

BroadcastReceiver for Bluetooth device discovery works on one device but not on another

Code:

I use the following code taken from here with a target API level 23 (and minimum API level 18).

private final BroadcastReceiver mReceiver = new BroadcastReceiver()
{
    public void onReceive(Context context, Intent intent)
    {
        String action = intent.getAction();

        if (BluetoothDevice.ACTION_FOUND.equals(action))
        {
            bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            deviceNameTextView.setText(bluetoothDevice.getName());
        }
    }
};

On a button pressed event I call:

IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter);
mBluetoothAdapter.startDiscovery(); // was initialized successsfully

My AndroidManifest.xml contains:

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

Devices:

  1. Samsung Galaxy S III (API level 18)
  2. Sony Xperia Z3 (API level 23)

Facts:

What is wrong?

Update 1: Since API level 23 permissions may have to be requested at run time. Yvette pointed me to that, thank you! Unfortunately it didn't solve my problem.

What speaks against her theory is the following:

mBluetoothAdapter.startDiscovery() returns true, which means success (see here).

// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity , Manifest.permission.BLUETOOTH_ADMIN);

if(permissionCheck == PackageManager.PERMISSION_GRANTED)
    Log.i("info", "Permission granted!");
else
    Log.i("info", "Permission not granted!");

Running this code with BLUETOOTH_ADMIN and BLUETOOTH returns both times:

Permission granted!

Upvotes: 2

Views: 3184

Answers (2)

Willi Mentzel
Willi Mentzel

Reputation: 29844

When doing some research, I found the following article from the official documentation regarding changes in Android 6.0 (API level 23).

To access the hardware identifiers of nearby external devices via Bluetooth and Wi-Fi scans, your app must now have the ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permissions:
- WifiManager.getScanResults()
- BluetoothDevice.ACTION_FOUND
- BluetoothLeScanner.startScan()

So, I was missing the permissions ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION all along. But just adding them in the AndroidManifest.xml file is not enough. You have to request those privileges at run time like Yvette suggested.

You can find here how you can do that or just use this piece of code I wrote to get the permissions needed for Bluetooth discovery.

final int CODE = 5; // app defined constant used for onRequestPermissionsResult

String[] permissionsToRequest =
{
    Manifest.permission.BLUETOOTH_ADMIN,
    Manifest.permission.BLUETOOTH,
    Manifest.permission.ACCESS_FINE_LOCATION,
    Manifest.permission.ACCESS_COARSE_LOCATION
};

boolean allPermissionsGranted = true;

for(String permission : permissionsToRequest)
{
    allPermissionsGranted = allPermissionsGranted && (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED);
}

if(!allPermissionsGranted)
{
    ActivityCompat.requestPermissions(this, permissionsToRequest, CODE);
}

mBluetoothAdapter.startDiscovery();

This code is assuming that the user grants the permissions (for simplicity). If you want your app to behave differently when the permissions are not granted see "Handle the permissions request response" in this article.

Upvotes: 5

user3956566
user3956566

Reputation:

Sounds like you're not managing runtime permissions for sdk 23 and higher. The phone with the sdk of 23 will either silently ignore any requests that require runtime permissions or crash.

Also see in the docs System Permissions.

If your app lists normal permissions in its manifest (that is, permissions that don't pose much risk to the user's privacy or the device's operation), the system automatically grants those permissions. If your app lists dangerous permissions in its manifest (that is, permissions that could potentially affect the user's privacy or the device's normal operation), the system asks the user to explicitly grant those permissions. The way Android makes the requests depends on the system version, and the system version targeted by your app:

If the device is running Android 6.0 (API level 23) or higher, and the app's targetSdkVersion is 23 or higher, the app requests permissions from the user at run-time. The user can revoke the permissions at any time, so the app needs to check whether it has the permissions every time it runs. For more information about requesting permissions in your app, see the Working with System Permissions training guide. If the device is running Android 5.1 (API level 22) or lower, or the app's targetSdkVersion is 22 or lower, the system asks the user to grant the permissions when the user installs the app. If you add a new permission to an updated version of the app, the system asks the user to grant that permission when the user updates the app. Once the user installs the app, the only way they can revoke the permission is by uninstalling the app. Often times a permission failure will result in a SecurityException being thrown back to the application. However, this is not guaranteed to occur everywhere. For example, the sendBroadcast(Intent) method checks permissions as data is being delivered to each receiver, after the method call has returned, so you will not receive an exception if there are permission failures. In almost all cases, however, a permission failure will be printed to the system log.

The permissions provided by the Android system can be found at Manifest.permission. Any application may also define and enforce its own permissions, so this is not a comprehensive list of all possible permissions.

A particular permission may be enforced at a number of places during your program's operation:

  • At the time of a call into the system, to prevent an application from executing certain functions.

  • When starting an activity, to prevent applications from launching activities of other applications.

  • Both sending and receiving broadcasts, to control who can receive your broadcast or who can send a broadcast to you.

  • When accessing and operating on a content provider.

  • Binding to or starting a service.

As for the app crashing:

Everything every Android Developer must know about new Android's Runtime Permission

Next question in your head right now. So will my application crash?

Such a kindness sent from god delivered through the Android team. When we call a function that requires a permission user revoked on application with targetSdkVersion less than 23, no any Exception will be thrown. Instead it will just simply do nothing. For the function that return value, it will return either null or 0 depends on the case.

But don't be too happy. Although application would not be crashed from calling a function. It may still can crash from what that application does next with those returned value.

Some more details in these answers Require dangerous permissions during installation When asking for runtime permission for location

Upvotes: 1

Related Questions