DEV Community

Lijiawei
Lijiawei

Posted on Originally published at tabqa.openutx.cn

How to Test Android Devices and Intercept WebView Traffic Directly in Chrome (Without Local ADB)

How to Test Android Devices and Intercept WebView Traffic Directly in Chrome (Without Local ADB)

If you have ever tested mobile apps on Android, you are likely familiar with the friction of the traditional toolchain:

  1. You plug in the phone and launch desktop mirroring tools like Scrcpy or Vysor;
  2. You open a terminal to stream adb logcat and manually grep for errors;
  3. If you need to inspect HTTPS traffic in an embedded WebView or hybrid app, you configure Charles or Fiddler, setup Wi-Fi proxies, and struggle with Android 7+ (API 24+) rejecting user-installed CA root certificates;
  4. When an intermittent crash or ANR occurs, you scramble to take screenshots, clip screen recordings, align timestamps with logs, and assemble everything into Jira or Notion.

A recurring question arose in our engineering team:

Can we consolidate device mirroring, log filtering, network traffic interception, and bug evidence collection natively inside the browser—without requiring local ADB binaries, drivers, or desktop proxy software?

This article explores the technical implementation of such a workspace, leveraging the WebUSB API for hardware communication and the Chrome DevTools Protocol (CDP) for certificate-free network interception, based on our open-source project TabQA.


1. Direct Hardware Communication: Implementing ADB over WebUSB

Traditional Android debugging relies on an ADB server process listening on host port 5037. In multi-developer environments or non-technical QA machines, this often results in SDK configuration hurdles or adb server version doesn't match this client conflicts.

Modern Chromium browsers provide the WebUSB API, enabling secure user-authorized contexts to communicate directly with physical USB bulk endpoints.

1.1 Device Filtering and Interface Claiming

According to the Android Open Source Project (AOSP) specification, the ADB interface defines a fixed descriptor:

  • bInterfaceClass: 0xff (Vendor Specific)
  • bInterfaceSubClass: 0x42 (ADB)
  • bInterfaceProtocol: 0x01

In the browser, we request the device via permission prompts:

// Filter and request Android devices matching ADB interface descriptors
const device = await navigator.usb.requestDevice({
  filters: [{
    classCode: 0xff,
    subclassCode: 0x42,
    protocolCode: 0x01
  }]
});

await device.open();
await device.selectConfiguration(1);

// Locate and claim the ADB interface
const adbInterface = device.configuration.interfaces.find(iface =>
  iface.alternates.some(alt => alt.interfaceClass === 0xff && alt.interfaceSubclass === 0x42)
);
await device.claimInterface(adbInterface.interfaceNumber);
Enter fullscreen mode Exit fullscreen mode

1.2 Framing ADB Packets & In-Memory RSA Authentication

Communication requires implementing the 24-byte binary ADB header directly in JavaScript:

// ADB message header format: command, arg0, arg1, data_length, data_checksum, magic
function createAdbPacket(command, arg0, arg1, payload = new Uint8Array(0)) {
  const header = new ArrayBuffer(24);
  const view = new DataView(header);

  view.setUint32(0, command, true);              // A_CNXN, A_OPEN, A_WRTE, etc.
  view.setUint32(4, arg0, true);
  view.setUint32(8, arg1, true);
  view.setUint32(12, payload.byteLength, true);  // Payload size
  view.setUint32(16, calculateChecksum(payload), true);
  view.setUint32(20, command ^ 0xffffffff, true);// Magic verification

  return concatBuffers(header, payload.buffer);
}
Enter fullscreen mode Exit fullscreen mode

When connecting to an unauthorized device, Android replies with an A_AUTH challenge. The browser extension generates a 2048-bit RSA key pair in-memory (or loads a stored key from chrome.storage.local), encodes the public key in Android's expected token format, and sends it to the device. Once the user taps "Allow USB Debugging" on their screen, the transport channel is established.

No system adb process is invoked, eliminating port contention entirely.


2. Inspecting WebView Traffic Without CA Certificates (CDP)

Inspecting HTTPS network requests in mobile WebViews or hybrid apps is notoriously painful due to SSL Pinning and Android 7+ Network Security Config restrictions.

