All articles

Technical

Using Web Bluetooth and WebUSB APIs in Android TWAs

September 12, 2026 · 8 min read

When developers build applications for hardware peripherals, such as medical monitors, thermal receipt printers, heart rate sensors, or industrial equipment, they often assume they must write native Java or Kotlin code. Historically, wrapping a web application in a standard Android WebView disabled direct hardware access. WebViews do not support the modern device communication APIs built into modern browser engines.

However, Trusted Web Activity (TWA) technology changes this dynamic. Because a TWA runs on top of the system's underlying Chromium engine, it inherits the full capability of modern web platform APIs, including Web Bluetooth and WebUSB. This allows web developers to build, convert, and distribute native-feeling hardware companion apps on the Google Play Store without maintaining dual codebases.

Understanding Hardware Support: WebView vs TWA

Before implementing hardware communication, it is crucial to understand why a TWA is required instead of a traditional WebView. The Android system WebView is a highly restricted rendering engine designed for displaying static document content or simple web interfaces. It lacks the complex user-permission prompt infrastructure required to securely pair Bluetooth or USB peripherals.

The table below highlights the differences in device API support across these container technologies:

Hardware APISystem WebView SupportTWA (Chromium-backed) SupportPrimary Android Requirement
Web BluetoothNoYesLocation Services & Bluetooth Permissions
WebUSBNoYesUSB Manager Host Permission
Web SerialNoYesUSB OTG Connection Support
Web MIDINoYesSystem MIDI Driver Support

Implementing Web Bluetooth inside your PWA

The Web Bluetooth API allows your application to connect to Bluetooth Low Energy (BLE) peripherals using standard JavaScript. Because your TWA is backed by Chromium, calling navigator.bluetooth.requestDevice() will trigger the native Android device chooser directly inside your application interface.

To connect to a device, your web application must call the API in response to a user action, such as clicking a button. Security policies prohibit initiating a device scan automatically on page load. Here is a standard implementation example for scanning and connecting to a BLE battery level monitor:

async function connectToBluetoothDevice() {
try {
const device = await navigator.bluetooth.requestDevice({
filters: [{ services: ['battery_service'] }]
});
const server = await device.gpt.connect();
const service = await server.getPrimaryService('battery_service');
const characteristic = await service.getCharacteristic('battery_level');
const value = await characteristic.readValue();
console.log('Battery level is: ' + value.getUint8(0));
} catch (error) {
console.error('Bluetooth connection failed:', error);
}
}

When this code executes within your TWA, the browser engine interrupts the web view to present a native bottom sheet containing a list of nearby discoverable BLE devices matching the filter. Once the user selects the peripheral, the secure pairing handshake completes, and your JavaScript retains access to read and write characteristics.

Integrating WebUSB for Direct Cable Connections

For applications that require stable, wired connections, WebUSB is the optimal choice. This is common for point-of-sale systems interacting with ticket printers, barcode scanners, and custom microcontrollers. WebUSB allows the web app to speak directly to the USB interface using standard control transfers and endpoints.

Similar to Web Bluetooth, WebUSB requires an explicit user gesture to trigger the device selection dialogue:

async function connectToUsbDevice() {
try {
const device = await navigator.usb.requestDevice({
filters: [{ vendorId: 0x2341 }]
});
await device.open();
await device.selectConfiguration(1);
await device.claimInterface(0);
console.log('USB Device connected successfully:', device.productName);
} catch (error) {
console.error('USB connection failed:', error);
}
}

On Android, when WebUSB requests a device, the host system displays a permission prompt asking the user to grant permission to the parent browser app. Once granted, raw data packets can be transmitted bidirectionally across endpoints.

Declaring Hardware Requirements in the Android Manifest

Even though the browser engine handles the low-level communication and pairing interfaces, you should declare these hardware requirements inside your TWA's native configuration files. This ensures your application is only distributed to devices that possess the physical capability to use your app.

If your application absolutely requires Bluetooth to function, you should declare this in the AndroidManifest.xml of your compiled wrapper. This prevents users with incompatible devices from downloading the app from the Google Play Store:

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />

If Bluetooth or USB functionality is an optional enhancement within your PWA, set the android:required attribute to false. You can then dynamically check capability in your web code by checking if navigator.bluetooth or navigator.usb is defined before rendering the connection controls.

Managing Permissions and User Experience Limits

Working with hardware APIs in a hybrid environment introduces unique edge cases that you must design your application to handle gracefully:

  • Operating System Permitting: On Android, Web Bluetooth requires location permissions to be enabled on the device. Your app should detect if permission is denied and provide a user friendly prompt explaining that Android groups BLE scanning under location permissions.
  • Foreground Constraints: Hardware connections are bound to the active tab context. If a user minimises your TWA, switches to another application, or locks their device, active BLE and USB connections will typically be suspended by the operating system to conserve power. Your application must detect sudden disconnections using the onadvertisementreceived or ondisconnect event handlers and offer an automated reconnection workflow.
  • Co-existing with Native Apps: Only one application can control a USB interface at a time. If a native Android app is currently communicating with a connected USB accessory, your TWA will receive an access denied error when calling device.open().

By leveraging Web Bluetooth and WebUSB inside your TWA, you bypass the complexity of building native Android plugins, custom Java bridges, and platform-specific drivers. Your web application can manage everything from interface rendering to peripheral communication, resulting in a single, maintainable codebase that deploys universally to both web browsers and mobile app stores.

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