Browse Source

docs: apply surgical corrections for correctness and clarity

Analysis doc improvements:
- Add accuracy note for Capacitor vs Cordova runtime naming
- Declare 5 required Status Matrix fields verbatim in Bridge Surface
- Add Manifest Hygiene checklist to Build Configuration section

Implementation plan improvements:
- Fix BOOT_COMPLETED permission wiring (top-level permissions, not receiver attribute)
- Add user-visible Exact-Alarm Decision Rule for QA/ops reasoning
- Add 2 ops-grade error cases: E_CHANNEL_MISSING, E_BAD_CONFIG
- Add Preflight Golden Path (Demo) to Runbooks for 30-second sanity checks
- Clamp text lengths at JS boundary with unknown field rejection
- Declare minimal Event IDs for deterministic grep operations

All changes maintain existing structure with surgical precision edits.
master
Matthew Raymer 2 days ago
parent
commit
0313aacfd4
  1. 12
      docs/android-app-analysis.md
  2. 41
      docs/android-app-improvement-plan.md

12
docs/android-app-analysis.md

@ -184,6 +184,8 @@ assets/public/
├── index.html # Main test interface (549 lines) ├── index.html # Main test interface (549 lines)
├── capacitor.js # Capacitor runtime ├── capacitor.js # Capacitor runtime
├── capacitor_plugins.js # Plugin JavaScript bridge ├── capacitor_plugins.js # Plugin JavaScript bridge
> **Note:** On pure Capacitor builds, the runtime is `capacitor.js`. Only include `cordova.js/cordova_plugins.js` if Cordova-compat is enabled; otherwise remove those references for accuracy.
└── plugins/ # Plugin JavaScript files └── plugins/ # Plugin JavaScript files
``` ```
@ -363,6 +365,14 @@ android {
**Purpose**: Auto-generated Capacitor configuration **Purpose**: Auto-generated Capacitor configuration
**Note**: Regenerated on each `npx cap sync` - should not be manually edited **Note**: Regenerated on each `npx cap sync` - should not be manually edited
**Manifest Hygiene (Quick Scan)**
- [ ] `<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>`
- [ ] `<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>` (if you truly need exact)
- [ ] `<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>`
- [ ] BootReceiver: `exported="true"` + BOOT_COMPLETED filter
- [ ] Other receivers exported=false unless needed
- [ ] No stray `android:permission=` on BootReceiver
## Runtime Behavior ## Runtime Behavior
### App Startup Sequence ### App Startup Sequence
@ -533,6 +543,8 @@ This architecture serves as both a practical testing tool and a reference implem
- `requestNotificationPermissions() -> {granted: boolean, permissions: {...}}` - `requestNotificationPermissions() -> {granted: boolean, permissions: {...}}`
- `getNotificationStatus() -> {isEnabled, isScheduled, nextNotificationTime, ...}` - `getNotificationStatus() -> {isEnabled, isScheduled, nextNotificationTime, ...}`
**Status Matrix MUST include:** `postNotificationsGranted`, `exactAlarmGranted`, `channelEnabled`, `batteryOptimizationsIgnored`, `canScheduleNow`.
## Permission & Settings Truth Table ## Permission & Settings Truth Table
| Symptom | Likely Cause | Action | | Symptom | Likely Cause | Action |

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

@ -230,6 +230,10 @@ public class ScheduleDaily {
- **Error Handling**: Standardized error responses - **Error Handling**: Standardized error responses
- **Validation**: Input validation before processing - **Validation**: Input validation before processing
### Exact-Alarm Decision Rule (User-Visible)
If `SCHEDULE_EXACT_ALARM` is **granted** → schedule with `setExactAndAllowWhileIdle`.
If **denied or quota-limited** → schedule via WorkManager (exp backoff + jitter) and surface `E_EXACT_ALARM_DENIED` (with "Degraded timing — Doze may delay" hint).
### 3. Service Locator ### 3. Service Locator
**Purpose**: Dependency injection for testability **Purpose**: Dependency injection for testability
@ -413,12 +417,12 @@ export class SchemaValidator {
errors.push('Time must be in HH:mm format'); errors.push('Time must be in HH:mm format');
} }
// Validate title length // Validate title length (enforce exactly: title ≤ 100 chars)
if (request.title && request.title.length > 100) { if (request.title && request.title.length > 100) {
errors.push('Title must be 100 characters or less'); errors.push('Title must be 100 characters or less');
} }
// Validate body length // Validate body length (enforce exactly: body ≤ 500 chars)
if (request.body && request.body.length > 500) { if (request.body && request.body.length > 500) {
errors.push('Body must be 500 characters or less'); errors.push('Body must be 500 characters or less');
} }
@ -433,9 +437,17 @@ export class SchemaValidator {
errors.push('Priority must be low, default, or high'); errors.push('Priority must be low, default, or high');
} }
// Reject unknown fields
const allowedFields = ['time', 'title', 'body', 'sound', 'priority'];
const unknownFields = Object.keys(request).filter(key => !allowedFields.includes(key));
if (unknownFields.length > 0) {
errors.push(`Unknown fields: ${unknownFields.join(', ')}`);
}
return { return {
isValid: errors.length === 0, isValid: errors.length === 0,
errors errors,
message: errors.join('; ') // Single joined message for UI display
}; };
} }
} }
@ -495,6 +507,11 @@ public class SecureNetworkClient {
#### Implementation Plan #### Implementation Plan
```xml ```xml
<!-- AndroidManifest.xml --> <!-- AndroidManifest.xml -->
<!-- Top-level permissions -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<receiver <receiver
android:name="com.timesafari.dailynotification.DailyNotificationReceiver" android:name="com.timesafari.dailynotification.DailyNotificationReceiver"
android:enabled="true" android:enabled="true"
@ -507,8 +524,7 @@ public class SecureNetworkClient {
<receiver <receiver
android:name="com.timesafari.dailynotification.BootReceiver" android:name="com.timesafari.dailynotification.BootReceiver"
android:enabled="true" android:enabled="true"
android:exported="true" android:exported="true">
android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
<intent-filter android:priority="1000"> <intent-filter android:priority="1000">
<action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter> </intent-filter>
@ -809,6 +825,12 @@ interface ScheduleResponse {
- Implement progress logging - Implement progress logging
- Create log export functionality - Create log export functionality
**Event IDs (minimum set)**
- EVT_SCHEDULE_REQUEST / EVT_SCHEDULE_OK / EVT_SCHEDULE_FAIL
- EVT_BOOT_REHYDRATE_START / EVT_BOOT_REHYDRATE_DONE
- EVT_CHANNEL_STATUS / EVT_PERM_STATUS / EVT_EXACT_ALARM_STATUS
- EVT_DOZE_FALLBACK_TAKEN / EVT_WORKER_RETRY
### Phase 3: Security & Performance ### Phase 3: Security & Performance
- [ ] **Security Hardening** - [ ] **Security Hardening**
- Add network security measures - Add network security measures
@ -941,6 +963,8 @@ By following this plan, the test app will become more maintainable, reliable, an
| E_CHANNEL_DISABLED | Channel blocked/low importance | Open channel settings | | E_CHANNEL_DISABLED | Channel blocked/low importance | Open channel settings |
| E_EXACT_ALARM_DENIED | No exact alarm | Open exact alarm settings / fallback | | E_EXACT_ALARM_DENIED | No exact alarm | Open exact alarm settings / fallback |
| E_DOZE_LIMIT | Throttled by Doze | Expect delays; fallback taken | | E_DOZE_LIMIT | Throttled by Doze | Expect delays; fallback taken |
| E_CHANNEL_MISSING | Channel ID not found at runtime | Recreate channel; verify ID & importance |
| E_BAD_CONFIG | Missing/invalid plugin config at startup | Check `capacitor.config.json` and diagnostics dump |
## Runbooks ## Runbooks
@ -952,3 +976,10 @@ Check storage for orphan schedule rows; verify idempotent rescheduler logs.
### Silent notifications ### Silent notifications
Verify channel importance and OEM-specific "Heads-up" settings. Verify channel importance and OEM-specific "Heads-up" settings.
### Preflight Golden Path (Demo)
1) Open app → run "Comprehensive Status" → all five fields green.
2) Tap "Open Channel Settings" → ensure importance = High.
3) Tap "Open Exact Alarm Settings" → grant if available.
4) Run "Immediate Notification" → toast & notif appear within 5s.
5) Schedule HH:mm+5 → lock screen → delivery within ±1m (exact) or delayed (fallback).

Loading…
Cancel
Save