Skip to content

Re-arm alarms after reboot; fix MediaPlayer leak; cut dead weight - #17

Merged
SusieeTheLinuxUser merged 1 commit into
masterfrom
claude/kind-ritchie-qn540r
Sep 24, 2026
Merged

SusieeTheLinuxUser merged 1 commit into
masterfrom
claude/kind-ritchie-qn540r

Conversation

@SusieeTheLinuxUser

@SusieeTheLinuxUser SusieeTheLinuxUser commented Sep 24, 2026 •

Copy link
Copy Markdown
Owner

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. BootReceiver only set a needs_reschedule flag, and nothing ever read it: consumeNeedsReschedule existed 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: AlarmScheduler now 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. BootReceiver re-arms from that list without needing Dart.

  • A dose that came due while the phone was off still rings, 10 s after boot, as long as it's within the 2 h missed-dose grace period. Late beats never.
  • MY_PACKAGE_REPLACED re-arms too.
  • ColorOS only delivers boot broadcasts to apps allowed to auto-launch, so the README now says to allow that.

Leak: MediaPlayer in AlarmService

player?.runCatching { stop(); release() } skipped release() whenever stop() 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

  • The reboot flag and consumeNeedsReschedule, in both Kotlin and Dart.
  • Dose.copyWith and DoseDatabase.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.
  • 58 lines of template comments in pubspec.yaml, and its "A new Flutter project" description.
  • A stale template TODO in build.gradle.kts.

Docs

  • Corrected AGENTS.md: release builds here are signed with the same debug key, not a different one.
  • Recorded the audit in AGENTS.md.

Testing

  • flutter analyze is clean, and all 27 tests pass.
  • The Kotlin compiles for the first time in CI's APK build.
  • Needs a check on the phone before relying on it:
    1. Install with adb install -r, then open the app once so the alarm list fills.
    2. Close the app and run adb reboot, then unlock the phone. Without opening DoseKeeper, run adb shell dumpsys alarm | grep dev.susiee.dosekeeper.
    3. The same alarms must be listed.

🤖 Generated with Claude Code

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
SusieeTheLinuxUser marked this pull request as ready for review September 24, 2026 20:52
@SusieeTheLinuxUser
SusieeTheLinuxUser merged commit 5d923a9 into master Sep 24, 2026
1 check 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

Development

Successfully merging this pull request may close these issues.

2 participants