2.1 Forwarding DevTools Sockets

Android Chrome and debug-enabled WebViews (WebView.setWebContentsDebuggingEnabled(true)) expose an abstract Unix domain socket on the device (e.g., @webview_devtools_remote_<pid>).

By issuing an ADB port forward command across our WebUSB connection, the browser connects to the target WebView via a standard WebSocket.

2.2 Direct CDP Network Inspection

Once connected, we send Chrome DevTools Protocol commands directly to the engine:

// Enable network event streaming from the WebView
function startNetworkCapture(ws) {
  ws.send(JSON.stringify({
    id: 1,
    method: 'Network.enable',
    params: {
      maxPostDataSize: 65536 // Capture request bodies up to 64KB
    }
  }));
}

// Receive parsed network events
ws.onmessage = (event) => {
  const data = JSON.parse(event.data);

  if (data.method === 'Network.responseReceived') {
    const { requestId, response } = data.params;
    console.log(`[Status ${response.status}] ${response.url}`);

    // Fetch response body on demand
    ws.send(JSON.stringify({
      id: generateUniqueId(),
      method: 'Network.getResponseBody',
      params: { requestId }
    }));
  }
};
Enter fullscreen mode Exit fullscreen mode

Because telemetry is extracted directly from the Chromium rendering process:

  • No CA root certificates are required on the Android device;
  • No Wi-Fi proxy or DNS tampering is needed;
  • Requests, response headers, payloads, and timings are extracted cleanly, ready for export as cURL or sanitized HAR.

3. Rolling Ring Buffers for Video and Log Synchronization

Capturing intermittent bugs requires capturing evidence before you realize a crash happened. Storing hours of continuous high-definition recording and full device logcat quickly exhausts memory.

We implemented an in-memory Rolling Ring Buffer:

class RollingRingBuffer {
  constructor(capacity = 180) { // Holds 180 seconds of 1-second chunks
    this.buffer = new Array(capacity);
    this.capacity = capacity;
    this.head = 0;
    this.size = 0;
  }

  push(slice) {
    this.buffer[this.head] = slice;
    this.head = (this.head + 1) % this.capacity;
    if (this.size < this.capacity) this.size++;
  }

  dump() {
    const result = [];
    let start = this.size < this.capacity ? 0 : this.head;
    for (let i = 0; i < this.size; i++) {
      result.push(this.buffer[(start + i) % this.capacity]);
    }
    return result;
  }
}
Enter fullscreen mode Exit fullscreen mode
  • Video Stream: Compressed via WebCodecs or MediaRecorder into 1-second GOPs and pushed to the buffer;
  • Logcat Stream: Read asynchronously over the ADB channel, with regex filters isolating the foreground package and flagging FATAL EXCEPTION or ANR signatures;
  • On-Demand Dump: When a bug occurs, clicking "Finish" dumps the synchronized video clip, structured Markdown summary, screenshots, and filtered logs into an all-in-one developer-ready package.

4. Architectural Comparison: Desktop vs. Browser

Architectural Dimension Traditional Desktop Tools (e.g., Scrcpy + Charles) Browser Native Workspace (TabQA)
Communication Layer Host adb binary (localhost:5037) Native WebUSB API (USB Bulk Transfer)
Setup & Dependencies Requires platform tools, drivers, proxies Zero host dependencies (Chrome 118+)
Frame Rates High (60–120fps, ideal for gaming/long video) Stable (Focus on UI testing and interaction)
Traffic Inspection MITM Proxy (Requires installed CA Root Cert) Built-in CDP (Zero certificates required)
Crash Extraction Manual terminal grep / full system dump Target app log filtering with ANR/Crash alerts
Bug Delivery Dispersed files across folders Integrated Markdown report + assets for Jira/Notion
Data Privacy Local host Local-first (Data never leaves browser sandbox)

5. Conclusion & Open Source Project

By combining WebUSB, WebCodecs, and Chrome DevTools Protocol, it is now entirely feasible to build a unified mobile testing workspace directly inside browser side panels.

We packaged this architecture into an open-source tool called TabQA:

We welcome feedback, issues, and contributions from testing and mobile development teams!

Top comments (0)