Skip to content

catch SecurityException on media URI access in the share flow - #143

Open
munzzyy wants to merge 1 commit into
guardianproject:mainfrom
munzzyy:fix-share-uri-securityexception
Open

catch SecurityException on media URI access in the share flow#143
munzzyy wants to merge 1 commit into
guardianproject:mainfrom
munzzyy:fix-share-uri-securityexception

Conversation

@munzzyy

@munzzyy munzzyy commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Fixes #137.

The crash report's java.lang.SecurityException: org.witness.proofmode has no access to content://media/external/video/media/1000011572 comes from ContentResolver.openInputStream(). That method documents SecurityException separately from FileNotFoundException, thrown when the caller lacks a valid grant for the URI. A content://media/... URI like this one is exactly what you get from Intent.EXTRA_STREAM on an incoming ACTION_SEND, and its read grant is temporary, tied to the sending app's intent rather than persisted. If that grant is gone by the time ProofMode tries to read it, openInputStream throws SecurityException.

ShareProofActivity only catches FileNotFoundException around these openInputStream calls, in checkProof()/proofExists() (runs automatically from onResume() the moment the share screen opens), generateProof(), and generateMultiProof(). Since SecurityException is a RuntimeException, not an IOException, none of those catch blocks touch it, so it escapes the coroutine and crashes the app. That matches a crash that fires right on opening the share screen, before the user does anything.

Fix: catch SecurityException alongside the existing FileNotFoundException in those three spots, with the same fallback that's already there for a missing file. Fail this one media item gracefully instead of taking the app down.

What I checked:

  • Traced the R8-mapped stack trace by hand against current main. The innermost real frame lines up with proofExists()'s openInputStream call inside checkProof()'s coroutine, matching the IO-dispatcher frames beneath it. checkProof() runs unconditionally in onResume() for both ACTION_SEND and ACTION_SEND_MULTIPLE.
  • Confirmed openInputStream's documented SecurityException behavior.
  • Compiled the exact catch-clause pattern standalone with kotlinc to confirm it's valid Kotlin. Order doesn't matter here since FileNotFoundException and SecurityException aren't related types.
  • Could not run the full :app Gradle build here. plugin-location-protocol depends on the generated :flutter module, which needs the Flutter SDK to scaffold flutter-location-protocol/.android, not installed in this environment and unrelated to this change. If you can run ./gradlew :app:compileDebugKotlin locally that would be good extra confirmation.

…hare flow

ContentResolver.openInputStream() throws SecurityException, not
FileNotFoundException, when the app no longer has access to a
content:// URI - e.g. a MediaStore URI shared in from another app
whose temporary read grant already expired. The share screen only
caught FileNotFoundException around these calls, so an expired grant
crashed the app instead of failing gracefully like every other
already-handled read error.

Catch SecurityException the same way FileNotFoundException is
already handled in checkProof/proofExists, generateProof, and
generateMultiProof - treat it as can't-read-this-media and let the
UI fall back to its existing no-proof/error state instead of dying.

Fixes guardianproject#137
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

share crash

1 participant