Skip to content

Generate 'characteristicvaluechanged' events without waiting for a CCCD update #514

@reillyeon

Description

@reillyeon

In this StackOverflow question a developer explains that they are trying to use Web Bluetooth to communicate with a device that appears to generate a number of Characteristic Value Notifications immediately after the GATT connection has been established. If they perform the following sequence of calls there is a high probability that some of the notifications will be lost.

const device = await navigator.bluetooth.requestDevice({ filters: [{ services: [serviceUuid] }] });
const server = await device.gatt.connect();
const service = await server.getPrimaryService(serviceUuid);
const characteristic = await service.getCharacteristic(characteristicUuid);
characteristic.addEventListener('characteristicvaluechanged', () => { ... });
await characteristic.startNotifications();

The issue is that each of these is an asynchronous operation which takes some time to complete and the startNotifications() call in particular depends on being able to update the Client Characteristic Configuration descriptor in order to enable notifications. While the device is not complying with the Bluetooth specification by sending notifications before requested the reality is that other Bluetooth APIs pass on these unsolicited notifications.

Properly fixing this seems to require two changes. First, the active notification context set must be removed and the steps for responding to notifications and indications updated to deliver events to all active Bluetooth globals regardless of whether startNotifications() or stopNotifications() have been called. Second, the bubbling of events through the Bluetooth tree needs to be stabilized so that the code above can be rewritten to the following.

const device = await navigator.bluetooth.requestDevice({ filters: [{ services: [serviceUuid] }] });
characteristic.addEventListener('characteristicvaluechanged', () => { ... });
const server = await device.gatt.connect();
const service = await server.getPrimaryService(serviceUuid);
const characteristic = await service.getCharacteristic(characteristicUuid);
await characteristic.startNotifications();

In this example an attempt to update the CCCD is made by calling startNotifications() but an event listener is added to the BluetoothDevice object to catch all characteristic value change notifications that are delivered before that process is complete.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions