NordicSemiconductor / Android-BLE-Library

A library that makes working with Bluetooth LE on Android a pleasure. Seriously.
BSD 3-Clause "New" or "Revised" License
2.02k stars 417 forks source link

GATT server doc improvement #155

Closed stefan-sedlak closed 4 years ago

stefan-sedlak commented 4 years ago

Hi @philips77 could you please provide an example how to integrate GATT server with multiple connected clients / BleManager-s? I have a use case where I need to notify different characteristic values to different clients.

From README file it is not clear. I miss link between connected client's BluetoothDevice and its BleManager. Or how to initialize BleManager for connected client.

I used in onDeviceConnectedToServer(@NonNull BluetoothDevice device):

final MyBleManager manager = new MyBleManager(context);
manager.setManagerCallbacks(this);
manager.useServer(serverManager);

and tried to add:

manager.connect(device).enqueue();

It is only method were I can set BluetoothDevice.

But it causes:

E/BleManager: onConnectionUpdated received status: 42, interval: 12, latency: 0, timeout: 200

42 is "Answer to the Ultimate Question of Life, the Universe, and Everything" however in our case status 42 means BLE_HCI_DIFFERENT_TRANSACTION_COLLISION what does not look well.

However it seems that MyBleManager's BleManagerGattCallback triggers onServerReady(@NonNull BluetoothGattServer server) with local GATT server service and characteristics. What I would expect.

So I am confused :)

Thanks for your advise.

philips77 commented 4 years ago

Hello, In my opinion, Deep Thought must have returned this error after 7.5 mln years of calculating. That means, it's not the Answer. It's just transaction collision and user should restart ;)

Back to your question. Just yesterday I released an updated version of nRF Toolbox, where the Proximity profile is using the GATT server and allows multiple connections. Have a look here: https://github.com/NordicSemiconductor/Android-nRF-Toolbox/tree/master/app/src/main/java/no/nordicsemi/android/nrftoolbox/proximity

  1. The server services are defined in ProximityServerManager.

  2. The ProximityServerManager instance is created in ProximityServer. There is just one instance of this manager. It will automatically handle requests from all devices and pass them to the right BleManager.

  3. Whenever a device is connected (the app acts like Proximity Client: it scans for Proximity tags (scanning returns BluetoothDevice object for each scanned device)) a ProximityManager (extends BleManager) is created. The useServer(server) method is used to assign the server manager and finally, connect(device) method is called.

  4. When the device connects, the following methods are called in the given order:

    1. isRequiredServiceSupported(BluetoothGatt) - this method is called after service discovery is complete. It should call gatt.getServices(), verify that required service has been found on the device (return true is so), and obtain references to characteristics and/or descriptors that are to be used.
    2. isOptionalServiceSupported(BluetoothGatt) - the same as above, but for optional services. The initialization will still proceed even if this method returns false (default). Implementing this method is optional.
    3. initialize() - here the initialization queue should be defined. Usually, this sets callbacks for notifications, indications, write requests, enables notifications, indications or writes some initial data to the devices.
    4. If server has been set for the BleManager, the onServerReady(BluetoothGattServer) will be called. This method should save references to server characteristics and/or descriptors.
  5. When the device disconnects, onDeviceDisconnected will be called. As services have been invalidated, this method should release all references to previously saved attributes.

Before 5., you will be able to perform client and server operations in your BleManagers. Each manager should be treated as a single device API. It will send and receive data to single specified device.

BluetoothDevice can be obtained in 2 ways.

  1. If your app acts as client, you can scan for the device. You will get onScanResult(int callbackType, ScanResult result) // We recommend using Android Scanner Compat Library if you plan to support older Android versions. You can also obtain an already bonded device using [BluetoothAdapter#getBondedDevices()](https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getBondedDevices()).
  2. If you act like server (e.g. the phone advertises and waits for client to connect, or you want to connect to a device that another app is connecting to (you will get this call whenever any BLE device connects after the server was open, or just after opening the server when a device was connected)) onDeviceConnectedToServer(@NonNull BluetoothDevice device) will be called for every connected device. You should be able to create BleManager instance there, assign the server to it and connect to this device (calling manager.connect(device).enqueue() will connect the manager to the server, despite the server being already connected, where it is the device that acts like client).
stefan-sedlak commented 4 years ago

Hi @philips77 ,

thanks for great answer!

I checked Android-nRF-Toolbox example after studying BLE standard and Android API (I like to understand what I develop). I miss example peripheral handles multiple centrals. As I understand there is just start of GATT server.

I thought that I will not use 3rd party libraries but I am really glad that you provided good one. iOS Bluetooth API is much more better than Android API(s) and you saved a lot of my time.

My central connects to multiple peripherals after scanning provided by your scanning library ;) My peripheral handles multiple centrals connections. BLEManager is created and connected in onDeviceConnectedToServer by manager.connect(device).enqueue() It is a bit confusing to (re)connect when device (central) is already connected.

I was just afraid of

E/BleManager: onConnectionUpdated received status: 42, interval: 12, latency: 0, timeout: 200

in log. It is still present in log however multiple connections (centrals->peripheral) work well. I already tested it.

Thanks, Stefan