Repository navigation
Conversation
Allowlisted accounts can record app log lines to bounded on-device files and share a snapshot manually. No logging tee is installed unless diagnostics are enabled and allowed. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved issues preserve diagnostic consent across account changes, leave displayed log state stale, prevent recovery after file-open failures, and can leave callers suspended indefinitely during shutdown.
4 open issues
What changed in this PR
Adds opt-in, on-device diagnostics to help investigate gaps in background update notifications. This complements the sync hardening in #186 by capturing logs for manual sharing, without automatic uploads.
Changes:
- Adds a hashed account allowlist and device-local, account-bound recording consent.
- Introduces bounded AppLog recording, startup snapshots, retention, sharing, and deletion.
- Wires diagnostics into Settings and dependency injection, with focused tests.
| File | Description |
|---|---|
| README.md | Documents configuration, consent, retention, and sharing. |
| app/src/test/java/com/anod/appwatcher/watchlist/WatchListPagingSourceRoomTest.kt | Supplies the explicit allowlist dependency. |
| app/src/test/java/com/anod/appwatcher/watchlist/AccountSelectionViewModelTest.kt | Updates Preferences construction. |
| app/src/test/java/com/anod/appwatcher/sync/UpdateCheckRegistrationTest.kt | Updates Preferences construction. |
| app/src/test/java/com/anod/appwatcher/preferences/SettingsScreenDiagnosticsTest.kt | Tests diagnostic click events and recomposition. |
| app/src/test/java/com/anod/appwatcher/diagnostics/TestDiagnostics.kt | Adds a disabled-recorder test helper. |
| app/src/test/java/com/anod/appwatcher/diagnostics/DiagnosticRecorderTest.kt | Tests recording, sharing, queue handling, and failures. |
| app/src/test/java/com/anod/appwatcher/diagnostics/DiagnosticPreferencesTest.kt | Tests consent persistence and account eligibility. |
| app/src/test/java/com/anod/appwatcher/diagnostics/DiagnosticFileStoreTest.kt | Tests rotation, retention, snapshots, and deletion. |
| app/src/test/java/com/anod/appwatcher/backup/gdrive/GDriveFailureTest.kt | Supplies the explicit allowlist dependency. |
| app/src/test/java/com/anod/appwatcher/accounts/DeviceRegistrationTest.kt | Updates Preferences construction. |
| app/src/test/java/com/anod/appwatcher/accounts/AuthRecoveringDfeApiTest.kt | Updates Preferences construction. |
| app/src/test/java/com/anod/appwatcher/accounts/AuthAccountInitializerTest.kt | Updates Preferences construction. |
| app/src/main/res/xml/diagnostic_paths.xml | Restricts sharing to diagnostic snapshots. |
| app/src/main/res/values/strings.xml | Adds diagnostic controls and status text. |
| app/src/main/java/com/anod/appwatcher/sync/SyncScheduler.kt | Exposes work names for diagnostic queries. |
| app/src/main/java/com/anod/appwatcher/preferences/SettingsViewModel.kt | Handles recording, sharing, deletion, and state updates. |
| app/src/main/java/com/anod/appwatcher/preferences/SettingsScreen.kt | Dispatches diagnostic control events. |
| app/src/main/java/com/anod/appwatcher/preferences/Preferences.kt | Adds account eligibility and device-local consent. |
| app/src/main/java/com/anod/appwatcher/preferences/PreferenceItems.kt | Builds diagnostic controls and storage status. |
| app/src/main/java/com/anod/appwatcher/diagnostics/DiagnosticRecorder.kt | Coordinates queued logging, metadata, and exports. |
| app/src/main/java/com/anod/appwatcher/diagnostics/DiagnosticLogger.kt | Adds a logging tee that isolates sink failures. |
| app/src/main/java/com/anod/appwatcher/diagnostics/DiagnosticFileStore.kt | Implements log segments, retention, and snapshots. |
| app/src/main/java/com/anod/appwatcher/diagnostics/DiagnosticAccounts.kt | Provides normalized hashing and allowlist parsing. |
| app/src/main/java/com/anod/appwatcher/AppWatcherApplication.kt | Initializes diagnostics after the crash logger. |
| app/src/main/java/com/anod/appwatcher/AppModule.kt | Registers diagnostic dependencies. |
| app/src/main/AndroidManifest.xml | Declares the diagnostic FileProvider. |
| app/build.gradle.kts | Generates hashed account allowlist constants. |
Review effort: Balanced
Comment on lines
+40
to
+41
| active = File(directory, "${sequence.toString().padStart(20, '0')}-$now.log") | ||
| output = BufferedOutputStream(active!!.outputStream()) |
| next = commands.tryReceive().getOrNull() | ||
| } | ||
| try { | ||
| store.flush() |
Comment on lines
+263
to
+265
| commands.send(Command.Operation(action, result)) | ||
| // The writer may have stopped after the send; nothing will consume this command. | ||
| if (stopped.get()) drainPending() |
| @Volatile | ||
| private var accountSnapshot: AuthAccount? = null | ||
| set(value) { | ||
| diagnosticAccountHash = value?.name?.let(DiagnosticAccounts::hash) |
- Show the Settings Local diagnostics section only when the selected account is on the diagnostic allowlist; drop the long description. - Read WorkManager state via suspend Flow instead of a blocking get. - Pass DiagnosticStorageState to preferenceItems instead of the recorder. - Make DiagnosticFileStore limits explicit constructor arguments. - Remove unused TestDiagnostics and the README digest claim. - Add PreferenceItemsDiagnosticsTest and a Pixel device recorder test. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
🔵 Needs a closer look
Unresolved defects can re-enable recording without renewed consent after account switches, leave saved logs unshareable, and cause persistent write failures or indefinitely suspended operations.
4 open issues
Review effort: Balanced
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Why
Beta testers stop receiving update notifications until they open the app, and existing logs (Crashlytics breadcrumbs, refresh history) cannot show what happened in between. This adds opt-in, on-device diagnostics for selected accounts so a tester can share a log covering the silent period.
Split out of #186, which keeps only the sync hardening. Independent of #186; based on
master.What
APPWATCHER_DIAGNOSTIC_ACCOUNTSin the Gradle user home, stored in BuildConfig as SHA-256 hashes; a rollout gate, not a security boundary). Off by default; consent is stored per device and bound to the selected account, so switching accounts disables it.AppLogtee also writes log lines tonoBackupFilesDir/diagnostics(30-day / 10 MiB rotation). Logs may contain package names and error messages. Enabling records a startup snapshot: power/background restrictions, standby bucket, recent process exit reasons, and sync WorkManager state.Performance
AppLogcost unchanged.Wiring
DiagnosticAllowlistandWorkInfoSourceare explicit Koin dependencies, soPreferencesandDiagnosticRecorderusesingleOfwithout hidden defaults.Tests
:app:testDebugUnitTest251/251;:app:assembleDebugand main ktlint clean (pre-existingAppItemStateTestunused import untouched).