Repository navigation
Re-arm alarms after reboot; fix MediaPlayer leak; cut dead weight - #17
Merged
Merged
Conversation
Audit findings ("ponytail" + leaks):
- Critical: Android deletes an app's alarms on reboot, and BootReceiver
only set a flag that nothing ever read (consumeNeedsReschedule had no
Dart caller). After any restart, no dose alarm was armed until the app
was opened. AlarmScheduler now keeps a native registry of armed
alarms (added on schedule, removed on cancel and on fire), and
BootReceiver re-arms from it without needing Dart. A dose that came
due while the phone was off still rings, late, within the 2h grace.
- Leak: AlarmService skipped MediaPlayer.release() when stop() threw,
and lost the player entirely when setup failed. Both now release.
- Personal data: drop the medication's brand name from AGENTS.md.
- Dead weight: the reboot flag and consumeNeedsReschedule, Dose.copyWith
and DoseDatabase.medication() (no callers), an unused return value,
LOCKED_BOOT_COMPLETED (never delivered to this receiver), template
comments in pubspec.yaml and a stale TODO in build.gradle.kts.
- Docs: correct the release-signing claim (release uses the debug key
here), and tell users to allow auto-launch so boot re-arming runs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SusieeTheLinuxUser
marked this pull request as ready for review
September 24, 2026 20:52
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Findings from a whole-repo audit ("ponytail", plus leaks), and their fixes.
Critical: no alarms after a reboot
Android deletes an app's alarms whenever the phone restarts.
BootReceiveronly set aneeds_rescheduleflag, and nothing ever read it:consumeNeedsRescheduleexisted in Kotlin and Dart but had no caller. So after any restart (an OS update, or the battery dying overnight and being recharged), no dose alarms were armed until the app happened to be opened. The tests never caught it because the phone never restarted during one.Fix:
AlarmSchedulernow keeps a native list of every armed alarm. An entry is added when an alarm is scheduled and removed when it's cancelled or fires.BootReceiverre-arms from that list without needing Dart.MY_PACKAGE_REPLACEDre-arms too.Leak: MediaPlayer in
AlarmServiceplayer?.runCatching { stop(); release() }skippedrelease()wheneverstop()threw, for example when the player never started. A failed setup (prepare()throwing) also left the new player unreferenced. Both paths now always release.Personal data
The medication's brand name, which reveals a health condition, was in the public
AGENTS.md; this replaces it with neutral wording. It's still in the history of three older commits; removing it from there would need a history rewrite, which is the owner's call.A scan of every commit in the history found no secrets (keys, tokens, passwords, signing configs). Commit identities use GitHub noreply addresses.
Dead weight cut
consumeNeedsReschedule, in both Kotlin and Dart.Dose.copyWithandDoseDatabase.medication(), which had no callers.applyPendingAlarmActions' unused return value.LOCKED_BOOT_COMPLETED: Android never delivers it to a receiver that isn't direct-boot-aware.pubspec.yaml, and its "A new Flutter project" description.build.gradle.kts.Docs
AGENTS.md: release builds here are signed with the same debug key, not a different one.AGENTS.md.Testing
flutter analyzeis clean, and all 27 tests pass.adb install -r, then open the app once so the alarm list fills.adb reboot, then unlock the phone. Without opening DoseKeeper, runadb shell dumpsys alarm | grep dev.susiee.dosekeeper.🤖 Generated with Claude Code