Skip to content

Enable strict StrictMode in the Android demo app #488

@jkmassel

Description

@jkmassel

Context

The demo app at android/app/ (package com.example.gutenbergkit) is the project's reference integrator and the natural canary for library-side regressions. Today it doesn't configure StrictMode at all, so disk I/O on the main thread, leaked closables, registration leaks, etc. introduced inside the library go unnoticed during development and only surface in third-party host apps that do run StrictMode.

This is a demo-app-only change — the library itself stays untouched. The goal is "if the library starts doing something StrictMode-bad, the demo catches it on the next dev build."

Scope

  • In scope: android/app/ (the :app module). Configure StrictMode in the demo's Application class.
  • Out of scope: android/Gutenberg/ (the :Gutenberg library module). Do not add StrictMode.allowThreadDiskReads() suppressions to library code as part of this work — if you find violations there, those are bugs to fix or document, not silence here.
  • Debug builds only: gate the entire setup behind if (BuildConfig.DEBUG). Release builds must not configure StrictMode.

Phased approach

Don't go straight to penaltyDeath(). Two PRs, in this order:

Phase 1 — penaltyLog() everywhere

  1. Find or create the demo's Application subclass. Search for class GutenbergKitApplication in android/app/src/main/java/.

  2. In onCreate(), after super.onCreate(), add a debug-only StrictMode setup:

    if (BuildConfig.DEBUG) {
        StrictMode.setThreadPolicy(
            StrictMode.ThreadPolicy.Builder()
                .detectAll()
                .penaltyLog()
                .build()
        )
        StrictMode.setVmPolicy(
            StrictMode.VmPolicy.Builder()
                .detectAll()
                .penaltyLog()
                .build()
        )
    }
  3. Verify the manifest registers the Application subclass via android:name. If GutenbergKitApplication isn't yet registered in android/app/src/main/AndroidManifest.xml, register it.

  4. Build, run the demo on a connected device, and exercise the full demo flow:

    • Launch the app
    • Tap Bundled Editor → site preparation → enable Native Inserter → load editor
    • Open the block inserter sheet (the + button)
    • Tap each category chip
    • Open the photos rationale (if first run), tap Allow / Reject / Try Again paths
    • Pick a photo via the Photos tile
    • Tap Camera tile
    • Dismiss and reopen the sheet a few times
    • Type into the search field
    • Use the overflow menu's "Reset Photo Permissions Prompts"
    • Background the app; resume
  5. Capture all StrictMode violations from logcat (filter on tag StrictMode). Expected violations to see, based on prior review of feat(android): add photos and camera media strip to block inserter #479:

    • One DiskReadViolation on first inserter open, from getSharedPreferences("gbk_inserter") in RecentImages.kt's hasPromptedForPhotos. This is the only one the recent media-strip work introduces; the read is sub-millisecond and one-time per process. Document it; don't fix it in this PR.
    • Possibly older violations from the editor's WebView setup, asset loading, network monitoring registration, and so on. Catalogue what you find.
  6. Add a new entry to docs/code/ (e.g. docs/code/strictmode.md) that:

    • Documents the configuration.
    • Lists the known violations the demo currently surfaces (with file:line references).
    • Names which are intentional (permit) vs. which are tracked for fix in follow-up issues.
    • References the path to penaltyDeath (Phase 2).
  7. Acceptance: demo app builds, installs, runs without crashing. Logcat shows StrictMode violations. None are unexplained — every violation is either documented or referenced by a tracking issue.

Phase 2 (separate follow-up PR) — penaltyDeath()

After Phase 1's documented violations have been fixed or explicitly permitted, switch the penalty:

.penaltyDeath()

This will crash the demo on any new violation, ensuring future library changes can't quietly regress. Reviewers note: this depends on Phase 1 having addressed every documented violation, so don't merge Phase 2 until that work is in.

Implementation notes

  • detectAll() is the right starting point. It's a superset that includes detectDiskReads, detectDiskWrites, detectNetwork, detectCustomSlowCalls, detectResourceMismatches, detectUnbufferedIo, and (on VmPolicy) detectLeakedClosableObjects, detectLeakedRegistrationObjects, detectActivityLeaks, detectFileUriExposure, detectCleartextNetwork, detectContentUriWithoutPermission, detectUntaggedSockets, detectImplicitDirectBoot, detectCredentialProtectedWhileLocked, detectIncorrectContextUse, detectUnsafeIntentLaunch. Some of these (detectCleartextNetwork, detectFileUriExposure) may already be tripped by demo-only test paths — note those explicitly.
  • Application.onCreate() is the right place, not Activity.onCreate(). StrictMode should be set as early as possible in process startup so it covers everything that follows.
  • Don't enable penaltyDialog(). It interrupts manual testing without adding signal. Stick to log + (eventually) death.
  • Don't enable penaltyDeath() on VmPolicy either, in Phase 2. VM violations like LeakedClosableObject happen during GC and can crash the app at unpredictable times unrelated to the offending code path. penaltyLog() for VmPolicy is sufficient.
  • The demo currently has at least one prefs read on inserter open (cited above). That's a known violation introduced in feat(android): add photos and camera media strip to block inserter #479 and is sub-millisecond, one-time per process. Either document-and-permit it via StrictMode.allowThreadDiskReads().resetAfter inside the library helper, or accept the logcat line as cost-of-doing-business and document it. Don't apply a permit at the demo level — that hides the violation from future hosts who would also see it.

Files to touch (expected)

  • android/app/src/main/java/com/example/gutenbergkit/GutenbergKitApplication.kt — add the StrictMode setup.
  • android/app/src/main/AndroidManifest.xml — verify or add android:name=".GutenbergKitApplication" on the <application> element.
  • docs/code/strictmode.md (new) — short doc per the structure above.

Out-of-scope but worth flagging in the PR description

  • This work makes no library-side changes; if the catalogue surfaces real bugs in :Gutenberg, file follow-up issues, don't fix them here.
  • Production builds are unaffected — BuildConfig.DEBUG guard.
  • Phase 2 (penaltyDeath) is its own PR.

Done when

  • Phase 1 PR opens with the demo configured for penaltyLog() on both policies.
  • The doc at docs/code/strictmode.md enumerates every violation the configured demo currently surfaces, each labelled either fix-tracked-in-#NNN or intentional-permit-because-X.
  • The author has manually run through the demo flow above with logcat open and captured the catalogue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions