This document describes an API to discover and communicate with devices over the Bluetooth 4 wireless standard using the Generic Attribute Profile (GATT).
Changes to this document may be tracked at https://github.com/WebBluetoothCG/web-bluetooth/commits/gh-pages.
Bluetooth is a standard for short-range wireless communication between devices. Bluetooth "Classic" (BR/EDR) defines a set of binary protocols and supports speeds up to about 24Mbps. Bluetooth 4.0 introduced a new "Low Energy" mode known as "Bluetooth Smart", BLE, or just LE which is limited to about 1Mbps but allows devices to leave their transmitters off most of the time. BLE provides most of its functionality through key/value pairs provided by the Generic Attribute Profile (GATT).
BLE defines multiple roles that devices can play. The Broadcaster and Observer roles are for transmitter- and receiver-only applications, respectively. Devices acting in the Peripheral role can receive connections, and devices acting in the Central role can connect to Peripheral devices.
A device acting in either the Peripheral or Central role can host a GATT Server, which exposes a hierarchy of Services, Characteristics, and Descriptors. See for more details about this hierarchy. Despite being designed to support BLE transport, the GATT protocol can also run over BR/EDR transport.
The first version of this specification allows web pages, running on a UA in the Central role, to connect to GATT Servers over either a BR/EDR or LE connection. While this specification cites the [[BLUETOOTH42]] specification, it intends to also support communication among devices that only implement Bluetooth 4.0 or 4.1.
When a website requests access to devices using
requestDevice
,
it gets the ability to access all GATT services mentioned in the call.
The UA MUST inform the user what capabilities these services give the website
before asking which devices to entrust to it.
If any services in the list aren't known to the UA,
the UA MUST assume they give the site complete control over the device
and inform the user of this risk.
The UA MUST also allow the user to inspect what sites have access to what devices
and revoke these pairings.
The UA MUST NOT allow the user to pair entire classes of devices with a website. It is possible to construct a class of devices for which each individual device sends the same Bluetooth-level identifying information. UAs are not required to attempt to detect this sort of forgery and MAY let a user pair this pseudo-device with a website.
To help ensure that only the entity the user approved for access actually has access, this specification requires that only secure contexts can access Bluetooth devices (requestDevice).
Communication from websites can break the security model of some devices, which assume they only receive messages from the trusted operating system of a remote device. Human Interface Devices are a prominent example, where allowing a website to communicate would allow that site to log keystrokes. This specification includes a blacklist of such vulnerable services, characteristics, and descriptors to prevent websites from taking advantage of them.
We expect that many devices are vulnerable to unexpected data delivered to their radio. In the past, these devices had to be exploited one-by-one, but this API makes it plausible to conduct large-scale attacks. This specification takes several approaches to make such attacks more difficult:
On the other hand, GATT's Characteristic and Descriptor values are still byte arrays, which may be set to lengths and formats the device doesn't expect. UAs are encouraged to validate these values when they can.
UAs can also take further steps to protect their users:
Each Bluetooth BR/EDR device has a unique 48-bit MAC address known as the BD_ADDR. Each Bluetooth LE device has at least one of a Public Device Address and a Static Device Address. The Public Device Address is a MAC address. The Static Device Address may be regenerated on each restart. A BR/EDR/LE device will use the same value for the BD_ADDR and the Public Device Address (specified in the Read BD_ADDR Command).
An LE device may also have a unique, 128-bit Identity Resolving Key, which is sent to trusted devices during the bonding process. To avoid leaking a persistent identifier, an LE device may scan and advertise using a random Resolvable or Non-Resolvable Private Address instead of its Static or Public Address. These are regenerated periodically (approximately every 15 minutes), but a bonded device can check whether one of its stored IRKs matches any given Resolvable Private Address using the Resolvable Private Address Resolution Procedure.
Each Bluetooth device also has a human-readable Bluetooth Device Name.
These aren't guaranteed to be unique, but may well be, depending on the device type.
If a website can retrieve any of the persistent device IDs,
these can be used, in combination with a large effort to catalog ambient devices,
to discover a user's location.
A device ID can also be used to identify that a user who
pairs two different websites with the same Bluetooth device
is a single user.
On the other hand, many GATT services are available
that could be used to fingerprint a device,
and a device can easily expose a custom GATT service to make this easier.
This specification
generates a new device ID
for each origin for a given device,
which makes it difficult for websites to abuse the device address like this.
Device makers can still design their devices to help track users,
but it takes work.
In BR/EDR mode, or in LE mode during active scanning without the Privacy Feature,
the UA broadcasts its persistent ID to any nearby Bluetooth radio.
This makes it easy to scatter hostile devices in an area and track the UA.
As of 2014-08, few or no platforms document that they implement the Privacy Feature,
so despite this spec recommending it, few UAs are likely to use it.
This spec does require a user gesture
for a website to trigger a scan, which reduces the frequency of scans some,
but it would still be better for more platforms to expose the Privacy Feature.
Identifiers for remote Bluetooth devices
The UA's Bluetooth address
dictionary BluetoothScanFilter { sequence<BluetoothServiceUUID> services; }; dictionary RequestDeviceOptions { required sequence<BluetoothScanFilter> filters; sequence<BluetoothServiceUUID> optionalServices = []; }; interface Bluetooth { Promise<BluetoothDevice> requestDevice(RequestDeviceOptions options); }; Bluetooth implements EventTarget; Bluetooth implements CharacteristicEventHandlers; Bluetooth implements ServiceEventHandlers;
requestDevice(options)
asks the user
to grant this origin access to a device
that matches a filter in options.filters
.
The user will be shown devices that support
all the GATT service UUIDs in the services list
of any BluetoothScanFilter in filters
.
After the user selects a device to pair with this origin,
the origin is allowed to access to any service whose UUID was listed
in the services list
in any element of options.filters
or in options.optionalServices
.
A device matches a filter filter if
the UA has received advertising data, an extended inquiry response,
or a service discovery response indicating that
the device supports each of the Service UUIDs
included in filter.services
as a primary (vs included) service.
The list of Service UUIDs that a device advertises might not include all the UUIDs the device supports. The advertising data does specify whether this list is complete. If a website filters for a UUID that a nearby device supports but doesn't advertise, that device might not be included in the list of devices presented to the user. The UA would need to connect to the device to discover the full list of supported services, which can impair radio performance and cause delays, so this spec doesn't require it.
The requestDevice(options)
method,
when invoked, MUST return a new promise promise
and run the following steps in parallel:
options.filters
,
do the following steps:
filter.services.length === 0
,
reject promise with a TypeError
and abort these steps.
Array.prototype.map.call(filter.services,
BluetoothUUID.getService)
.
{services: services}
to uuidFilters.Array.prototype.map.call(options.optionalServices,
BluetoothUUID.getService)
.
The UA MAY allow the user to select a nearby device that does not match uuidFilters.
BluetoothDevice
representing device
and resolve promise with the result.
To scan for devices with an optional set of Service UUIDs, defaulting to the set of all UUIDs, the UA MUST perform the following steps:
Both passive scanning and the Privacy Feature avoid leaking the unique, immutable device ID. We ought to require UAs to use either one, but none of the OS APIs appear to expose either. Bluetooth also makes it hard to use passive scanning since it doesn't require Central devices to support the Observation Procedure.
All forms of BR/EDR inquiry/discovery appear to leak the unique, immutable device address.
For BR/EDR devices, there is no way to distinguish GATT from non-GATT services
in the Extended Inquiry Response.
If a site filters to the UUID of a non-GATT service,
the user may be able to select a device
for the result of requestDevice
that this API provides no way to interact with.
Connecting to every nearby device to discover services costs power and can slow down other use of the Bluetooth radio. UAs should only discover extra services on a device if they have some reason to expect that device to be interesting.
UAs should also help developers avoid relying on this extra discovery behavior. For example, say a developer has previously connected to a device, so the UA knows the device's full set of supported services. If this developer then filters using a non-advertised UUID, the dialog they see may include this device, even if the filter would likely exclude the device on users' machines. The UA could provide a developer option to warn when this happens or to include only advertised services in matching filters.
We need a way for a site to register to receive an event when an interesting device comes within range.
The UA needs to track Bluetooth device properties at several levels: globally, per origin, and per script execution environment.
The physical Bluetooth device may be guaranteed to have some properties that the UA may not have received. Those properties are described as optional here.
A Bluetooth device has the following properties. Optional properties are not present, and sequence and map properties are empty, unless/until described otherwise. Other properties have a default specified or are specified when a device is introduced.
The Bluetooth standard uses "pairing" to refer to creating and exchanging a shared secret, and "bonding" to refer to storing that secret for use in subsequent connections. Platform APIs don't give independent control of the two processes, and they present the combined process to users under the label "pairing", so this specification uses "pairing" and "bonding" interchangeably to refer to Bluetooth bonding.
The UA SHOULD determine that two Bluetooth devices are the same device if and only if they have the same Public Bluetooth Address, Static Address, Private Address, or Identity Resolving Key, or if the Resolvable Private Address Resolution Procedure succeeds using one device's IRK and the other's Resolvable Private Address. However, because platform APIs don't document how they determine device identity, the UA MAY use another procedure.
For each origin, the UA MUST maintain an allowed devices map,
whose keys are the Bluetooth devices the origin is allowed to access,
and whose values are pairs of a DOMString
device id and
an allowed services list consisting of UUIDs
for GATT Primary Services the origin is allowed to access on the device.
The UA MAY remove devices from the allowed devices map at any time based on signals from the user. This needs a definition involving removing BluetoothDevice instances from device instance maps and clearing out their [[\representedDevice]] fields. For example, if the user chooses not to remember access, the UA might remove a device when the tab that was granted access to it is closed. Or the UA might provide a revocation UI that allows the user to explicitly remove a device even while a tab is actively using that device. If a device is removed from this list while a Promise is pending to do something with the device, it MUST be treated the same as if the device moved out of Bluetooth range.
To add an allowed Bluetooth device device to an origin's allowed devices map with an optional allowed services list of UUIDs, the UA MUST run the following steps.
DOMString
that
isn't equal to any of the device ids in the origin's allowed devices map
and isn't equal to or derived from any of device's
Public Bluetooth Address, Static Address,
or Identity Resolving Key.
id MUST, with very high probability,
be unequal to the device ids used for keys
in any other origin's allowed devices map
or previously in this origin's allowed devices map
that are the same device as device.
A BluetoothDevice instance represents a Bluetooth device inside a particular script execution environment.
// Allocation authorities for Vendor IDs: enum VendorIDSource { "bluetooth", "usb" }; interface BluetoothDevice { readonly attribute DOMString id; readonly attribute DOMString? name; readonly attribute BluetoothAdvertisingData adData; readonly attribute unsigned long? deviceClass; readonly attribute VendorIDSource? vendorIDSource; readonly attribute unsigned long? vendorID; readonly attribute unsigned long? productID; readonly attribute unsigned long? productVersion; readonly attribute boolean paired; readonly attribute BluetoothGATTRemoteServer? gattServer; readonly attribute UUID[] uuids; Promise<BluetoothGATTRemoteServer> connectGATT(); }; BluetoothDevice implements EventTarget; BluetoothDevice implements CharacteristicEventHandlers; BluetoothDevice implements ServiceEventHandlers;
id uniquely identifies a device to the extent that the UA can determine that two Bluetooth connections are to the same device. It is computed as the device id in add an allowed Bluetooth device. This ID can't be used to match a device across origins or after the user revokes and re-grants access to the device.
name is the human-readable name of the device.
adData contains the most recent advertising data received for this device. TODO: Write the algorithm to update this when an advertising packet is received.
deviceClass is the class of the device, a bit-field defined by [[!BLUETOOTH-ASSIGNED-BASEBAND]].
vendorIDSource is the Vendor ID Source field in the pnp_id characteristic in the device_information service.
vendorID is the 16-bit Vendor ID field in the pnp_id characteristic in the device_information service.
productID is the 16-bit Product ID field in the pnp_id characteristic in the device_information service.
productVersion is the 16-bit Product Version field in the pnp_id characteristic in the device_information service.
paired indicates whether or not the device is paired with the system.
If the UA is currently connected to this device's GATT server,
gattServer provides a way to interact with it.
While this device is disconnected,
gattServer is null
.
uuids lists the UUIDs of GATT services known to be on the device, that the current origin is allowed to access.
For each script execution environment,
the UA must maintain a device instance map
mapping Bluetooth devices
to Promise<BluetoothDevice>
instances.
To get the BluetoothDevice
representing
a Bluetooth device device,
the UA MUST run the following steps:
null
.result@[[\representedDevice]]
field
to device.
result.id
to the device id this key maps to,
and initialize an internal
result@[[\allowedServices]]
field
to the allowed services list this key maps to.
result.name
to that string.
BluetoothAdvertisingData
from device
and result@[[\allowedServices]]
,
and set result.adData
to the result.
result.deviceClass
to that value.
1
or 2
,
abort these substeps.
result.vendorIDSource
according to the following table:
Parsed Vendor Id Source | vendorIDSource |
---|---|
1 | "bluetooth" |
2 | "usb" |
result.vendorID
to the value parsed for Vendor ID.
result.productID
to the value parsed for Product ID.
result.productVersion
to
the value parsed for Product Version.
result.paired
to
the value of device's bonded flag.
result.uuids
to
a read only Array holding the elements of uuids
that are also in result@[[\allowedServices]]
.
The connectGATT()
method, when invoked,
MUST return a new promise promise and
run the following steps in parallel:
this@[[\representedDevice]]
has no ATT Bearer,
attempt to create one using the procedures described
in "Connection Establishment" under GAP Interoperability Requirements.
this.gattServer
is null
,
set it to a new BluetoothGATTRemoteServer instance
with its device
attribute initialized to this
and its connected
attribute initialized to true
.
this.gattServer
.
interface BluetoothAdvertisingData { readonly attribute unsigned short? appearance; readonly attribute byte? txPower; readonly attribute byte? rssi; readonly attribute Map manufacturerData; readonly attribute Map serviceData; };
appearance is an Appearance, one of the values defined by the org.bluetooth.characteristic.gap.appearance characteristic.
txPower is
the transmission power at which the device is broadcasting, measured in dBm.
This is used to compute the path loss as this.txPower - this.rssi
.
rssi is
the power at which the device's packets are being received, measured in dBm.
This is used to compute the path loss as this.txPower - this.rssi
.
manufacturerData maps unsigned short
Company Identifier Codes
to ArrayBuffers.
serviceData maps UUIDs to ArrayBuffers.
All fields in BluetoothAdvertisingData return the last value they were initialized or set to.
To create a BluetoothAdvertisingData from a Bluetooth device device and an allowed services list, the UA must perform the following steps:
adData.appearance
to its value.
Otherwise, initialize it to null
.
adData.txPower
to its value.
Otherwise initialize it to null
.
adData.rssi
to its value.
Otherwise initialize it to null
.
adData.manufacturerData
with
the return value of these substeps:
adData.serviceData
with
the return value of these substeps:
The GATT Profile Hierarchy describes how a GATT Server contains a hierarchy of Profiles, Primary Services, Included Services, Characteristics, and Descriptors.
Profiles are purely logical: the specification of a Profile describes the expected interactions between the other GATT entities the Profile contains, but it's impossible to query which Profiles a device supports.
GATT Clients can discover and interact with the Services, Characteristics, and Descriptors on a device using a set of GATT procedures. This spec refers to Services, Characteristics, and Descriptors collectively as Attributes. All Attributes have a type that's identified by a UUID. Each Attribute also has a 16-bit Attribute Handle that distinguishes it from other Attributes of the same type on the same GATT Server. Attributes are notionally ordered within their GATT Server by their Attribute Handle, but while platform interfaces provide these entities in some order, they do not guarantee that it's consistent with the Attribute Handle order.
A Service contains a collection of Included Services and Characteristics. The Included Services are references to other Services, and a single Service can be included by more than one other Service. Services are known as Primary Services if they appear directly under the GATT Server, and Secondary Services if they're only included by other Services, but Primary Services can also be included.
A Characteristic contains a value, which is an array of bytes, and a collection of Descriptors. Depending on the properties of the Characteristic, a GATT Client can read or write its value, or register to be notified when the value changes.
Finally, a Descriptor contains a value (again an array of bytes) that describes or configures its Characteristic.
The UA MUST maintain a Bluetooth cache
of the hierarchy of Services, Characteristics, and Descriptors
it has discovered on a device.
The UA MAY share this cache between multiple origins accessing the same device.
Each potential entity in the cache is either known-present, known-absent, or unknown.
The cache MUST NOT contain two entities that are for the same attribute.
Each known-present entity in the cache is associated with an optional
Promise<BluetoothGATTService>
,
Promise<BluetoothGATTCharacteristic>
,
or Promise<BluetoothGATTDescriptor>
instance
for each script execution environment.
For example, if a user calls the serviceA.getCharacteristic(uuid1)
function
with an initially empty Bluetooth cache,
the UA uses the Discover Characteristics by UUID procedure
to fill the needed cache entries,
and the UA ends the procedure early because
it only needs one Characteristic to fulfil the returned Promise,
then the first Characteristic with UUID uuid1
inside serviceA
is known-present,
and any subsequent Characteristics with that UUID remain unknown.
If the user later calls serviceA.getCharacteristics(uuid1)
,
the UA needs to resume or restart the Discover Characteristics by UUID procedure.
If it turns out that
serviceA
only has one Characteristic with UUID uuid1
,
then the subsequent Characteristics become known-absent.
The known-present entries in the Bluetooth cache are ordered: Primary Services appear in a particular order within a device, Included Services and Characteristics appear in a particular order within Services, and Descriptors appear in a particular order within Characteristics. The order SHOULD match the order of Attribute Handles on the device, but UAs MAY use another order if the device's order isn't available.
To populate the Bluetooth cache with entries matching some description, the UA MUST run the following steps. Note that these steps can block, so uses of this algorithm must be in parallel.
To query the Bluetooth cache for entries matching some description, the UA MUST return a new promise promise and run the following steps in parallel:
Promise<BluetoothGATT*>
instance
for the current script execution environment,
create a BluetoothGATTService
representing entry,
create a BluetoothGATTCharacteristic
representing entry,
or create a BluetoothGATTDescriptor
representing entry,
depending on whether entry is a Service, Characteristic, or Descriptor,
and associate the resulting Promise
with entry.
Promise<BluetoothGATT*>
instance
associated with entry
for the current script execution environment.
This can happen if
a Service Changed indication arrives
while the new Promise<BluetoothGATT*>
instances are settling.
The algorithm restarts to make sure the result is consistent with
serviceadded, servicechanged, and serviceremoved events
that have already been delivered.
Service Changed indications are expected to be rare,
so this shouldn't cost too much.
To GetGATTChildren(attribute: GATT Attribute,
single: boolean,
uuidCanonicalizer: function,
uuid: optional (DOMString or unsigned int)
,
child type: GATT declaration type),
the UA MUST perform the following steps:
When checking whether two Services, Characteristics, or Descriptors a and b are the same attribute, the UA SHOULD determine that they are the same if a and b are inside the same device and have the same Attribute Handle, but MAY use any algorithm it wants with the constraint that a and b MUST NOT be considered the same attribute if they fit any of the following conditions:
This definition is loose because platform APIs expose their own notion of identity without documenting whether it's based on Attribute Handle equality.
For two Javascript objects representing Services, Characteristics, or Descriptors,
x === y
returns
whether the objects represent the same attribute,
because of how the query the Bluetooth cache algorithm
creates and caches new objects.
BluetoothGATTRemoteServer represents a GATT Server on a remote device.
interface BluetoothGATTRemoteServer { readonly attribute BluetoothDevice device; readonly attribute boolean connected; void disconnect(); Promise<BluetoothGATTService> getPrimaryService(BluetoothServiceUUID service); Promise<sequence<BluetoothGATTService>> getPrimaryServices(optional BluetoothServiceUUID service); }; BluetoothGATTRemoteServer implements EventTarget; BluetoothGATTRemoteServer implements CharacteristicEventHandlers; BluetoothGATTRemoteServer implements ServiceEventHandlers;
device is the device running this server.
connected is true while this script execution environment
is connected to this.device
.
It can be false while the UA is physically connected.
The disconnect()
method, when invoked,
MUST perform the following steps:
this.connected
to false
.
device
with device@[[\representedDevice]]
the same device as this.device@[[\representedDevice]]
,
device.gattServer === null
or !device.gattServer.connected
,
the UA MAY destroy
device@[[\representedDevice]]
's ATT Bearer.
The getPrimaryService(service)
method, when invoked,
MUST return
GetGATTChildren(attribute=this@[[\representedDevice]]
,
single=true,
uuidCanonicalizer=BluetoothUUID.getService
,
uuid=service
,
child type="GATT Primary Service")
The getPrimaryServices(service)
method, when invoked,
MUST return
GetGATTChildren(attribute=this@[[\representedDevice]]
,
single=false,
uuidCanonicalizer=BluetoothUUID.getService
,
uuid=service
,
child type="GATT Primary Service")
BluetoothGATTService represents a GATT Service, a collection of characteristics and relationships to other services that encapsulate the behavior of part of a device.
interface BluetoothGATTService { readonly attribute BluetoothDevice device; readonly attribute UUID uuid; readonly attribute boolean isPrimary; Promise<BluetoothGATTCharacteristic> getCharacteristic(BluetoothCharacteristicUUID characteristic); Promise<sequence<BluetoothGATTCharacteristic>> getCharacteristics(optional BluetoothCharacteristicUUID characteristic); Promise<BluetoothGATTService> getIncludedService(BluetoothServiceUUID service); Promise<sequence<BluetoothGATTService>> getIncludedServices(optional BluetoothServiceUUID service); }; BluetoothGATTService implements EventTarget; BluetoothGATTService implements CharacteristicEventHandlers; BluetoothGATTService implements ServiceEventHandlers;
device is the BluetoothDevice representing the remote peripheral that the GATT service belongs to.
uuid is the UUID of the service,
e.g. '0000180d-0000-1000-8000-00805f9b34fb'
for the
Heart Rate service.
isPrimary indicates whether the type of this service is primary or secondary.
To create a BluetoothGATTService
representing
a Service service,
the UA must return a new promise promise
and run the following steps in parallel.
BluetoothDevice
representing
the device in which service appears,
and let devicePromise be the result.
result.device
from
the value of devicePromise.
result.uuid
from the UUID of service.
result.isPrimary
to true.
Otherwise initialize result.isPrimary
to false.
The getCharacteristic(characteristic)
method
retrieves a Characteristic inside this Service.
When invoked, it MUST return
GetGATTChildren(attribute=this
,
single=true,
uuidCanonicalizer=BluetoothUUID.getCharacteristic
,
uuid=characteristic
,
child type="GATT Characteristic")
The getCharacteristics(characteristic)
method
retrieves a list of Characteristics inside this Service.
When invoked, it MUST return
GetGATTChildren(attribute=this
,
single=false,
uuidCanonicalizer=BluetoothUUID.getCharacteristic
,
uuid=characteristic
,
child type="GATT Characteristic")
The getIncludedService(service)
method
retrieves an Included Service inside this Service.
When invoked, it MUST return
GetGATTChildren(attribute=this
,
single=true,
uuidCanonicalizer=BluetoothUUID.getService
,
uuid=service
,
child type="GATT Included Service")
The getIncludedServices(service)
method
retrieves a list of Included Services inside this Service.
When invoked, it MUST return
GetGATTChildren(attribute=this
,
single=false,
uuidCanonicalizer=BluetoothUUID.getService
,
uuid=service
,
child type="GATT Included Service")
BluetoothGATTCharacteristic represents a GATT Characteristic, which is a basic data element that provides further information about a peripheral's service.
interface BluetoothGATTCharacteristic { readonly attribute BluetoothGATTService service; readonly attribute UUID uuid; readonly attribute CharacteristicProperties properties; readonly attribute ArrayBuffer? value; Promise<BluetoothGATTDescriptor> getDescriptor(BluetoothDescriptorUUID descriptor); Promise<sequence<BluetoothGATTDescriptor>> getDescriptors(optional BluetoothDescriptorUUID descriptor); Promise<ArrayBuffer> readValue(); Promise<void> writeValue(BufferSource value); Promise<void> startNotifications(); Promise<void> stopNotifications(); }; BluetoothGATTCharacteristic implements EventTarget; BluetoothGATTCharacteristic implements CharacteristicEventHandlers;
service is the GATT service this characteristic belongs to.
uuid is the UUID of the characteristic,
e.g. '00002a37-0000-1000-8000-00805f9b34fb'
for the
Heart Rate Measurement characteristic.
properties holds the properties of this characteristic.
value is the currently cached characteristic value. This value gets updated when the value of the characteristic is read or updated via a notification or indication.
To create a BluetoothGATTCharacteristic
representing
a Characteristic characteristic,
the UA must return a new promise promise
and run the following steps in parallel.
result.service
from
the BluetoothGATTService instance representing
the Service in which characteristic appears.
result.uuid
from
the UUID of characteristic.
CharacteristicProperties
instance
from the Characteristic characteristic,
and let propertiesPromise be the result.
result.properties
from
the value propertiesPromise was fulfilled with.
result.value
to null
.
The UA MAY initialize result.value
to
a new ArrayBuffer
containing
the most recently read value from characteristic
if this value is available.
The getDescriptor(descriptor)
method
retrieves a Descriptor inside this Characteristic.
When invoked, it MUST return
GetGATTChildren(attribute=this
,
single=true,
uuidCanonicalizer=BluetoothUUID.getDescriptor
,
uuid=descriptor
,
child type="GATT Descriptor")
The getDescriptors(descriptor)
method
retrieves a list of Descriptors inside this Characteristic.
When invoked, it MUST return
GetGATTChildren(attribute=this
,
single=false,
uuidCanonicalizer=BluetoothUUID.getDescriptor
,
uuid=descriptor
,
child type="GATT Descriptor")
The readValue()
method, when invoked,
MUST return a new promise promise
and run the following steps in parallel:
this.uuid
is blacklisted for reads,
return a promise rejected with a SecurityError
and abort these steps.
this
represents.
Read
bit is not set
in characteristic's properties,
reject promise with a NotSupportedError and abort these steps.
ArrayBuffer
holding the retrieved value,
and assign it to this.value
.
this.value
.
characteristicvaluechanged
with its bubbles
attribute initialized to true
at this
.
The writeValue(value)
method, when invoked,
MUST run the following steps:
this.uuid
is blacklisted for writes,
return a promise rejected with a SecurityError
and abort these steps.
this
represents.
value
.
this.value
to
a new ArrayBuffer containing bytes.
undefined
.
For each known GATT Characteristic, the UA MUST maintain
an active notification context set of Bluetooth objects.
This is a single set for the whole UA,
pointing to the navigator.bluetooth
object
for each separate script execution environment that has registered for notifications.
The startNotifications()
method, when invoked,
MUST return a new promise promise
and run the following steps in parallel.
See for details of receiving notifications.
this.uuid
is blacklisted for reads,
reject promise with a SecurityError and abort these steps.
this
represents.
Notify
or Indicate
bits are set
in characteristic's properties,
reject promise with a NotSupportedError and abort these steps.
navigator.bluetooth
,
resolve promise with undefined
and abort these steps.
Notification
or Indication
bits in
characteristic's Client Characteristic Configuration descriptor
is set, matching the constraints
in characteristic's properties.
The UA SHOULD avoid setting both bits,
and MUST deduplicate value-change events
if both bits are set.
Handle errors as described in .
navigator.bluetooth
to the active notification context set.
undefined
.
After notifications are enabled,
the resulting value-change events won't be delivered
until after the current
microtask checkpoint.
This allows a developer to set up handlers
in the .then
handler of the result promise.
The stopNotifications()
method, when invoked,
MUST return a new promise promise
and run the following steps in parallel:
this
represents.
navigator.bluetooth
,
remove it.
Notification
and Indication
bits in
characteristic's Client Characteristic Configuration descriptor.
undefined
.
Queuing a task to resolve the promise ensures that no value change events due to notifications arrive after the promise resolves.
Each BluetoothGATTCharacteristic exposes its characteristic properties through a CharacteristicProperties object. These properties express what operations are valid on the characteristic.
interface BluetoothCharacteristicProperties { readonly attribute boolean broadcast; readonly attribute boolean read; readonly attribute boolean writeWithoutResponse; readonly attribute boolean write; readonly attribute boolean notify; readonly attribute boolean indicate; readonly attribute boolean authenticatedSignedWrites; readonly attribute boolean reliableWrite; readonly attribute boolean writableAuxiliaries; };
To create a CharacteristicProperties
instance
from the Characteristic characteristic,
the UA MUST return a new promise promise
and run the following steps in parallel:
Attribute | Bit |
---|---|
broadcast | Broadcast |
read | Read |
writeWithoutResponse | Write Without Response |
write | Write |
notify | Notify |
indicate | Indicate |
authenticatedSignedWrites |
Authenticated Signed Writes |
propertiesObj.reliableWrite
and
propertiesObj.writableAuxiliaries
to false
.
Otherwise, run the following steps:
Characteristic Extended Properties isn't clear whether the extended properties are immutable for a given Characteristic. If they are, the UA should be allowed to cache them.
propertiesObj.reliableWrite
from
the Reliable Write bit of extendedProperties.
propertiesObj.writableAuxiliaries
from
the Writable Auxiliaries bit of extendedProperties.
BluetoothGATTDescriptor represents a GATT Descriptor, which provides further information about a Characteristic's value.
interface BluetoothGATTDescriptor { readonly attribute BluetoothGATTCharacteristic characteristic; readonly attribute UUID uuid; readonly attribute ArrayBuffer? value; Promise<ArrayBuffer> readValue(); Promise<void> writeValue(BufferSource value); };
characteristic is the GATT characteristic this descriptor belongs to.
uuid is the UUID of the characteristic descriptor,
e.g. '00002902-0000-1000-8000-00805f9b34fb'
for the
Client Characteristic Configuration descriptor.
value is the currently cached descriptor value. This value gets updated when the value of the descriptor is read.
To create a BluetoothGATTDescriptor
representing
a Descriptor descriptor,
the UA must return a new promise promise
and run the following steps in parallel.
result.characteristic
from
the BluetoothGATTCharacteristic instance representing
the Characteristic in which descriptor appears.
result.uuid
from the UUID of descriptor.
result.value
to null
.
The UA MAY initialize result.value
to
a new ArrayBuffer
containing
the most recently read value from descriptor
if this value is available.
The readValue()
method, when invoked,
MUST return a new promise promise
and run the following steps in parallel:
this.uuid
is blacklisted for reads,
return a promise rejected with a SecurityError
and abort these steps.
this
represents.
ArrayBuffer
holding the retrieved value,
and assign it to this.value
.
this.value
.
The writeValue(value)
method, when invoked,
MUST run the following steps:
this.uuid
is blacklisted for writes,
return a promise rejected with a SecurityError
and abort these steps.
this
represents.
value
.
this.value
to
a new ArrayBuffer containing bytes.
undefined
.
navigator.bluetooth
and
objects implementing the BluetoothDevice, BluetoothGATTService,
BluetoothGATTCharacteristic, or BluetoothGATTDescriptor interface
participate in a tree,
simply named the Bluetooth tree.
navigator.bluetooth
are the BluetoothDevice objects representing
devices on the origin's allowed devices map,
in an unspecified order.
characteristicvaluechanged
serviceadded
servicechanged
serviceremoved
When the UA receives a Bluetooth Characteristic Value Notification or Indication, it must perform the following steps:
characteristicObject.value
to
a new ArrayBuffer
holding the new value of the Characteristic.
characteristicvaluechanged
with its bubbles
attribute initialized to true
at characteristicObject.
The Bluetooth Attribute Caching system allows clients to track changes to Services, Characteristics, and Descriptors. Before discovering any of these entities for the purpose of exposing them to a web page the UA MUST subscribe to Indications from the Service Changed characteristic, if it exists. When the UA receives an Indication on the Service Changed characteristic, it MUST perform the following steps.
getPrimaryService
, getPrimaryServices
,
getIncludedService
, or getIncludedServices
,
the UA MAY remove the Service from addedEntities.
serviceremoved
with its bubbles
attribute initialized to true
at the BluetoothGATTService representing the Service.
Then remove this BluetoothGATTService from the Bluetooth tree.
serviceadded
with its bubbles
attribute initialized to true
at the BluetoothGATTService.
servicechanged
with its bubbles
attribute initialized to true
at the BluetoothGATTService representing the Service.
[NoInterfaceObject] interface CharacteristicEventHandlers { attribute EventHandler oncharacteristicvaluechanged; };
oncharacteristicvaluechanged
is an Event handler IDL attribute
for the characteristicvaluechanged
event type.
[NoInterfaceObject] interface ServiceEventHandlers { attribute EventHandler onserviceadded; attribute EventHandler onservicechanged; attribute EventHandler onserviceremoved; };
onserviceadded
is an Event handler IDL attribute
for the serviceadded
event type.
onservicechanged
is an Event handler IDL attribute
for the servicechanged
event type.
onserviceremoved
is an Event handler IDL attribute
for the serviceremoved
event type.
This section primarily defines the mapping from system errors to Javascript error names and allows UAs to retry certain operations. The retry logic and possible error distinctions are highly constrained by the operating system, so places these requirements don't reflect reality are likely spec bugs instead of browser bugs.
When the UA is using a GATT procedure
to execute a step in an algorithm or to handle a query to the Bluetooth cache
(both referred to as a "step", here),
and the GATT procedure returns an Error Response
,
the UA MUST perform the following steps:
Error Code
:
Invalid PDU
Invalid Offset
Attribute Not Found
Unsupported Group Type
Invalid Handle
Return an InvalidStateError from the step.
Invalid Attribute Value Length
Attribute Not Long
If this error code is received without having used a "Long" sub-procedure, this may indicate a device bug. Return a NotSupportedError from the step.
Otherwise, retry the step without using a "Long" sub-procedure. If this is impossible due to the length of the value being written, return an InvalidModificationError from the step.
Insufficient Authentication
Insufficient Encryption
Insufficient Encryption Key Size
Insufficient Authorization
Application Error
Read Not Permitted
Write Not Permitted
Request Not Supported
Prepare Queue Full
Insufficient Resources
Unlikely Error
typedef DOMString UUID;
A UUID string represents a 128-bit [[!RFC4122]] UUID.
A valid UUID is a string that matches
the [[!ECMAScript]] regexp
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
.
That is, a valid UUID is lower-case and
does not use the 16- or 32-bit abbreviations defined by the Bluetooth standard.
All UUIDs returned from functions and attributes in this specification
MUST be valid UUIDs.
If a function in this specification takes a parameter whose type is UUID
or a dictionary including a UUID attribute,
and the argument passed in any UUID slot is not a valid UUID,
the function MUST return a promise rejected with a TypeError
and abort its other steps.
This standard provides the
BluetoothUUID.canonicalUUID(alias)
function
to map a 16- or 32-bit Bluetooth UUID alias to its 128-bit form.
Bluetooth devices are required to convert 16- and 32-bit UUIDs to 128-bit UUIDs before comparing them (as described in Attribute Type), but not all devices do so. To interoperate with these devices, if the UA has received a UUID from the device in one form (16-, 32-, or 128-bit), it should send other aliases of that UUID back to the device in the same form.
The Bluetooth SIG maintains a registry at [[BLUETOOTH-ASSIGNED]] of UUIDs that identify services, characteristics, descriptors, and other entities. This section provides a way for script to look up those UUIDs by name so they don't need to be replicated in each application.
interface BluetoothUUID { static UUID getService((DOMString or unsigned long) name); static UUID getCharacteristic((DOMString or unsigned long) name); static UUID getDescriptor((DOMString or unsigned long) name); static UUID canonicalUUID([EnforceRange] unsigned long alias); }; typedef (DOMString or unsigned long) BluetoothServiceUUID; typedef (DOMString or unsigned long) BluetoothCharacteristicUUID; typedef (DOMString or unsigned long) BluetoothDescriptorUUID;
The static BluetoothUUID.canonicalUUID(alias)
method, when invoked,
MUST return the 128-bit UUID represented
by the 16- or 32-bit UUID alias alias.
This algorithm consists of
replacing the top 32 bits of "00000000-0000-1000-8000-00805f9b34fb
"
with the bits of the alias.
For example, canonicalUUID(0xDEADBEEF)
returns
"deadbeef-0000-1000-8000-00805f9b34fb"
.
BluetoothServiceUUID represents 16- and 32-bit UUID aliases, valid UUIDs, and names defined in [[BLUETOOTH-ASSIGNED-SERVICES]], or, equivalently, the values for which BluetoothUUID.getService does not throw an exception.
The static BluetoothUUID.getService(name)
method, when invoked,
MUST run the following steps:
unsigned long
,
return BluetoothUUID.canonicalUUID(name)
and abort these steps.
org.bluetooth.service.name
appears in [[!BLUETOOTH-ASSIGNED-SERVICES]],
let alias be its assigned number,
and return BluetoothUUID.canonicalUUID(alias)
.
BluetoothCharacteristicUUID represents 16- and 32-bit UUID aliases, valid UUIDs, and names defined in [[BLUETOOTH-ASSIGNED-CHARACTERISTICS]], or, equivalently, the values for which BluetoothUUID.getCharacteristic does not throw an exception.
The static BluetoothUUID.getCharacteristic(name)
method, when invoked,
MUST run the following steps:
unsigned long
,
return BluetoothUUID.canonicalUUID(name)
and abort these steps.
org.bluetooth.characteristic.name
appears in [[!BLUETOOTH-ASSIGNED-CHARACTERISTICS]],
let alias be its assigned number,
and return BluetoothUUID.canonicalUUID(alias)
.
BluetoothDescriptorUUID represents 16- and 32-bit UUID aliases, valid UUIDs, and names defined in [[BLUETOOTH-ASSIGNED-DESCRIPTORS]], or, equivalently, the values for which BluetoothUUID.getDescriptor does not throw an exception.
The static BluetoothUUID.getDescriptor(name)
method, when invoked,
MUST run the following steps:
unsigned long
,
return BluetoothUUID.canonicalUUID(name)
and abort these steps.
org.bluetooth.descriptor.name
appears in [[!BLUETOOTH-ASSIGNED-DESCRIPTORS]],
let alias be its assigned number,
and return BluetoothUUID.canonicalUUID(alias)
.
This specification relies on a blacklist file in the https://github.com/WebBluetoothCG/registries repository to restrict the set of GATT attributes a website can access.
The result of parsing the blacklist at a URL url is a map from valid UUIDs to tokens, or an error, produced by the following algorithm:
'\n'
.'#'
,
continue to the next line.
exclude
".
exclude-reads
" or "exclude-writes
",
let uuid be that UUID and
let token be that token.
The GATT blacklist is the result of parsing the blacklist at https://github.com/WebBluetoothCG/registries/blob/master/gatt_blacklist.txt. The UA should re-fetch the blacklist periodically, but it's unspecified how often.
A UUID is blacklisted if either
the GATT blacklist's value is an error,
or the UUID maps to "exclude
" in the GATT blacklist.
A UUID is blacklisted for reads if either
the GATT blacklist's value is an error,
or the UUID maps to either "exclude
" or "exclude-reads
"
in the GATT blacklist.
A UUID is blacklisted for writes if either
the GATT blacklist's value is an error,
or the UUID maps to either "exclude
" or "exclude-writes
"
in the GATT blacklist.
partial interface Navigator { readonly attribute Bluetooth bluetooth; };
This specification uses a few conventions and several terms from other specifications. This section lists those and links to their primary definitions.
Inspired by the Streams specification, we use the notation x@[[\y]] to refer to internal slots of an object, instead of saying "the [[\y]] internal slot of x."
When an algorithm in this specification uses a name defined in this or another specification,
the name MUST resolve to its initial value,
ignoring any changes that have been made to the name in the current execution environment.
For example, when the requestDevice algorithm says to call
Array.prototype.map.call(filter.services,
BluetoothUUID.getService)
,
this MUST apply the
Array.prototype.map
algorithm defined in [[ECMAScript]]
with filter.services
as its this
parameter and
the algorithm defined in for BluetoothUUID.getService
as its callbackfn
parameter,
regardless of any modifications that have been made to window
,
Array
, Array.prototype
, Array.prototype.map
,
Function
, Function.prototype
, BluetoothUUID
,
BluetoothUUID.getService
, or other objects.
This specification uses a few read-only types that are similar to WebIDL's read only Array.
clear
, delete
, and set
methods
are omitted.