This document describes an API to discover and communicate with devices over the Bluetooth 4 wireless standard using the Generic Attribute Profile (GATT).

If you wish to make comments regarding this document, please send them to public-web-bluetooth@w3.org ( subscribe , archives ).

Changes to this document may be tracked at https://github.com/WebBluetoothCG/web-bluetooth/commits/gh-pages .

This specification was published by the Web Bluetooth Community Group . It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups .

1. Introduction

This section is non-normative.

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 § 5.1 GATT Information Model 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.

1.1. Examples

let chosenHeartRateService = null ; navigator . bluetooth . requestDevice ({ filters : [{ services : [ 'heart_rate' ], }] }). then ( device => device . gatt . connect () ) . then ( server => server . getPrimaryService ( 'heart_rate' )) . then ( service => { chosenHeartRateService = service ; return Promise . all ([ service . getCharacteristic ( 'body_sensor_location' ) . then ( handleBodySensorLocationCharacteristic ), service . getCharacteristic ( 'heart_rate_measurement' ) . then ( handleHeartRateMeasurementCharacteristic ), ]); }); function handleBodySensorLocationCharacteristic ( characteristic ) { if ( characteristic === null ) { console . log ( "Unknown sensor location." ); return Promise . resolve (); } return characteristic . readValue () . then ( sensorLocationData => { const sensorLocation = sensorLocationData . getUint8 ( 0 ); switch ( sensorLocation ) { case 0 : return 'Other' ; case 1 : return 'Chest' ; case 2 : return 'Wrist' ; case 3 : return 'Finger' ; case 4 : return 'Hand' ; case 5 : return 'Ear Lobe' ; case 6 : return 'Foot' ; default : return 'Unknown' ; } }). then ( location => console . log ( location )); } function handleHeartRateMeasurementCharacteristic ( characteristic ) { return characteristic . startNotifications () . then ( char => { characteristic . addEventListener ( ' characteristicvaluechanged ' , onHeartRateChanged ); }); } function onHeartRateChanged ( event ) { const characteristic = event . target ; console . log ( parseHeartRate ( characteristic . value )); } To discover and retrieve data from a standard heart rate monitor, a website would use code like the following: parseHeartRate() would be defined using the heart_rate_measurement documentation to read the DataView stored in a BluetoothRemoteGATTCharacteristic 's value field. function parseHeartRate ( data ) { const flags = data . getUint8 ( 0 ); const rate16Bits = flags & 0x1 ; const result = {}; let index = 1 ; if ( rate16Bits ) { result . heartRate = data . getUint16 ( index , /*littleEndian=*/ true ); index += 2 ; } else { result . heartRate = data . getUint8 ( index ); index += 1 ; } const contactDetected = flags & 0x2 ; const contactSensorPresent = flags & 0x4 ; if ( contactSensorPresent ) { result . contactDetected = !! contactDetected ; } const energyPresent = flags & 0x8 ; if ( energyPresent ) { result . energyExpended = data . getUint16 ( index , /*littleEndian=*/ true ); index += 2 ; } const rrIntervalPresent = flags & 0x10 ; if ( rrIntervalPresent ) { const rrIntervals = []; for (; index + 1 < data . byteLength ; index += 2 ) { rrIntervals . push ( data . getUint16 ( index , /*littleEndian=*/ true )); } result . rrIntervals = rrIntervals ; } return result ; } onHeartRateChanged() might log an object like { heartRate : 70 , contactDetected : true , energyExpended : 750 , // Meaning 750kJ. rrIntervals : [ 890 , 870 ] // Meaning .87s and .85s. } If the heart rate sensor reports the energyExpended field, the web application can reset its value to 0 by writing to the heart_rate_control_point characteristic: function resetEnergyExpended () { if ( ! chosenHeartRateService ) { return Promise . reject ( new Error ( 'No heart rate sensor selected yet.' )); } return chosenHeartRateService . getCharacteristic ( 'heart_rate_control_point' ) . then ( controlPoint => { const resetEnergyExpended = new Uint8Array ([ 1 ]); return controlPoint . writeValue ( resetEnergyExpended ); }); }

2. Security and privacy considerations

2.1. Device access is powerful

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.

2.2. Trusted servers can serve malicious code

This section is non-normative.

Even if the user trusts an origin, that origin’s servers or developers could be compromised, or the origin’s site could be vulnerable to XSS attacks. Either could lead to users granting malicious code access to valuable devices. Origins should define a Content Security Policy ([CSP3]) to reduce the risk of XSS attacks, but this doesn’t help with compromised servers or developers.

The ability to retrieve granted devices after a page reload, provided by § 3.1 Permission API Integration, makes this risk worse. Instead of having to get the user to grant access while the site is compromised, the attacker can take advantage of previously-granted devices if the user simply visits while the site is compromised. On the other hand, when sites can keep access to devices across page reloads, they don’t have to show as many permission prompts overall, making it more likely that users will pay attention to the prompts they do see.

2.3. Attacks on devices

This section is non-normative.

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 GATT blocklist 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:

Pairing individual devices instead of device classes requires at least a user action before a device can be exploited.

Constraining access to GATT, as opposed to generic byte-stream access, denies malicious websites access to most parsers on the device. 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.

This API never exposes Bluetooth addressing, data signing or encryption keys (Definition of Keys and Values) to websites. This makes it more difficult for a website to predict the bits that will be sent over the radio, which blocks packet-in-packet injection attacks. Unfortunately, this only works over encrypted links, which not all BLE devices are required to support.

UAs can also take further steps to protect their users:

A web service may collect lists of malicious websites and vulnerable devices. UAs can deny malicious websites access to any device and any website access to vulnerable devices.

2.4. Bluetooth device identifiers

