Skip to content

fix(ble): Fail bonding promptly when polled state returns none#5982

Merged
jamesarich merged 4 commits into
meshtastic:mainfrom
jeremiah-k:bugfix/ble-fail-bond-none-poll-early
Jun 27, 2026
Merged

fix(ble): Fail bonding promptly when polled state returns none#5982
jamesarich merged 4 commits into
meshtastic:mainfrom
jeremiah-k:bugfix/ble-fail-bond-none-poll-early

Conversation

@jeremiah-k

@jeremiah-k jeremiah-k commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Overview

This pull request makes Android BLE bonding fail promptly when the platform bond state returns to BOND_NONE after bonding was observed in progress, even if Android does not deliver the terminal bond-state broadcast.

This builds on the recent BLE bonding work in #5967, #5969, and #5973: bonding waits are now finite, failed user-facing pairing waits for an explicit retry instead of immediately arming the transport, and the BLE transport no longer continues into GATT setup after failed bonding when Android still reports the device as not bonded.

This change handles the remaining app-side wait case. Android may already know bonding failed, but the app may miss the terminal ACTION_BOND_STATE_CHANGED failure broadcast. Before this change, the polling path only completed early when Android reported BOND_BONDED. If pairing failed and polling saw BOND_NONE, the app could still wait until the full bonding timeout before surfacing the failure.

With this change, polling can now also fail the bond early when it is safe to treat BOND_NONE as terminal.

Key Changes

  • Preserved the receiver-based bonding path.
  • Preserved the 30-second upper bound around Android bonding.
  • Preserved early success when polling sees BOND_BONDED.
  • Added early failure when polling sees terminal BOND_NONE after bonding was observed in progress.
  • Added a short grace window after createBond() returns true so a slow BOND_NONE -> BOND_BONDING transition is not failed immediately.
  • Avoided treating the initial pre-bond BOND_NONE state as an immediate failure.
  • Reused the same bonding failure message for receiver-based and polling-based terminal failures.
  • Added timeout diagnostics so field logs can show what bond state Android exposed when no terminal state arrived.
  • Added Android host-test coverage for missed terminal failure broadcasts and grace handling.

Testing

  • Added coverage proving a missed terminal failure broadcast fails before the full bonding timeout.
  • Added coverage proving a newly initiated bond does not fail immediately while Android still reports initial BOND_NONE.
  • Added coverage proving persistent BOND_NONE after the grace window fails.
  • Added coverage proving BOND_BONDING followed by polled BOND_NONE fails.
  • Added coverage proving a spurious BOND_NONE broadcast is ignored while a later polled terminal BOND_NONE still fails.
  • Preserved coverage for missed successful terminal broadcasts.
  • Preserved coverage for timeout when no terminal bond state is observed.
  • Preserved coverage for timeout-boundary success when Android reports BOND_BONDED.

Migration Notes

  • No user data migration is required.
  • Existing BLE addresses and OS bonds are unchanged.
  • Bluetooth permission behavior is unchanged.
  • This only makes the app-side bond wait fail faster when Android already reports that bonding has ended unsuccessfully, and improves diagnostics when Android does not expose that terminal state.

Android can miss the terminal BOND_NONE broadcast after a failed pairing attempt. That left the app waiting until the full bonding timeout even when the platform had already returned the device to BOND_NONE.

Track whether bonding has been observed or newly initiated, then let the polling loop fail promptly on BOND_NONE only after bonding was in flight. Keep the existing BOND_BONDED polling success path and preserve a short initial grace poll for createBond() returning true before Android reports BOND_BONDING.

Add host-test coverage for missed terminal failure broadcasts and for the initial BOND_NONE race guard.
The grace-poll race test needs to observe BOND_NONE until the production polling code reaches the intended checkpoint. Robolectric's bond shadow may advance state as a side effect of createBond(), so reset the shadow after launching bond() before advancing virtual time.

This keeps the test focused on the initial BOND_NONE grace behavior rather than a shadow-driven transition out of the unbonded state.
The early BOND_NONE polling change introduced an extra private helper, pushing AndroidBluetoothRepository over the core:ble TooManyFunctions threshold. Inline the existing-bond observation branch back into startOrObserveBond so the class stays within its detekt budget without adding a suppression.

Run :core:ble:spotlessApply so the Kotlin formatting matches CI, including the BondWaitStart class signature and updated host-test setup.
Add focused Android host coverage for createBond() returning true while Android initially reports BOND_NONE, including persistent BOND_NONE after the grace window and BONDING followed by a polled terminal BOND_NONE.

Share the receiver and polling failure message through a single internal constant, add debug logging for polling-based failures, and document the fixed two-poll grace used to avoid failing a slow BOND_NONE to BOND_BONDING transition immediately.

Clarify the Robolectric createBond() shadow reset needed to model Android's asynchronous BOND_NONE to BOND_BONDING transition and document the polling state-machine invariant for terminal BOND_NONE handling.
@github-actions github-actions Bot added the bugfix PR tag label Jun 27, 2026
@jeremiah-k jeremiah-k marked this pull request as ready for review June 27, 2026 18:43
@jamesarich jamesarich added this pull request to the merge queue Jun 27, 2026
Merged via the queue into meshtastic:main with commit 1303f62 Jun 27, 2026
23 checks passed
@jeremiah-k jeremiah-k deleted the bugfix/ble-fail-bond-none-poll-early branch June 27, 2026 19:57
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.

2 participants