You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(app): release the live session on dispose and stop background polling - #177
CgmAppController.dispose() cancelled the scan iterator, reconnect timer,
history-persist timer and both subscriptions, then left _session alive. _CgmHomePageState.didChangeAppLifecycleState returned early for every state
except resumed, so the 45 s _foregroundFreshnessInterval timer kept calling ensureFreshData() - which connects when no session is held.
RED
cd openhealth && flutter test test/app_controller_persistence_test.dart --plain-name 'disconnects its live session'
exit 1
Expected: <1>
Actual: <0>
a disposed controller must not leave a GATT client open
test/app_controller_persistence_test.dart 2570:5
cd openhealth && flutter test test/app_controller_persistence_test.dart --plain-name 'backgrounded app stops foreground polling'
exit 1
Expected: <1>
Actual: <3>
a backgrounded app must not keep driving its session
test/app_controller_persistence_test.dart 2623:5
Both runs are against 17db5b4, the base this branch forks.
GREEN
Same two commands on this branch: exit 0, +1: All tests passed! each.
The fix
dispose() takes _session, clears it, and hands it to session.disconnect() with the same fail-soft shape the subscription cancels
already use: unawaited, errors swallowed. Nothing else claimed ownership, so a
throw here has no reporter.
main.dart replaces the resumed-only early return with a full switch:
resumed - restart the foreground poll, then ensureFreshData(force: true)
as before.
inactive - no work. This is a permission sheet, the app switcher or a
call; the user has not left the app.
hidden / paused / detached - stop the foreground poll.
_startForegroundFreshness / _stopForegroundFreshness are the only places
that touch _freshnessTimer, so dispose() and the lifecycle handler cannot
drift apart.
What this deliberately does not do
It does not disconnect the session on background. Three pieces of the
existing design say not to:
openhealth/ios/Runner/Info.plist declares UIBackgroundModes: bluetooth-central, so a held link is a supported background state, not a leak.
The lock-screen surface is fed from that same session: disconnect() pushes _pushLiveActivity(), and a disconnected snapshot is not publishable, so
tearing down on background would end() the Live Activity every time the user
leaves the app.
AidexBackgroundMonitor scans natively only while !isAppActive, which is a
hand-off model: it is only coherent if the Dart session is allowed to persist.
The leak in the background path is the poll, not the link, so the poll is what
was removed.
Verification
flutter test test/app_controller_persistence_test.dart - exit 0, +51
flutter test - exit 0, +482: All tests passed!
flutter analyze - No issues found!
dart format --output=none --set-exit-if-changed lib/src/app_controller.dart test/app_controller_persistence_test.dart - clean
lib/main.dart still carries one pre-existing, unrelated formatting drift
(lines 1131-1133, the
8000
"Latest reading at ..." expression) that a whole-file dart format rewrites. It is on another lane and is left exactly as found; this
change is confined to the lifecycle region.
Rebase reminder (comment only, no edits to this branch).
The lane head is now 05785b0 (#192 merged). This PR is still based on 17db5b4; the merge-base is fine, but expect a conflict in packages/cgm_cbio/lib/src/cbio_glucose_session.dart around credential resolution. Resolve it by keeping the injected path:
the session resolves CbioCredentials once per session into _resolved, before the transport is touched, and every mask/unmask/builder call receives _streamKey/the material explicitly;
CbioCompiledAuthMaterialProvider and the compiled constants are gone; a build without material fails closed with cbio.auth.material;
the lane also reads _clockReferenceEpochSeconds (set in _writeClock) when it derives the record-index anchor, so keep that field and the _historyReadingsFor(anchor) / metadata spread when you resolve.
Rebase onto feature/cbio-gs1-app-live before review so CI runs against the lane head.
Rebased onto the lane tip 9de5544 (05785b0 + #193 + #197) and force-pushed. No conflicts: this PR's main.dart hunk is in the home-page lifecycle state, while the lane's new hunk is in the hero card.
Verification on the rebased head: openhealth flutter test 490 passed, flutter analyze clean, dart format clean. One full-suite run reported a single failure before the fix was touched again; three further runs (including the whole suite twice and app_controller_persistence_test.dart three times) were green, so it looks like a load-sensitive flake rather than a regression - flagged here rather than hidden.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #176.
The defect
dispose()CgmAppController.dispose()cancelled the scan iterator, reconnect timer,history-persist timer and both subscriptions, then left
_sessionalive._CgmHomePageState.didChangeAppLifecycleStatereturned early for every stateexcept
resumed, so the 45 s_foregroundFreshnessIntervaltimer kept callingensureFreshData()- which connects when no session is held.RED
Both runs are against
17db5b4, the base this branch forks.GREEN
Same two commands on this branch:
exit 0,+1: All tests passed!each.The fix
dispose()takes_session, clears it, and hands it tosession.disconnect()with the same fail-soft shape the subscription cancelsalready use: unawaited, errors swallowed. Nothing else claimed ownership, so a
throw here has no reporter.
main.dartreplaces theresumed-only early return with a full switch:resumed- restart the foreground poll, thenensureFreshData(force: true)as before.
inactive- no work. This is a permission sheet, the app switcher or acall; the user has not left the app.
hidden/paused/detached- stop the foreground poll._startForegroundFreshness/_stopForegroundFreshnessare the only placesthat touch
_freshnessTimer, sodispose()and the lifecycle handler cannotdrift apart.
What this deliberately does not do
It does not disconnect the session on background. Three pieces of the
existing design say not to:
openhealth/ios/Runner/Info.plistdeclaresUIBackgroundModes: bluetooth-central, so a held link is a supported background state, not a leak.disconnect()pushes_pushLiveActivity(), and a disconnected snapshot is not publishable, sotearing down on background would
end()the Live Activity every time the userleaves the app.
AidexBackgroundMonitorscans natively only while!isAppActive, which is ahand-off model: it is only coherent if the Dart session is allowed to persist.
The leak in the background path is the poll, not the link, so the poll is what
was removed.
Verification
flutter test test/app_controller_persistence_test.dart- exit 0,+51flutter test- exit 0,+482: All tests passed!flutter analyze-No issues found!dart format --output=none --set-exit-if-changed lib/src/app_controller.dart test/app_controller_persistence_test.dart- cleanlib/main.dartstill carries one pre-existing, unrelated formatting drift(lines 1131-1133, the 8000 "Latest reading at ..." expression) that a whole-file
dart formatrewrites. It is on another lane and is left exactly as found; thischange is confined to the lifecycle region.