Conversation
Android's BluetoothGatt enforces a strict single-outstanding-operation rule per device (the AOSP mDeviceBusy lock). Bypassing the Dart queue (QueueType.none, or the unqueued readRssi from #298) made concurrent writes/rssi/mtu/discovery collide, returning false or status 133 (GATT_ERROR). Add PerDeviceGattQueue, a per-device FIFO that runs GATT operations one at a time and only dispatches the next operation once the in-flight one's matching GATT callback fires (or it completes synchronously). Different devices stay independent. Route read/write/descriptor-read/descriptor-write/setNotifiable/ readRssi/requestMtu/discoverServices through the queue; requestConnectionPriority is serialized on O+ where onConnectionUpdated is delivered. Refs #297
fotiDim
force-pushed
the
feat/android-native-per-device-queue
branch
from
September 7, 2026 21:08
94fb620 to
8a12a93
Compare
- Bump version to 3.0.0 (breaking: default queueType changed from global) - BleCommandQueue now defaults to QueueType.none (parallel) - serialization is handled natively per platform (Android PerDeviceGattQueue, Apple CoreBluetooth write pipelining) - Update docstrings, CHANGELOG, and README Command Queue sections - Update tests that assumed the global default; add default-is-none test Refs #297
…priority from the GATT queue
Contributor
Author
|
From my testing, there is no need to queue requests on Apple and Windows 11. Linux also behaved fine for the most part. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #297
Problem
Android's
BluetoothGattenforces a strict single-outstanding-operation rule per device (the AOSPmDeviceBusylock). When the Dart queue is bypassed —QueueType.none, or the unqueuedreadRssifrom #298 — concurrent writes/rssi/mtu/discovery calls collide:readRemoteRssi()/writeCharacteristic()returnfalseimmediately and Dart futures fail with status133 (GATT_ERROR).The cross-platform Dart queue can't protect Android: it's the very abstraction Apple requires to be bypassed for write throughput (#271), and Android is the only platform whose native stack has no queue of its own.
Solution
Add
PerDeviceGattQueue, a per-device FIFO that serializes GATT operations natively in Kotlin:onCharacteristicWrite,onCharacteristicRead,onDescriptorWrite,onDescriptorRead,onReadRemoteRssi,onMtuChanged,onServicesDiscovered), or it completes synchronously.cleanUpConnection) and engine detach.Routed through the queue:
writeValue,readValue,writeDescriptorValue,readDescriptorValue,setNotifiable(CCCD),readRssi,requestMtu,discoverServices(including the background discovery fromgetSystemDevices), andrequestConnectionPriorityon Android O+ (whereonConnectionUpdated— which clearsmDeviceBusy— is actually delivered).Why native, not a Dart-side guard
With a native per-device queue, Android is protected regardless of
QueueType, so the API stays zero-configuration andQueueType.noneremains safe on Android — no Dart-side enforcement needed. A Dart-side "don't allow none on Android" would just reintroduce slow Dart serialization for everyone.Testing
PerDeviceGattQueueTestcovering FIFO ordering, kind-matching, cross-device independence, cancel-on-disconnect, sync completion, and exception safety (8 tests).WriteCompletionTest,DisconnectCloseTest,UniversalBleHelperTest) all pass.flutter analyzeclean.No API changes; this is purely native serialization behind the existing method channel.