Android-BLE-Library icon indicating copy to clipboard operation
Android-BLE-Library copied to clipboard

Is there any way to decrease or bypass Service Discovery?

Open onatakduman opened this issue 3 years ago • 1 comments

Hi Android-Ble-Library developers,

I tried to illustrate the communication diagram between a BLE Device and an Android device when I tried to send the data (with my observations). And I have a question:
First of all, I am trying to make a connection as fast as possible but there is something wrong with this.
Service Discovery is taking too much time sometimes (5-8 seconds), and sometimes less (2-3 seconds)and it still is too much.

  • Is there any way to decrease or bypass Service Discovery?
  • Is there any way to cache Gatt Services?
  • If there are fewer services and characteristics, will it help reduce service discovery? like this:
Service Characteristic Size
Service 1 1

It might be because our services and characteristics sizes are big:

Service Characteristic Size
Service 1 4
Service 2 1
Service 3 1
Service 4 1
Service 5 5
Service 6 1

So I splited the services and charactersitics on BleManager classes and if I need a use some service I create by what I needed in BleManager.
With example 2 classes:

class Manager1(
    context: Context,
    private val scope: CoroutineScope
) : BleManager(context) {

    private var characteristic1: BluetoothGattCharacteristic? = null
    private var characteristic2: BluetoothGattCharacteristic? = null
    private var characteristic3: BluetoothGattCharacteristic? = null
    private var characteristic4: BluetoothGattCharacteristic? = null
    private var characteristic5: BluetoothGattCharacteristic? = null

    private val data: MutableStateFlow<CommunicationData> =
        MutableStateFlow(CommunicationData())
    val dataHolder = ConnectionObserverAdapter<CommunicationData>()

    init {
        connectionObserver = dataHolder
        data.onEach {
            dataHolder.setValue(it)
        }.launchIn(scope)
    }

    override fun getMinLogPriority(): Int = Log.VERBOSE

    override fun initialize() {
        requestMtu(500).enqueue()
        setNotificationCallback(characteristicOnOff).with(dataCallback)
        enableNotifications(characteristicOnOff).enqueue()
    }

    override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean {
        val service1: BluetoothGattService? =
            gatt.getService(SERVICE_1_UUID)

        if (service1!= null) {
            characteristic1 =
                service1.getCharacteristic(CHARACTERISTIC_UUID_1)
            characteristic2 =
                service1.getCharacteristic(CHARACTERISTIC_UUID_2)
            characteristic3 =
                service1.getCharacteristic(CHARACTERISTIC_UUID_3)
            characteristic4 =
                service1.getCharacteristic(CHARACTERISTIC_UUID_4)
        }

        val service6: BluetoothGattService? =
            gatt.getService(SERVICE_6_UUID)
        if (service6!= null) {
            characteristic5 =
                service6.getCharacteristic(CHARACTERISTIC_UUID_6)
        }

        return characteristis1 != null && characteristic2 = null...
    }

    override fun onServicesInvalidated() {
        characteristic1 = null
        characteristic2 = null
        characteristic3 = null
        characteristic4 = null
        characteristic5 = null
    }

    fun sendCommand(command: Boolean) {
        val commandData = if (command) Command.SOME else Command.SOME
        val request: WriteRequest =
            writeCharacteristic(
                characteristic,
                commandData.data,
                BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
            )
        request.with(dataCallback).enqueue()
    }
...
    private val dataCallback = ...

}
class Manager2(
    context: Context,
    private val scope: CoroutineScope
) : BleManager(context) {

    private var characteristic: BluetoothGattCharacteristic? = null

    private val data: MutableStateFlow<CommunicationData> =
        MutableStateFlow(CommunicationData())
    val dataHolder = ConnectionObserverAdapter<CommunicationData>()

    init {
        connectionObserver = dataHolder
        data.onEach {
            dataHolder.setValue(it)
        }.launchIn(scope)
    }

    override fun getMinLogPriority(): Int = Log.VERBOSE

    override fun initialize() {
        setNotificationCallback(characteristicOnOff).with(dataCallback)
        enableNotifications(characteristicOnOff).enqueue()
        sendCommand()
    }

    override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean {
        val service2: BluetoothGattService? =
            gatt.getService(SERVICE_2_UUID)

        if (service2!= null) {
            characteristic =
                service2.getCharacteristic(CHARACTERISTIC_UUID_2)
        }

        return characteristic != null
    }

    override fun onServicesInvalidated() {
        characteristic = null
    }

    fun sendCommand() {
        val data = byteArrayOf(
            1.convertByte(),
            0,
            0,
            0
        )
        val request: WriteRequest =
            writeCharacteristic(
                characteristic,
                data,
                BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
            )
        request.with(dataCallback).enqueue()
    }

    private val dataCallback = ...

}
sequenceDiagram

actor User
participant Android
participant Bluetooth Gatt
participant BLEDevice

User->>Android: userAction()

activate Android

Android-)Bluetooth Gatt: connect()
Android-)Bluetooth Gatt: sendData() when connect()

activate Bluetooth Gatt

Bluetooth Gatt->>BLEDevice: connect()

