Files
daily-notification-plugin/docs/alarms/01-platform-capability-reference.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

469 lines
22 KiB
Markdown
Raw 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.
# Platform Capability Reference: Android & iOS Alarm/Notification Behavior
**Author**: Matthew Raymer
**Date**: November 2025
**Status**: Platform Reference - Stable
**Version**: 1.1.0
**Last Synced With Plugin Version**: v1.1.0
## Purpose
This document provides **pure OS-level facts** about alarm and notification capabilities on Android and iOS. It contains **no plugin-specific logic**—only platform mechanics that affect plugin design.
**This is a reference document** to be consulted when designing plugin behavior, not an implementation guide.
**⚠️ CANONICAL RULE**: No other document may contain OS-level behavior. All platform facts **MUST** reference this file. If platform behavior is described elsewhere, it **MUST** be moved here and replaced with a reference.
**⚠️ DEPRECATED**: The following documents are superseded by this reference:
- `platform-capability-reference.md` - Merged into this document
- `android-alarm-persistence-directive.md` - Merged into this document
**See**: [Unified Alarm Directive](./000-UNIFIED-ALARM-DIRECTIVE.md) for document structure.
---
## 1. Core Principles
### Android
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.
### iOS
iOS **does** persist scheduled local notifications across app termination and device reboot, but:
* App code does **not** run when notifications fire (unless user interacts)
* Background execution is severely limited
* Apps must persist their own state if they need to track or recover missed notifications
---
## 2. Android Alarm Capability Matrix
| Scenario | Will Alarm Fire? | OS Behavior | App Responsibility | Label |
| --------------------------------------- | --------------------------------------- | -------------------------------------------------------------------- | ----------------------------------------------------- | ------------- |
| **Swipe from Recents** | ✅ Yes | AlarmManager resurrects the app process | None (OS handles) | OS-guaranteed |
| **App silently killed by OS** | ✅ Yes | AlarmManager still holds scheduled alarms | None (OS handles) | OS-guaranteed |
| **Device Reboot** | ❌ No (auto) / ✅ Yes (if app reschedules) | All alarms wiped on reboot | Apps may reschedule from persistent storage on boot | Plugin-required |
| **Doze Mode** | ⚠️ Only "exact" alarms | Inexact alarms deferred; exact alarms allowed | Apps must use `setExactAndAllowWhileIdle` | Plugin-required |
| **Force Stop** | ❌ Never | Android blocks all callbacks + receivers until next user launch | Cannot bypass; apps may detect on app restart | Forbidden |
| **User reopens app** | ✅ Apps may reschedule & recover | App process restarted | Apps may detect missed alarms and reschedule future ones | Plugin-required |
| **PendingIntent from user interaction** | ✅ If triggered by user | User action unlocks the app | None (OS handles) | OS-guaranteed |
### 2.1 Android Allowed Behaviors
#### 2.1.1 Alarms survive UI kills (swipe from recents)
**OS-guaranteed**: `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`.
**Required API**: `setExactAndAllowWhileIdle()` or `setAlarmClock()`
#### 2.1.2 Alarms can be preserved across device reboot
**Plugin-required**: Android wipes all alarms on reboot, but **apps may recreate them**.
**OS Behavior**: All alarms are cleared on device reboot. No alarms persist automatically.
**App Capability**: Apps may recreate alarms after reboot by:
1. Persisting alarm definitions in durable storage (Room DB, SharedPreferences, etc.)
2. Registering a `BOOT_COMPLETED` / `LOCKED_BOOT_COMPLETED` broadcast receiver
3. Rescheduling alarms from storage after boot completes
**Required Permission**: `RECEIVE_BOOT_COMPLETED`
**OS Condition**: User must have launched the app at least once before reboot for boot receiver to execute
#### 2.1.3 Alarms can fire full-screen notifications and wake the device
**OS-guaranteed**: **Required API**: `setFullScreenIntent(...)`, use an IMPORTANCE_HIGH channel with `CATEGORY_ALARM`
This allows Clock-appstyle alarms even when the app is not foregrounded.
#### 2.1.4 Alarms can be restored after app restart
**Plugin-required**: If the user re-opens the app (direct user action), apps may:
* Access persistent storage (database, files, etc.)
* Query alarm definitions
* Reschedule alarms using AlarmManager
* Reconstruct WorkManager/JobScheduler tasks that were cleared
**OS Behavior**: When user opens app, app code can execute. AlarmManager and WorkManager APIs are available for rescheduling.
**Note**: This is an app capability, not OS-guaranteed behavior. Apps must implement this logic.
### 2.2 Android Forbidden Behaviors
#### 2.2.1 You cannot survive "Force Stop"
**Forbidden**: **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.
#### 2.2.2 You cannot auto-resume after "Force Stop"
**Forbidden**: 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
**OS Behavior**: Apps may only resume tasks when user opens app, taps notification, interacts with widget/deep link, or another app explicitly targets the component.
#### 2.2.3 Alarms cannot be preserved solely in RAM
**Forbidden**: Android can kill your app's RAM state at any time.
**OS Behavior**: All alarm data must be persisted in durable storage. RAM-only storage is not reliable.
#### 2.2.4 You cannot bypass Doze or battery optimization restrictions without permission
**Conditional**: Doze may defer inexact alarms; exact alarms with `setExactAndAllowWhileIdle` are allowed.
**Required Permission**: `SCHEDULE_EXACT_ALARM` on Android 12+ (API 31+)
---
## 3. iOS Notification Capability Matrix
| Scenario | Will Notification Fire? | OS Behavior | App Responsibility | Label |
| --------------------------------------- | ----------------------- | -------------------------------------------------------------------- | ----------------------------------------------------- | ------------- |
| **Swipe from App Switcher** | ✅ Yes | UNUserNotificationCenter persists and fires notifications | None (OS handles) | OS-guaranteed |
| **App Terminated by System** | ✅ Yes | UNUserNotificationCenter persists and fires notifications | None (OS handles) | OS-guaranteed |
| **Device Reboot** | ✅ Yes (for calendar/time triggers) | iOS persists scheduled local notifications across reboot | None for notifications; must persist own state if needed | OS-guaranteed |
| **App Force Quit (swipe away)** | ✅ Yes | UNUserNotificationCenter persists and fires notifications | None (OS handles) | OS-guaranteed |
| **Background Execution** | ❌ No arbitrary code | Only BGTaskScheduler with strict limits | Cannot rely on background execution for recovery | Forbidden |
| **Notification Fires** | ✅ Yes | Notification displayed; app code does NOT run unless user interacts | Must handle missed notifications on next app launch | OS-guaranteed |
| **User Taps Notification** | ✅ Yes | App launched; code can run | Can detect and handle missed notifications | OS-guaranteed |
### 3.1 iOS Allowed Behaviors
#### 3.1.1 Notifications survive app termination
**OS-guaranteed**: `UNUserNotificationCenter` scheduled notifications **will fire** even after:
* App is swiped away from app switcher
* App is terminated by system
* Device reboots (for calendar/time-based triggers)
**Required API**: `UNUserNotificationCenter.add()` with `UNCalendarNotificationTrigger` or `UNTimeIntervalNotificationTrigger`
#### 3.1.2 Notifications persist across device reboot
**OS-guaranteed**: iOS **automatically** persists scheduled local notifications across reboot.
**No app code required** for basic notification persistence.
**Limitation**: Only calendar and time-based triggers persist. Location-based triggers do not.
#### 3.1.3 Background tasks for prefetching
**Conditional**: **Required API**: `BGTaskScheduler` with `BGAppRefreshTaskRequest`
**Limitations**:
* Minimum interval between tasks (system-controlled, typically hours)
* System decides when to execute (not guaranteed)
* Cannot rely on background execution for alarm recovery
* Must schedule next task immediately after current one completes
### 3.2 iOS Forbidden Behaviors
#### 3.2.1 App code does not run when notification fires
**Forbidden**: When a scheduled notification fires:
* Notification is displayed to user
* **No app code executes** unless user taps the notification
* Cannot run arbitrary code at notification time
**Workaround**: Use notification actions or handle missed notifications on next app launch.
#### 3.2.2 No repeating background execution
**Forbidden**: iOS does not provide repeating background execution APIs except:
* `BGTaskScheduler` (system-controlled, not guaranteed)
* Background fetch (deprecated, unreliable)
**OS Behavior**: Apps cannot rely on background execution to reconstruct alarms. Apps must persist state and recover on app launch.
#### 3.2.3 No arbitrary code on notification trigger
**Forbidden**: Unlike Android's `PendingIntent` which can execute code, iOS notifications only:
* Display to user
* Launch app if user taps
* Execute notification action handlers (if configured)
**OS Behavior**: All recovery logic must run on app launch, not at notification time.
#### 3.2.4 Background execution limits
**Forbidden**: **BGTaskScheduler Limitations**:
* Minimum intervals between tasks (system-controlled)
* System may defer or skip tasks
* Tasks have time budgets (typically 30 seconds)
* Cannot guarantee execution timing
**Directive**: Use BGTaskScheduler for prefetching only, not for critical scheduling.
---
## 4. Cross-Platform Comparison
| Feature | Android | iOS | Label |
| -------------------------------- | --------------------------------------- | --------------------------------------------- | ------------- |
| **Survives swipe/termination** | ✅ Yes (with exact alarms) | ✅ Yes (automatic) | OS-guaranteed |
| **Survives reboot** | ❌ No (must reschedule) | ✅ Yes (automatic for calendar/time triggers) | Mixed |
| **App code runs on trigger** | ✅ Yes (via PendingIntent) | ❌ No (only if user interacts) | Mixed |
| **Background execution** | ✅ WorkManager, JobScheduler | ⚠️ Limited (BGTaskScheduler only) | Mixed |
| **Force stop equivalent** | ✅ Force Stop (hard kill) | ❌ No user-facing equivalent | Android-only |
| **Boot recovery required** | ✅ Yes (must implement) | ❌ No (OS handles) | Android-only |
| **Missed alarm detection** | ✅ Must implement on app launch | ✅ Must implement on app launch | Plugin-required |
| **Exact timing** | ✅ Yes (with permission) | ⚠️ ±180s tolerance | Mixed |
| **Repeating notifications** | ✅ Must reschedule each occurrence | ✅ Can use `repeats: true` in trigger | Mixed |
---
## 5. Android API Level Matrix
### 5.1 Alarm Scheduling APIs by API Level
| API Level | Available APIs | Label | Notes |
| --------- | -------------- | ----- | ----- |
| **API 19-20** (KitKat) | `setExact()` | OS-Permitted | May be deferred in Doze |
| **API 21-22** (Lollipop) | `setExact()`, `setAlarmClock()` | OS-Guaranteed | `setAlarmClock()` preferred |
| **API 23+** (Marshmallow+) | `setExact()`, `setAlarmClock()`, `setExactAndAllowWhileIdle()` | OS-Guaranteed | `setExactAndAllowWhileIdle()` required for Doze |
| **API 31+** (Android 12+) | All above + `SCHEDULE_EXACT_ALARM` permission required | Conditional | Permission must be granted by user |
### 5.2 Android S+ Exact Alarm Permission Decision Tree
**Android 12+ (API 31+) requires `SCHEDULE_EXACT_ALARM` permission**:
```
Is API level >= 31?
├─ NO → No permission required
└─ YES → Check permission status
├─ Granted → Can schedule exact alarms
├─ Not granted → Must request permission
│ ├─ User grants → Can schedule exact alarms
│ └─ User denies → Cannot schedule exact alarms (use inexact or show error)
└─ Revoked → Cannot schedule exact alarms (user must re-enable in Settings)
```
**Label**: Conditional (requires user permission on Android 12+)
### 5.3 Required Platform APIs
**Alarm Scheduling**:
* `AlarmManager.setExactAndAllowWhileIdle()` - Android 6.0+ (API 23+) - **OS-Guaranteed**
* `AlarmManager.setAlarmClock()` - Android 5.0+ (API 21+) - **OS-Guaranteed**
* `AlarmManager.setExact()` - Android 4.4+ (API 19+) - **OS-Permitted** (may be deferred in Doze)
**Permissions**:
* `RECEIVE_BOOT_COMPLETED` - Boot receiver - **OS-Permitted** (requires user to launch app once)
* `SCHEDULE_EXACT_ALARM` - Android 12+ (API 31+) - **Conditional** (user must grant)
**Background Work**:
* `WorkManager` - Deferrable background work - **OS-Permitted** (timing not guaranteed)
* `JobScheduler` - Alternative (API 21+) - **OS-Permitted** (timing not guaranteed)
### 5.2 iOS
**Notification Scheduling**:
* `UNUserNotificationCenter.add()` - Schedule notifications
* `UNCalendarNotificationTrigger` - Calendar-based triggers
* `UNTimeIntervalNotificationTrigger` - Time interval triggers
**Background Tasks**:
* `BGTaskScheduler.submit()` - Schedule background tasks
* `BGAppRefreshTaskRequest` - Background fetch requests
**Permissions**:
* Notification authorization (requested at runtime)
---
## 6. iOS Timing Tolerance Table
### 6.1 Notification Timing Accuracy
| Trigger Type | Timing Tolerance | Label | Notes |
| ------------ | ---------------- | ----- | ----- |
| **Calendar-based** (`UNCalendarNotificationTrigger`) | ±180 seconds | OS-Permitted | System may defer for battery optimization |
| **Time interval** (`UNTimeIntervalNotificationTrigger`) | ±180 seconds | OS-Permitted | System may defer for battery optimization |
| **Location-based** (`UNLocationNotificationTrigger`) | Not applicable | OS-Permitted | Does not persist across reboot |
**Source**: [Apple Developer Documentation - UNNotificationTrigger](https://developer.apple.com/documentation/usernotifications/unnotificationtrigger)
### 6.2 Background Task Timing
| Task Type | Execution Window | Label | Notes |
| --------- | ---------------- | ----- | ----- |
| **BGAppRefreshTask** | System-controlled (hours between tasks) | OS-Permitted | Not guaranteed, system decides |
| **BGProcessingTask** | System-controlled | OS-Permitted | Not guaranteed, system decides |
**Source**: [Apple Developer Documentation - BGTaskScheduler](https://developer.apple.com/documentation/backgroundtasks/bgtaskscheduler)
---
## 7. Platform-Specific Constraints Summary
### 6.1 Android Constraints
1. **Reboot**: All alarms wiped; must reschedule from persistent storage
2. **Force Stop**: Hard kill; cannot bypass until user opens app
3. **Doze**: Inexact alarms deferred; must use exact alarms
4. **Exact Alarm Permission**: Required on Android 12+ for precise timing
5. **Boot Receiver**: Must be registered and handle `BOOT_COMPLETED`
### 6.2 iOS Constraints
1. **Background Execution**: Severely limited; cannot rely on it for recovery
2. **Notification Firing**: App code does not run; only user interaction triggers app
3. **Timing Tolerance**: ±180 seconds for calendar triggers
4. **BGTaskScheduler**: System-controlled; not guaranteed execution
5. **State Persistence**: Must persist own state if tracking missed notifications
---
## 8. Revision Sources
### 8.1 AOSP Version
**Android Open Source Project**: Based on AOSP 14 (Android 14) behavior
**Last Validated**: November 2025
**Source Files Referenced**:
* `frameworks/base/core/java/android/app/AlarmManager.java`
* `frameworks/base/core/java/android/app/PendingIntent.java`
### 8.2 Official Documentation
**Android**:
* [AlarmManager - Android Developers](https://developer.android.com/reference/android/app/AlarmManager)
* [Schedule exact alarms - Android Developers](https://developer.android.com/training/scheduling/alarms)
**iOS**:
* [UNUserNotificationCenter - Apple Developer](https://developer.apple.com/documentation/usernotifications/unusernotificationcenter)
* [BGTaskScheduler - Apple Developer](https://developer.apple.com/documentation/backgroundtasks/bgtaskscheduler)
### 8.3 Tested Device Set
**Android Devices Tested**:
* Pixel 7 (Android 14)
* Samsung Galaxy S23 (Android 13)
* OnePlus 11 (Android 13)
**iOS Devices Tested**:
* iPhone 15 (iOS 17)
* iPhone 14 (iOS 16)
**Note**: OEM-specific behavior variations documented in [§8 - OEM Variation Policy](#8-oem-variation-policy)
### 8.4 Last Validated on Physical Devices
**Last Validation Date**: November 2025
**Validation Scenarios**:
* Swipe from recents - ✅ Validated on all devices
* Device reboot - ✅ Validated on all devices
* Force stop (Android) - ✅ Validated on Android devices
* Background execution (iOS) - ✅ Validated on iOS devices
**Unvalidated Scenarios**:
* OEM-specific variations (Xiaomi, Huawei) - ⚠️ Not yet tested
---
## 9. Label Definitions
**Required Labels** (every platform behavior MUST be tagged):
| Label | Definition | Usage |
| ----- | ---------- | ----- |
| **OS-Guaranteed** | The operating system provides this behavior automatically. No plugin code required. | Use when OS handles behavior without app intervention |
| **OS-Permitted but not guaranteed** | The OS allows this behavior, but timing/execution is not guaranteed. Plugin may need fallbacks. | Use for background execution, system-controlled timing |
| **Forbidden** | This behavior is not possible on this platform. Plugin must not attempt it. | Use for hard OS limitations (e.g., Force Stop bypass) |
| **Undefined / OEM-variant** | Behavior varies by device manufacturer or OS version. Not universal. | Use when behavior differs across OEMs or OS versions |
**Legacy Labels** (maintained for backward compatibility):
- **Plugin-required**: The plugin must implement this behavior. The OS does not provide it automatically.
- **Conditional**: This behavior is possible but requires specific conditions (permissions, APIs, etc.).
---
## 10. OEM Variation Policy
**Android is not monolithic** — behavior may vary by OEM (Samsung, Xiaomi, Huawei, etc.).
**Policy**:
* **Do not document** until reproduced in testing
* **Mark as "Observed-variant (not universal)"** if behavior differs from AOSP
* **Test on multiple devices** before claiming universal behavior
* **Document OEM-specific workarounds** in Doc C (Requirements), not Doc A (Platform Facts)
**Example**:
***Wrong**: "All Android devices wipe alarms on reboot"
***Correct**: "AOSP Android wipes alarms on reboot. Observed on: Samsung, Pixel, OnePlus. Not tested on: Xiaomi, Huawei."
---
## 11. Citation Rule
**Platform facts must come from authoritative sources**:
**Allowed Sources**:
1. **AOSP source code** - Direct inspection of Android Open Source Project
2. **Official Android/iOS documentation** - developer.android.com, developer.apple.com
3. **Reproducible test results** (Doc B) - Empirical evidence from testing
**Prohibited Sources**:
* Stack Overflow answers (unless verified)
* Blog posts (unless citing official docs)
* Assumptions or "common knowledge"
* Unverified OEM-specific claims
**Citation Format**:
* For AOSP: `[AOSP: AlarmManager.java:123]`
* For official docs: `[Android Docs: AlarmManager]`
* For test results: `[Doc B: Test 4 - Device Reboot]`
**If source is unclear**: Mark as "Unverified" or "Needs citation" until verified.
---
## Related Documentation
- [Unified Alarm Directive](./000-UNIFIED-ALARM-DIRECTIVE.md) - Master coordination document
- [Plugin Behavior Exploration](./02-plugin-behavior-exploration.md) - Uses this reference
- [Plugin Requirements](./03-plugin-requirements.md) - Implementation based on this reference
---
## Version History
- **v1.1.0** (November 2025): Enhanced with API levels, timing tables, revision sources
- Added Android API level matrix
- Added Android S+ exact alarm permission decision tree
- Added iOS timing tolerance table
- Added revision sources section
- Added tested device set
- Enhanced labeling consistency
- **v1.0.0** (November 2025): Initial platform capability reference
- Merged from `platform-capability-reference.md` and `android-alarm-persistence-directive.md`
- Android alarm matrix with labels
- iOS notification matrix with labels
- Cross-platform comparison
- Label definitions