kshoji / BLE-HID-Peripheral-for-Android

BLE HID over GATT Profile for Android
Apache License 2.0
239 stars 76 forks source link

Race condition in HidPeripheral.addService #11

Open dbw9580 opened 5 years ago

dbw9580 commented 5 years ago

According to Android doc BluetoothGattServer.addService:

The BluetoothGattServerCallback.onServiceAdded(int, BluetoothGattService) callback will indicate whether this service has been added successfully. Do not add another service before this callback.

The current codes in HidPeripheral.java break this promise and add several services in a row without proper precautions. This results in race condition and failure adding services. The errors from logcat:

2019-01-16 15:21:18.859 26268-26268/me.dbw9580.apps.BridgeTooth D/BluetoothGattServer: registerCallback()
2019-01-16 15:21:18.859 26268-26268/me.dbw9580.apps.BridgeTooth D/BluetoothGattServer: registerCallback() - UUID=9f9ae403-ef94-4cfb-9925-4410ba150dff
2019-01-16 15:21:18.861 26268-26281/me.dbw9580.apps.BridgeTooth D/BluetoothGattServer: onServerRegistered() - status=0 serverIf=5
2019-01-16 15:21:18.861 26268-26268/me.dbw9580.apps.BridgeTooth D/BluetoothGattServer: addService() - service: 00001812-0000-1000-8000-00805f9b34fb
2019-01-16 15:21:18.869 26268-26268/me.dbw9580.apps.BridgeTooth D/HidPeripheral: Service: 00001812-0000-1000-8000-00805f9b34fb added.
2019-01-16 15:21:18.869 26268-26268/me.dbw9580.apps.BridgeTooth D/BluetoothGattServer: addService() - service: 0000180a-0000-1000-8000-00805f9b34fb
2019-01-16 15:21:18.871 26268-26281/me.dbw9580.apps.BridgeTooth D/BluetoothGattServer: onServiceAdded() - handle=40 uuid=00001812-0000-1000-8000-00805f9b34fb status=0
2019-01-16 15:21:18.871 26268-26268/me.dbw9580.apps.BridgeTooth D/HidPeripheral: Service: 0000180a-0000-1000-8000-00805f9b34fb added.
2019-01-16 15:21:18.871 26268-26268/me.dbw9580.apps.BridgeTooth D/BluetoothGattServer: addService() - service: 0000180f-0000-1000-8000-00805f9b34fb
2019-01-16 15:21:18.872 26268-26281/me.dbw9580.apps.BridgeTooth W/Binder: Caught a RuntimeException from the binder stub implementation.
    java.lang.IndexOutOfBoundsException: Index: 3, Size: 3
        at java.util.ArrayList.get(ArrayList.java:437)
        at android.bluetooth.BluetoothGattServer$1.onServiceAdded(BluetoothGattServer.java:121)
        at android.bluetooth.IBluetoothGattServerCallback$Stub.onTransact(IBluetoothGattServerCallback.java:85)
        at android.os.Binder.execTransact(Binder.java:674)
2019-01-16 15:21:18.872 26268-26268/me.dbw9580.apps.BridgeTooth D/HidPeripheral: Service: 0000180f-0000-1000-8000-00805f9b34fb added.
2019-01-16 15:21:18.874 26268-26281/me.dbw9580.apps.BridgeTooth D/BluetoothGattServer: onServiceAdded() - handle=56 uuid=0000180a-0000-1000-8000-00805f9b34fb status=0
2019-01-16 15:21:18.874 26268-26268/me.dbw9580.apps.BridgeTooth D/BluetoothKeyboardService: onStartCommand: Intent { cmp=me.dbw9580.apps.BridgeTooth/.BluetoothKeyboardService }
2019-01-16 15:21:18.874 26268-29450/me.dbw9580.apps.BridgeTooth D/BluetoothKeyboardService: received intent: null
2019-01-16 15:21:18.875 26268-26281/me.dbw9580.apps.BridgeTooth W/Binder: Caught a RuntimeException from the binder stub implementation.
    java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
        at java.util.ArrayList.get(ArrayList.java:437)
        at android.bluetooth.BluetoothGattServer$1.onServiceAdded(BluetoothGattServer.java:121)
        at android.bluetooth.IBluetoothGattServerCallback$Stub.onTransact(IBluetoothGattServerCallback.java:85)
        at android.os.Binder.execTransact(Binder.java:674)
2019-01-16 15:21:18.877 26268-26268/me.dbw9580.apps.BridgeTooth W/Notification: Use of stream types is deprecated for operations other than volume control
2019-01-16 15:21:18.877 26268-26268/me.dbw9580.apps.BridgeTooth W/Notification: See the documentation of setSound() for what to use instead with android.media.AudioAttributes to qualify your playback use case
2019-01-16 15:21:18.881 26268-26281/me.dbw9580.apps.BridgeTooth D/BluetoothGattServer: onServiceAdded() - handle=63 uuid=0000180f-0000-1000-8000-00805f9b34fb status=0
2019-01-16 15:21:18.889 26268-26268/me.dbw9580.apps.BridgeTooth D/HidPeripheral: advertiseData: AdvertiseData [mServiceUuids=[0000180a-0000-1000-8000-00805f9b34fb, 00001812-0000-1000-8000-00805f9b34fb, 0000180f-0000-1000-8000-00805f9b34fb], mManufacturerSpecificData={}, mServiceData={}, mIncludeTxPowerLevel=false, mIncludeDeviceName=true], scanResult: AdvertiseData [mServiceUuids=[0000180a-0000-1000-8000-00805f9b34fb, 00001812-0000-1000-8000-00805f9b34fb, 0000180f-0000-1000-8000-00805f9b34fb], mManufacturerSpecificData={}, mServiceData={}, mIncludeTxPowerLevel=false, mIncludeDeviceName=false]
2019-01-16 15:21:18.891 26268-26268/me.dbw9580.apps.BridgeTooth D/BluetoothAdapter: isLeEnabled(): ON

I have created a pull request #10 that tries to solve this by using a local flag. I have tried mutex locks, but they only work when the shared object is accessed from two different threads. In our situation, it seems that the callback is executed on the same thread as the other codes, so they do not work. I am not aware of any better solution, though.

I have tested the fix in my own application on Android 8.0 on a Sony XZ1 Compact device, and it seems to be working fine.