Cancelling a Pending Web Bluetooth Connect (and my first AOSP patch)

โšก Chromium ๐Ÿ”ง C++ / Java / Web Bluetooth ๐Ÿ‘ค Helmut Januschka

disconnect() should actually stop a connect() that never finished - on every platform, down to the OS

Status: ๐Ÿšง In Progress

The Problem

The Web Bluetooth spec is explicit: step 1 of disconnect() clears the active algorithms map to cancel any pending connections. In practice, a gatt.connect() aimed at a device that is not currently available would just hang, and calling disconnect() did not reliably cancel it.

The expected behavior is simple:

This one has been open a while: issue 40502943 was filed in January 2017.

The Background

The cross-platform core fix landed first:

That wires up the general "disconnect cancels a pending connect" behavior in Chromium. But the actual cancellation has to be honored by each platform backend - and below the browser, by the OS Bluetooth stack itself.

The Windows Backend

IN REVIEW bluetooth: Enable pending GATT connect cancellation on Windows

On Windows, a disconnect() during an in-flight connect needs to fail the pending connection callbacks, and the canceled WinRT service discovery has to be handled without tripping the async-results DCHECK. This CL makes the Windows path actually reject the pending connect instead of leaving it dangling.

Down to the OS: My First AOSP Patch

The most interesting part is that the fix does not stop at Chromium. On Android, BluetoothGatt.disconnect() did not cancel a connectGatt() that was still waiting for client registration - so a pending connect could not be cleanly aborted at the platform level.

That fix lives in the Android OS itself, in packages/modules/Bluetooth:

Bluetooth: Cancel pending GATT connects - make BluetoothGatt.disconnect() cancel a connectGatt() request that is still waiting for client registration, and report the cancellation through onConnectionStateChange().

This is my first patch to the Android platform (AOSP), and it is still being iterated on. It is a fun milestone: a Web Bluetooth spec line about disconnect() ends up requiring a change three layers down, in the OS Bluetooth stack.

The Test Rig

Verifying a "cancel" path needs a device that you can make appear and then yank away. The sampler uses ESP32-C3 firmware advertising as dino c(h)ancler with serial commands to start/stop advertising, disconnect the central, or deep-sleep - so you can reliably create the "device went away mid-connect" condition.

The flow:

  1. Select the ESP32-C3 while it is advertising.
  2. Make it unavailable (stop advertising / deep sleep) and start a connect.
  3. Hit Disconnect / cancel pending connect - the pending promise should reject with AbortError.
  4. Confirm a fresh requestDevice() + connect to another device still works without reloading.

The sampler ships a patched ARM64 ChromePublic APK and the firmware source so reviewers can reproduce it on real hardware.