BLEDevice-->>Bluetooth Gatt: onConnectionUpdated()

loop
Bluetooth Gatt-->>Android: onDeviceConnecting()
BLEDevice-->>Bluetooth Gatt: onConnectionUpdated()
opt optional
BLEDevice-xAndroid:connectionFailed()
end
end

BLEDevice-->>Bluetooth Gatt: onDeviceConnected()
Bluetooth Gatt-->>Android: onDeviceConnected()
Bluetooth Gatt->>BLEDevice: discoverServices()
Note over Bluetooth Gatt, BLEDevice: Takes too much time
BLEDevice-->Bluetooth Gatt: Services Handshake
Bluetooth Gatt -->> Android: onSearchComplete()

deactivate Bluetooth Gatt
Bluetooth Gatt->>BLEDevice:setCharacteristicNotification()
Bluetooth Gatt->>BLEDevice:setCharacteristicNotification()
BLEDevice-->>Bluetooth Gatt: onConnectionUpdated()
Bluetooth Gatt-->>Android:onDeviceReady()

Bluetooth Gatt->>BLEDevice:sendData()
BLEDevice-->>Bluetooth Gatt:dataResponse()
Bluetooth Gatt-->>Android:dataResponse()
Android-->>User:notifiyAboutAction()

activate Android 
Android-->>Android:Wait User action for 5 seconds
Android->>Bluetooth Gatt: sendDisconnect()
deactivate Android

Bluetooth Gatt->>BLEDevice: sendDisconnect()

Bluetooth Gatt-->>Android:onDeviceDisconnecting()
BLEDevice-->Bluetooth Gatt: Disconnect Handshake


activate Bluetooth Gatt
Bluetooth Gatt-->>Android:onDeviceDisconnected()
deactivate Bluetooth Gatt


Example 1

15:27:50.489 selectedDevices       E  C8:F0:9E:A0:82:5A
15:27:50.789 BluetoothGatt         D  onClientConnectionState() - status=133 clientIf=9 device=C8:F0:9E:A0:82:5A
15:27:51.796 BluetoothGatt         D  close()
15:27:51.796 BluetoothGatt         D  unregisterApp() - mClientIf=9
15:27:52.003 BluetoothGatt         D  connect() - device: C8:F0:9E:A0:82:5A, auto: false
15:27:52.003 BluetoothGatt         D  registerApp()
15:27:52.004 BluetoothGatt         D  registerApp() - UUID=d0bc6014-4436-43c5-99a8-0073d2c2618d
15:27:52.010 BluetoothGatt         D  onClientRegistered() - status=0 clientIf=9
15:27:52.026 BLE-CONNECTION        D  onDeviceConnecting()
15:27:52.298 BluetoothGatt         D  onClientConnectionState() - status=133 clientIf=9 device=C8:F0:9E:A0:82:5A
15:27:53.318 BluetoothGatt         D  close()
15:27:53.319 BluetoothGatt         D  unregisterApp() - mClientIf=9
15:27:53.527 BluetoothGatt         D  connect() - device: C8:F0:9E:A0:82:5A, auto: false
15:27:53.527 BluetoothGatt         D  registerApp()
15:27:53.529 BluetoothGatt         D  registerApp() - UUID=102aee31-2fe8-4170-8163-abe0ee0ef581
15:27:53.539 BluetoothGatt         D  onClientRegistered() - status=0 clientIf=9
15:27:53.580 BLE-CONNECTION        D  onDeviceConnecting()
15:27:53.827 BluetoothGatt         D  onClientConnectionState() - status=133 clientIf=9 device=C8:F0:9E:A0:82:5A
15:27:54.855 BluetoothGatt         D  close()
15:27:54.855 BluetoothGatt         D  unregisterApp() - mClientIf=9
15:27:55.065 BluetoothGatt         D  connect() - device: C8:F0:9E:A0:82:5A, auto: false
15:27:55.066 BluetoothGatt         D  registerApp()
15:27:55.067 BluetoothGatt         D  registerApp() - UUID=da8be66d-7352-4c29-be54-2da42fd3310f
15:27:55.074 BluetoothGatt         D  onClientRegistered() - status=0 clientIf=9
15:27:55.078 BLE-CONNECTION        D  onDeviceConnecting()
15:27:55.248 BluetoothGatt         D  onClientConnectionState() - status=0 clientIf=9 device=C8:F0:9E:A0:82:5A
15:27:55.265 BLE-CONNECTION        D  onDeviceConnected()
15:27:55.566 BluetoothGatt         D  discoverServices() - device: C8:F0:9E:A0:82:5A
15:27:55.704 BluetoothGatt         D  onConnectionUpdated() - Device=C8:F0:9E:A0:82:5A interval=6 latency=0 timeout=500 status=0
15:27:56.530 BluetoothGatt         D  onSearchComplete() = Device=C8:F0:9E:A0:82:5A Status=0
15:27:56.540 BluetoothGatt         D  setCharacteristicNotification() - uuid: 47421404-f259-11ec-b939-0242ac120002 enable: true
15:27:56.571 BluetoothGatt         D  setCharacteristicNotification() - uuid: 47421402-f259-11ec-b939-0242ac120002 enable: true
15:27:56.591 BluetoothGatt         D  onConnectionUpdated() - Device=C8:F0:9E:A0:82:5A interval=30 latency=0 timeout=500 status=0
15:27:56.636 BLE-CONNECTION        D  onDeviceReady()
15:27:56.670 onDataSent            E  1B000000
... /*Disconnects after 7 seconds*/
15:28:01.908 BluetoothGatt         D  cancelOpen() - device: C8:F0:9E:A0:82:5A
15:28:01.916 BLE-CONNECTION        D  onDeviceDisconnecting()
15:28:01.928 BluetoothGatt         D  onClientConnectionState() - status=0 clientIf=9 device=C8:F0:9E:A0:82:5A
15:28:01.936 BluetoothGatt         D  refresh() - device: C8:F0:9E:A0:82:5A
15:28:01.942 BluetoothGatt         D  close()
15:28:01.942 BluetoothGatt         D  unregisterApp() - mClientIf=9
15:28:01.954 BLE-CONNECTION        D  onDeviceDisconnected(), reason: 0

