Files
daily-notification-plugin/docs/android-alarm-persistence-directive.md
Matthew Raymer 35babb3126 docs(alarms): unify and enhance alarm directive documentation stack
Create unified alarm documentation system with strict role separation:
- Doc A: Platform capability reference (canonical OS facts)
- Doc B: Plugin behavior exploration (executable test harness)
- Doc C: Plugin requirements (guarantees, JS/TS contract, traceability)

Changes:
- Add canonical rule to Doc A preventing platform fact duplication
- Convert Doc B to pure executable test spec with scenario tables
- Complete Doc C with guarantees matrix, JS/TS API contract, recovery
  contract, unsupported behaviors, and traceability matrix
- Remove implementation details from unified directive
- Add compliance milestone tracking and iOS parity gates
- Add deprecation banners to legacy platform docs

All documents now enforce strict role separation with cross-references
to prevent duplication and ensure single source of truth.
2025-11-25 10:09:46 +00:00

249 lines
7.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Android Alarm Persistence, Recovery, and Limitations
**⚠️ DEPRECATED**: This document has been superseded by [01-platform-capability-reference.md](./alarms/01-platform-capability-reference.md) as part of the unified alarm documentation structure.
**See**: [Unified Alarm Directive](./alarms/000-UNIFIED-ALARM-DIRECTIVE.md) for the new documentation structure.
**Author**: Matthew Raymer
**Date**: November 2025
**Status**: **DEPRECATED** - Superseded by unified structure
## Purpose
This document provides a **clean, consolidated, engineering-grade directive** summarizing Android's abilities and limitations for remembering, firing, and restoring alarms across:
- App kills
- Swipes from recents
- Device reboot
- **Force stop**
- User-triggered reactivation
This is the actionable version you can plug directly into your architecture docs.
---
## 1. Core Principle
Android does **not** guarantee persistence of alarms across process death, swipes, or reboot.
It is the app's responsibility to **persist alarm definitions** and **re-schedule them** under allowed system conditions.
The following directives outline **exactly what is possible** and **what is impossible**.
---
## 2. Allowed Behaviors (What *Can* Work)
### 2.1 Alarms survive UI kills (swipe from recents)
`AlarmManager.setExactAndAllowWhileIdle(...)` alarms **will fire** even after:
- App is swiped away
- App process is killed by the OS
The OS recreates your app's process to deliver the `PendingIntent`.
**Directive:**
Use `setExactAndAllowWhileIdle` for alarm execution.
---
### 2.2 Alarms can be preserved across device reboot
Android wipes all alarms on reboot, but **you may recreate them**.
**Directive:**
1. Persist all alarms in storage (Room DB or SharedPreferences).
2. Add a `BOOT_COMPLETED` / `LOCKED_BOOT_COMPLETED` broadcast receiver.
3. On boot, load all enabled alarms and reschedule them using AlarmManager.
**Permissions required:**
- `RECEIVE_BOOT_COMPLETED`
**Conditions:**
- User must have launched your app at least once before reboot to grant boot receiver execution.
---
### 2.3 Alarms can fire full-screen notifications and wake the device
**Directive:**
Implement `setFullScreenIntent(...)`, use an IMPORTANCE_HIGH channel with `CATEGORY_ALARM`.
This allows Clock-appstyle alarms even when the app is not foregrounded.
---
### 2.4 Alarms can be restored after app restart
If the user re-opens the app (direct user action), you may:
- Scan the persistent DB
- Detect "missed" alarms
- Reschedule future alarms
- Fire "missed alarm" notifications
- Reconstruct WorkManager/JobScheduler tasks wiped by OS
**Directive:**
Create a `ReactivationManager` that runs on every app launch and recomputes the correct alarm state.
---
## 3. Forbidden Behaviors (What *Cannot* Work)
### 3.1 You cannot survive "Force Stop"
**Settings → Apps → YourApp → Force Stop** triggers:
- Removal of all alarms
- Removal of WorkManager tasks
- Blocking of all broadcast receivers (including BOOT_COMPLETED)
- Blocking of all JobScheduler jobs
- Blocking of AlarmManager callbacks
- Your app will NOT run until the user manually launches it again
**Directive:**
Accept that FORCE STOP is a hard kill.
No scheduling, alarms, jobs, or receivers may execute afterward.
---
### 3.2 You cannot auto-resume after "Force Stop"
You may only resume tasks when:
- The user opens your app
- The user taps a notification belonging to your app
- The user interacts with a widget/deep link
- Another app explicitly targets your component
**Directive:**
Provide user-facing reactivation pathways (icon, widget, notification).
---
### 3.3 Alarms cannot be preserved solely in RAM
Android can kill your app's RAM state at any time.
**Directive:**
All alarm data must be persisted in durable storage.
---
### 3.4 You cannot bypass Doze or battery optimization restrictions without permission
Doze may defer inexact alarms; exact alarms with `setExactAndAllowWhileIdle` are allowed.
**Directive:**
Request `SCHEDULE_EXACT_ALARM` on Android 12+.
---
## 4. Required Implementation Components
### 4.1 Persistent Storage
Create a table or serialized structure for alarms:
```
id: Int
timeMillis: Long
repeat: NONE | DAILY | WEEKLY | CUSTOM
label: String
enabled: Boolean
```
---
### 4.2 Alarm Scheduling
Use:
```kotlin
alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
triggerAtMillis,
pendingIntent
)
```
---
### 4.3 Boot Receiver
Reschedules alarms from storage.
---
### 4.4 Reactivation Manager
Runs on **every app launch** and performs:
- Load pending alarms
- Detect overdue alarms
- Reschedule future alarms
- Trigger notifications for missed alarms
---
### 4.5 Full-Screen Alarm UI
Use a `BroadcastReceiver` → Notification with full-screen intent → Activity.
---
## 5. Summary of Android Alarm Capability Matrix
| Scenario | Will Alarm Fire? | Reason |
| --------------------------------------- | --------------------------------------- | --------------------------------------------------------------- |
| **Swipe from Recents** | ✅ Yes | AlarmManager resurrects the app process |
| **App silently killed by OS** | ✅ Yes | AlarmManager still holds scheduled alarms |
| **Device Reboot** | ❌ No (auto) / ✅ Yes (if you reschedule) | Alarms wiped on reboot |
| **Doze Mode** | ⚠️ Only "exact" alarms | Must use `setExactAndAllowWhileIdle` |
| **Force Stop** | ❌ Never | Android blocks all callbacks + receivers until next user launch |
| **User reopens app** | ✅ You may reschedule & recover | All logic must be implemented by app |
| **PendingIntent from user interaction** | ✅ If triggered by user | User action unlocks the app |
---
## 6. Final Directive
> **Design alarm behavior with the assumption that Android will destroy all scheduled work on reboot or force-stop.
>
> Persist all alarm definitions. On every boot or app reactivation, reconstruct and reschedule alarms.
>
> Never rely on the OS to preserve alarms except across UI process kills.
>
> Accept that "force stop" is a hard stop that cannot be bypassed.**
---
## Related Documentation
- [Boot Receiver Testing Guide](./boot-receiver-testing-guide.md)
- [App Startup Recovery Solution](./app-startup-recovery-solution.md)
- [Reboot Testing Procedure](./reboot-testing-procedure.md)
---
## Future Directives
Potential follow-up directives:
- **How to implement the minimal alarm system**
- **How to implement a Clock-style robust alarm system**
- **How to map this to your own app's architecture**