Tutorial
Handling WebRTC Camera and Microphone Permissions in a TWA
September 18, 2026 · 8 min read
Implementing real-time video, audio, or QR scanning inside a Progressive Web App requires using WebRTC. When your PWA is running inside a standard browser, browser-level permissions manage access to the device camera and microphone. However, wrapping that same PWA inside an Android app using a Trusted Web Activity introduces a double-permission layer that can prevent your WebRTC streams from opening if not configured correctly.
The Android Double-Permission Challenge
In a standard mobile browser, when a website calls navigator.mediaDevices.getUserMedia, Chrome asks the user for permission. If the user accepts, the camera stream starts. Inside a Trusted Web Activity, this process is split. First, the host Android application itself must have permission from the Android operating system to access the camera or microphone. Second, the web engine running inside the TWA must also have permission to access those hardware resources.
If your Android package does not explicitly declare that it uses the camera and microphone, the underlying browser engine cannot delegate those permissions, and your WebRTC calls will fail silently or throw permission denied errors. To fix this, you must declare these capabilities in your Android project configuration.
Declaring Hardware Features in AndroidManifest.xml
To enable WebRTC features, you must modify your Android project manifest file. This file tells the Android OS what permissions your application requires to run. You must declare both the permission request and the hardware requirements. Declaring the hardware features ensures that Google Play knows your app needs these capabilities and configures store compatibility filters appropriately.
Add the following declarations inside your AndroidManifest.xml file:
<!-- Request permission to use the camera -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- Request permission to record audio -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- Declare that the app uses hardware features (optional: set required to false if the app can run without them) -->
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
Setting android:required to false is highly recommended. This ensures that users with devices that lack specific autofocus capabilities can still install your application from the Google Play Store, while your software gracefully handles the lack of those hardware features.
How TWA Permission Delegation Works
Modern Trusted Web Activity libraries handle permission delegation automatically. When your web application requests access to the camera or microphone via standard JavaScript, the Custom Tab engine intercepts the request and queries the parent Android app to see if it has the required native permissions. If the Android app has been granted the camera permission, the web engine will automatically allow the site to use the camera without showing a second web-style permission prompt. This creates a seamless, native-feeling user experience.
To ensure this delegation functions correctly, make sure you are using the latest version of the Android Browser Helper library in your build.gradle file:
dependencies {
implementation 'com.google.android.browser:helper:2.4.0'
}
Common WebRTC Permissions Matrix
The table below explains how the TWA behaves based on the state of the native Android system permissions and the web permissions.
| Android System Permission | Web Request Permission | Resulting Behaviour |
|---|---|---|
| Granted | Requested | WebRTC stream starts instantly without redundant web prompt. |
| Denied | Requested | Request fails; web code catches a PermissionDeniedError. |
| Not Declared | Requested | App crash or immediate permission failure as OS blocks access. |
Implementing Graceful Web-Side Fallbacks
Since your PWA can run in both native browsers and inside your TWA wrapper, your frontend JavaScript must handle permissions defensively. Always wrap your media stream requests in a try-catch block to handle rejections gracefully and present actionable feedback to your users.
async function startVideoSession() {
const constraints = {
video: { width: 1280, height: 720 },
audio: true
};
try {
const stream = await navigator.mediaDevices.getUserMedia(constraints);
const videoElement = document.getElementById('camera-preview');
videoElement.srcObject = stream;
} catch (error) {
handleMediaError(error);
}
}
function handleMediaError(error) {
console.error('Error accessing media devices:', error);
if (error.name === 'NotAllowedError' || error.name === 'PermissionDeniedError') {
alert('Camera and microphone access is required. Please enable permissions in your Android system settings under Apps > App Info.');
} else {
alert('Could not start video: ' + error.message);
}
}
Testing WebRTC in Your TWA
Testing WebRTC functionality requires secure contexts (HTTPS). Chrome blocks camera and microphone access on insecure connections. When testing your TWA locally, you must serve your web app over HTTPS, or configure Chrome flags on your test device to treat your local development IP address as a secure origin.
To test the flow:
- First Launch: Trigger the WebRTC camera action in your app. The Android OS permission prompt should appear, asking the user to allow your application to take pictures and record video.
- Subsequent Launches: Triggering the action again should open the camera instantly with no additional prompts, confirming that the TWA is successfully delegating the permission.
By correctly matching the native Android declarations with web standards, your converted application can utilise advanced real-time communication features without sacrificing user experience or triggering store rejections.
Ready to ship your Android app?
Paste your PWA URL, get a signed APK and a Google Play ready AAB in minutes.
Build my app