This section is non-normative.

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.

2.4.1. Identifiers for remote Bluetooth devices

This section is non-normative.

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 suggests that the UA use different device IDs for a single device when its user doesn’t intend scripts to learn that it’s a single 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.

2.4.2. The UA’s Bluetooth address

This section is non-normative.

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.

2.5. Exposing Bluetooth availability

This section is non-normative.

navigator.bluetooth. getAvailability() exposes whether a Bluetooth radio is available on the user’s system, regardless of whether it is powered on or not. The availability is also affected if the user has configured the UA to block Web Bluetooth. Some users might consider this private, although it’s hard to imagine the damage that would result from revealing it. This information also increases the UA’s fingerprinting surface by a bit. This function returns a Promise , so UAs have the option of asking the user what value they want to return, but we expect the increased risk to be small enough that UAs will choose not to prompt.

3. Device Discovery

dictionary BluetoothDataFilterInit { BufferSource dataPrefix ; BufferSource mask ; }; dictionary BluetoothLEScanFilterInit { sequence < BluetoothServiceUUID > services ; DOMString name ; DOMString namePrefix ; // Maps unsigned shorts to BluetoothDataFilters. object manufacturerData ; // Maps BluetoothServiceUUIDs to BluetoothDataFilters. object serviceData ; }; dictionary RequestDeviceOptions { sequence < BluetoothLEScanFilterInit > filters ; sequence < BluetoothServiceUUID > optionalServices = []; boolean acceptAllDevices = false ; }; [ Exposed = Window , SecureContext ] interface Bluetooth : EventTarget { Promise < boolean > getAvailability (); attribute EventHandler onavailabilitychanged ; [ SameObject ] readonly attribute BluetoothDevice ? referringDevice ; Promise < sequence < BluetoothDevice >> getDevices (); Promise < BluetoothDevice > requestDevice ( optional RequestDeviceOptions options = {}); }; Bluetooth includes BluetoothDeviceEventHandlers ; Bluetooth includes CharacteristicEventHandlers ; Bluetooth includes ServiceEventHandlers ;

Bluetooth members NOTE:members getAvailability() informs the page whether Bluetooth is available at all. An adapter that’s disabled through software should count as available. Changes in availability, for example when the user physically attaches or detaches an adapter, are reported through the availabilitychanged event. Note:informs the page whether Bluetooth is available at all. An adapter that’s disabled through software should count as available. Changes in availability, for example when the user physically attaches or detaches an adapter, are reported through theevent. referringDevice gives access to the device from which the user opened this page, if any. For example, an Eddystone beacon might advertise a URL, which the UA allows the user to open. A BluetoothDevice representing the beacon would be available through navigator.bluetooth. referringDevice . getDevices() enables the page to retrieve Bluetooth devices that the user has granted access to. requestDevice(options) asks the user to grant this origin access to a device that matches any filter in options. filters . To match a filter, the device has to: support all the GATT service UUIDs in the services list if that member is present,

have a name equal to name if that member is present,

have a name starting with namePrefix if that member is present,

manufacturerData if that member is present, and advertise manufacturer specific data matching all of the key/value pairs inif that member is present, and

serviceData if that member is present. advertise service data matching all of the key/value pairs inif that member is present. Both Manufacturer Specific Data and Service Data map a key to an array of bytes. BluetoothDataFilterInit filters these arrays. An array matches if it has a prefix such that prefix & mask is equal to dataPrefix & mask . Note that if a device changes its behavior significantly when it connects, for example by not advertising its identifying manufacturer data anymore and instead having the client discover some identifying GATT services, the website may need to include filters for both behaviors. In rare cases, a device may not advertise enough distinguishing information to let a site filter out uninteresting devices. In those cases, a site can set acceptAllDevices to true and omit all filters . This puts the burden of selecting the right device entirely on the site’s users. If a site uses acceptAllDevices , it will only be able to use services listed in optionalServices . After the user selects a device to pair with this origin, the origin is allowed to access any service whose UUID was listed in the services list in any element of options.filters or in options. optionalServices . This implies that if developers filter just by name, they must use optionalServices to get access to any services.

Device Advertised Services D1 A, B, C, D D2 A, B, E D3 C, D D4 E D5 <none> Say the UA is close to the following devices: If the website calls navigator . bluetooth . requestDevice ({ filters : [ { services : [ A , B ]} ] }); the user will be shown a dialog containing devices D1 and D2. If the user selects D1, the website will not be able to access services C or D. If the user selects D2, the website will not be able to access service E. On the other hand, if the website calls navigator . bluetooth . requestDevice ({ filters : [ { services : [ A , B ]}, { services : [ C , D ]} ] }); the dialog will contain devices D1, D2, and D3, and if the user selects D1, the website will be able to access services A, B, C, and D. If the website then calls navigator . bluetooth . getDevices (); the resulting Promise will resolve into an array containing device D1, and the website will be able to access services A, B, C, and D. The optionalServices list doesn’t add any devices to the dialog the user sees, but it does affect which services the website can use from the device the user picks. navigator . bluetooth . requestDevice ({ filters : [ { services : [ A , B ]} ], optionalServices : [ E ] }); Shows a dialog containing D1 and D2, but not D4, since D4 doesn’t contain the required services. If the user selects D2, unlike in the first example, the website will be able to access services A, B, and E. If the website calls navigator . bluetooth . getDevices (); again, then the resulting Promise will resolve into an array containing the devices D1 and D2. The A, B, C, and D services will be accessible on device D1, while A, B, and E services will be accessible on device D2. The allowed services also apply if the device changes after the user grants access. For example, if the user selects D1 in the previous requestDevice() call, and D1 later adds a new E service, that will fire the serviceadded event, and the web page will be able to access service E.

Device Advertised Device Name D1 First De… D2 <none> D3 Device Third D4 Device Fourth D5 Unique Name Say the devices in the previous example also advertise names as follows: The following table shows which devices the user can select between for several values of filters passed to navigator.bluetooth.requestDevice({filters: filters }) . filters Devices Notes [{ name : "Unique Name" }] D5 [{ namePrefix : "Device" }] D3, D4 [{ name : "First De" }, { name : "First Device" }] <none> D1 only advertises a prefix of its name, so trying to match its whole name fails. [{ namePrefix : "First" }, { name : "Unique Name" }] D1, D5 [{ services : [ C ], namePrefix : "Device" }, { name : "Unique Name" }] D3, D5

Device Manufacturer Data Service Data D1 17: 01 02 03 D2 A: 01 02 03 Say the devices in the previous example also advertise manufacturer or service data as follows: The following table shows which devices the user can select between for several values of filters passed to navigator.bluetooth.requestDevice({filters: filters }) . filters Devices [{ manufacturerData : { 17 : {}}}] D1 [{ serviceData : { "A" : {}}}] D2 [{ manufacturerData : { 17 : {}}}, { serviceData : { "A" : {}}}] D1, D2 [{ manufacturerData : { 17 : {}}, serviceData : { "A" : {}}}] <none> [{ manufacturerData : { 17 : { dataPrefix : new Uint8Array ([ 1 , 2 , 3 ])}, }}] D1 [{ manufacturerData : { 17 : { dataPrefix : new Uint8Array ([ 1 , 2 , 3 , 4 ])}, }}] <none> [{ manufacturerData : { 17 : { dataPrefix : new Uint8Array ([ 1 ])}, }}] D1 [{ manufacturerData : { 17 : { dataPrefix : new Uint8Array ([ 0x91 , 0xAA ]), mask : new Uint8Array ([ 0x0F , 0x57 ])}, }}] D1 [{ manufacturerData : { 17 : {}, 18 : {}, }}] <none>

TypeError s. To accept all devices, use acceptAllDevices instead. Call Notes requestDevice ({}) Invalid: An absent list of filters doesn’t accept any devices. requestDevice ({ filters : []}) Invalid: An empty list of filters doesn’t accept any devices. requestDevice ({ filters : [{}]}) Invalid: An empty filter accepts all devices, and so isn’t allowed either. requestDevice ({ acceptAllDevices : true }) Valid: Explicitly accept all devices with acceptAllDevices . requestDevice ({ filters : [...], acceptAllDevices : true }) Invalid: acceptAllDevices would override any filters . requestDevice ({ filters : [{ namePrefix : "" }] }) Invalid: namePrefix , if present, must be non-empty to filter devices. requestDevice ({ filters : [{ manufacturerData : {}}] }) Invalid: manufacturerData , if present, must be non-empty to filter devices. requestDevice ({ filters : [{ serviceData : {}}] }) Invalid: serviceData , if present, must be non-empty to filter devices. Filters that either accept or reject all possible devices causes. To accept all devices, useinstead.

Instances of Bluetooth are created with the internal slots described in the following table:

data matches a BluetoothDataFilterInit filter if the following steps return match . filter has already been Note: This algorithm assumes thathas already been canonicalized Let expectedPrefix be a copy of the bytes held by filter . dataPrefix . Let mask be a copy of the bytes held by filter . mask . If data has fewer bytes than expectedPrefix , return mismatch . For each 1 bit in mask , if the corresponding bit in data is not equal to the corresponding bit in expectedPrefix , return mismatch . Return match . An array of bytesif the following steps return

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.

getDevices() method, when invoked and given a BluetoothPermissionStorage storage , MUST return promise and run the following steps Let devices be a new empty Array . For each allowedDevice in storage . allowedDevices , add the BluetoothDevice object representing allowedDevice @ [[device]] to devices . Resolve promise with devices . BluetoothDevice s in devices may not be in range of the Bluetooth radio. For a given device in devices , the watchAdvertisements() method can be used to observe when device is in range and broadcasting advertisement packets. When an advertisementreceived event event is fired on a device , it may indicate that it is close enough for a connection to be established by calling event .device.gatt. connect() . Note: Thes inmay not be in range of the Bluetooth radio. For a givenin, themethod can be used to observe whenis in range and broadcasting advertisement packets. When aneventis fired on a, it may indicate that it is close enough for a connection to be established by calling Tomethod, when invoked and given a, MUST return a new promise and run the following steps in parallel

canonicalizing the BluetoothDataFilterInit filter , is the BluetoothDataFilterInit returned from the following steps: If filter . dataPrefix is present, let dataPrefix be a copy of the bytes held by filter .dataPrefix . Otherwise, let dataPrefix be an empty sequence of bytes. If filter . mask is present, let mask be a copy of the bytes held by filter .mask . Otherwise, let mask be a sequence of 0xFF bytes the same length as dataPrefix . If mask is not the same length as dataPrefix , throw a TypeError and abort these steps. Return {dataPrefix: new Uint8Array(|dataPrefix|), mask: new Uint8Array(|mask|)} . The result ofthe, is thereturned from the following steps:

We need a way for a site to register to receive an event when an interesting device comes within range.

add an allowed Bluetooth device device to BluetoothPermissionStorage storage given a set of requiredServiceUUIDs and a set of optionalServiceUUIDs , the UA MUST run the following steps: Let grantedServiceUUIDs be a new Set . Add the contents of requiredServiceUUIDs to grantedServiceUUIDs . Add the contents of optionalServiceUUIDs to grantedServiceUUIDs . Search for an element allowedDevice in storage . allowedDevices where device is equal to allowedDevice @ [[device]] . If one is found, perform the following sub-steps: Add the contents of allowedDevice . allowedServices to grantedServiceUUIDs . If one is not found, perform the following sub-steps: Let allowedDevice . deviceId be a unique ID to the extent that the UA can determine that two Bluetooth connections are the same device and to the extent that the user wants to expose that fact to script. Set allowedDevice . allowedServices to grantedServiceUUIDs . Set allowedDevice . mayUseGATT to true . Totogiven a set ofand a set of, the UA MUST run the following steps:

3.2. Overall Bluetooth availability

The UA may be running on a computer that has no Bluetooth radio. requestDevice() handles this by failing to discover any devices, which results in a NotFoundError , but websites may be able to handle it more gracefully.

const bluetoothUI = document . querySelector ( '#bluetoothUI' ); navigator . bluetooth . getAvailability () . then ( isAvailable => { bluetoothUI . hidden = ! isAvailable ; }); navigator . bluetooth . addEventListener ( ' availabilitychanged ' , e => { bluetoothUI . hidden = ! e . value ; }); To show Bluetooth UI only to users who have a Bluetooth adapter:

getAvailability promise with false, the following can be used to detect when Bluetooth is available again to show Bluetooth UI: function checkAvailability () { const bluetoothUI = document . querySelector ( '#bluetoothUI' ); navigator . bluetooth . getAvailability () . then ( isAvailable => { bluetoothUI . hidden = ! isAvailable ; }); } navigator . permissions . query ({ name : "bluetooth" }). then ( status => { if ( status . state !== 'denied' ) checkAvailability (); // Bluetooth is blocked, listen for change in PermissionStatus. status . onchange = () => { if ( this . state !== 'denied' ) checkAvailability (); }; }); If the user has blocked the permission and the UA resolves thepromise with false, the following can be used to detect when Bluetooth is available again to show Bluetooth UI:

[ Exposed = Window , SecureContext ] interface ValueEvent : Event { constructor ( DOMString type , optional ValueEventInit initDict = {}); readonly attribute any value ; }; dictionary ValueEventInit : EventInit { any value = null ; };

ValueEvent instances are constructed as specified in DOM §2.5 Constructing events.

The value attribute must return the value it was initialized to.

Such a generic event type belongs in [HTML] or [DOM], not here.

4. Device Representation

The UA needs to track Bluetooth device properties at several levels: globally, per origin, and per global object.

4.1. Global Bluetooth device properties

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 UA SHOULD determine that two Bluetooth devices are the same Bluetooth 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.

4.2. BluetoothDevice

A BluetoothDevice instance represents a Bluetooth device for a particular global object (or, equivalently, for a particular Realm or Bluetooth instance).

[ Exposed = Window , SecureContext ] interface BluetoothDevice : EventTarget { readonly attribute DOMString id ; readonly attribute DOMString ? name ; readonly attribute BluetoothRemoteGATTServer ? gatt ; Promise < void > watchAdvertisements ( optional WatchAdvertisementsOptions options = {}); readonly attribute boolean watchingAdvertisements ; }; BluetoothDevice includes BluetoothDeviceEventHandlers ; BluetoothDevice includes CharacteristicEventHandlers ; BluetoothDevice includes ServiceEventHandlers ; dictionary WatchAdvertisementsOptions { AbortSignal signal ; };

BluetoothDevice attributes NOTE:attributes id uniquely identifies a device to the extent that the UA can determine that two Bluetooth connections are to the same device and to the extent that the user uniquely identifies a device to the extent that the UA can determine that two Bluetooth connections are to the same device and to the extent that the user wants to expose that fact to script name is the human-readable name of the device. gatt provides a way to interact with this device’s GATT server if the site has permission to do so. watchingAdvertisements is true if the UA is currently scanning for advertisements from this device and firing events for them.

Instances of BluetoothDevice are created with the internal slots described in the following table:

Internal Slot Initial Value Description (non-normative) [[context]] <always set in prose> The Bluetooth object that returned this BluetoothDevice . [[representedDevice]] <always set in prose> The Bluetooth device this object represents, or null if access has been revoked. [[gatt]] a new BluetoothRemoteGATTServer instance with its device attribute initialized to this and its connected attribute initialized to false . Does not change. [[allowedServices]] <always set in prose> This device’s allowedServices list for this origin or "all" if all services are allowed. For example, a UA may grant an origin access to all services on a referringDevice that advertised a URL on that origin. [[watchAdvertisementsState]] 'not-watching' An string enumeration describing the current state of a watchAdvertisements() operation. The possible enumeration values are: 'not-watching'

'pending-watch'

'watching'

A user agent has an associated watch advertisements manager which is the result of starting a new parallel queue.

abort watchAdvertisements for a BluetoothDevice device , run these steps: Set this. [[watchAdvertisementsState]] to 'not-watching' . Set device . watchingAdvertisements to false . Enqueue the following steps to watch advertisements manager: If no more BluetoothDevice s in the whole UA have watchingAdvertisements set to true , the UA SHOULD stop scanning for advertisements. Otherwise, if no more BluetoothDevice s representing the same device as this have watchingAdvertisements set to true , the UA SHOULD reconfigure the scan to avoid receiving reports for this device. Tofor a, run these steps:

abort all active watchAdvertisements operations, run these steps: For each device in Bluetooth . [[deviceInstanceMap]] , perform the following steps: If device . [[watchAdvertisementsState]] is pending-watch or watching , run abort watchAdvertisements with device . Tooperations, run these steps:

5. GATT Interaction

5.1. GATT Information Model

5.1.1. Persistence across connections

The Bluetooth Attribute Caching system allows bonded clients to save references to attributes from one connection to the next. Web Bluetooth treats websites as not bonded to devices they have permission to access: BluetoothRemoteGATTService , BluetoothRemoteGATTCharacteristic , and BluetoothRemoteGATTDescriptor objects become invalid on disconnection, and the site must retrieved them again when it re-connects.

5.1.2. The Bluetooth cache

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 entry in the cache is either known-present, known-absent, or unknown. The cache MUST NOT contain two entries that are for the same attribute. Each known-present entry in the cache is associated with an optional Promise< BluetoothRemoteGATTService > , Promise< BluetoothRemoteGATTCharacteristic > , or Promise< BluetoothRemoteGATTDescriptor > instance for each Bluetooth instance.

serviceA.getCharacteristic(uuid1) function with an initially empty 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 serviceA only has one Characteristic with UUID uuid1 , then the subsequent Characteristics become known-absent. Note: For example, if a user calls thefunction 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, then the first Characteristic with UUIDinsideis known-present, and any subsequent Characteristics with that UUID remain unknown. If the user later calls, the UA needs to resume or restart the Discover Characteristics by UUID procedure. If it turns out thatonly has one Characteristic with UUID, 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.

populate the Bluetooth cache with entries matching some description, the UA MUST run the following steps. Note: These steps can block, so uses of this algorithm must be in parallel Attempt to make all matching entries in the cache either known-present or known-absent, using any sequence of GATT procedures that [BLUETOOTH42] specifies will return enough information. Handle errors as described in § 5.7 Error handling. If the previous step returns an error, return that error from this algorithm. Towith entries matching some description, the UA MUST run the following steps.

Represented ( obj : Device or GATT Attribute) returns, depending on the type of obj : BluetoothDevice obj . [[representedDevice]] BluetoothRemoteGATTService obj . [[representedService]] BluetoothRemoteGATTCharacteristic obj . [[representedCharacteristic]] BluetoothRemoteGATTDescriptor obj . [[representedDescriptor]] : Device or GATT Attribute) returns, depending on the type of

5.1.3. Navigating the Bluetooth Hierarchy

5.1.4. Identifying Services, Characteristics, and Descriptors

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:

They are not both Services, both Characteristics, or both Descriptors.

They are both Services, but are not both primary or both secondary services.

They have different UUIDs.

Their parent Devices aren’t the same device or their parent Services or Characteristics aren’t the same attribute.

Note: This definition is loose because platform APIs expose their own notion of identity without documenting whether it’s based on Attribute Handle equality.

x and y representing Services, Characteristics, or Descriptors, x === y returns whether the objects represent the Note: For two Javascript objectsandrepresenting Services, Characteristics, or Descriptors,returns whether the objects represent the same attribute , because of how the query the Bluetooth cache algorithm creates and caches new objects.

5.2. BluetoothRemoteGATTServer

BluetoothRemoteGATTServer represents a GATT Server on a remote device.

[ Exposed = Window , SecureContext ] interface BluetoothRemoteGATTServer { [ SameObject ] readonly attribute BluetoothDevice device ; readonly attribute boolean connected ; Promise < BluetoothRemoteGATTServer > connect (); void disconnect (); Promise < BluetoothRemoteGATTService > getPrimaryService ( BluetoothServiceUUID service ); Promise < sequence < BluetoothRemoteGATTService >> getPrimaryServices ( optional BluetoothServiceUUID service ); };

BluetoothRemoteGATTServer attributes NOTE:attributes device is the device running this server. is the device running this server. connected is true while this instance is connected to this.device . It can be false while the UA is physically connected, for example when there are other connected BluetoothRemoteGATTServer instances for other global objects.

When no ECMAScript code can observe an instance of BluetoothRemoteGATTServer server anymore, the UA SHOULD run server . disconnect() .

BluetoothDevice instances are stored in navigator.bluetooth. [[deviceInstanceMap]] , this can’t happen at least until navigation releases the global object or closing the tab or window destroys the Note: Becauseinstances are stored in, this can’t happen at least until navigation releases the global object or closing the tab or window destroys the browsing context

Note: Disconnecting on garbage collection ensures that the UA doesn’t keep consuming resources on the remote device unnecessarily.

Instances of BluetoothRemoteGATTServer are created with the internal slots described in the following table:

Internal Slot Initial Value Description (non-normative) [[activeAlgorithms]] new Set () Contains a Promise corresponding to each algorithm using this server’s connection. disconnect() empties this set so that the algorithm can tell whether its realm was ever disconnected while it was running.

garbage-collect the connection of a device , the UA must, do the following steps If systems that aren’t using this API, either inside or outside of the UA, are using the device ’s ATT Bearer, abort this algorithm. For all BluetoothDevice s deviceObj in the whole UA: If deviceObj . [[representedDevice]] is not the same device as device , continue to the next deviceObj . If deviceObj .gatt. connected is true , abort this algorithm. If deviceObj .gatt. [[activeAlgorithms]] contains the Promise of a call to connect() , abort this algorithm. Destroy device ’s ATT Bearer. Toof a, the UA must, do the following steps in parallel

BluetoothRemoteGATTServer was disconnected while they were running, even if the UA stays connected the whole time and the BluetoothRemoteGATTServer is subsequently re-connected before they finish. We wrap the returned Promise to accomplish this. Note: Algorithms need to fail if theirwas disconnected while they were running, even if the UA stays connected the whole time and theis subsequently re-connected before they finish. We wrap the returnedto accomplish this. To create a gattServer - connection-checking wrapper around a Promise promise , the UA MUST: If gattServer .connected is true , add promise to gattServer . [[activeAlgorithms]] . React to promise : If promise was fulfilled with value result , then: If promise is in gattServer . [[activeAlgorithms]] , remove it and return result . Otherwise, throw a NetworkError . Note: This error is thrown because gattServer was disconnected during the execution of the main algorithm.

If promise was rejected with reason error , then: If promise is in gattServer . [[activeAlgorithms]] , remove it and throw error . Otherwise, throw a NetworkError . Note: This error is thrown because gattServer was disconnected during the execution of the main algorithm.



getPrimaryService( service ) method, when invoked, MUST perform the following steps: If this.device. [[allowedServices]] is not "all" and service is not in this.device. [[allowedServices]] , return a promise rejected with a SecurityError and abort these steps. Return GetGATTChildren(attribute= this.device ,

single=true,

uuidCanonicalizer= BluetoothUUID.getService ,

uuid= service ,

allowedUuids= this.device. [[allowedServices]] ,

child type="GATT Primary Service") Themethod, when invoked, MUST perform the following steps:

getPrimaryServices( service ) method, when invoked, MUST perform the following steps: If this.device. [[allowedServices]] is not "all" , and service is present and not in this.device. [[allowedServices]] , return a promise rejected with a SecurityError and abort these steps. Return GetGATTChildren(attribute= this.device ,

single=false,

uuidCanonicalizer= BluetoothUUID.getService ,

uuid= service ,

allowedUuids= this.device. [[allowedServices]] ,

child type="GATT Primary Service") Themethod, when invoked, MUST perform the following steps:

5.3. BluetoothRemoteGATTService

BluetoothRemoteGATTService represents a GATT Service, a collection of characteristics and relationships to other services that encapsulate the behavior of part of a device.

[ Exposed = Window , SecureContext ] interface BluetoothRemoteGATTService : EventTarget { [ SameObject ] readonly attribute BluetoothDevice device ; readonly attribute UUID uuid ; readonly attribute boolean isPrimary ; Promise < BluetoothRemoteGATTCharacteristic > getCharacteristic ( BluetoothCharacteristicUUID characteristic ); Promise < sequence < BluetoothRemoteGATTCharacteristic >> getCharacteristics ( optional BluetoothCharacteristicUUID characteristic ); Promise < BluetoothRemoteGATTService > getIncludedService ( BluetoothServiceUUID service ); Promise < sequence < BluetoothRemoteGATTService >> getIncludedServices ( optional BluetoothServiceUUID service ); }; BluetoothRemoteGATTService includes CharacteristicEventHandlers ; BluetoothRemoteGATTService includes ServiceEventHandlers ;

BluetoothRemoteGATTService attributes NOTE:attributes device is the BluetoothDevice representing the remote peripheral that the GATT service belongs to. is therepresenting 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.

Instances of BluetoothRemoteGATTService are created with the internal slots described in the following table:

Internal Slot Initial Value Description (non-normative) [[representedService]] <always set in prose> The Service this object represents, or null if the Service has been removed or otherwise invalidated.

create a BluetoothRemoteGATTService representing a Service service , the UA must return promise and run the following steps Let result be a new instance of BluetoothRemoteGATTService with its [[representedService]] slot initialized to service . Get the BluetoothDevice representing the device in which service appears, and let device be the result. If the previous step threw an error, reject promise with that error and abort these steps. Initialize result .device from device . Initialize result .uuid from the UUID of service . If service is a Primary Service, initialize result .isPrimary to true. Otherwise initialize result .isPrimary to false. Resolve promise with result . Toa Service, the UA must return a new promise and run the following steps in parallel

getCharacteristic( characteristic ) method retrieves a GetGATTChildren(attribute= this ,

single=true,

uuidCanonicalizer= BluetoothUUID.getCharacteristic ,

uuid= characteristic ,

allowedUuids= undefined ,

child type="GATT Characteristic") Themethod retrieves a Characteristic inside this Service. When invoked, it MUST return

getCharacteristics( characteristic ) method retrieves a list of GetGATTChildren(attribute= this ,

single=false,

uuidCanonicalizer= BluetoothUUID.getCharacteristic ,

uuid= characteristic ,

allowedUuids= undefined ,

child type="GATT Characteristic") Themethod retrieves a list of Characteristic s inside this Service. When invoked, it MUST return

getIncludedService( service ) method retrieves an GetGATTChildren(attribute= this ,

single=true,

uuidCanonicalizer= BluetoothUUID.getService ,

uuid= service ,

allowedUuids= undefined ,

child type="GATT Included Service") Themethod retrieves an Included Service inside this Service. When invoked, it MUST return

getIncludedServices( service ) method retrieves a list of GetGATTChildren(attribute= this ,

single=false,

uuidCanonicalizer= BluetoothUUID.getService ,

uuid= service ,

allowedUuids= undefined ,

child type="GATT Included Service") Themethod retrieves a list of Included Service s inside this Service. When invoked, it MUST return

5.4. BluetoothRemoteGATTCharacteristic

BluetoothRemoteGATTCharacteristic represents a GATT Characteristic, which is a basic data element that provides further information about a peripheral’s service.

[ Exposed = Window , SecureContext ] interface BluetoothRemoteGATTCharacteristic : EventTarget { [ SameObject ] readonly attribute BluetoothRemoteGATTService service ; readonly attribute UUID uuid ; readonly attribute BluetoothCharacteristicProperties properties ; readonly attribute DataView ? value ; Promise < BluetoothRemoteGATTDescriptor > getDescriptor ( BluetoothDescriptorUUID descriptor ); Promise < sequence < BluetoothRemoteGATTDescriptor >> getDescriptors ( optional BluetoothDescriptorUUID descriptor ); Promise < DataView > readValue (); Promise < void > writeValue ( BufferSource value ); Promise < void > writeValueWithResponse ( BufferSource value ); Promise < void > writeValueWithoutResponse ( BufferSource value ); Promise < BluetoothRemoteGATTCharacteristic > startNotifications (); Promise < BluetoothRemoteGATTCharacteristic > stopNotifications (); }; BluetoothRemoteGATTCharacteristic includes CharacteristicEventHandlers ;

BluetoothRemoteGATTCharacteristic attributes NOTE:attributes service is the GATT service this characteristic belongs to. 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.

Instances of BluetoothRemoteGATTCharacteristic are created with the internal slots described in the following table:

Internal Slot Initial Value Description (non-normative) [[representedCharacteristic]] <always set in prose> The Characteristic this object represents, or null if the Characteristic has been removed or otherwise invalidated.

create a BluetoothRemoteGATTCharacteristic representing a Characteristic characteristic , the UA must return promise and run the following steps Let result be a new instance of BluetoothRemoteGATTCharacteristic with its [[representedCharacteristic]] slot initialized to characteristic . Initialize result .service from the BluetoothRemoteGATTService instance representing the Service in which characteristic appears. Initialize result .uuid from the UUID of characteristic . Create a BluetoothCharacteristicProperties instance from the Characteristic characteristic , and let propertiesPromise be the result. Wait for propertiesPromise to settle. If propertiesPromise was rejected, resolve promise with propertiesPromise and abort these steps. Initialize result .properties from the value propertiesPromise was fulfilled with. Initialize result .value to null . The UA MAY initialize result .value to a new DataView wrapping a new ArrayBuffer containing the most recently read value from characteristic if this value is available. Resolve promise with result . Toa Characteristic, the UA must return a new promise and run the following steps in parallel

getDescriptor( descriptor ) method retrieves a GetGATTChildren(attribute= this ,

single=true,

uuidCanonicalizer= BluetoothUUID.getDescriptor ,

uuid= descriptor ,

allowedUuids= undefined ,

child type="GATT Descriptor") Themethod retrieves a Descriptor inside this Characteristic. When invoked, it MUST return

getDescriptors( descriptor ) method retrieves a list of GetGATTChildren(attribute= this ,

single=false,

uuidCanonicalizer= BluetoothUUID.getDescriptor ,

uuid= descriptor ,

allowedUuids= undefined ,

child type="GATT Descriptor") Themethod retrieves a list of Descriptor s inside this Characteristic. When invoked, it MUST return

Deprecated. Use writeValueWithResponse() and writeValueWithoutResponse() instead. Useandinstead. The writeValue( value ) method, when invoked, MUST return WriteCharacteristicValue( this= this ,

value= value ,

response="optional") This method is for backwards compatibility only. New implementations should not implement this method. <https://github.com/WebBluetoothCG/web-bluetooth/issues/238>

writeValueWithResponse( value ) method, when invoked, MUST return WriteCharacteristicValue( this= this ,

value= value ,

response="required") Themethod, when invoked, MUST return

writeValueWithoutResponse( value ) method, when invoked, MUST return WriteCharacteristicValue( this= this ,

value= value ,

response="never") Themethod, when invoked, MUST return

The UA MUST maintain a map from each known GATT Characteristic to a set of Bluetooth objects known as the characteristic’s active notification context set .

navigator.bluetooth objects for each startNotifications() again, and there is an unavoidable risk that some notifications will be missed in the gap before startNotifications() takes effect. Note: The set for a given characteristic holds theobjects for each Realm that has registered for notifications. All notifications become inactive when a device is disconnected. A site that wants to keep getting notifications after reconnecting needs to callagain, and there is an unavoidable risk that some notifications will be missed in the gap beforetakes effect.

5.4.1. BluetoothCharacteristicProperties

Each BluetoothRemoteGATTCharacteristic exposes its characteristic properties through a BluetoothCharacteristicProperties object. These properties express what operations are valid on the characteristic.

[ Exposed = Window , SecureContext ] 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 ; };

5.5. BluetoothRemoteGATTDescriptor

BluetoothRemoteGATTDescriptor represents a GATT Descriptor, which provides further information about a Characteristic’s value.

[ Exposed = Window , SecureContext ] interface BluetoothRemoteGATTDescriptor { [ SameObject ] readonly attribute BluetoothRemoteGATTCharacteristic characteristic ; readonly attribute UUID uuid ; readonly attribute DataView ? value ; Promise < DataView > readValue (); Promise < void > writeValue ( BufferSource value ); };

BluetoothRemoteGATTDescriptor attributes NOTE:attributes characteristic is the GATT characteristic this descriptor belongs to. 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.

Instances of BluetoothRemoteGATTDescriptor are created with the internal slots described in the following table:

Internal Slot Initial Value Description (non-normative) [[representedDescriptor]] <always set in prose> The Descriptor this object represents, or null if the Descriptor has been removed or otherwise invalidated.

create a BluetoothRemoteGATTDescriptor representing a Descriptor descriptor , the UA must return promise and run the following steps Let result be a new instance of BluetoothRemoteGATTDescriptor with its [[representedDescriptor]] slot initialized to descriptor . Initialize result .characteristic from the BluetoothRemoteGATTCharacteristic instance representing the Characteristic in which descriptor appears. Initialize result .uuid from the UUID of descriptor . Initialize result .value to null . The UA MAY initialize result .value to a new DataView wrapping a new ArrayBuffer containing the most recently read value from descriptor if this value is available. Resolve promise with result . Toa Descriptor, the UA must return a new promise and run the following steps in parallel

5.6. Events

5.6.2. Event types

5.6.3. Responding to Disconnection

5.6.4. Responding to Notifications and Indications

5.6.6. IDL event handlers

[ SecureContext ] interface mixin CharacteristicEventHandlers { attribute EventHandler oncharacteristicvaluechanged ; };

oncharacteristicvaluechanged is an Event handler IDL attribute for the characteristicvaluechanged event type.

[ SecureContext ] interface mixin BluetoothDeviceEventHandlers { attribute EventHandler onadvertisementreceived ; attribute EventHandler ongattserverdisconnected ; };

onadvertisementreceived is an Event handler IDL attribute for the advertisementreceived event type.

ongattserverdisconnected is an Event handler IDL attribute for the gattserverdisconnected event type.

[ SecureContext ] interface mixin 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.

5.7. Error handling

Note: 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.

Error Response , the UA MUST perform the following steps: If the procedure times out or the ATT Bearer (described in Profile Fundamentals) is absent or terminated for any reason, return a NetworkError from the step and abort these steps. Take the following actions depending on the Error Code : Invalid PDU Invalid Offset Attribute Not Found Unsupported Group Type These error codes indicate that something unexpected happened at the protocol layer, likely either due to a UA or device bug. Return a NotSupportedError from the step. Invalid Handle Return an InvalidStateError from the step. Invalid Attribute Value Length Return an InvalidModificationError from the step. 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 The UA SHOULD attempt to increase the security level of the connection. If this attempt fails or the UA doesn’t support any higher security, Return a SecurityError from the step. Otherwise, retry the step at the new higher security level. Insufficient Authorization Return a SecurityError from the step. Application Error If the GATT procedure was a Write, return an InvalidModificationError from the step. Otherwise, return a NotSupportedError from the step. Read Not Permitted Write Not Permitted Request Not Supported Prepare Queue Full Insufficient Resources Unlikely Error Anything else Return a NotSupportedError from the step. 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, the UA MUST perform the following steps:

6. UUIDs

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.

BluetoothUUID.canonicalUUID( alias ) function to map a 16- or 32-bit Bluetooth Note: This standard provides thefunction to map a 16- or 32-bit Bluetooth UUID alias to its 128-bit form.

Note: 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.

6.1. Standardized UUIDs

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.

The static BluetoothUUID. canonicalUUID( alias ) method, when invoked, MUST return the 128-bit UUID represented by the 16- or 32-bit UUID alias alias .

Note: 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, BluetoothUUID.getService() does not throw an exception. represents 16- and 32-bit UUID aliases, valid UUID s, and names defined in [BLUETOOTH-ASSIGNED-SERVICES] , or, equivalently, the values for whichdoes not throw an exception. 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. 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.

ResolveUUIDName ( name , assigned numbers table , prefix ), the UA MUST perform the following steps: If name is an unsigned long , return BluetoothUUID.canonicalUUID (name) and abort these steps. If name is a valid UUID, return name and abort these steps. If the string prefix + "." + name appears in assigned numbers table , let alias be its assigned number, and return BluetoothUUID.canonicalUUID ( alias ). Otherwise, throw a TypeError . To), the UA MUST perform the following steps:

