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

22 KiB
Raw Permalink Blame History

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 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

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


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:

iOS:

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.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.



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