Skip to content
webtoapkfree

How do I allow camera and microphone access in an Android WebView app?

WebView camera and microphone permission needs two grants: the app's CAMERA and RECORD_AUDIO runtime permissions, then onPermissionRequest for the page.

GuidesNookesh Karri

Light cover card with the title 'How do I allow camera and microphone access in an Android WebView app?' in dark text and a green accent bar

To allow WebView camera and microphone permission on Android, you need two grants. First, the app declares CAMERA and RECORD_AUDIO and asks the user for them at runtime. Then your WebChromeClient overrides onPermissionRequest and grants the page the camera or microphone resource it asked for.

Why a web page can’t reach the camera in a WebView

A web page in a WebView can’t use the camera because Android denies the request unless the app handles it. The WebChromeClient reference is blunt: “If this method isn’t overridden, the permission is denied.”

Chrome shows its own prompt. Your app shows nothing until you write the code. That’s why a video call page, a QR scanner or a voice note recorder that works in Chrome gives a black preview or an error inside the wrapper.

The two layers of WebView camera and microphone permission

Camera access in a WebView app passes through two separate gates, and both must say yes. HTTPS sits under both, as the table’s third row.

Layer Who asks What you write If it’s missing
Android app permission The app, at runtime CAMERA / RECORD_AUDIO in the manifest plus a runtime request The page’s request is denied
WebView page permission The web page, via getUserMedia() onPermissionRequest that calls grant() Denied by default
Secure context The browser engine Load the page over HTTPS navigator.mediaDevices is undefined

Order matters: get the Android permission first, then grant the page.

Step 1: Declare and request the Android permissions

Your app needs the CAMERA and RECORD_AUDIO permissions in its manifest and a runtime request. The Manifest.permission reference lists both with protection level “dangerous”.

Declaring them isn’t enough. The runtime permissions guide says that on Android 6.0 (API level 23) or higher, dangerous permissions must be requested at runtime.

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />

Check with ContextCompat.checkSelfPermission(), which returns PERMISSION_GRANTED or PERMISSION_DENIED. Ask with the RequestMultiplePermissions contract when a page needs both. Skip RECORD_AUDIO unless your site records sound. A QR scanner needs the camera alone.

Step 2: Grant the page in onPermissionRequest

Once the app holds the permission, override onPermissionRequest and grant the specific resource the page asked for. The PermissionRequest reference defines RESOURCE_VIDEO_CAPTURE for the camera and RESOURCE_AUDIO_CAPTURE for the microphone.

webView.webChromeClient = object : WebChromeClient() {
    override fun onPermissionRequest(request: PermissionRequest) {
        if (request.origin.host != "yoursite.com") {
            request.deny()
            return
        }
        val allowed = request.resources.filter {
            (it == PermissionRequest.RESOURCE_VIDEO_CAPTURE && hasPermission(Manifest.permission.CAMERA)) ||
            (it == PermissionRequest.RESOURCE_AUDIO_CAPTURE && hasPermission(Manifest.permission.RECORD_AUDIO))
        }
        runOnUiThread {
            if (allowed.isEmpty()) request.deny() else request.grant(allowed.toTypedArray())
        }
    }
}

Three details in that code come from the docs:

  1. UI thread only. The reference says grant() or deny() “must be called in UI thread”.
  2. Filter the resources. The docs warn against grant(request.getResources()) and mark that line “This is wrong!!!”. A blind grant can hand a page permissions you never planned for.
  3. Check the origin. getOrigin() tells you which page is asking. Deny every origin except your own. A third-party iframe has no business using your users’ microphone.

Each grant is “only valid for this WebView”, so a second WebView in your app asks again.

Fixing NotAllowedError and an undefined navigator.mediaDevices

The error the page sees tells you which layer failed. Read it in the WebView’s console through Chrome remote debugging.

  • navigator.mediaDevices is undefined. The page isn’t a secure context. MDN’s getUserMedia page says a secure context is a page loaded over HTTPS, from file:/// or from localhost. An http:// site fails here, the same mixed-security trouble behind ERR_CLEARTEXT_NOT_PERMITTED.
  • NotAllowedError right away. Your onPermissionRequest is missing or called deny(). The Hotwire Native Android pull request shows another cause: when a required manifest entry is missing, the whole request is denied and the page sees NotAllowedError.
  • The camera works in the page but stays black inside an iframe. MDN says an iframe needs allow="camera; microphone".
  • It worked once, then stopped. On Android 11 (API level 30) and later, the dialog offers “Only this time”, so the grant can lapse.

When the user says no

Plan for a denial, because users tap Deny often. In that case, call request.deny() so the page gets a clean error and can explain itself.

After a user taps Deny more than once, the runtime permissions guide says they stop seeing the system dialog. From then on, your page has to send them to the app’s settings. And from Android 12 (API level 31), privacy indicators show whenever an app uses the camera or microphone, so users will see yours light up.

A limit of no-code WebView wrappers

Both permission layers live in the app’s native code, so you can only fix them if you control that code. Web2Apk’s own guides say it doesn’t guarantee every website works in a WebView, and that permissions can behave differently than in Chrome. If your site needs the camera or microphone, test a build on a real phone before you share it. If it fails there, a no-code wrapper won’t fix it; you need a hand-written one with the code above.

Downloads have their own fix, covered in why WebView downloads fail, and links to other sites need the rules in opening external links in the browser. Google sign-in won’t work inside a WebView at all, as explained in why Google sign-in fails in a WebView app.

FAQ

Why does getUserMedia fail with NotAllowedError in my WebView app?

Usually one of the two permission layers is missing. Either the app never got CAMERA or RECORD_AUDIO from the user, or the WebChromeClient doesn’t override onPermissionRequest, in which case Android denies the request.

Is navigator.mediaDevices undefined in an Android WebView?

It is undefined when the page isn’t a secure context. MDN says getUserMedia only works on pages loaded over HTTPS, from file:/// or from localhost, so load your site over HTTPS.

Do I need both CAMERA in the manifest and onPermissionRequest?

Yes. CAMERA and RECORD_AUDIO are dangerous permissions, so the app must declare them and ask the user at runtime. onPermissionRequest is a separate step that lets the web page use what the app already holds.

Is it safe to call grant(request.getResources())?

Android’s PermissionRequest docs warn against it, because it can grant permissions you didn’t intend. Check the origin and grant only the camera or microphone resources your site needs.

What if the user denied the camera permission?

Deny the WebView request so the page gets NotAllowedError and can show a message. After a user taps Deny more than once, Android stops showing the dialog, so point them to the app’s settings.

Sources