The static BluetoothUUID. getService( name ) method, when invoked, MUST return ResolveUUIDName( name , [BLUETOOTH-ASSIGNED-SERVICES], "org.bluetooth.service").

The static BluetoothUUID. getCharacteristic( name ) method, when invoked, MUST return ResolveUUIDName( name , [BLUETOOTH-ASSIGNED-CHARACTERISTICS], "org.bluetooth.characteristic").

The static BluetoothUUID. getDescriptor( name ) method, when invoked, MUST return ResolveUUIDName( name , [BLUETOOTH-ASSIGNED-DESCRIPTORS], "org.bluetooth.descriptor").

7. The GATT Blocklist

This specification relies on a blocklist file in the https://github.com/WebBluetoothCG/registries repository to restrict the set of GATT attributes a website can access.

parsing the blocklist at a URL url is a map from Fetch url , and let contents be its body, decoded as UTF-8. Let lines be contents split on '

' . Let result be an empty map. For each line in lines , do the following sub-steps: If line is empty or its first character is '#' , continue to the next line. If line consists of just a valid UUID, let uuid be that UUID and let token be " exclude ". If line consists of a valid UUID, a space (U+0020), and one of the tokens " exclude-reads " or " exclude-writes ", let uuid be that UUID and let token be that token. Otherwise, return an error and abort these steps. If uuid is already in result , return an error and abort these steps. Add a mapping in result from uuid to token . Return result . The result ofat a URLis a map from valid UUID s to tokens, or an error, produced by the following algorithm:

