Repository navigation
Record dropped schedules, and ask Android whether it is restricting this app - #4
Merged
Merged
Conversation
Re-arming on app start fixes a dropped schedule, but on its own it also destroys the evidence that anything went wrong: by the time anyone opens the app to look, it has been repaired and everything reads healthy. That is exactly the case where knowing whether it happened at all IS the diagnosis - a dropped schedule means the OS killed the app, while an alarm that stayed registered and still didn't ring is a bug in here, and the two need completely different fixes. The startup path now runs the health check first, records the finding, and only then re-arms. Settings shows the count and the most recent occurrence when there is one, so each failure leaves a trail instead of costing a night of observation to catch in the act. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01C7x8v5c5Lu29PBWy1LdfCe
Confirmed today that the ringing sensor never changed state, so the ring service never started; combined with the alarm still showing as enabled in the app's own widget yesterday (which means the receiver never ran and never consumed it), AlarmManager did not deliver the alarm at all. The entry was gone before its trigger time. Force-stopping an app is what drops its alarm entries, and Android will say whether it has this app in that position rather than leaving it to be inferred from a missed alarm. ActivityManager.isBackgroundRestricted() reports Restricted background mode, and UsageStatsManager .getAppStandbyBucket() reports the standby bucket - unprivileged for the calling app, so neither needs a permission. Settings now warns when either says alarms cannot be relied on, and opens the app's settings page, since no intent toggles this directly. This is diagnosis rather than a fix: nothing here can stop a force-stop. But it turns "the alarm didn't ring and we don't know why" into the OS stating its own position, which is what has been missing for five failures. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01C7x8v5c5Lu29PBWy1LdfCe
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.
What's now established
Two observations settle the mechanism:
AlarmReceivernever ran and never consumed it.binary_sensor.<phone>_ringingnever changed state overnight. The ring service never started.Together:
AlarmManagernever delivered the alarm. The entry was gone before its trigger time ever arrived. This was never a "didn't ring" problem — it's a "wasn't scheduled any more" problem, which is why none of the ring-path fixes moved the needle.1. Record the drop before repairing it
The self-repair from #3 had a flaw: re-arming on app start erases the evidence that it was needed. By the time anyone looks, it's fixed and everything reads healthy.
The startup path now runs the health check first, records the finding via
AlarmScheduleAudit, and only then re-arms. Settings shows the count and most recent occurrence, so future failures leave a trail rather than costing a night of observing at exactly the right moment — which, as it turns out, is impossible anyway while asleep.2. Ask the OS what it thinks of this app
Force-stopping is what drops alarm entries, and Android will simply say whether it has the app in that position instead of leaving it inferred from a missed alarm:
ActivityManager.isBackgroundRestricted()— Restricted background mode.UsageStatsManager.getAppStandbyBucket()— the standby bucket (Rare/Restricted are the dangerous ones).Both are unprivileged for the calling app, so neither needs a permission. Settings warns when either says alarms can't be relied on, and opens the app's settings page — no intent toggles this directly.
Scope
This is diagnosis, not a fix. Nothing here can prevent a force-stop. What it does is turn "the alarm didn't ring and nobody knows why" into the OS stating its own position — which is what has been missing across five failures.
Not verified
Uncompiled here —
./gradlew assembleDebugfirst.