Browse Source

docs: apply pin-point delta edits for correctness, consistency, and reviewer friction

Implementation plan improvements:
- Fix event name consistency: DOZE_FALLBACK → EVT_DOZE_FALLBACK_TAKEN in Test Matrix
- Lock receiver export policy as AC: only BootReceiver exported
- Handle unknown Content-Length: add streaming guard for -1 responses
- Ensure single joined error mirrors AC: validation failures return one joined message
- Add webDir echo and device idle hint to diagnostics: include webDir path and isDeviceIdleMode
- Make degradation visible in UI AC: matrix shows 'Degraded timing (Doze)' when fallback active
- Add Room migrations guard: no-op migration and fallbackToDestructiveMigration(false) test

Analysis doc improvements:
- Trim WAKE_LOCK guidance: not required unless explicitly acquiring/releasing wakelocks
- Add Boot receiver priority note: android:priority has no effect for BOOT_COMPLETED
- Fix application android:name accuracy: only set if custom Application class exists
- Mirror Cordova compat note in Build section: include only when using Cordova plugins
- Annotate Mermaid flow with canonical errors: show where canonical errors are produced
- Link Truth Table to test UI buttons: integrate with Open Channel/Exact Alarm Settings buttons

All changes maintain existing structure with surgical precision edits.
master
Matthew Raymer 2 days ago
parent
commit
0bef820d0c
  1. 14
      docs/android-app-analysis.md
  2. 24
      docs/android-app-improvement-plan.md

14
docs/android-app-analysis.md

