15
votes

We are doing below process to do pair with BLE Device.

Connect() + discoverServices() + Pairing(Bonding) .

Sometimes Android OS unpaired our BT device in a weird way, that is:

  • without sending broadcast notification that bonding state has changed
  • even system Bluetooth settings app thinks that device is still paired
  • only bt restart (turning off and on via settings app) refreshes state and shows that device is not paired any longer

When Device is Successfully Paired the ACTION_BOND_STATE is change as below.

[6:19:28 PM] Himen Patel: 04-09 18:18:27.325: D/BluetoothGatt(8380): onCharacteristicWrite() - Device=C2:69:E9:57:93:A4 UUID=860b2c07-e3c5-11e2-a28f-0800200c9a66 Status=5 04-09 18:18:27.365: E/millisUntilFinished(8380): millisUntilFinished = 15 04-09 18:18:28.105: E/BelwithDeviceActor(8380): Bond state changed for: C2:69:E9:57:93:A4 new state: 11 previous: 10

04-09 18:18:28.105: E/millisUntilFinished(8380): millisUntilFinished = 20 04-09 18:18:29.135: E/millisUntilFinished(8380): millisUntilFinished = 18 04-09 18:18:30.135: E/millisUntilFinished(8380): millisUntilFinished = 17 04-09 18:18:31.145: E/millisUntilFinished(8380): millisUntilFinished = 16 04-09 18:18:32.145: E/millisUntilFinished(8380): millisUntilFinished = 15

04-09 18:18:33.105: D/BluetoothGatt(8380): onCharacteristicWrite() - Device=C2:69:E9:57:93:A4 UUID=032a0000-0000-0000-0000-000000000000 Status=137 04-09 18:18:33.115: E/BelwithDeviceActor(8380): Bond state changed for: C2:69:E9:57:93:A4 new state: 12 previous: 11

04-09 18:18:33.115: I/System.out(8380): unregisterReceiver true

Now when Pairing is removed by OS in weird way the ACTION_BOND_STATE is change as below. . . . . Bond state changed for: C2:69:E9:57:93:A4 new state: 10.

we also get immediate event of act=android.bluetooth.device.action.ACL_DISCONNECTED flg=0x4000010 in our APP.

what's important here, at this point we just lost pairing with the device and protected characteristics don't work for us any longer. if we restart bt using system settings app or BluetoothAdapter::disable() and enable() we can see that we are not paired with the device.

what's funny, without the bt restart, system settings app still thinks and shows that we are paired with the device.

tested with nexus 4 running 4.4.2, nexus 5 running 4.4.2 and even Samsung galaxy s4 running 4.3.

our expectation is that:

  • in case of unpairing there should be system broadcast
  • system preferences app should show current paring status even without bt restart

We have also Observed and get the sniffed data in which we found that our encryption is set to 0x000000 when our bonding is removed by OS in weird way.

2
There is a bug on this in the Android bug tracker you should be able to search for it.Ifor
I am also suffering from this in a Moto G with kit kat 4.4.3 Did you make it to get a workaround for this issue?Ziul

2 Answers

12
votes

I have no idea whether you still need help or whether you eventually solved your own problem (you know, since you did post this question back in April), but I wanted to go ahead and post the workaround I came up with because I know other people are having this problem.

Using a Nexus 7, I ran basically the same tests you did and came to the same conclusion: If the Android tablet and the remote device were already bonded, there was a high chance that calling BluetoothGatt.discoverServices() would both disconnect and unbond the tablet from the remote device. But, certain parts of the Android OS seemed completely oblivious to the unbonding; although the Broadcast Receiver you registered to listen for bonding changes was notified that the bond between the two devices had been broken, the rest of the Android OS considered the bond to still be intact. Since the OS considered the tablet and the remote device to be bonded, the tablet could not write to any of the encrypted descriptors on the remote device, giving a write status of 15 (GATT_INSUFFICIENT_ENCRYPTION) whenever a descriptor write was attempted.

The Solution

The key is to unpair the Nexus and the remote device before they have the chance to unpair themselves in that weird way. What I do is check to see if the tablet and the remote device are bonded right before I start a Bluetooth Low Energy scan. If they are paired, I remove the bond using the function below and then start the scan. Unpairing the two devices programmatically ensures that the Android OS is aware that they are no longer bonded and, therefore, will go through the usual bonding process.

Below is the code is use to check to see if the remote device is paired with the tablet and unpair it if it is:

// Get the paired devices and put them in a Set
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();

// Loop through the Set of paired devices, checking to see
// if one of the devices is the device you need to unpair
// from. I use the device name, but I'm sure you can find
// another way to determine whether or not its your device
// -- if you need to. :)
for (BluetoothDevice bt : pairedDevices) {
        if (bt.getName().contains("String you know has to be in device name")) {
            unpairDevice(bt);
        }
}

// Function to unpair from passed in device
private void unpairDevice(BluetoothDevice device) {
    try {
        Method m = device.getClass().getMethod("removeBond", (Class[]) null);
        m.invoke(device, (Object[]) null);
    } catch (Exception e) { Log.e(TAG, e.getMessage()); }
}



Why waiting for the error and then solving it by restarting the Bluetooth is a bad idea...

As you have already pointed out in your question, restarting the Bluetooth after the tablet and the remote device mysteriously unbond from each other forces the Android OS to realize that it is no longer bonded to the remote deivce. This was the original workaround I used, but it soon became clear that there were two major problems that came with this "solution":

  1. Turning the Bluetooth on and off will disconnect all of the devices that were connected to the tablet.
  2. Turning the Bluetooth on and off wastes a lot of time.

I would only restart the Bluetooth as a last resort. For example, if the unbonding error still miraculously occurred, your only choice would be to restart the Bluetooth.

0
votes

We had the same issue and we've figured out that "connectGatt" has new "transport" argument (which defines transport protocol of connection), starting from SDK v 23. So if you want to pair your devices you should use "TRANSPORT_BREDR", if you want to only connect to peripheral without bonding - use "TRANSPORT_LE" Documentation: https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int)