Skip to main content

Request device permission

note

This page only applies to browsers. For Node.js, you can skip to the next step.

Request permission in WebUSB

In Web browsers, each website needs to request the permission to access each device separately using WebUSB's USB#requestDevice method.

User activation

USB#requestDevice method requires user activation, it must be called in response to a mouse click or keyboard press event.

Device picker

After calling USB#requestDevice, the browser shows a list of connected devices to the user. The user can select one (and only one) device from the list, or cancel the picker without selecting any device.

You can filter the devices shown in the picker, but otherwise you can't control the picker's appearance or behavior.

Return value

It will return an USBDevice instance representing the selected device, or throws an DOMException with name set to NotFoundError if the user cancelled the picker.

note

This code is for demonstration purposes only. You should use AdbDaemonWebUsbDeviceManager#requestDevice described below in Tango.

document.getElementById("button").addEventListener("click", () => {
try {
const device: USBDevice = await navigator.usb.requestDevice({
filters: [
{
classCode: 0xff,
subclassCode: 0x42,
protocolCode: 1,
},
],
});
alert(`Selected device: ${device.serialNumber}`);
} catch (e) {
if (e instanceof DOMException && e.name === "NotFoundError") {
alert("No device selected");
return;
}

throw e;
}
});

Permission persistence

The permission will be persisted by the browser, so from next time, the website can access that device without calling requestDevice again. The user can revoke the permission in "Settings -> Site Permissions -> USB devices" at any time.

USB mode affects permissions

When Android devices works in USB peripheral mode, they can have multiple USB interfaces, like MTP (file transfer), ADB, RNDIS (reverse tethering), MIDI, etc.

When any interface is turned on or off from system UI, the device will disconnect and reconnect to the USB host and report a different set of interfaces.

As a result, in Web browsers, the same Android device with different sets of interfaces will be treated as different devices, and have different permissions. The website might need to request permission again when Android device switches to another mode.

Request permission in Tango

AdbDaemonWebUsbDeviceManager#requestDevice method provides a more convenient way to request permission for Android devices with ADB interface enabled.

declare class AdbDaemonWebUsbDeviceManager {
requestDevice(): Promise<AdbDaemonWebUsbDevice | undefined>;
}

It calls USB#requestDevice internally, thus the user activation, device picker, and permission persistence behaviors are the same as USB#requestDevice.

The only difference is the return value:

  • When the user selects a device, it returns an AdbDaemonWebUsbDevice instance, which includes the raw USBDevice handle and additional information required by later steps.
  • If the user cancels the picker, it returns undefined, instead of throwing an error.
import { AdbDaemonWebUsbDevice } from "@yume-chan/adb-daemon-webusb";

document.getElementById("button").addEventListener("click", () => {
const device: AdbDaemonWebUsbDevice | undefined = await Manager.requestDevice();
if (!device) {
alert("No device selected");
return;
}
});

Filter devices in the picker

interface AdbDeviceFilter {
classCode: number;
subclassCode: number;
protocolCode: number;
vendorId?: number | undefined;
productId?: number | undefined;
serialNumber?: string | undefined;
}

namespace AdbDaemonWebUsbDeviceManager {
interface RequestDeviceOptions {
filters?: AdbDeviceFilter[] | undefined;
exclusionFilters?: USBDeviceFilter[] | undefined;
}
}

declare class AdbDaemonWebUsbDeviceManager {
requestDevice(
options?: AdbDaemonWebUsbDeviceManager.RequestDeviceOptions
): Promise<AdbDaemonWebUsbDevice | undefined>;
}
note

See Device filter for more information about how does the filter work and how to define a filter.

filters

The filters option defaults to [ADB_DEFAULT_DEVICE_FILTER], which matches the ADB interface. If you want to filter devices by vendor ID, product ID, or serial number, you can specify a custom filter array.

filters can be undefined, but it can't be an empty array. If you want to allow all devices, use undefined or [ADB_DEFAULT_DEVICE_FILTER].

exclusionFilters

It's also possible to exclude specific devices from the picker using the exclusionFilters option. It works in reverse: if a device matches any filter in exclusionFilters, it will be excluded from the picker.

It can be used to exclude devices that are already paired, or exclude certain devices that are incompatible.

info

Support for exclusive filters was added in Chrome 105. It will be ignored in older versions.

Examples

To select a specific manufacturer and model:

import { AdbDaemonWebUsbDevice, ADB_DEFAULT_DEVICE_FILTER } from "@yume-chan/adb-daemon-webusb";

const device: AdbDaemonWebUsbDevice | undefined = await Manager.requestDevice({
filters: [
{
...ADB_DEFAULT_DEVICE_FILTER,
vendorId: 0x18d1,
productId: 0x4ee2,
},
],
});

To allow multiple manufacturers/models:

import { AdbDaemonWebUsbDevice, ADB_DEFAULT_DEVICE_FILTER } from "@yume-chan/adb-daemon-webusb";

const device: AdbDaemonWebUsbDevice | undefined = await Manager.requestDevice({
filters: [
{
...ADB_DEFAULT_DEVICE_FILTER,
vendorId: 0x18d1,
productId: 0x4ee2,
},
{
...ADB_DEFAULT_DEVICE_FILTER,
vendorId: 0x04e8,
productId: 0x6860,
},
],
});

Drop permission

If a device is connected and the permission to access it was granted, but you don't want to access it anymore, you can drop the permission.

device.raw.forget();

Because there is no way to retrieve permission-granted but disconnected devices (unless you got the device before it was disconnected), it's also not possible to drop permissions for them. (See https://github.com/WICG/webusb/issues/166)