Browse Source

docs: apply surgical edits to harden edge-cases and clarify behavior

Implementation plan hardening:
- Document force-stop limitation: system cancels alarms until next explicit launch
- Add force-stop test case: no delivery until launch, then rescheduler restores schedules
- Make Doze degradation unmistakable: fixed string badge 'Degraded (Doze)' with EVT_DOZE_FALLBACK_TAKEN
- Freeze PendingIntent flags rule as Security AC: FLAG_IMMUTABLE unless mutation required

Analysis doc clarification:
- Add closed vs force-stopped distinction: closing/swiping doesn't affect alarms, force-stopping cancels them

These edits harden edge-cases around force-stop behavior and make Doze degradation UI requirements crystal clear for QA testing.
master
Matthew Raymer 2 days ago
parent
commit
92210398ae
  1. 2
      docs/android-app-analysis.md
  2. 4
      docs/android-app-improvement-plan.md

2
docs/android-app-analysis.md

@ -491,6 +491,8 @@ JavaScript Promise Resolution
- **BootReceiver**: Reschedules notifications after reboot - **BootReceiver**: Reschedules notifications after reboot
- **Doze Mode**: Handles Android's battery optimization - **Doze Mode**: Handles Android's battery optimization
> **Closed vs force-stopped:** Closing/swiping the app does not affect alarms or WorkManager. **Force-stopping** from Settings cancels alarms and suppresses receivers until the next launch, after which Boot/rehydration logic can restore future schedules.
## Testing Capabilities ## Testing Capabilities
### Interactive Testing Features ### Interactive Testing Features

4
docs/android-app-improvement-plan.md

@ -940,6 +940,7 @@ interface ScheduleResponse {
- [ ] When fallback is active, matrix shows **"Degraded timing (Doze)"** and last event includes `EVT_DOZE_FALLBACK_TAKEN` - [ ] When fallback is active, matrix shows **"Degraded timing (Doze)"** and last event includes `EVT_DOZE_FALLBACK_TAKEN`
- [ ] If app is not ignoring battery optimizations, we **do not** prompt `ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS`; we only deep-link documentation (policy choice) - [ ] If app is not ignoring battery optimizations, we **do not** prompt `ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS`; we only deep-link documentation (policy choice)
- [ ] Visual badge (e.g., "Degraded (Doze)") plus one-tap link to exact-alarm settings when fallback is active - [ ] Visual badge (e.g., "Degraded (Doze)") plus one-tap link to exact-alarm settings when fallback is active
- [ ] When fallback is active, show a **fixed string** badge: "Degraded (Doze)". Ensure last event includes `EVT_DOZE_FALLBACK_TAKEN`.
### Error Handling ### Error Handling
- [ ] All @PluginMethod calls validate inputs - [ ] All @PluginMethod calls validate inputs
@ -962,6 +963,7 @@ interface ScheduleResponse {
- [ ] Rescheduler uses unique key `(requestCode|channelId|time)` and **UPSERT** semantics; log `EVT_BOOT_REHYDRATE_DONE(count=n)` - [ ] Rescheduler uses unique key `(requestCode|channelId|time)` and **UPSERT** semantics; log `EVT_BOOT_REHYDRATE_DONE(count=n)`
- [ ] Only `BootReceiver` is exported; all other receivers remain `exported="false"` - [ ] Only `BootReceiver` is exported; all other receivers remain `exported="false"`
- [ ] Timezone and manual clock changes trigger rescheduler with idempotent rehydration - [ ] Timezone and manual clock changes trigger rescheduler with idempotent rehydration
- [ ] **Force-stop limitation:** If the user force-stops the app from Settings, the system cancels all alarms and suppresses receivers until the next explicit app launch. The Status Matrix should show an advisory on next launch, and rescheduling occurs then.
### Testing ### Testing
- [ ] Test UI modularized into scenarios - [ ] Test UI modularized into scenarios
@ -977,6 +979,7 @@ interface ScheduleResponse {
- [ ] No hardcoded secrets or API keys - [ ] No hardcoded secrets or API keys
- [ ] Secure network communication (HTTPS only) - [ ] Secure network communication (HTTPS only)
- [ ] Proper permission handling - [ ] Proper permission handling
- [ ] All notification and alarm `PendingIntent`s use **`FLAG_IMMUTABLE`** unless mutation is required; if mutation is required, use `FLAG_UPDATE_CURRENT | FLAG_MUTABLE` with a stable `requestCode`
### Documentation ### Documentation
- [ ] "How it Works" page with lifecycle diagrams - [ ] "How it Works" page with lifecycle diagrams
@ -1070,6 +1073,7 @@ By following this plan, the test app will become more maintainable, reliable, an
| Closed app delivery | scheduleDailyNotification + DailyNotificationReceiver | App closed, screen off | Exact alarm delivers via receiver; log shows `EVT_SCHEDULE_OK` → receiver → notification | | Closed app delivery | scheduleDailyNotification + DailyNotificationReceiver | App closed, screen off | Exact alarm delivers via receiver; log shows `EVT_SCHEDULE_OK` → receiver → notification |
| Closed app fallback | scheduleDailyNotification + WorkManager | App closed, device idle, exact alarm denied | WorkManager fires eventually; UI shows "Degraded timing (Doze)" | | Closed app fallback | scheduleDailyNotification + WorkManager | App closed, device idle, exact alarm denied | WorkManager fires eventually; UI shows "Degraded timing (Doze)" |
| Closed app reboot | BootReceiver | App closed, device reboot | Single notification posts at next window; UPSERT prevents duplicates | | Closed app reboot | BootReceiver | App closed, device reboot | Single notification posts at next window; UPSERT prevents duplicates |
| Force-stopped app | scheduleDailyNotification + app launch | Force-stop from Settings, then launch | No delivery until next explicit launch; on launch, rescheduler restores future schedules; matrix shows advisory |
## Error Codes (canonical) ## Error Codes (canonical)

Loading…
Cancel
Save