Skip to content

fix: skip Dart-side reconnect polling after manual disconnect#5915

Open
mdmohsin7 wants to merge 1 commit intomainfrom
fix/skip-reconnect-on-manual-disconnect
Open

fix: skip Dart-side reconnect polling after manual disconnect#5915
mdmohsin7 wants to merge 1 commit intomainfrom
fix/skip-reconnect-on-manual-disconnect

Conversation

@mdmohsin7
Copy link
Member

@mdmohsin7 mdmohsin7 commented Mar 22, 2026

Summary

When the user manually disconnects from the Device Info page, onDeviceDisconnected calls periodicConnect after 1 second. periodicConnect calls ensureConnection, but since the device was unpaired, there's no paired device ID — so it falls through to _startPollingReconnect, triggering a 15-second scan timer that keeps searching for a device to connect to. This is unnecessary because:

  1. The user explicitly disconnected — there's no reason to search for a device
  2. BLE reconnection for non-manual disconnects is now handled entirely on the native side (Android: autoConnect=true with unlimited retries, iOS: centralManager.connect() chipset-level reconnect)

Added a _manualDisconnect flag that is set in _bleDisconnectDevice and checked in onDeviceDisconnected. When true, skips periodicConnect and cancels the reconnection timer.

Test plan

  • Disconnect device from Device Info page — device should stay disconnected, no polling timer
  • Device disconnects on its own — native side should auto-reconnect
  • Close and reopen app — should reconnect to paired device

🤖 Generated with Claude Code

When the user disconnects from Device Info page, set _manualDisconnect
flag so onDeviceDisconnected skips periodicConnect and cancels the
reconnection timer.
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 22, 2026

Greptile Summary

This PR introduces a _manualDisconnect boolean flag in DeviceProvider to distinguish intentional user-triggered disconnects from organic ones (out-of-range, signal loss), skipping the 1-second-delayed periodicConnect call in onDeviceDisconnected when the flag is set. The approach is sound and addresses a real regression where the device would reconnect immediately after the user explicitly disconnected from the Device Info page.

Key issues found:

  • Disconnect notification still fires on manual disconnect — the _disconnectNotificationTimer (30-second "Your Omi Device Disconnected" push notification) is started before the _manualDisconnect guard is evaluated, so users who intentionally disconnect will still receive a misleading "please reconnect" notification. The guard must be checked before the timer is armed.
  • Flag not reset when ensureConnection returns null_manualDisconnect is set to true before awaiting ensureConnection. If the call returns null, the method returns early but leaves the flag true. Any subsequent organic disconnect will consume the stale flag and skip auto-reconnect.
  • DFU flow regressionprepareDFU() calls _bleDisconnectDevice() (which now sets _manualDisconnect = true), causing onDeviceDisconnected to cancel the reconnection timer and return early. Previously _reconnectAt was used to delay polling so it would resume after the firmware reboot; now polling is never restarted from the Dart side. Whether this is a problem depends on whether the native layer (CompanionDeviceService / CoreBluetooth) reliably reconnects post-DFU; if it does not, the device will stay disconnected after a firmware update.

Confidence Score: 3/5

  • The core reconnect-skip logic is correct, but three concrete bugs need to be addressed before merging: a misleading push notification on manual disconnect, a stale-flag edge case, and a potential DFU reconnect regression.
  • The intent and overall approach are correct, but the implementation has three distinct logic issues that affect real user-facing behavior: (1) the disconnect notification fires even on manual disconnect, (2) the flag can be left stale if ensureConnection returns null, and (3) DFU reconnection may be broken. Issue 1 is definitely a regression from this PR; issues 2 and 3 are latent bugs introduced by the change. All three have straightforward fixes.
  • app/lib/providers/device_provider.dart — specifically the onDeviceDisconnected, _bleDisconnectDevice, and prepareDFU methods.

Important Files Changed

Filename Overview
app/lib/providers/device_provider.dart Adds _manualDisconnect flag to prevent auto-reconnect after explicit user disconnect; three issues: notification timer still fires on manual disconnect, flag not reset when ensureConnection returns null, and DFU flow now suppresses post-update reconnect polling.

Sequence Diagram

sequenceDiagram
    participant User
    participant DeviceInfoPage
    participant DeviceProvider
    participant BLELayer
    participant NativeLayer

    Note over User,NativeLayer: Manual Disconnect Flow (new behavior)
    User->>DeviceInfoPage: Tap Disconnect
    DeviceInfoPage->>DeviceProvider: _bleDisconnectDevice(device)
    DeviceProvider->>DeviceProvider: _manualDisconnect = true
    DeviceProvider->>BLELayer: ensureConnection()
    BLELayer-->>DeviceProvider: connection
    DeviceProvider->>BLELayer: connection.disconnect()
    BLELayer-->>DeviceProvider: onDeviceConnectionStateChanged(disconnected)
    DeviceProvider->>DeviceProvider: onDeviceDisconnected()
    DeviceProvider->>DeviceProvider: _disconnectNotificationTimer starts ⚠️
    DeviceProvider->>DeviceProvider: _manualDisconnect == true → reset, cancel timer, return
    Note over DeviceProvider: periodicConnect() NOT called ✓

    Note over User,NativeLayer: Organic Disconnect Flow (unchanged)
    BLELayer-->>DeviceProvider: onDeviceConnectionStateChanged(disconnected)
    DeviceProvider->>DeviceProvider: onDeviceDisconnected()
    DeviceProvider->>DeviceProvider: _manualDisconnect == false → continue
    DeviceProvider->>DeviceProvider: Future.delayed(1s) → periodicConnect()
    DeviceProvider->>NativeLayer: ensureConnection / scan
Loading

Comments Outside Diff (3)

  1. app/lib/providers/device_provider.dart, line 397-416 (link)

    P1 Disconnect notification still fires on manual disconnect

    The _disconnectNotificationTimer is started unconditionally before the _manualDisconnect check (lines 398–404). This means when a user intentionally disconnects from the Device Info page, they will still receive a "Your Omi Device Disconnected — Please reconnect" push notification 30 seconds later, which is misleading and annoying since the disconnect was deliberate.

    The _manualDisconnect check (line 413) should come before the timer is started, or the timer should be cancelled inside the early-return block.

  2. app/lib/providers/device_provider.dart, line 106-113 (link)

    P1 _manualDisconnect not reset when ensureConnection returns null

    _manualDisconnect is set to true on line 107 before ensureConnection is awaited. If ensureConnection returns null (e.g. the device is already gone from the BLE layer), the function returns early on line 110 without resetting the flag to false. No disconnect() call follows, so onDeviceDisconnected will not be triggered by this call — but the flag stays true.

    If the device then disconnects organically (e.g. goes out of range) and onDeviceDisconnected fires from a separate code path, the stale true flag will be consumed, causing periodicConnect to be skipped for what is really an involuntary disconnect, so the app won't attempt to auto-reconnect.

  3. app/lib/providers/device_provider.dart, line 689-695 (link)

    P1 DFU disconnect now suppresses post-update reconnect

    prepareDFU calls _bleDisconnectDevice, which now sets _manualDisconnect = true. When the BLE connection drops for the DFU bootloader, onDeviceDisconnected will fire, see _manualDisconnect = true, cancel _reconnectionTimer, and skip periodicConnect.

    Previously, _reconnectAt was set 30 s in the future to delay (not skip) reconnection polling so it would resume after DFU completed. With the new early-return, polling is never restarted at all from the Dart side after a DFU disconnect. If the native BLE layer (CompanionDeviceService / CoreBluetooth) reliably re-connects the device after the firmware reboot, this is fine. If it does not, the device will stay disconnected until the user manually triggers a reconnect.

    Consider either:

    • Not marking DFU as a manual disconnect (set _manualDisconnect = false immediately after _bleDisconnectDevice in prepareDFU), or
    • Restarting periodicConnect inside prepareDFU after the intended 30 s delay, bypassing the flag entirely for the DFU flow.

Reviews (1): Last reviewed commit: "fix: skip Dart-side reconnect polling af..." | Re-trigger Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant