Skip to content

fix: harden device connect/disconnect and teardown paths#161

Merged
mortenboye merged 1 commit into
masterfrom
fix/connection-teardown-bugs
Jul 18, 2026
Merged

fix: harden device connect/disconnect and teardown paths#161
mortenboye merged 1 commit into
masterfrom
fix/connection-teardown-bugs

Conversation

@mortenboye

Copy link
Copy Markdown
Contributor

Summary

Fixes four connection-robustness reports. Each fix has a regression test.

| Report | Fix |
|––––|—–|
| "Peer removed pairing information" shown in the app | Map the universal_ble error (iOS CBErrorPeerRemovedPairingInformation) to a typed MidiPairingInfoRemovedException; best-effort clear the stale bond (Android; iOS requires the user to forget the device) instead of leaking a raw UniversalBleException. |
| #158 Android EPIPE crash on USB unplug | ConnectedDevice.close() routes each teardown step through a quiet helper, so an IOException: EPIPE from inputPort.flush() on a dead fd can't abort cleanup or escape onDeviceRemoved; disconnection notifications always fire. |
| #159 .devices clobbers a connecting device | _fromHostDevice no longer applies connected while a device is in a transitional connecting/disconnecting state. |
| #160 unhandled error disconnecting mid-connect | Eagerly guard the connectionEstablished future so a disconnect racing an in-flight connect can't leak to PlatformDispatcher.onError. |

Closes #158
Closes #159
Closes #160

Testing

  • flutter test for the main, platform-interface, and BLE packages
  • :flutter_midi_command_android:testDebugUnitTest (Kotlin)
  • flutter analyze clean across changed packages

Address four connection-robustness reports:

- BLE: map the universal_ble "Peer removed pairing information" error
  (iOS CBErrorPeerRemovedPairingInformation) to a typed
  MidiPairingInfoRemovedException, best-effort clearing the stale bond
  (works on Android; on iOS the user must forget the device) instead of
  leaking a raw UniversalBleException to the parent app.
- Android: make ConnectedDevice.close() resilient to a removed device.
  Unplugging a connected USB MIDI device threw IOException: EPIPE from
  inputPort.flush(), which aborted the rest of teardown and escaped the
  onDeviceRemoved callback, crashing the app. Each teardown step now runs
  quietly so the disconnection notifications always fire. Closes #158.
- Platform interface: stop a passive .devices refresh from collapsing a
  device's transitional connecting/disconnecting state to disconnected.
  Closes #159.
- Core: eagerly guard the connectionEstablished future so a disconnect
  that races an in-flight connect can no longer surface as an unhandled
  async error on PlatformDispatcher.onError. Closes #160.

Each fix is covered by a regression test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mortenboye
mortenboye merged commit 2ed06c6 into master Jul 18, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant