dariuszseweryn / RxAndroidBle

An Android Bluetooth Low Energy (BLE) Library with RxJava3 interface
http://polidea.github.io/RxAndroidBle/
Apache License 2.0
3.44k stars 583 forks source link

Bonding best practices #420

Open skykelsey opened 6 years ago

skykelsey commented 6 years ago

I needed to initiate a bond with a peripheral using "Just Works" bonding. This means no exchange of a PIN. I got it to work by simply grabbing the underlying BluetoothDevice and calling createBond(), but I wanted to check to see if this is somehow wrong, and I should be doing something else, or in a more idiomatic way.

val device: RxBleDevice = bluetoothService.getBleDevice(selectedDevice.macAddress)

Observable.fromCallable<RxBleDevice> {
    device.bluetoothDevice.createBond()
    device
}
.flatMap {
    it.establishConnection(false)
}
.doOnNext {
    Timber.e("Connection established.")
}
...
dariuszseweryn commented 6 years ago

There are currently no guidelines nor even too much tests with this library using bonding. It the terms of code expressiveness you approach is ok. You could also write it like this:

val device: RxBleDevice = bluetoothService.getBleDevice(selectedDevice.macAddress)

Observable.defer<RxBleConnection> {
    device.bluetoothDevice.createBond() // it is a blocking function
    device.establishConnection(false) // return Observable<RxBleConnection>
}
.doOnNext {
    Timber.e("Connection established.")
}
RobLewis commented 6 years ago

I took a stab at making a Java version. Please comment (in particular, is there a better exception type to use?):

    public static Observable getBondedConnectionObservable( RxBleDevice device ) {
        return Observable.defer( () -> {
            if( device.getBluetoothDevice().createBond() ) {  // bonding will begin
                return device.establishConnection( false );
            } else {  // immediate error
                return Observable.error( new IOException( "Error initiating bonding of device " + device.getName() ) );
            }
        });
    }
nateridderman commented 6 years ago

Actually createBond returns immediately, and you need to create a broadcast receiver to listen for the updated bond states. https://developer.android.com/reference/android/bluetooth/BluetoothDevice#createbond

Here's an example: https://stackoverflow.com/questions/18197444/android-bluetooth-low-energy-pairing?rq=1 The receiver operates the same whether you initiate the bond request yourself or the system does in response to a GATT_INSUFFICIENT_AUTHENTICATION error.

RobLewis commented 6 years ago

@nateridderman Once again the "pairing" vs. "bonding" semantics confuse things. From everything I can determine, they are not the same thing, even though many people—including Google in their official Android docs—use the terms interchangeably. As explained fairly well here, pairing refers to a client-initiated exchange of security features, based on a temporary encryption key. Bonding means that these encryption keys are then remembered by the devices, so the next time they connect they can use those keys without exchanging any unsecured information.

nateridderman commented 6 years ago

Right, they are related but slightly different concepts. This site has a good explanation - https://eewiki.net/display/Wireless/A+Basic+Introduction+to+BLE+Security#ABasicIntroductiontoBLESecurity-PairingOverview:

ardmn commented 6 years ago

I'm not sure but when I was try using createBond it not work well. @skykelsey Can you describe your case of bonding ? It is very interesting for me :)

craigzour commented 6 years ago

Hello,

Here is a way to implement pairing/bonding with RxAndroidBle. It might be useful for people having to deal with that process.

https://gist.github.com/craigzour/edf7f3bd8bef4b162887b4244e27dc1f

skykelsey commented 6 years ago

@craigzour Were you actually able to get the BOND_BONDED Intent? I'm not seeing that. Seems like it's not always emitted.

craigzour commented 6 years ago

@skykelsey Well at least with all the devices I have tested my code on it worked pretty well. The only thing that is kind of random and depends on devices is the way Android prompts the Pairing alert. Sometimes it goes silently in the notification center and thus you can miss it.

dglozano commented 5 years ago

I came up with a slightly different helper class based on @craigzour solution. The main difference is that in Craig's solution, the completable completed successfully if the device was already bonded. In my case, that wasn't useful, because when I try to read or write the first characteristic to be encrypted, the operation failed anyway. To avoid that, I always unbond and bond again before establishing the connection, so I added an unbonding operation before.

https://gist.github.com/dglozano/9b0ce38a558eeca16137909bd368698c

ivnsch commented 5 years ago

@dglozano Would unbonding/bonding each time not be the same as not bonding at all? So far I understand, the point of bonding is to persist the encryption keys, so you would be regenerating the keys each time, which is what you do when there's no bonding.