Skip to content

fix(ble): ensure GATT cleanup runs under NonCancellable on cancellation#5207

Merged
jamesarich merged 5 commits intomeshtastic:mainfrom
jdogg172:fix/ble-reconnect-crash
Apr 22, 2026
Merged

fix(ble): ensure GATT cleanup runs under NonCancellable on cancellation#5207
jamesarich merged 5 commits intomeshtastic:mainfrom
jdogg172:fix/ble-reconnect-crash

Conversation

@jdogg172
Copy link
Copy Markdown
Contributor

Summary

Fixes 2 crash-level bugs in the Android BLE reconnection stack.

Changes

BleRadioTransport.kt

  • CancellationException / GATT 133 fix: discoverServicesAndSetupCharacteristics() had a bare catch (e: Exception) that swallowed CancellationException, causing disconnect() to be skipped when the scope was cancelled. Skipping disconnect() leaks the BluetoothGatt handle and reliably triggers GATT status 133 on the next reconnect. Now re-throws CancellationException after running disconnect() under NonCancellable. Error path disconnect also wrapped in NonCancellable.

KableBleConnection.kt

  • Thread visibility: Added @Volatile to peripheral, stateJob, and connectionScope fields to ensure cross-thread visibility between connect() and disconnect() callers.

Tests

Added BleRadioTransportReconnectCrashTest with 4 new tests covering GATT handle cleanup, CancellationException path, failure disconnect, and transient-only disconnect signalling. All 108 tests pass (BUILD SUCCESSFUL on JVM).

…on discoverServicesAndSetupCharacteristics() caught bare Exception which swallowed CancellationException, causing disconnect() to be skipped when the scope was cancelled. Skipping disconnect() leaks the BluetoothGatt handle and reliably triggers GATT status 133 on the next reconnect attempt. Now re-throws CancellationException after running disconnect() under NonCancellable, and also wraps the error-path disconnect() in NonCancellable to guard against late cancellation during cleanup. Also mark KableBleConnection.peripheral/stateJob/connectionScope as @volatile to ensure cross-thread visibility between connect() and disconnect() callers.
…fix Tests verify: - close() calls disconnect() to release GATT handle (prevents status 133 on reconnect) - disconnect() is called when profile setup throws CancellationException - disconnect() is called on connection failure - only transient (non-permanent) onDisconnect is signalled after failure threshold All 4 new tests pass alongside 104 existing tests (BUILD SUCCESSFUL).
Copilot AI review requested due to automatic review settings April 21, 2026 21:07
@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Apr 21, 2026

CLA assistant check
All committers have signed the CLA.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the BLE reconnection path by ensuring GATT cleanup (disconnect) still runs when coroutines are cancelled, preventing leaked BluetoothGatt handles that can lead to GATT status 133 on subsequent reconnects. It also adds tests targeting the reconnect crash scenarios and improves cross-thread visibility in the Kable-based BLE connection implementation.

Changes:

  • Ensure BleRadioTransport.discoverServicesAndSetupCharacteristics() disconnects under NonCancellable and rethrows CancellationException.
  • Add @Volatile to key fields in KableBleConnection to improve cross-thread visibility between connect/disconnect callers.
  • Add BleRadioTransportReconnectCrashTest with coverage for cleanup and reconnect signaling behaviors.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
core/network/src/commonMain/kotlin/org/meshtastic/core/network/radio/BleRadioTransport.kt Ensures disconnect cleanup runs under NonCancellable, including the cancellation path.
core/ble/src/commonMain/kotlin/org/meshtastic/core/ble/KableBleConnection.kt Adds @Volatile to shared mutable fields used across connect/disconnect.
core/network/src/commonTest/kotlin/org/meshtastic/core/network/radio/BleRadioTransportReconnectCrashTest.kt Adds regression tests for cancellation + failure cleanup and transient disconnect behavior.

Comment on lines +94 to +98
@Volatile private var peripheral: Peripheral? = null

@Volatile private var stateJob: Job? = null

@Volatile private var connectionScope: CoroutineScope? = null
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: added import kotlin.concurrent.Volatile at the top of KableBleConnection.kt so the annotation resolves correctly in commonMain.

Comment on lines +64 to +65
private val testScope = TestScope()
private val scanner = FakeBleScanner()
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: removed the unused testScope = TestScope() field. Also restored the SharedFlow, asSharedFlow, and advanceTimeBy imports that the CancellingProfileBleConnection inner class and virtual-time tests depend on.

jdogg172 and others added 3 commits April 21, 2026 16:28
- KableBleConnection: add missing kotlin.concurrent.Volatile import (was causing compile error in commonMain)
- BleRadioTransportReconnectCrashTest: remove unused testScope field; restore SharedFlow/asSharedFlow/advanceTimeBy imports that were needed by CancellingProfileBleConnection inner class
Refactor exception handling in `BleRadioTransport` to catch `CancellationException` separately from general exceptions. This ensures that GATT cleanup runs correctly during coroutine cancellation without logging the event as a service discovery failure.
@jamesarich jamesarich merged commit 6547877 into meshtastic:main Apr 22, 2026
1 check passed
jamesarich added a commit that referenced this pull request Apr 22, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI pushed a commit that referenced this pull request Apr 24, 2026
…on (#5207)

Co-authored-by: James Rich <james.a.rich@gmail.com>
Co-authored-by: James Rich <2199651+jamesarich@users.noreply.github.com>
Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com>
Copilot AI pushed a commit that referenced this pull request Apr 24, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix PR tag

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants