Inside a WebView, a web page asking for the camera or location must pass two separate gates — the OS permission and the WebView permission — and on Android the default is to deny the second one if the app does not implement the callback. Worse, Android's WebView gives the embedding app full control over web permission handling with no per-site consent like a real browser: once the OS grant exists, the app can share location or camera with any page in the WebView without another prompt. On iOS, WKWebView adds a second prompt on top of the app's, and in-app browsers like TikTok and Instagram are officially unsupported. A host-owned runtime ships one permission broker that maps native and web consent, enforces per-origin grants, requires a secure context, and shows one consistent prompt.
Two gates, and the second one defaults to no
On Android, a page calling getUserMedia or navigator.geolocation hits two checks. First the OS permission — CAMERA, RECORD_AUDIO, or location declared in the manifest and granted at runtime (API 23+). Then the WebView permission: even with the OS grant, the page surfaces to the app as WebChromeClient.onPermissionRequest (camera/mic) or onGeolocationPermissionsShowPrompt (location), and the default answer is no. Skip implementing the callback and the symptom is baffling — permission granted, getUserMedia called, nothing happens, no dialog, a rejected promise. Geolocation is only called for secure origins on apps targeting Android 7.0+, so an HTTP page is denied before anyone taps anything.
No per-site consent, unlike a browser
A 2025 study of Android's WebView permission enforcement shows the structural gap: traditional browsers prompt per site and let users grant or deny each one, but Android's WebView delegates the entire decision to the embedding app. If the app implements the callback, it can grant or deny for any origin, and there is no built-in per-site memory or user-facing control. The deeper risk is that once the OS location permission is granted to the app, the app can hand that location to any website loaded in the WebView without further user consent — the user cannot see which page is accessing what. Developers must build that consent UX themselves, or it does not exist.
iOS: a second prompt, and silent downgrades
On iOS, WKWebView's camera and microphone access requires the app to implement requestMediaCapturePermissionFor (iOS 15+) and the relevant entitlements; before that, WebRTC was simply unavailable in WKWebView. Even when implemented, the user sees two prompts — one from the app, one from the site — and the documented workaround is for the app to skip the second via a delegate, which means the user's per-page choice is decided by the app, not the page. When the page is loaded through a custom scheme handler, the prompt shows the app's name as the origin, which users find confusing. In-app browsers (Instagram, TikTok) carry additional constraints and broken UI and are explicitly not supported.
What you lose without a permission broker
- A single source of truth. Native and web permission states live in different places; they drift and contradict.
- Per-origin control. Android WebView has no per-site grant memory; the app either grants everything or denies everything.
- User visibility. The user cannot tell which page is accessing the camera or location, because the consent lives in app code.
- Secure-context enforcement. An HTTP page silently loses geolocation on modern Android, with no visible reason.
- A consistent prompt. iOS double prompts; in-app browsers break entirely.
The runtime fix
A host-owned runtime ships one permission broker below the page. It keeps a single, unified consent store for native and web, enforces per-origin grants the way a browser does, requires a secure context before any sensitive API is offered, and presents one consistent, user-readable prompt that names the page and the resource. The app's OS grant and the page's web request are reconciled in one place, so consent is visible, revocable, and never silently shared with an unknown origin.
| Dimension | WebView | Host-owned runtime |
|---|---|---|
| Consent model | App decides everything, no per-site memory | Unified store, per-origin grants |
| Android gate 2 | onPermissionRequest default denies | Broker grants per origin, visible |
| iOS prompts | Two prompts, app can skip site prompt | One consistent prompt |
| Secure context | HTTP geolocation auto-denied, silent | Enforced, explained |
| User visibility | Cannot see which page accesses what | Prompt names page and resource |
| In-app browsers | Unsupported, broken UI | One runtime, supported everywhere |
A browser earns trust by asking the right question, to the right party, at the right moment. A stock WebView pushes that decision into opaque app code and then defaults to no. A runtime restores the browser's consent contract — per origin, visible, and revocable — so a page that needs the camera gets a prompt the user actually understands.
Sources
- Android Developers — WebChromeClient.onPermissionRequest and PermissionRequest: the host must implement the callback or all web permission requests are denied by default — the second gate and its silent default.
- Android Developers — WebSettings.setGeolocationEnabled: geolocation only called for secure origins on apps targeting Android 7.0+ — why an HTTP page loses location silently.
- CISPA — Studying the Privacy Risks in Android WebView's Web Permission Enforcement (2025): no per-site consent, app can share OS grants with any page — the structural privacy gap.
- Apple Developer Documentation — WKUIDelegate.requestMediaCapturePermissionFor and WKPermissionDecision — the iOS double-prompt and delegate skip.
- web.dev — Web on Android: WebView delegates sensitive behavior to the embedding app — why permission handling is app-side, not web-side.