Example 2

15:28:04.279 BLE-CONNECTION        D  onDeviceConnecting()
15:28:04.279 BluetoothGatt         D  connect() - device: C8:F0:9E:A0:82:5A, auto: false
15:28:04.279 BluetoothGatt         D  registerApp()
15:28:04.280 BluetoothGatt         D  registerApp() - UUID=ded3b352-53d5-42d7-8c1a-893afb323eff
15:28:04.284 BluetoothGatt         D  onClientRegistered() - status=0 clientIf=9
15:28:04.389 selectedDevices       E  C8:F0:9E:A0:82:5A
15:28:04.625 BluetoothGatt         D  onClientConnectionState() - status=133 clientIf=9 device=C8:F0:9E:A0:82:5A
15:28:05.639 BluetoothGatt         D  close()
15:28:05.640 BluetoothGatt         D  unregisterApp() - mClientIf=9
15:28:05.845 BluetoothGatt         D  connect() - device: C8:F0:9E:A0:82:5A, auto: false
15:28:05.845 BluetoothGatt         D  registerApp()
15:28:05.846 BluetoothGatt         D  registerApp() - UUID=b7ff9713-afbb-43b8-9693-ea25745d6b72
15:28:05.852 BluetoothGatt         D  onClientRegistered() - status=0 clientIf=9
15:28:05.854 BLE-CONNECTION        D  onDeviceConnecting()
15:28:06.103 BluetoothGatt         D  onClientConnectionState() - status=0 clientIf=9 device=C8:F0:9E:A0:82:5A
15:28:06.142 BLE-CONNECTION        D  onDeviceConnected()
15:28:06.435 BluetoothGatt         D  discoverServices() - device: C8:F0:9E:A0:82:5A
15:28:06.616 BluetoothGatt         D  onConnectionUpdated() - Device=C8:F0:9E:A0:82:5A interval=6 latency=0 timeout=500 status=0
15:28:07.550 BluetoothGatt         D  onSearchComplete() = Device=C8:F0:9E:A0:82:5A Status=0
15:28:07.561 BluetoothGatt         D  setCharacteristicNotification() - uuid: 47421404-f259-11ec-b939-0242ac120002 enable: true
15:28:07.592 BluetoothGatt         D  onConnectionUpdated() - Device=C8:F0:9E:A0:82:5A interval=30 latency=0 timeout=500 status=0
15:28:07.666 BluetoothGatt         D  setCharacteristicNotification() - uuid: 47421402-f259-11ec-b939-0242ac120002 enable: true
15:28:07.745 BLE-CONNECTION        D  onDeviceReady()
15:28:07.765 onDataSent            E  27000000
15:28:07.911 onDataReceived        E  0027AC2345
... /*Disconnects after 7 seconds*/
15:28:13.028 BluetoothGatt         D  cancelOpen() - device: C8:F0:9E:A0:82:5A
15:28:13.032 BluetoothGatt         D  onClientConnectionState() - status=19 clientIf=9 device=C8:F0:9E:A0:82:5A
15:28:13.036 BLE-CONNECTION        D  onDeviceDisconnecting()
15:28:13.067 BluetoothGatt         D  refresh() - device: C8:F0:9E:A0:82:5A
15:28:13.070 BluetoothGatt         D  close()
15:28:13.071 BluetoothGatt         D  unregisterApp() - mClientIf=9
15:28:13.081 BLE-CONNECTION        D  onDeviceDisconnected(), reason: 2

onatakduman avatar Jan 18 '23 10:01 onatakduman

You may check https://github.com/NordicSemiconductor/Android-BLE-Library/blob/cf95abb3742a1fdcfe3fd65e6fd29eb79508259f/ble/src/main/java/no/nordicsemi/android/ble/BleManager.java#L646-L648 override it and return 0.

I believe we could do some experiments with requesting high MTU before starting service discovery. Don't know whether it would make it faster, but possibly yes. Try requesting it from the device side, if possible, just after connection.

philips77 avatar Jan 18 '23 14:01 philips77