On 2014-09-10, 6:00 AM, Ben Tian wrote:
> Hi Ehsan/Jeffrey,
>
> Thanks for your questions and feedbacks. Please see my response below.
>
> ===
>
> *Q1: (BluetoothAdapter.startLeScan) Do we only recognize the
> standardized service uuids here?*
>
>
> No, not only the standardized service uuids but also customized ones.
> This allows applications for customized services and hardwares.
OK, fair enough.
> *Q2: (BluetoothDevice.connectGatt) The prose says that it creates a
> *new* BluetoothGatt object, and assigns it to the gatt property, and
> resolves the promise with it once the connection is established. Here
> are some questions:*
>
> * 1. What happens if you call this method twice? Will the gatt
> property get overridden by the second BluetoothGatt object?*
>
>
> It should check whether a BluetoothGatt object already exists before
> creating a new one. If yes it would reuse the existing one. *I'll revise
> the prose.*
Having this method do different things depending on whether it has been
called before is really suboptimal. Please don't do that!
> *Q3: 2. What is the use case for accessing the BluetoothGatt property
> before the connection is established?*
>
>
> They would be default values: an empty array [1] and
> BluetoothConnectionState.disconnected [2]. *I'll add explanation for this.*
> *Q4: 3. If there is a use case in #2, we should probably resolve the
> promise to the BluetoothGatt object, and just rely on the caller to call
> connect() on it. Then we can remove the gatt property.*
>
> * 4. Otherwise, we should remove the gatt property, and remove the
> connect() method on BluetoothGatt as well.*
This semantic is really awkward, and I think we can do much better.
Please consider the following counter proposal (based on my poor
understanding of what we want to achieve here, please suggest corrections!)
* BluetoothDevice will have a gatt property, which is always available.
That is just a JS object, and accessing that property doesn't initiate
any Bluetooth activity.
* Add a discover() method or some such that will start the discovery
process, which returns a promise resolving to void (or something
sensible) once the discovery is finished.
* Have the promise returned by connect() rejected if the service
discovery has not been performed yet.
* Remove the connectGatt() method altogether.
Here is how the API would be used:
device.gatt.discover().then(() => {
device.gatt.connect().then(() => {
// do something here
})
})
> *Q5: Is the BluetoothGattService object returned from findService
> guaranteed to be available synchronously?*
>
> Jeffrey: It depends on your implementation. It's totally plausible to
> discover all Services, Characteristics, and Descriptors during the
> connection process, in which case all the find*() methods can return
> synchronously.
>
>
> This is the way we'd like to implement. That's why we make the method
> synchronous.
That wasn't really my question. My question was, is this guaranteed to
be available synchronously on all implementations, always? If not, we
should make the method asynchronous *even* if we can implement it
synchronously right now.
> Jeffery: On the other hand, it's also plausible to discover these
> entities lazily, when they're first needed. This may save some power on
> both devices, since less data may need to cross the radio. Requiring a
> synchronous return precludes this potential optimization. On the other
> hand, the "optimization" may wind up costing power if the application
> accesses services in a pattern that confuses the browser logic.
And FWIW, this is an example of a scenario where this won't be available
synchronously.
> The W3C proposal currently returns Promises from these methods, but we
> can change that if implementers think the optimization isn't worth
> supporting.
>
>
> *Q6: I think that given the above, it's a mistake to design the API
> synchronously.*
>
>
> Why? It's the decision of implementation, right?
No it's not. By making the *API* synchronous, you force all future
implementations of the same API to be synchronous as well. I understand
that this is a certified API which we can at least in theory change at
will, but proper API designs involves thinking about implementation
issues beyond what the implementer of the API in one engine tends to
think about.
> *Q7: (BluetoothGattCharacteristic) Please use WebIDL enums, not numeric
> enums.*
>
> Jeffery: Note that these are bitfields intended to represent a set of
> boolean options, rather than enumerations representing a single choice.
> If we (including myself for
>
http://webbluetoothcg.github.io/web-bluetooth/) switch to string names,
> we should probably do it as a dictionary with boolean values rather than
> an enum.
>
>
> Boolean dictionary seems a suitable option that we didn’t consider for
> PROPERTY_*, PERMISSION_*, and WRITE_TYPE_*. Let us discuss internally
> and bring back some conclusion.
Sounds good!
> Jeffery: The write type interacts with the Characteristic Properties
> (WRITE_NO_RESPONSE, WRITE, and SIGNED_WRITE). For Characteristics that
> support more than one of these, WRITE_TYPE_* could be useful as an
> optional argument to writeValue(), but I don't see that in the Mozilla
> API spec. Permissions should probably be a dictionary like Properties.
> If WRITE_TYPE is intended to control how a particular writeValue()
> operation works, it needs to be an enum rather than a bit field.
>
>
> We don’t make WRITE_TYPE_* an optional argument since we think it lasts
> through writeValue() operations instead of a one-time setting for a
> particular writeValue() operation. Also I prefer boolean dictionary for
> WRITE_TYPE_* since enum values cannot be combined for Characteristics
> that support more than one of WRITE_TYPE_*.
>
>
> *Q8: Is there any reason for having the "value" property?*
>
> Jeffrey: Including 'value' allows
>
https://wiki.mozilla.org/B2G/Bluetooth/WebBluetooth-v2/BluetoothGatt#oncharacteristicchanged
> to pass just the characteristic when a change is notified, rather than
> having to pass both the characteristic and its value. (Calling
> readValue() redundantly in that event handler would waste radio and
> might fail entirely because not all notifiable characteristics are
> readable.) I'm not sure that's enough benefit to justify the property
> and the risk of stale reads that it implies.
>
>
> BluetoothGattCharacteristic.value is the cached value of the
> characteristic. Having “value” property so that apps don’t have to
> always queryremote device (i.e., call
> BluetoothGattCharacteristic.readValue()) for the characteristic’s value.
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.
> *Q9: (BluetoothGatt) Reliable Writes*
Ah I think I understand this part now! This is basically some kind of a
transaction concept, is that correct?
> *Q10: (BluetoothLeDeviceEvent) When can |device| be null here?*
>
>
> No, it shouldn’t. That was a typo and I’ve removed the “?” already.
Great, thanks!
Cheers,
Ehsan
> ------------------------------------------------------------------------
> *寄件者: *"Jeffrey Yasskin" <
jyas...@google.com>
> *收件者: *"Ehsan Akhgari" <
ehsan....@gmail.com>
> *副本: *"Ben Tian" <
bt...@mozilla.com>, "dev-webapi"
> <
dev-w...@lists.mozilla.org>, "Eric Chou" <
ec...@mozilla.com>, "Shawn
> *寄件備份: *2014 9 月 10 星期三 上午 4:26:18
> *主旨: *Re: WebBluetooth BLE GATT client API
> >>> The prose says that it creates a *new* BluetoothGatt object, and
> assigns
> >>> it
> >>> to the gatt property, and resolves the promise with it once the
> >>> connection
> >>> is established. Here are some questions:
> >>>
> >>> 1. What happens if you call this method twice? Will the gatt property
> >>> get
> >>> overridden by the second BluetoothGatt object?
> >>> 2. What is the use case for accessing the BluetoothGatt property before
> >>> the
> >>> connection is established?
> >>> 3. If there is a use case in #2, we should probably resolve the promise
> >>> to
> >>> the BluetoothGatt object, and just rely on the caller to call connect()
> >>> on
> >>> it. Then we can remove the gatt property.
> >>> 4. Otherwise, we should remove the gatt property, and remove the
> >>> connect()
> >>> method on BluetoothGatt as well.
> >>>
> >>>> *
> >>>> Interfaces
> >>>>
> >>>> * BluetoothGatt
> >>>
> >>>
> >>>
> >>> Is the BluetoothGattService object returned from findService guaranteed
> >>> to
> >>> be available synchronously?
> >>
> >>
> >> It depends on your implementation. It's totally plausible to discover
> >> all Services, Characteristics, and Descriptors during the connection
> >> process, in which case all the find*() methods can return
> >> synchronously.
> >>
> >> On the other hand, it's also plausible to discover these entities
> >> lazily, when they're first needed. This may save some power on both
> >> devices, since less data may need to cross the radio. Requiring a
> >> synchronous return precludes this potential optimization. On the other
> >> hand, the "optimization" may wind up costing power if the application
> >> accesses services in a pattern that confuses the browser logic.
> >>
> >> The W3C proposal currently returns Promises from these methods, but we
> >> can change that if implementers think the optimization isn't worth
> >> supporting.
> >
> >
> > I think that given the above, it's a mistake to design the API
> > synchronously.
> >
> >>>> * BluetoothGattService
> >>>
> >>>
> >>>
> >>> Ditto for findCharactersitic().
> >>>
> >>>> * BluetoothGattCharacteristic
> >>>
> >>>
> >>>
> >>> Please use WebIDL enums, not numeric enums.
> >>
> >>
> >> Note that these are bitfields intended to represent a set of boolean
> >> options, rather than enumerations representing a single choice. If we
> >> (including myself for
http://webbluetoothcg.github.io/web-bluetooth/)
> >> switch to string names, we should probably do it as a dictionary with
> >> boolean values rather than an enum.
> >
> >
> >> The write type interacts with the Characteristic Properties
> >> (WRITE_NO_RESPONSE, WRITE, and SIGNED_WRITE). For Characteristics that
> >> support more than one of these, WRITE_TYPE_* could be useful as an
> >> optional argument to writeValue(), but I don't see that in the Mozilla
> >> API spec.
> >>
> >> Permissions should probably be a dictionary like Properties. If
> >> WRITE_TYPE is intended to control how a particular writeValue()
> >> operation works, it needs to be an enum rather than a bit field.
> >
> >
> > I think there is an argument to be made for making this API easier for JS
> > progreammers who may not be familiar with the Bluetooth spec, etc.
> However,
> > there are other examples of using these C-style numeric enums in the
> > platform too, so the situation is definitely not clear-cut. However, most
> > modern APIs attempt to do things "the JS way."
> >
> >>> Is there any reason for having the "value" property?
> >>
> >>
> >> Including 'value' allows
> >>
> >>
>
https://wiki.mozilla.org/B2G/Bluetooth/WebBluetooth-v2/BluetoothGatt#oncharacteristicchanged
> >> to pass just the characteristic when a change is notified, rather than
> >> having to pass both the characteristic and its value. (Calling
> >> readValue() redundantly in that event handler would waste radio and
> >> might fail entirely because not all notifiable characteristics are
> >> readable.) I'm not sure that's enough benefit to justify the property
> >> and the risk of stale reads that it implies.
> >
> >
> > Why can't the value live on the event though?
>
> I think it can. You'd have
>
https://wiki.mozilla.org/B2G/Bluetooth/WebBluetooth-v2/BluetoothGattCharacteristicEvent
> include both a 'characteristic' field and a 'value' field.
> Alternately, I've been considering having those events dispatched to
> the Characteristic object itself and bubble up to the equivalent of
> BluetoothGatt, so the 'target' field would point to the
> Characteristic. We'd still want the 'value' field on the event if it's
> not on the Characteristic.
>
> A downside of dispatching the event to the Characteristic object would
> be that it could make garbage-collecting the Characteristics hard. We
> might have to specify when the "active" characteristic object is
> replaced. e.g. when the page is unloaded? when the application
> explicitly disconnects? when the device loses its connection by moving
> out of range, but then moves back into range and auto-reconnects? But
> we might have to specify that anyway.
>
> >> The cached value is also used in the case of reliable writes, where
> >> the Server echoes the written value, and the Client is expected to
> >> check that against what it just wrote. However, that will likely be
> >> handled inside the Browser without needing Javascript to be involved.
> >
> >
> > I'm having difficulty understanding this bit (I'm not familiar with the
> > Bluetooth spec.)
>
> Sorry. This is the "Reliable Writes" algorithm in section 3.G.4.9.5 of
>
https://www.bluetooth.org/DocMan/handlers/DownloadDoc.ashx?doc_id=282159
> (BT4.1). That's defined in terms of Attribute Protocol messages
> defined in 3.F.3.4.6, although you probably don't need to understand
> those well. The application would call writeValue(big_array_buffer),
> and the Browser needs to save that value through the whole sequence of
> 'Prepare Write Request' and 'Prepare Write Response' messages so that
> it can validate the data in the Response and decide whether to commit
> the write or abort it. But again, I think that's all internal to the
> implementation and doesn't really need to be exposed to Javascript as
> a 'value' field on the Characteristic.
>
> It's also possible I'm missing another use for the 'value' field that
> does need it to be exposed.
>
> >>> And same question about findDescriptor() as above.
> >>>
> >>>> * BluetoothGattDescriptor
> >>>
> >>>
> >>>
> >>> Same comment about enums.
> >>>
> >>> Also, again I don't understand why |value| is useful.
> >>>
> >>>> * BluetoothLeDeviceEvent
> >>>
> >>>
> >>>
> >>> When can |device| be null here?
> >>>