Windows: Harden BLE connection lifetime, async callbacks, and notification subscription handling - #278
Conversation
- synchronize connected-device and GATT state access - retain device agents safely across in-flight operations - reject callbacks from stale connection instances - make disconnect and reconnect cleanup deterministic - validate WinRT completion status before reading results - handle cancelled asynchronous operations explicitly - own notification arguments across coroutine suspension - prevent Flutter reply exceptions from terminating the runner - avoid unsafe reference captures in asynchronous callbacks
- ignore stale connect completions after reconnect or disconnect - prevent superseded attempts from replacing active connections - catch exceptions from WinRT completion callbacks and Flutter replies - serialize notification changes per characteristic - update subscription tokens only after native handler changes succeed - preserve notification state during concurrent disconnect cleanup - document the additional Windows hardening - restore the example lockfile package version
There was a problem hiding this comment.
Pull request overview
This PR hardens the Windows BLE central/client implementation against connection lifecycle races and unsafe async callback handling, with additional serialization around notification subscription updates.
Changes:
- Introduces synchronized, generation-guarded connection ownership using
shared_ptrplus a connection-attempt generation map to ignore stale attempts/callbacks. - Adds WinRT completion safety (status checking before
GetResults(), exception containment, and safer value-captures in async callbacks). - Serializes notification subscription changes per characteristic using an agent-level mutex and “operation in progress” tracking; updates changelog.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| windows/src/universal_ble_plugin.h | Adds agent-level GATT mutex + per-characteristic notification-operation tracking; switches connected device storage to shared_ptr and adds connection-generation helpers. |
| windows/src/universal_ble_plugin.cpp | Implements generation-guarded connection install/removal, safer WinRT completion handling, and serialized notification subscribe/unsubscribe flows with rollback. |
| CHANGELOG.md | Adds an Unreleased entry describing the Windows hardening work. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
rohitsangwan01
left a comment
There was a problem hiding this comment.
Thankyou for the PR, LGTM
|
@rohitsangwan01 @fotiDim I have another potential PR relevant to this. To verify these changes on Windows side, I have implemented 38 additional hardware in loop tests using a bare nRF52DK development kit. Some focus on baseline BLE features like scanning, connection, MTU exchange, data exchange etc., and a good portion of them are fault injection tests which use the hardware jig to create real peripheral side faults and test the edge cases of this library. The fault injection tests make the nRF52 deliberately return ATT errors, delay operations, disconnect while operations are pending, interrupt notification bursts, and create connection and subscription races. The tests then verify error propagation, cleanup, reconnection, event ordering, and continued operation after recovery. I ran the changes in this PR against this suite today. The HIL setup could be useful as a separate, opt-in regression test suite for Windows BLE changes that cannot be exercised reliably with mocks or unit tests. In future it can also be extended to all other platforms. |
Summary
This PR hardens the Windows BLE implementation against connection lifecycle races, stale native callbacks, unsafe WinRT completion handling, and inconsistent notification subscription state.
It builds on the exception handling introduced in #217, focusing on cases where GATT operations, disconnects, reconnects, and asynchronous callbacks overlap.
Problem
Connected-device state is accessed by Flutter method handlers, WinRT callbacks, and coroutine continuations. Without synchronized ownership and stale-operation protection, this can allow:
GetResults();Changes
Connection ownership and synchronization
shared_ptr.Stale connection protection
Deterministic cleanup
GATT and notification safety
WinRT completion safety
AsyncStatus::Completedbefore callingGetResults().Scope
The changes are limited to the Windows central/client implementation. Public Dart APIs and behavior on Android, Apple, Linux, and Web are unchanged.
Verification