Adds documentation and test harness for Phase 3 (Boot-Time Recovery). Changes: - Update android-implementation-directive-phase3.md with concise boot recovery flow - Add PHASE3-EMULATOR-TESTING.md with detailed test procedures - Add PHASE3-VERIFICATION.md with test matrix and verification template - Add test-phase3.sh automated test harness Test harness features: - 4 test cases: future alarms, past alarms, no schedules, silent recovery - Automatic emulator reboot handling - Log parsing for boot recovery scenario and results - UI prompts for plugin configuration and scheduling - Verifies silent recovery without app launch Related: - Directive: android-implementation-directive-phase3.md - Requirements: docs/alarms/03-plugin-requirements.md §3.1.1 - Testing: docs/alarms/PHASE3-EMULATOR-TESTING.md - Verification: docs/alarms/PHASE3-VERIFICATION.md
222 lines
5.7 KiB
Markdown
222 lines
5.7 KiB
Markdown
# Android Implementation Directive – Phase 3
|
||
## Boot-Time Recovery (Device Reboot / System Restart)
|
||
|
||
**Plugin:** Daily Notification Plugin
|
||
**Author:** Matthew Raymer
|
||
**Applies to:** Android Plugin (Kotlin), Capacitor Bridge
|
||
**Related Docs:**
|
||
- `03-plugin-requirements.md`
|
||
- `000-UNIFIED-ALARM-DIRECTIVE.md`
|
||
- `android-implementation-directive-phase1.md`
|
||
- `android-implementation-directive-phase2.md`
|
||
- `ACTIVATION-GUIDE.md`
|
||
|
||
---
|
||
|
||
## 1. Purpose
|
||
|
||
Phase 3 introduces **Boot-Time Recovery**, which restores daily notifications after:
|
||
|
||
- Device reboot
|
||
- OS restart
|
||
- Update-related restart
|
||
- App not opened after reboot (silent recovery)
|
||
|
||
Android clears **all alarms** on reboot.
|
||
Therefore, if our plugin is not actively rescheduling on boot, the user will miss all daily notifications until they manually launch the app.
|
||
|
||
Phase 3 ensures:
|
||
|
||
1. Schedules stored in SQLite survive reboot
|
||
2. Alarms are fully reconstructed
|
||
3. No duplication / double-scheduling
|
||
4. Boot behavior avoids unnecessary heavy recovery
|
||
5. Recovery occurs even if the user does **not** manually open the app
|
||
|
||
---
|
||
|
||
## 2. Boot-Time Recovery Flow
|
||
|
||
### Trigger:
|
||
|
||
`BOOT_COMPLETED` broadcast received
|
||
→ Plugin's Boot Receiver invoked
|
||
→ Recovery logic executed with `scenario=BOOT`
|
||
|
||
### Recovery Steps
|
||
|
||
1. **Load all schedules** from SQLite (`NotificationRepository.getAllSchedules()`)
|
||
|
||
2. **For each schedule:**
|
||
- Calculate next runtime based on cron expression
|
||
- Compare with current time
|
||
|
||
3. **If the next scheduled time is in the future:**
|
||
- Recreate alarm with `setAlarmClock`
|
||
- Log:
|
||
`Rescheduled alarm: <id> for <ts>`
|
||
|
||
4. **If schedule was *in the past* at boot time:**
|
||
- Mark as missed
|
||
- Schedule next run according to cron rules
|
||
|
||
5. **If no schedules found:**
|
||
- Quiet exit, log only one line:
|
||
`BOOT: No schedules found`
|
||
|
||
6. **Safeties:**
|
||
- Boot recovery must **not** modify Plugin Settings
|
||
- Must not regenerate Fetcher configuration
|
||
- Must not overwrite database records
|
||
|
||
---
|
||
|
||
## 3. Required Android Components
|
||
|
||
### 3.1 Boot Receiver
|
||
|
||
```xml
|
||
<receiver
|
||
android:name=".BootReceiver"
|
||
android:enabled="true"
|
||
android:exported="true">
|
||
<intent-filter>
|
||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||
</intent-filter>
|
||
</receiver>
|
||
```
|
||
|
||
### 3.2 Kotlin Class
|
||
|
||
```kotlin
|
||
class BootReceiver : BroadcastReceiver() {
|
||
override fun onReceive(context: Context, intent: Intent?) {
|
||
if (intent?.action != Intent.ACTION_BOOT_COMPLETED) return
|
||
|
||
ReactivationManager.runBootRecovery(context)
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 4. ReactivationManager – Boot Logic
|
||
|
||
### Method Signature
|
||
|
||
```kotlin
|
||
fun runBootRecovery(context: Context)
|
||
```
|
||
|
||
### Required Logging (canonical)
|
||
|
||
```
|
||
DNP-REACTIVATION: Starting boot recovery
|
||
DNP-REACTIVATION: Loaded <N> schedules from DB
|
||
DNP-REACTIVATION: Rescheduled alarm: <id> for <ts>
|
||
DNP-REACTIVATION: Marked missed notification: <id>
|
||
DNP-REACTIVATION: Boot recovery complete: missed=X, rescheduled=Y, errors=Z
|
||
```
|
||
|
||
### Required Fields
|
||
|
||
* `scenario=BOOT`
|
||
* `missed`
|
||
* `rescheduled`
|
||
* `verified` **MUST BE 0** (boot has no verification phase)
|
||
|
||
---
|
||
|
||
## 5. Constraints & Guardrails
|
||
|
||
1. **No plugin initialization**
|
||
Boot must *not* require running the app UI.
|
||
|
||
2. **No heavy processing**
|
||
* limit to 2 seconds
|
||
* use the same timeout guard as Phase 2
|
||
|
||
3. **No scheduling duplicates**
|
||
* Must detect existing AlarmManager entries
|
||
* Boot always clears them, so all reschedules should be fresh
|
||
|
||
4. **App does not need to be opened**
|
||
* Entire recovery must run in background context
|
||
|
||
5. **Idempotency**
|
||
* Running twice should produce identical logs
|
||
|
||
---
|
||
|
||
## 6. Implementation Checklist
|
||
|
||
### Mandatory
|
||
|
||
* [ ] BootReceiver included
|
||
* [ ] Manifest entry added
|
||
* [ ] `runBootRecovery()` implemented
|
||
* [ ] Scenario logged as `BOOT`
|
||
* [ ] All alarms recreated
|
||
* [ ] Timeout protection
|
||
* [ ] No modifications to preferences or plugin settings
|
||
|
||
### Optional
|
||
|
||
* [ ] Additional telemetry for analytics
|
||
* [ ] Optional debug toast for dev builds only
|
||
|
||
---
|
||
|
||
## 7. Expected Output Examples
|
||
|
||
### Example 1 – Normal Boot (future alarms exist)
|
||
|
||
```
|
||
DNP-REACTIVATION: Starting boot recovery
|
||
DNP-REACTIVATION: Loaded 2 schedules from DB
|
||
DNP-REACTIVATION: Rescheduled alarm: daily_1764233911265 for 1764236120000
|
||
DNP-REACTIVATION: Rescheduled alarm: daily_1764233465343 for 1764233700000
|
||
DNP-REACTIVATION: Boot recovery complete: missed=0, rescheduled=2, errors=0
|
||
```
|
||
|
||
### Example 2 – Schedules present but some in past
|
||
|
||
```
|
||
Marked missed notification: daily_1764233300000
|
||
Rescheduled alarm: daily_1764233300000 for next day
|
||
```
|
||
|
||
### Example 3 – No schedules
|
||
|
||
```
|
||
DNP-REACTIVATION: BOOT: No schedules found
|
||
```
|
||
|
||
---
|
||
|
||
## 8. Status
|
||
|
||
| Item | Status |
|
||
| -------------------- | -------------------------------- |
|
||
| Directive | **Complete** |
|
||
| Implementation | ☐ Pending / ✅ **Complete** (plugin v1.2+) |
|
||
| Emulator Test Script | Ready (`test-phase3.sh`) |
|
||
| Verification Doc | Ready (`PHASE3-VERIFICATION.md`) |
|
||
|
||
---
|
||
|
||
## 9. Related Documentation
|
||
|
||
- [Unified Alarm Directive](./alarms/000-UNIFIED-ALARM-DIRECTIVE.md) - Master coordination document
|
||
- [Plugin Requirements](./alarms/03-plugin-requirements.md) - Requirements this phase implements
|
||
- [Platform Capability Reference](./alarms/01-platform-capability-reference.md) - OS-level facts
|
||
- [Phase 1](./android-implementation-directive-phase1.md) - Prerequisite
|
||
- [Phase 2](./android-implementation-directive-phase2.md) - Prerequisite
|
||
- [Phase 3 Emulator Testing](./alarms/PHASE3-EMULATOR-TESTING.md) - Test procedures
|
||
- [Phase 3 Verification](./alarms/PHASE3-VERIFICATION.md) - Verification report
|
||
|
||
---
|
||
|
||
**Status**: Directive complete, ready for implementation
|
||
**Last Updated**: November 2025
|