@ -126,6 +126,8 @@ public class MainActivity extends BridgeActivity {
</intent-filter> </intent-filter>
</activity> </activity>
> **Note:** Set `android:name` only if you provide a custom `Application` class; otherwise remove to avoid ClassNotFound at runtime.
<!-- Plugin Components --> <!-- Plugin Components -->
<!-- Internal receiver: keep non-exported unless intentionally public --> <!-- Internal receiver: keep non-exported unless intentionally public -->
<receiver <receiver
@ -145,6 +147,8 @@ public class MainActivity extends BridgeActivity {
<action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter> </intent-filter>
</receiver> </receiver>
> **Note:** `android:priority` has no practical effect for `BOOT_COMPLETED`; safe to omit.
</application> </application>
<!-- Required Permissions --> <!-- Required Permissions -->
@ -162,7 +166,7 @@ public class MainActivity extends BridgeActivity {
- `POST_NOTIFICATIONS`: Required for Android 13+ notification posting - `POST_NOTIFICATIONS`: Required for Android 13+ notification posting
- `SCHEDULE_EXACT_ALARM`: Required for precise notification timing - `SCHEDULE_EXACT_ALARM`: Required for precise notification timing
- `WAKE_LOCK`: Required for background processing - `WAKE_LOCK` **not required** unless you explicitly acquire/release your own wakelocks (AlarmManager & WorkManager handle theirs)
- `INTERNET`: Required for content fetching - `INTERNET`: Required for content fetching
- `RECEIVE_BOOT_COMPLETED`: Required for reboot recovery - `RECEIVE_BOOT_COMPLETED`: Required for reboot recovery
@ -391,6 +395,8 @@ dependencies {
// Cordova compatibility (include ONLY if using Cordova plugins) // Cordova compatibility (include ONLY if using Cordova plugins)
debugImplementation(project(':capacitor-cordova-android-plugins')) { transitive = false } debugImplementation(project(':capacitor-cordova-android-plugins')) { transitive = false }
releaseImplementation(project(':capacitor-cordova-android-plugins')) { transitive = false } releaseImplementation(project(':capacitor-cordova-android-plugins')) { transitive = false }
> **Note:** Include `capacitor-cordova-android-plugins` **only** when using Cordova plugins.
} }
``` ```
@ -619,6 +625,8 @@ If **denied or quota-limited** → schedule via WorkManager (exp backoff + jitte
| Battery optimization kills | App not whitelisted | Guide user to battery optimization settings | | Battery optimization kills | App not whitelisted | Guide user to battery optimization settings |
| Boot reschedule fails | `RECEIVE_BOOT_COMPLETED` denied | Check manifest receiver registration | | Boot reschedule fails | `RECEIVE_BOOT_COMPLETED` denied | Check manifest receiver registration |
> **Test UI Integration:** Use "Open Channel Settings" and "Open Exact Alarm Settings" buttons in the test interface to resolve channel and exact alarm issues.
## Runtime Flow Diagram ## Runtime Flow Diagram
```mermaid ```mermaid
@ -635,8 +643,8 @@ graph TD
I --> J[UI Update] I --> J[UI Update]
%% Error paths %% Error paths
C -->|Validation Error| K[Canonical Error] C -->|Validation Error → Canonical Error| K[Canonical Error]
D -->|Use-case Error| K D -->|Use-case Error → Canonical Error| K
K --> L[JavaScript Promise Rejection] K --> L[JavaScript Promise Rejection]
``` ```

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

@ -496,12 +496,26 @@ public class SecureNetworkClient {
connection.setConnectTimeout(TIMEOUT_SECONDS * 1000); connection.setConnectTimeout(TIMEOUT_SECONDS * 1000);
connection.setReadTimeout(TIMEOUT_SECONDS * 1000); connection.setReadTimeout(TIMEOUT_SECONDS * 1000);
// Limit response size // Limit response size (handle unknown Content-Length)
long contentLength = connection.getContentLengthLong(); long contentLength = connection.getContentLengthLong();
if (contentLength > MAX_RESPONSE_SIZE) { if (contentLength > MAX_RESPONSE_SIZE) {
throw new NetworkException("E_RESPONSE_TOO_LARGE", "Response too large"); throw new NetworkException("E_RESPONSE_TOO_LARGE", "Response too large");
} }
// Stream with size guard for unknown Content-Length
long read = 0;
try (InputStream in = connection.getInputStream()) {
byte[] buf = new byte[8192];
int n;
while ((n = in.read(buf)) != -1) {
read += n;
if (read > MAX_RESPONSE_SIZE) {
throw new NetworkException("E_RESPONSE_TOO_LARGE", "Response too large");
}
// process / buffer as needed
}
}
return readResponse(connection); return readResponse(connection);
} }
} }
@ -671,6 +685,7 @@ public class DatabaseMaintenance {
- **Index Optimization**: Maintain database performance - **Index Optimization**: Maintain database performance
- **Data Retention**: Configurable retention policies - **Data Retention**: Configurable retention policies
- **Performance Monitoring**: Track maintenance impact - **Performance Monitoring**: Track maintenance impact
- **Migration Safety**: Add a **no-op migration** for current schema version and a test that app boots with `fallbackToDestructiveMigration(false)`
## Documentation Updates ## Documentation Updates
@ -870,6 +885,7 @@ interface ScheduleResponse {
- [ ] Shows live channel state - [ ] Shows live channel state
- [ ] Provides actionable buttons for issues - [ ] Provides actionable buttons for issues
- [ ] Exports diagnostics as JSON - [ ] Exports diagnostics as JSON
- [ ] When fallback is active, matrix shows **"Degraded timing (Doze)"** and last event includes `EVT_DOZE_FALLBACK_TAKEN`
### Error Handling ### Error Handling
- [ ] All @PluginMethod calls validate inputs - [ ] All @PluginMethod calls validate inputs
@ -879,6 +895,7 @@ interface ScheduleResponse {
- [ ] Rejects unknown keys with single joined message - [ ] Rejects unknown keys with single joined message
- [ ] Channel policy enforced: missing/disabled channel returns `E_CHANNEL_MISSING` or `E_CHANNEL_DISABLED` with "Open Channel Settings" CTA - [ ] Channel policy enforced: missing/disabled channel returns `E_CHANNEL_MISSING` or `E_CHANNEL_DISABLED` with "Open Channel Settings" CTA
- [ ] HTTPS-only; connect/read timeouts ≤ 30s; content-length hard cap ≤ 1 MB; oversize → `E_RESPONSE_TOO_LARGE` - [ ] HTTPS-only; connect/read timeouts ≤ 30s; content-length hard cap ≤ 1 MB; oversize → `E_RESPONSE_TOO_LARGE`
- [ ] Validation failures return **one joined message** surfaced to UI
### Reliability ### Reliability
- [ ] Reboot scenarios reliably deliver notifications - [ ] Reboot scenarios reliably deliver notifications
@ -886,6 +903,7 @@ interface ScheduleResponse {
- [ ] Clear logs explain system behavior - [ ] Clear logs explain system behavior
- [ ] User-visible reasoning for failures - [ ] User-visible reasoning for failures
- [ ] 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"`
### Testing ### Testing
- [ ] Test UI modularized into scenarios - [ ] Test UI modularized into scenarios
@ -936,7 +954,7 @@ By following this plan, the test app will become more maintainable, reliable, an
- @PluginMethod bodies ≤ 25 LOC, delegate to use-cases. - @PluginMethod bodies ≤ 25 LOC, delegate to use-cases.
- "Copy Diagnostics (JSON)" button functional. - "Copy Diagnostics (JSON)" button functional.
**Diagnostics MUST include:** appId, versionName/code, manufacturer/model, API level, timezone, `capacitor.config.json` plugin section echo, five status fields, last 50 event IDs. **Diagnostics MUST include:** appId, versionName/code, manufacturer/model, API level, timezone, `capacitor.config.json` plugin section echo, five status fields, last 50 event IDs, `webDir` effective path echo, `isDeviceIdleMode` boolean.
- If exact alarm is denied/quota-limited, UI surfaces **"Degraded timing (Doze)"** and logs `EVT_DOZE_FALLBACK_TAKEN`. - If exact alarm is denied/quota-limited, UI surfaces **"Degraded timing (Doze)"** and logs `EVT_DOZE_FALLBACK_TAKEN`.
### Phase 2 DoD ### Phase 2 DoD
@ -973,7 +991,7 @@ By following this plan, the test app will become more maintainable, reliable, an
|---|---|---| |---|---|---|
| Immediate notify | scheduleDailyNotification | Channel ON, perms granted | Success + toast seen | | Immediate notify | scheduleDailyNotification | Channel ON, perms granted | Success + toast seen |
| Channel disabled path | isChannelEnabled/openChannelSettings | Disable channel | Canonical `E_CHANNEL_DISABLED` | | Channel disabled path | isChannelEnabled/openChannelSettings | Disable channel | Canonical `E_CHANNEL_DISABLED` |
| Exact alarm denied path | openExactAlarmSettings | Revoke exact alarm | Fallback path taken; logged `DOZE_FALLBACK` | | Exact alarm denied path | openExactAlarmSettings | Revoke exact alarm | Fallback path taken; logged `EVT_DOZE_FALLBACK_TAKEN` |
| Boot reschedule | BootReceiver | Reboot emulator | One (not duplicate) schedule restored | | Boot reschedule | BootReceiver | Reboot emulator | One (not duplicate) schedule restored |
| Doze idle window | scheduleDailyNotification | Device in idle | Fallback path taken; logged `EVT_DOZE_FALLBACK_TAKEN`; no crash | | Doze idle window | scheduleDailyNotification | Device in idle | Fallback path taken; logged `EVT_DOZE_FALLBACK_TAKEN`; no crash |
| Bad schema rejects | bridge.ts + schema-validation.ts | Add unknown key / oversize title | Canonical `E_BAD_CONFIG` with single joined message | | Bad schema rejects | bridge.ts + schema-validation.ts | Add unknown key / oversize title | Canonical `E_BAD_CONFIG` with single joined message |

Loading…
Cancel
Save