The GATT blocklist is the result of parsing the blocklist at https://github.com/WebBluetoothCG/registries/blob/master/gatt_blocklist.txt. The UA should re-fetch the blocklist periodically, but it’s unspecified how often.

A UUID is blocklisted if either the GATT blocklist’s value is an error, or the UUID maps to " exclude " in the GATT blocklist.

A UUID is blocklisted for reads if either the GATT blocklist’s value is an error, or the UUID maps to either " exclude " or " exclude-reads " in the GATT blocklist.

A UUID is blocklisted for writes if either the GATT blocklist’s value is an error, or the UUID maps to either " exclude " or " exclude-writes " in the GATT blocklist.

8. Extensions to the Navigator Interface

[ SecureContext ] partial interface Navigator { [ SameObject ] readonly attribute Bluetooth bluetooth ; };

9. Terminology and Conventions

This specification uses a few conventions and several terms from other specifications. This section lists those and links to their primary definitions.

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 § 6.1 Standardized UUIDs 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 read-only type that is similar to WebIDL’s FrozenArray .

A read only ArrayBuffer has ArrayBuffer 's values and interface, except that attempting to write to its contents or transfer it has the same effect as trying to write to a FrozenArray 's contents. This applies to TypedArray s and DataView s wrapped around the ArrayBuffer too.