webbluetoothcg / web-bluetooth Goto Github PK
View Code? Open in Web Editor NEWBluetooth support for the Web.
Home Page: http://www.w3.org/community/web-bluetooth/
License: Other
Bluetooth support for the Web.
Home Page: http://www.w3.org/community/web-bluetooth/
License: Other
Section Responding to Service Changes details the dispatch of events serviceadded, servicechanged, serviceremoved when the Service Changed characteristic is notified.
Currently it is difficult for an application to understand the entire context of changes because sets of added/changed/removed services are sent separately.
Likely the best option is to dispatch a single serviceschanged event with all sets of added/changed/removed included in the event, similar to MutationRecord.
An alternative would be to add two additional events sent before and after the serviceadded, servicechanged, serviceremoved events are sent, allowing applications to cache the data and process it after it has all been presented.
GATT services can be primary and secondary and a usually service discovery procedure will involve two separate requests to obtain the handles of primary/secondary services (the attribute types for the service declaration attributes have different UUIDs for primary and secondary services). I guess the spec should say that the implementation should discover ALL GATT services?
Following on the above, should the getAllServices methods then return ALL services? The documentation currently says that they will only return primary services.
From what I've seen, there are almost NO BLE devices out in the field that have bothered with exposing secondary services (or even included services for that matter) so clients will probably be mostly interested in primary services. That said, maybe this API function should accept some sort of filter to limit the result to primary vs secondary services and return both types by default? What do you think?
I don't have a concrete proposal, but feel something "map-like" might do the trick. This would handle all the look ups. Something like:
[MapClass] interface BluetoothUnitMap{
void clear(); // clear only custom types, maybe?
boolean delete(BlueToothKey key); //as above?
void forEach(BTMapCallBack callback, optional any thisArg);
BlueToothUnit get(BlueToothKey key);
boolean has(BlueToothKey key);
BluetoothUnitMap set(BlueToothKey key, UUID128 value);
sequence<BlueToothKey> keys();
}
callback BTMapCallBack(UUID128 value, BlueToothKey key, BluetoothUnitMap object);
typedef DOMString UUID128; //force it to conform to canonical form in prose.
typedef CustomBlueToothKey DOMString;
typedef (StandardBlueToothKey or CustomBlueToothKey) BlueToothKey;
enum StandardBlueToothKey{
"energy.kilogram_calorie",
...
}
Discovery and connection are separate steps in the Bluetooth spec and in most APIs that access Bluetooth. However, these APIs generally don't expose as compressed a discovery flow as this one, nor do they involve an "open-like" dialog where the user selects a device.
I don't know of a use for getting a handle to an un-connected device, but if you know of one please mention it here.
If there's a use, we need to expose that possibility. It's not clear whether we should expose it by having requestDevice()
return an un-connected device regardless and making all developers call connect()
explicitly, or by having requestDevice()
default to returning a connected device, but with an option to override that default.
The definitions of requestDevice()
and the getPrimaryService()
, getIncludedService()
getCharacteristic()
, and getDescriptor()
functions are missing a description of what happens when a string that isn't a BluetoothServiceUuid
, etc. is passed as a uuid.
SyntaxError
?
In discussion on dev-webapi@mozilla, @ehsan argued that
The apps can cache the value property themselves if they want to. These types of weird cache only properties don't have any prior art. In general, if something is as simple as this (by setting an expando property) to implement in the application code in JS, we should try to avoid adding it to the API.
We can also add the cache later if we find everyone using it.
Removing this field would mean that the notification/indication events need to include an ArrayBuffer
in their fields, in addition to the Characteristic or Descriptor, but that's not terrible.
If one document calls requestDevice()
and gets a BluetoothDevice
, and then passes that object to another document (through same-origin iframe
or window.open
?), does the object stay usable after the originating document is closed? After the originating frame is closed?
What do we need to write in the spec to pin that down?
The current Device interface exposes 2-3 unique IDs for the device (deviceID
, address
, and possibly name
), and allows sites to enumerate the Services the device defines, which is probably also enough to fingerprint the device.
How much of this do we need to expose to let people write useful apps? Does the fact that a user has to explicitly pair each device with the site mitigate the privacy concern?
@slightlyoff pointed out that we might be able to use Object.observe
instead of events to track changes to various bits of Bluetooth metadata. For example, if we define BluetoothDevice.prototype.services
as the cached array of discovered BluetoothGattServices
, we could observe it for changes instead of needing to define the serviceadded, etc. events. We'd need to be clear that this is a cache like BluetoothGattCharacteristic.value
(#37), rather than an up-front-discovered list of all the services.
Similarly, we could define a navigator.bluetooth.connectedDevices
array to handle devices getting disconnected and reconnected (#31). That also handles the problem of a re-opened web page finding the devices it already has access to.
The spec should probably define the set of Bluetooth host operations used in each step, while right now it just says things like "support a service" and "enable notifications". For security and privacy related operations, we'll need to say something about the allowed packet sequences.
This is extra tricky because it needs to be implementable on top of several platform APIs that say little about what operations they cause.
e.g. bluetooth.getCharacteristics(DOMString serviceId);
should become Service.getCharacteristics()
.
Whitelisting a set of standardized Services would allow us to provide a better interface than the UUIDs-and-ArrayBuffers that most Bluetooth APIs require. It would also provide some nice security properties since we could hide the precise identity of the devices speaking these Services and could enforce that written values match the requirements set by the Service.
On the other hand, we want to let device vendors write web pages for their devices instead of making them resort to native apps, and lots of new devices, like plant health sensors, barcode scanners, lightbulbs, and dive meters aren't covered by the standard services.
An interesting point that came up today involved connection security: should the API require all outgoing connections to be at a certain security level (encrypted, requiring authorization/authentication, etc)? Or should apps have the ability to specify the security level of the connection if they initiated it?
http://webbluetoothcg.github.io/web-bluetooth/use-cases.html#usecase_lazy_scale
The notification could be an event handler in an open web page, or a mechanism like registerProtocolHandler that opens a new tab with a device ID embedded in the URL, or an event handler in a service worker. The event handlers could receive a BluetoothDevice
instance, but the URL needs a stringified form, which the page could vivify using a to-be-defined function.
My feeling is that 'All' in getAll* methods is overkill.
I would then replace all of getAll* by get*, e.g.:
BluetoothDevice.getServices
BluetoothGATTService.getCharacteristics
BluetoothGATTService.getIncludedServices
BluetoothGATTCharacteristic.getDescriptors
This would also be aligned with most existing native APIs (e.g. Android)
Do we need to have BluetoothGATTBeginReliableWrite, BluetoothGATTEndReliableWrite, BluetoothGATTAbortReliableWrite, or did I miss something?
A Bluetooth audio device would be exposed through the WebAudio and getUserMedia specs, but we might want to allow its control channel to run over the WebBluetooth API. This will require being able to match the navigator.mediaDevices device ID with the Bluetooth device ID.
The wording right now is focused on the LE transport layer, but it might be possible to get the same security and privacy guarantees from GATT over BR/EDR. I'm inclined to focus on getting 1 transport layer right before adding a second, but would be happy to hear any more informed thoughts on the subject.
This came up in #14.
Value types turn out to be hard in Javascript. Let's just use lower-case String
s to represent UUIDs. Lower-case mimics chrome.bluetooth and I think the Windows API.
MacOS doesn't provide the BT address for a peripheral, just a generated identifier.
This may also help avoid confusion between the public address, static address, private addresses, and IRK in picking a device's "address".
How do we need to change the spec to accommodate future extensions for non-Client/Central/GATT/LE operation?
Per @marcoscaceres' argument in http://lists.w3.org/Archives/Public/public-web-bluetooth/2014Aug/0024.html, we should allow browsers to let users pick devices that don't match the filter the website requested. This is likely to cause the website to fail, but we shouldn't make allowing it non-conforming.
http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#attr-input-accept uses "User agents should prevent the user from selecting files that are not accepted by one (or more) of these tokens.", so that's probably good wording for this spec also.
Currently requestDevice() defines two behaviors--connect and discovery services, after that I assume it cached everything, and API doesn't provide discover service method. It doesn't give applications refresh service lists.
Suppose there is one Bluetooth Smart device with 3 sensors, when we connect with the device, only one sensor is being activated, so only one Thermometer Service shows up, and we only cache one service. I guess it's possible to activate other sensors and the device update service list again, in this case, the application may need to discovery service again.
The reason for having the BluetoothGattCharacteristic.startNotifications|stopNotifications methods in the first place is to prevent apps from directly writing to the Client Characteristic Configuration (CCC below) descriptor (which is used to enable/disable notifications and indications from the characteristic). This raises the following two points:
I'm expecting to write the spec in terms of the Bluetooth operations that each javascript function needs to map to, but implementations are going to need to work in terms of platform features. It'll be helpful to have a document for implementers to follow that explains what platform (Win, MacOS, iOS, Linux (BlueZ?), Android) commands to use for each operation.
The Characteristic Aggregate Format and Characteristic Presentation Format Descriptors constrain the array buffers representing a characteristic's value. If we restrict writes to match those formats, we might be able to avoid some device exploits.
USB devices are trusted as keyboards and mice by default, which leads to well-known exploits like https://srlabs.de/badusb/.
How vulnerable are current operating systems to the same attacks over Bluetooth? For example, if I pair a device that looks like a fitbit, but which also exposes a HID keyboard service, will my OS trust the keyboard without warning me? If I pair a device, and it adds a keyboard service later, will my OS trust the new keyboard?
This is relevant to this project because we have to assume some devices will be vulnerable to GATT writes in ways that let the attacker compromise the firmware and add new services.
https://webbluetoothcg.github.io/web-bluetooth/#idl-def-BluetoothInteraction currently offers
Promise<BluetoothGATTService> getService (DOMString serviceInstanceId);
Promise<BluetoothGATTCharacteristic> getCharacteristic (DOMString characteristicInstanceId);
Promise<BluetoothGATTDescriptor> getDescriptor (DOMString descriptorInstanceId);
I motion we remove these from the initial spec
a. To keep initial spec as simple as possible. Access to Service, Characteristic, and Descriptor objects is already exposed by the chain of objects, get*, and returned promises.
b. Scanning e.g. Android SDK, I don't see a way for the implementation to efficiently implement loading Service, Characteristic, and Descriptor objects from ID, the implementation would need to enumerate the full hierarchy anyway to search for matches.
c. InstanceIDs may not be easily accessible on all platforms, see #51 which proposes removing all instanceid
attributes.
The Bluetooth tree I added in bc0b42b says that an Included Service's parent is the Service that includes it. However, I can't find anything in the BT standard that says a Service may only be included by 1 parent service, and the Windows API actually exposes a list of parents.
Let's not specify an ordering for the secondary services under a Device since they may be included by different services in different orders, and those inclusion orders are the only thing most implementations would have to go on. We'll need to specify "the first GATT included service" without relying on the tree order.
Most existing Bluetooth devices don't expect untrusted websites to be interacting with them. The use cases document mentions that "a mechanism similar to the GPU Blacklist may be appropriate". However there is many orders of magnitude more variety in bluetooth devices than there is in GPUs, so it seems infeasible to blacklist vulnerable devices. Instead, it might be wise to require devices to opt in to being accessible via the web API.
On the other hand, native apps on all major OSes already get unrestricted access to bluetooth with barely any prompting. So maybe we're already doomed :-(
https://webbluetoothcg.github.io/web-bluetooth/#standard-gatt-units defines attributes like velocity.metres_per_second
, where the ".
" isn't actually allowed in WebIDL. We need to split attributes like this into a velocity
attribute whose type is BluetoothUuidsUnitVelocity
, which contains a metres_per_second
attribute.
The documentation for BluetoothScanFilter says the following:
When determining the list of Service UUIDs a device supports, the UA must include Services advertised by the device, and may include Services previously discovered through the Bluetooth Service Discovery Protocol (SDP).
This should probably be clarified a bit. SDP is used for discovering services over BR/EDR and, while GATT over BR/EDR is possible, these are not limited to GATT services. This should really read something like "..., and may include GATT services previously discovered through the Attribute Protocol (ATT) and/or Service Discovery Protocol (SDP)"
Responding to Service Changes specificaly avoids specifying how to identify services that are the same after a Service Changed indications ([[!BLUETOOTH41]] 3.G.7.1).
If the same Service appears in both removedEntities and addedEntities, remove it from both, and add it to changedServices. Services with different UUIDs or Primary/Secondary status must not be considered "the same", but this specification says nothing else about determining identity
The challenge is that operating system level APIs may limit a user agent's ability to determine which services are the same based on UUIDs & attribute handle.
@jyasskin mentioned:
Raw Bluetooth would let us say that one Service is "the same" as another if they have the same UUID and attribute handle. The wrappers that give things instance IDs would let us say two Services are "the same" if they have the same instance ID. But Apple doesn't expose either, so there's no guaranteed way to match things up.
https://developer.apple.com/library/ios/documentation/CoreBluetooth/Reference/CBPeripheralDelegate_Protocol/index.html#//apple_ref/occ/intfm/CBPeripheralDelegate/peripheral:didModifyServices: does talk about "find out whether any of the invalidated services that you were using (and want to continue using) have been added back to a different location in the peripheral’s database.", but it doesn't say how to do that, and if they have a different location (attribute handle?), they're not really the same service anyway.
Perhaps the specification can make a best-effort suggestion, and possibly a standard fallback algorithm if attribute handles are not available.
Is it possible that multiple applications request UA to perform LE scan at the same time? I understand it may depend on OS level, but it will be better to give some implementation suggestions.
The chrome.bluetoothLowEnergy API makes developers asynchronously retrieve the full list of supported Services, then iterate over the list to find the UUID they want. Then they must repeat this for Included Services, Characteristics and Descriptors.
Most events in the web platform are specified along the lines of "Fire a trusted event with the name 'foo' at 'bar', using the Baz interface." Event names are almost always all-lowercase. The bluetooth events should be specified and named the same way, as opposed to being defined on their handler attributes.
We should also consider whether to make the various GATT objects participate in a tree so we could fire the events directly at the service or characteristic they relate to. There's some tension here with the desire to eventually expose these events to Service Workers, since a Service Worker that's just been woken up won't have GATT instances around to attach event handlers to. However, having navigator.bluetooth
act as the root of any tree will at least make it possible to handle the events in a Service Worker.
In current draft, "characteristics and/or descriptors that get added or removed from the service" are covered in onServiceChanged event handler.
Have you considered to separate characteristics added/removed to onCharacteristicAdded/Removed, and so as descriptors? Which might make the name more clear?
Besides, if we use a ServiceEvent with a BluetoothGattService attribute for "Service Changed", is there a way to let application know that it was a characteristic or descriptor added/removed from the service in your approach?
Could you share why you chose to aggregate them in onServiceChanged?
By the way, there is a DescriptorEvent in your spec which is not actually used, what's that for?
The instanceid
attribute currently exists on BluetoothDevice
, BluetoothGATTService
, BluetoothGATTCharacteristic
, and BluetoothGATTDescriptor
. The instanceid
can be used to
BluetoothInteraction
interface).However, instance IDs may not be exposed on all platform APIs. E.G. See Apple's CBService.
Implementations of Web Bluetooth could manufacture IDs even when the OS API does not provide them to work around this. IDs would need to be invalidated whenever the association can't be maintained, e.g. device disconnection or on Service Change.
Further, the information does not seem essential to a minimum viable Web Bluetooth specification. Multiple service, characteristic, descriptor instances will be returned as unique objects, and applications can assign ID if necessary to them in the same way this specification wound need to on operating systems that do not expose an instance ID.
Currently BluetoothDevice define 3 variants of getAllServices:
Promise<BluetoothGATTServiceSequence> getAllServices ()
Promise<BluetoothGATTServiceSequence> getAllServices (BluetoothServiceUuid service)
Promise<BluetoothGATTServiceSequence> getAllServices (sequence<BluetoothServiceUuid> services)
I propose we keep only the first one, as most native APIs do.
Forcing browsers to implement the last 2 methods is overkill IMO.
In the same idea, I would also remove from BluetoothGATTService:
Promise<BluetoothGATTCharacteristicSequence> getAllCharacteristics (sequence<BluetoothCharacteristicUuid> characteristics);
Promise<BluetoothGATTServiceSequence> getAllIncludedServices (sequence<BluetoothServiceUuid> services);
Finally, I'll remove from BluetoothGATTCharacteristic:
Promise<BluetoothGATTDescriptorSequence> getAllDescriptors (sequence<BluetoothDescriptorUuid> descriptors);
Discussion on dev-webapi@mozilla indicates that it would probably be more webby to expose the set of Characteristic Properties as a dictionary of boolean values rather than the bitfield that raw Bluetooth exposes. This will also let us expose the Extended Properties descriptor (3.G.3.3.3.1) uniformly with the basic properties (3.G.3.3.1.1).
The Chrome Apps API exposes a list of strings, which splits the difference. I think it'll be easier for developers to use if we expose this as a dictionary (or Set?) rather than making them search a list.
Trying to expose the extended properties uniformly with the basic properties raises the question of whether properties
should return a value or a Promise
. The ATT message that discovers the Characteristic itself includes the basic properties, but if the Extended Properties
bit is set there, the UA will need to discover a prefix of the Characteristic's Descriptors and read the value of the Characteristic Extended Properties, for at least 2 extra radio round-trips before the Characteristic can be returned. It's likely that developers won't inspect the properties
field in most cases, since they're writing an application against a specific Service whose definition defines which characteristic properties are set, so I'm inclined to have properties
return a Promise
.
Right now it's in ReSpec with HTML sectioning.
stringifier
(w3c/respec#31). It generates weird sub-sections for members of dictionaries, and while there's an undocumented noLegacyStyle
switch to turn them off, that simply deletes the associated documentation rather than just formatting it better (w3c/respec#334).I'm not going to investigate this in the short run, but if a clear preference emerges here, we can switch.
The battery level service might not be in a device's advertising data, but once the device is connected, a website interested in battery levels should be able to give users the option to pair it.
The overloaded variants of getAllServices that filter the result by UUID should probably accept an argument called uuids or uuid instead of services/service. It's not clear just by looking at the signature that these are meant to be UUIDs and not service IDs.
Should also add a note on the format of the UUID. I.e. if 16-bit, 32-bit, 128-bit, or all formats are accepted.
Once a user has paired a site with a device, the current spec allows the site to completely enumerate the Services, Characteristics, and Descriptors defined by the device. We could instead require the site to explicitly list all the Services it expects to interact with, and then invent some UI (but what?) to warn the user what abilities the site will have. Should we?
The description should read "Once enabled, an application can listen to notifications /indications using the onCharacteristicValueChanged event." If the behavior will follow chrome.bluetoothLowEnergy, then this method (and the event) should enable (and propagate) handle-value indications as well.
BT4.1 3.F.3.2.9 Long Attribute Values says:
The maximum length of an attribute value shall be 512 octets.
We should fail an attempt to write a value longer than that before sending it down to the OS or over the radio, in order to protect any fixed-length buffers hiding in those layers.
I realize that I added this event to chrome.bluetoothLowEnergy (mostly for convenience/consistency with onCharacteristicValueChanged) but there may not be much value to it, since BluetoothGattDescriptor.readValue achieves the same thing and there is no notification/indication mechanism for descriptor values. I'm actually thinking of removing it in a future Chrome release from the Chrome API.
Just something to think about.
While reading device discovery and issue #2, came to my mind the issue where an app wants to communicate with more than 1 device. I'm not talking about connecting to a class of devices, but am thinking of a control panel (say mouse/keyboard configuration in my case) where user will configure devices which expose same vendor-defined service. User would have to select more than 1 device in a popup, which is fine.
Currently connect()
is missing auto-connect optional parameter. Do we have any concern to do this? Without auto-connect parameter, I think it will be hard to handle reconnection scenario. If a website already paired and connect with a Smart Device, Smart Device moves out-of-range. Is it nature to reconnect smart device if the user already trusted this website to connect with that specific device?
Aside from removing 'All' from getAll* methods (#40) and removing a few of getAll* methods (#39), we could specify in the interface that we're returning only primary services.
Debating with myself, this would however not be in line with most native APIs, which solely specify getService(s).
This would give:
BluetoothDevice.getPrimaryService(s)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.