# Platform Capability Reference: Android & iOS Alarm/Notification Behavior **Author**: Matthew Raymer **Date**: November 2025 **Status**: Platform Reference - Stable ## 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. --- ## 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 * Plugin must persist its own state if it needs to track or recover missed notifications --- ## 2. Android Alarm Capability Matrix | Scenario | Will Alarm Fire? | OS Behavior | App Responsibility | | --------------------------------------- | --------------------------------------- | -------------------------------------------------------------------- | ----------------------------------------------------- | | **Swipe from Recents** | ✅ Yes | AlarmManager resurrects the app process | None (OS handles) | | **App silently killed by OS** | ✅ Yes | AlarmManager still holds scheduled alarms | None (OS handles) | | **Device Reboot** | ❌ No (auto) / ✅ Yes (if you reschedule) | All alarms wiped on reboot | Must reschedule from persistent storage on boot | | **Doze Mode** | ⚠️ Only "exact" alarms | Inexact alarms deferred; exact alarms allowed | Must use `setExactAndAllowWhileIdle` | | **Force Stop** | ❌ Never | Android blocks all callbacks + receivers until next user launch | Cannot bypass; must detect on app restart | | **User reopens app** | ✅ You may reschedule & recover | App process restarted | Must detect missed alarms and reschedule future ones | | **PendingIntent from user interaction** | ✅ If triggered by user | User action unlocks the app | None (OS handles) | ### Android Allowed Behaviors #### 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`. **Required API**: `setExactAndAllowWhileIdle()` or `setAlarmClock()` #### 2.2 Alarms can be preserved across device reboot Android wipes all alarms on reboot, but **you may recreate them**. **Required Components**: 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 #### 2.3 Alarms can fire full-screen notifications and wake the device **Required API**: `setFullScreenIntent(...)`, use an IMPORTANCE_HIGH channel with `CATEGORY_ALARM` This allows Clock-app–style 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 **Required**: Create a `ReactivationManager` that runs on every app launch ### Android Forbidden Behaviors #### 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. **Required Permission**: `SCHEDULE_EXACT_ALARM` on Android 12+ (API 31+) --- ## 3. iOS Notification Capability Matrix | Scenario | Will Notification Fire? | OS Behavior | App Responsibility | | --------------------------------------- | ----------------------- | -------------------------------------------------------------------- | ----------------------------------------------------- | | **Swipe from App Switcher** | ✅ Yes | UNUserNotificationCenter persists and fires notifications | None (OS handles) | | **App Terminated by System** | ✅ Yes | UNUserNotificationCenter persists and fires notifications | None (OS handles) | | **Device Reboot** | ✅ Yes (for calendar/time triggers) | iOS persists scheduled local notifications across reboot | None for notifications; must persist own state if needed | | **App Force Quit (swipe away)** | ✅ Yes | UNUserNotificationCenter persists and fires notifications | None (OS handles) | | **Background Execution** | ❌ No arbitrary code | Only BGTaskScheduler with strict limits | Cannot rely on background execution for recovery | | **Notification Fires** | ✅ Yes | Notification displayed; app code does NOT run unless user interacts | Must handle missed notifications on next app launch | | **User Taps Notification** | ✅ Yes | App launched; code can run | Can detect and handle missed notifications | ### iOS Allowed Behaviors #### 3.1 Notifications survive app termination `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.2 Notifications persist across device reboot 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.3 Background tasks for prefetching **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 ### iOS Forbidden Behaviors #### 4.1 App code does not run when notification fires 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. #### 4.2 No repeating background execution iOS does not provide repeating background execution APIs except: * `BGTaskScheduler` (system-controlled, not guaranteed) * Background fetch (deprecated, unreliable) **Directive**: Plugin cannot rely on background execution to reconstruct alarms. Must persist state and recover on app launch. #### 4.3 No arbitrary code on notification trigger 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) **Directive**: All recovery logic must run on app launch, not at notification time. #### 4.4 Background execution limits **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 | | -------------------------------- | --------------------------------------- | --------------------------------------------- | | **Survives swipe/termination** | ✅ Yes (with exact alarms) | ✅ Yes (automatic) | | **Survives reboot** | ❌ No (must reschedule) | ✅ Yes (automatic for calendar/time triggers) | | **App code runs on trigger** | ✅ Yes (via PendingIntent) | ❌ No (only if user interacts) | | **Background execution** | ✅ WorkManager, JobScheduler | ⚠️ Limited (BGTaskScheduler only) | | **Force stop equivalent** | ✅ Force Stop (hard kill) | ❌ No user-facing equivalent | | **Boot recovery required** | ✅ Yes (must implement) | ❌ No (OS handles) | | **Missed alarm detection** | ✅ Must implement on app launch | ✅ Must implement on app launch | | **Exact timing** | ✅ Yes (with permission) | ⚠️ ±180s tolerance | | **Repeating notifications** | ✅ Must reschedule each occurrence | ✅ Can use `repeats: true` in trigger | --- ## 5. Required Platform APIs ### Android **Alarm Scheduling**: * `AlarmManager.setExactAndAllowWhileIdle()` - Android 6.0+ (API 23+) * `AlarmManager.setAlarmClock()` - Android 5.0+ (API 21+) * `AlarmManager.setExact()` - Android 4.4+ (API 19+) **Permissions**: * `RECEIVE_BOOT_COMPLETED` - Boot receiver * `SCHEDULE_EXACT_ALARM` - Android 12+ (API 31+) **Background Work**: * `WorkManager` - Deferrable background work * `JobScheduler` - Alternative (API 21+) ### 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. Platform-Specific Constraints Summary ### 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` ### 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 --- ## Related Documentation - [Plugin Behavior Exploration Template](./plugin-behavior-exploration-template.md) - Uses this reference - [Plugin Requirements & Implementation](./plugin-requirements-implementation.md) - Implementation based on this reference - [Android Alarm Persistence Directive](./android-alarm-persistence-directive.md) - Original Android reference - [Improve Alarm Directives](./improve-alarm-directives.md) - Improvement directive --- ## Version History - **v1.0** (November 2025): Initial platform capability reference - Android alarm matrix - iOS notification matrix - Cross-platform comparison