Skip to content

feat(android): add scoped WebView permission handler for getUserMedia - #5855

Open
mortenolsrud wants to merge 2 commits into
wailsapp:masterfrom
mortenolsrud:feat/android-webview-permissions
Open

feat(android): add scoped WebView permission handler for getUserMedia#5855
mortenolsrud wants to merge 2 commits into
wailsapp:masterfrom
mortenolsrud:feat/android-webview-permissions

Conversation

@mortenolsrud

@mortenolsrud mortenolsrud commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Description

The WebChromeClient (added in #5807) had no onPermissionRequest override, so in-page permission prompts — navigator.getUserMedia for camera/mic, WebRTC — were silently auto-denied by the WebView's default implementation. Any web app feature relying on in-page media capture simply didn't work on Android.

This adds a scoped onPermissionRequest handler that:

  • Origin-gates all grants: only the local Wails origin (wails.localhost) is granted permissions. Requests from any other origin (if the WebView were ever navigated away) are unconditionally denied. This is a security gate that prevents remote content from accessing the camera/mic.
  • Maps WebView resources to Android runtime permissions: RESOURCE_VIDEO_CAPTUREandroid.permission.CAMERA, RESOURCE_AUDIO_CAPTUREandroid.permission.RECORD_AUDIO.
  • Coordinates with Android runtime permissions: holds the PermissionRequest until onRequestPermissionsResult delivers the user's verdict, then grants or denies accordingly.
  • Implicitly grants safe resources: RESOURCE_PROTECTED_MEDIA_ID and RESOURCE_MIDI_SYSEX require no runtime permission — they pass through.

Also adds RECORD_AUDIO to the Android manifest (CAMERA was already declared).

Depends on #5807 (which adds the WebChromeClient). This PR extends that handler.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Verified end-to-end on an Android emulator (Pixel 7, API 35, x86_64, headless/swiftshader):

  1. A throwaway wails3 init project with a getUserMedia({video:true, audio:true}) button was built from this branch's templates.
  2. Tapping the button triggered onPermissionRequest → the Android camera permission dialog appeared ("Allow Wails App to take pictures and record video?").
  3. Granting camera → a second microphone permission dialog appeared ("Allow Wails App to record audio?") — confirming both RESOURCE_VIDEO_CAPTURE→CAMERA and RESOURCE_AUDIO_CAPTURE→RECORD_AUDIO mappings work.
  4. After granting both, getUserMedia({video:true}) returned SUCCESS: 1 track (video:camera2 0, facing front) from the emulator's virtual camera.
  5. Audio failed with NotReadableError: Could not start audio source — this is expected when the emulator runs with -no-audio (no hardware audio device); the permission itself was correctly granted.

Also:

  • Java compiled via gradle (./gradlew compileDebugJavaWithJavac) — BUILD SUCCESSFUL, no warnings.

  • Host-side command/template test suite (go test ./internal/commands/...) — passes.

  • Windows

  • macOS

  • Linux

Linux: Ubuntu 24.04.4 LTS (build host). Target: Android.

Test Configuration

  • Wails CLI: v3.0.0-alpha2.119
  • Go: go1.26.5
  • OS: Ubuntu 24.04.4 LTS, amd64
  • Android NDK: 26.3.11579264 (target: android/arm64)

Checklist:

  • (v2 only) I have updated website/src/pages/changelog.mdx with details of this PR
  • My code follows the general coding style of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Notes on unchecked boxes:

  • Documentation: internal to the generated Android host — no user-facing docs needed.
  • Tests: the permission flow involves a runtime permission dialog on an Android device; the v3 tree has no host-runnable Android test harness. The implementation mirrors the existing camera-permission pattern that's been in production since the feature layer was added.

Summary by CodeRabbit

  • New Features
    • Added microphone access support for Android.
    • Enabled camera and microphone permission handling within the app’s web content.
    • Added file selection support, including multi-file uploads.
    • Improved WebView console logging for troubleshooting.
    • Added clearer handling for canceled, unavailable, or denied permission and file-selection requests.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Android WebView support now includes console logging, local camera and microphone permission handling, and HTML file selection. The manifest declares microphone access. MainActivity resolves runtime permission results and returns selected file URIs.

Changes

Android WebView access

Layer / File(s) Summary
Access setup and request state
v3/internal/commands/build_assets/android/app/src/main/AndroidManifest.xml, v3/internal/commands/build_assets/android/app/src/main/java/com/wails/app/MainActivity.java
The manifest declares RECORD_AUDIO. MainActivity adds WebView imports, request codes, and pending request state.
WebView camera and microphone permissions
v3/internal/commands/build_assets/android/app/src/main/java/com/wails/app/MainActivity.java
WebChromeClient logs console messages, validates local origins, maps camera and microphone resources to Android permissions, and grants or denies pending requests.
HTML file chooser handling
v3/internal/commands/build_assets/android/app/src/main/java/com/wails/app/MainActivity.java
The file chooser cancels existing callbacks, handles unavailable activities, preserves multiple selections, supports single-selection fallback, and clears callback state.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WebView
  participant WebChromeClient
  participant MainActivity
  participant AndroidRuntimePermissions
  WebView->>WebChromeClient: Request camera or microphone access
  WebChromeClient->>MainActivity: Request unresolved Android permissions
  MainActivity->>AndroidRuntimePermissions: Start runtime permission request
  AndroidRuntimePermissions-->>MainActivity: Return permission results
  MainActivity->>WebView: Grant or deny WebView resources
Loading

Possibly related PRs

  • wailsapp/wails#5735: Updates MainActivity.java with camera and microphone permission handling.
  • wailsapp/wails#5807: Extends related MainActivity.java WebView file chooser and console logging functionality.

Poem

A rabbit checks the WebView gate,
Camera and mic permissions wait.
Files hop through the picker bright,
One URI or many take flight.
Console messages guide the way,
Permissions answer yes or nay.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the Android WebView permission handler for getUserMedia, which is the primary change.
Description check ✅ Passed The description explains the change, security behavior, testing, configuration, dependencies, and checklist status in sufficient detail.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mortenolsrud
mortenolsrud marked this pull request as ready for review July 31, 2026 07:55

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@v3/internal/commands/build_assets/android/app/src/main/java/com/wails/app/MainActivity.java`:
- Around line 260-268: Before assigning the new request in the
runtime-permission branch of onPermissionRequest, check whether
pendingWebViewPermissionRequest is non-null and call its deny() method. Then
replace it with the new request and continue requesting permissions, preserving
the existing immediate-grant path.
- Around line 242-262: Build a separate list of grantable resources while
iterating through request.getResources() in the permission-mapping loop, adding
each validated resource (like RESOURCE_VIDEO_CAPTURE or RESOURCE_AUDIO_CAPTURE)
to this grantable list alongside the Android permissions. In the anchor location
at lines 242-262, replace the request.grant(request.getResources()) call with
request.grant() passing only the validated grantable resources. Apply the same
fix at the sibling location lines 341-359 in onRequestPermissionsResult: build
and pass only the grantable resources list instead of passing all original
resources.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9e6b39da-7b38-465d-be1f-fd0facbe0796

📥 Commits

Reviewing files that changed from the base of the PR and between f478764 and 33a120c.

📒 Files selected for processing (2)
  • v3/internal/commands/build_assets/android/app/src/main/AndroidManifest.xml
  • v3/internal/commands/build_assets/android/app/src/main/java/com/wails/app/MainActivity.java

@mortenolsrud
mortenolsrud force-pushed the feat/android-webview-permissions branch from 33a120c to acd1098 Compare July 31, 2026 10:46
@mortenolsrud

Copy link
Copy Markdown
Contributor Author

Both findings addressed in the force-pushed commit:

1. Resource whitelist (L262): request.grant() no longer receives request.getResources(). Instead, we build a granted list containing only the resources we explicitly handle: RESOURCE_VIDEO_CAPTURE, RESOURCE_AUDIO_CAPTURE, RESOURCE_PROTECTED_MEDIA_ID, RESOURCE_MIDI_SYSEX. Unknown future resources are silently omitted. Same filtering applied in onRequestPermissionsResult.

2. Deny-before-overwrite (L268): If pendingWebViewPermissionRequest is non-null when a new onPermissionRequest arrives, the previous one is now deny()'d before being replaced — matching the existing file-chooser callback pattern (webViewFileChooserCallback.onReceiveValue(null)).

…5781)

HTML <input type="file"> elements did nothing on Android because the
generated MainActivity set only a WebViewClient and never a
WebChromeClient, so the WebView had no onShowFileChooser handler to open
the system document picker.

Add a WebChromeClient whose onShowFileChooser launches the intent from
FileChooserParams and delivers the selected URIs back via
FileChooserParams.parseResult in onActivityResult. This also enables JS
dialogs, console logging and in-page permission prompts that require a
WebChromeClient.
@mortenolsrud
mortenolsrud force-pushed the feat/android-webview-permissions branch 3 times, most recently from cbd6fa2 to a9a8459 Compare August 4, 2026 10:30
The WebChromeClient had no onPermissionRequest override, so in-page
permission prompts (navigator.getUserMedia for camera/mic, WebRTC) were
silently auto-denied by the default implementation.

Add an onPermissionRequest handler that:
- Only grants for the local Wails origin (wails.localhost); requests from
  any other origin are unconditionally denied (security gate)
- Maps WebView resources (VIDEO_CAPTURE, AUDIO_CAPTURE) to Android
  runtime permissions (CAMERA, RECORD_AUDIO)
- Requests runtime permissions before granting, holding the
  PermissionRequest until onRequestPermissionsResult delivers the verdict
- Grants RESOURCE_PROTECTED_MEDIA_ID and RESOURCE_MIDI_SYSEX implicitly
  (no runtime permission required)

Also adds RECORD_AUDIO to the manifest (CAMERA was already declared).
@mortenolsrud
mortenolsrud force-pushed the feat/android-webview-permissions branch from a9a8459 to ed72c7f Compare August 4, 2026 11:01
@taliesin-ai taliesin-ai added this to the v3.1.0 milestone Aug 5, 2026
@taliesin-ai taliesin-ai added v3 and removed v3-alpha labels Aug 9, 2026
@leaanthony leaanthony added android Android platform mobile Mobile platform work labels Aug 9, 2026 — with ChatGPT Codex Connector
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

android Android platform cli mobile Mobile platform work v3

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

3 participants