weliem / blessed-android

BLESSED, a Bluetooth Low Energy (BLE) library for Android
MIT License
557 stars 119 forks source link

Library not waiting for BOND_BONDED event #104

Closed Maragues closed 3 years ago

Maragues commented 3 years ago

Hi, after reading your article on bonding, I wanted to check how you had implemented the following constraint:

Don’t do anything while bonding is in progress. If you do things like starting service discovery or reads/writes while bonding is in progress, you will get errors and the connection may drop as well. Just let Android do it’s thing.

It seems to be very important, since you remark it again at the bottom:

But the important part here is that you should not try to do any BLE action while popup is on the screen. Wait for success or failure and the proceed.

I'm working on a weight balance that requires bonding, so I tweaked a bit the example app to scan for it and establish a connection

private void initBluetoothHandler() {
        BluetoothHandler handler = BluetoothHandler.getInstance(getApplicationContext());

        handler.central.scanForPeripheralsWithNames(new String[]{"MyBalance"});
    }

My surprise was, you don't seem to respect the advice on the article. Here are the relevant logs

11:24:30.953 22157-22157 I/BluetoothHandler: Found peripheral 'MyBalance'
11:24:30.955 22157-22157 I/BluetoothCentralManager: scan stopped
11:24:31.057 22157-22157 I/BluetoothPeripheral: connect to 'MyBalance' (...) using TRANSPORT_LE
11:24:31.063 22157-22157 I/BluetoothPeripheral: peripheral '...' is connecting
11:24:31.486 22157-22175 I/BluetoothPeripheral: connected to 'MyBalance' (NONE) in 0,4s

-> Service discovery starts right away

11:24:31.488 22157-22157 D/BluetoothPeripheral: discovering services of 'MyBalance' with delay of 0 ms
11:24:31.569 22157-22157 D/BluetoothCentralManager: popup hack completed
11:24:31.573 22157-22175 D/BluetoothLeScanner: onScannerRegistered() - status=0 scannerId=13 mScannerId=0
11:24:31.576 22157-22157 I/BluetoothCentralManager: scan started
11:24:31.638 22157-22157 D/BluetoothPeripheral: starting bonding with 'MyBalance' (...)
11:24:31.639 22157-22157 D/BluetoothPeripheral: pairing request received: PAIRING_VARIANT_CONSENT (3)
11:24:32.122 22157-22175 D/BluetoothPeripheral: connection parameters: interval=7.5ms latency=0 timeout=5s
11:24:32.558 22157-22175 I/BluetoothPeripheral: discovered 7 services for 'MyBalance'
11:24:32.559 22157-22157 I/BluetoothHandler: connected to 'MyBalance'
11:24:32.560 22157-22157 I/BluetoothPeripheral: requesting MTU of 185
11:24:32.578 22157-22157 I/BluetoothHandler: new MTU set: 128
11:24:32.578 22157-22157 D/BluetoothPeripheral: requesting connection priority HIGH
11:24:32.646 22157-22175 D/BluetoothPeripheral: connection parameters: interval=15.0ms latency=0 timeout=0s
11:24:32.819 22157-22175 D/BluetoothPeripheral: connection parameters: interval=11.3ms latency=0 timeout=5s
11:24:33.080 22157-22157 D/BluetoothPeripheral: reading characteristic <00002a19-0000-1000-8000-00805f9b34fb>
11:24:36.279 22157-22157 E/BluetoothCentralManager: other scan still active, stopping scan
11:24:36.282 22157-22157 I/BluetoothCentralManager: scan stopped
11:24:36.285 22157-22175 D/BluetoothLeScanner: onScannerRegistered() - status=0 scannerId=13 mScannerId=0
11:24:36.306 22157-22157 I/BluetoothCentralManager: scan started
11:24:36.389 22157-22157 I/BluetoothHandler: Received battery level 99%
11:24:36.816 22157-22157 I/BluetoothHandler: SUCCESS: Notify set to 'true' for ...
11:24:36.889 22157-22157 D/BluetoothHandler: 0,0 kg, user null, BMI null, height null at (09-07-2021 11:24:36)
11:24:36.973 22157-22157 D/BluetoothPeripheral: bonded with 'MyBalance' (...)

-> Here's where I expected service discovery to happen

Did I misunderstood something from your article?

Since you can't know beforehand if a device requires bonding or not, Is it just not possible to halt service discovery until bonding is completed?

I also checked on a BLE device that doesn't require bonding, and you don't get any ACTION_BOND_STATE_CHANGED broadcast. Which means, you can't block service discovery until you know the bonding state, because you won't get any event when the BLE device doesn't require bonding.

Also, nothing prevents the clients of blessed to attempt to read/write on a characteristic while bonding is ongoing.

Looking forward to your answer. Thank you very much for your work and for the articles, they really helped me grasp the concept.

weliem commented 3 years ago

Hi, you are right. There is another case that I didn't discuss in my article. I happened to run into it as when this week when I was playing with the Omron X4. I just published version 2.0.9 of Blessed that addresses this issue.

However, it is a tricky issue. The Omron is the first device I have seen that does a 'peripheral initiated bond request' that is not done directly after connecting. In your case the same is happening:

So I put a fix in now that checks if we are bonding whenever a new command is about to start. If so, it pauses the queue until bonding succeeds.

This is however, not a 100% foolproof fix. As in your case, the discoverServices() command has already started. Luckily in your case, it still succeeds so no issues. This way of triggering a bond is not desirable. It is better to have the bonding start by Android because of a GATT authorization error. Apple also recommends this approach. But I guess in your case, and mine, we cannot control the peripheral firmware so we just have to deal with it.

Maragues commented 3 years ago

Thank you very much for a detailed answer.

Fortunately in my case, I can ask our firmware team to look into this. The product is in early development stage, so hopefully we can improve the behavior.

Maragues commented 3 years ago

Answer from firmware dev

I had seen that Apple recommended that approach of having the bonding start from the phone upon GATT authorization error . However the way we do bonding is the way it is (poorly) documented in the ESP32 BLE stack documentation. What it does behind the scene is not documented anywhere I could find. Until now, I did not even know if it actively requested bonding or waited for the phone to ask. Now I know :disappointed:. Now I don't know if doing as Apple recommends is just outright impossible on the ESP32 or if it's possible but not well documented.

Do you happen to know where to find this documentation 😅 ?

On a different topic, I tested the fix on Blessed. Here are the logs

Non-bonded Balance

12:35:30.744 8898-8898 BluetoothPeripheral: connect to 'MyBalance' (...) using TRANSPORT_LE
12:35:30.772 8898-8898 BluetoothPeripheral: peripheral '...' is connecting
12:35:34.883 8898-8961 BluetoothPeripheral: connected to 'MyBalance' (NONE) in 4,1s
12:35:34.887 8898-8898 BluetoothPeripheral: discovering services of 'MyBalance' with delay of 0 ms
12:35:35.001 8898-8898 BluetoothPeripheral: starting bonding with 'MyBalance' (...)
12:35:35.030 8898-8898 BluetoothPeripheral: pairing request received: PAIRING_VARIANT_CONSENT (3)
12:35:35.347 8898-8961 BluetoothPeripheral: connection parameters: interval=7.5ms latency=0 timeout=5s
12:35:35.821 8898-8961 BluetoothPeripheral: discovered 7 services for 'MyBalance'
12:35:35.823 8898-8898 BluetoothHandler: connected to 'MyBalance'

12:35:35.824 8898-8898 BluetoothPeripheral: bonding is in progress, waiting for bonding to complete
12:35:35.825 8898-8898 BluetoothPeripheral: bonding is in progress, waiting for bonding to complete

