# 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-app–style 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