12:35:35.916 8898-8961 BluetoothPeripheral: connection parameters: interval=15.0ms latency=0 timeout=0s
12:35:36.681 8898-8898 BluetoothCentralManager: other scan still active, stopping scan
12:35:36.693 8898-8898 BluetoothCentralManager: scan stopped
12:35:36.712 8898-8961 BluetoothLeScanner: onScannerRegistered() - status=0 scannerId=10 mScannerId=0
12:35:36.718 8898-8898 BluetoothCentralManager: scan started
12:35:37.563 8898-8898 BluetoothPeripheral: bonded with 'MyBalance' (...)
12:35:37.567 8898-8898 BluetoothPeripheral: requesting MTU of 185
12:35:37.620 8898-8898 BluetoothHandler: new MTU set: 128
12:35:37.628 8898-8898 BluetoothPeripheral: requesting connection priority HIGH
12:35:37.788 8898-8961 BluetoothPeripheral: connection parameters: interval=11.3ms latency=0 timeout=5s
12:35:38.133 8898-8898 BluetoothPeripheral: reading characteristic <...>
12:35:38.164 8898-8898 BluetoothHandler: Received battery level 99%
12:35:38.195 8898-8898 BluetoothHandler: SUCCESS: Notify set to 'true' for ...

Already bonded

12:39:53.917 11450-11450 BluetoothPeripheral: connect to 'MyBalance' (...) using TRANSPORT_LE
12:39:53.922 11450-11450 BluetoothPeripheral: peripheral '...' is connecting
12:39:54.056 11450-11450 BluetoothCentralManager: popup hack completed
12:39:54.060 11450-11485 BluetoothLeScanner: onScannerRegistered() - status=0 scannerId=12 mScannerId=0
12:39:54.065 11450-11450 BluetoothCentralManager: scan started
12:39:54.263 11450-11485 BluetoothPeripheral: connected to 'MyBalance' (BONDED) in 0,3s
12:39:54.264 11450-11450 BluetoothPeripheral: discovering services of 'MyBalance' with delay of 0 ms
12:39:54.790 11450-11485 BluetoothPeripheral: connection parameters: interval=7.5ms latency=0 timeout=5s
12:39:55.319 11450-11485 BluetoothPeripheral: discovered 7 services for 'MyBalance'
12:39:55.324 11450-11450 BluetoothHandler: connected to 'MyBalance'
12:39:55.331 11450-11450 BluetoothPeripheral: requesting MTU of 185
12:39:55.354 11450-11450 BluetoothHandler: new MTU set: 128
12:39:55.358 11450-11450 BluetoothPeripheral: requesting connection priority HIGH
12:39:55.389 11450-11485 BluetoothPeripheral: connection parameters: interval=15.0ms latency=0 timeout=0s
12:39:55.562 11450-11485 BluetoothPeripheral: connection parameters: interval=11.3ms latency=0 timeout=5s
12:39:55.863 11450-11450 BluetoothPeripheral: reading characteristic <...>
12:39:55.890 11450-11450 BluetoothHandler: Received battery level 99%
12:39:55.923 11450-11450 BluetoothHandler: SUCCESS: Notify set to 'true' for ...
weliem commented 3 years ago

Ok, so it looks like the fix works. Right?

As for documentation for your firmware engineer: https://developer.apple.com/accessories/Accessory-Design-Guidelines.pdf

Quote from page 155:

The accessory should not request pairing until an ATT request is rejected using the Insufficient Authentication error code. See the Bluetooth 4.0 specification, Volume 3, Part F, Section 4 for details.

If, for security reasons,the accessory requires a bonded relationship with the Central,the Peripheral should reject the ATT request using the Insufficient Authentication error code, as appropriate. As a result, the Apple product may proceed with the necessary security procedures.

Maragues commented 3 years ago

Yes, I'll close the issue

Thank you very much for your work 🙏🏻