Skip to content

Commit d9e74bf

Browse files
authored
bluetooth.gattConnectionAttempted and bluetooth.simulateGattConnectionResponse for Web Bluetooth automation (#650)
* support gattEventGenerated and simulateGattResponse * some fix * Revise to use pendingAutomationResponse * Remove service discovery simulation * Remove promise from activeAlgorithms * Use wait for automatedGATTConnectionResponse * Reset automatedGATTConnectionResponse * automatedGATTConnectionResponse to have not-expected initial value * Immediately return a resolved promise if connected
1 parent c845e58 commit d9e74bf

1 file changed

Lines changed: 162 additions & 43 deletions

File tree

index.bs

Lines changed: 162 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ spec: WEBDRIVER; urlPrefix: https://w3c.github.io/webdriver/
110110
type: dfn
111111
text: error; url: dfn-error
112112
text: local end; url: dfn-local-ends
113+
text: invalid element state; url: dfn-invalid-element-state
113114

114115
</pre>
115116
<pre class="link-defaults">
@@ -3111,6 +3112,13 @@ slots</a> described in the following table:
31113112
tell whether its <a>realm</a> was ever disconnected while it was running.
31123113
</td>
31133114
</tr>
3115+
<tr>
3116+
<td><dfn>\[[automatedGATTConnectionResponse]]</dfn></td>
3117+
<td><code>"not-expected"</code></td>
3118+
<td>
3119+
The simulated GATT connection response code for a GATT connection attempt.
3120+
</td>
3121+
</tr>
31143122
</table>
31153123

31163124
<div algorithm="BluetoothRemoteGATTServer connect">
@@ -3128,45 +3136,65 @@ method, when invoked, MUST perform the following steps:
31283136
but for now sites need to serialize their use of this API
31293137
and/or give the user a way to retry failed operations.
31303138
1. Let |promise| be [=a new promise=].
3139+
1. If [=this=].{{BluetoothRemoteGATTServer/connected}} is `true`,
3140+
[=resolve=] |promise| with [=this=] and return |promise|.
31313141
1. Add |promise| to [=this=].{{[[activeAlgorithms]]}}.
31323142
1. Run the following steps [=in parallel=]:
3133-
1. If [=this=].{{BluetoothRemoteGATTServer/device}}.{{[[representedDevice]]}}
3134-
has no [=ATT Bearer=], do the following sub-steps:
3135-
1. <p id="create-an-att-bearer">Attempt to create an <a>ATT Bearer</a>
3136-
using the procedures described in "Connection Establishment" under
3137-
<a>GAP Interoperability Requirements</a>. Abort this attempt if
3138-
|promise| is removed from
3139-
[=this=].{{[[activeAlgorithms]]}}.</p>
3140-
3141-
<div class="note">
3142-
Note: These procedures can wait forever
3143-
if a connectable advertisement isn't received.
3144-
The website should call {{disconnect()}}
3145-
if it no longer wants to connect.
3146-
</div>
3147-
1. If this attempt was aborted because |promise| was removed from
3148-
[=this=].{{[[activeAlgorithms]]}}, [=queue a global task=] on
3149-
the [=Bluetooth task source=] given |global| to [=reject=]
3150-
|promise| with an "{{AbortError}}" {{DOMException}} and abort these
3151-
steps.
3152-
1. If this attempt failed for another reason, [=queue a global task=]
3153-
on the [=Bluetooth task source=] given |global| to [=reject=]
3154-
|promise| with a "{{NetworkError}}" {{DOMException}} and abort
3155-
these steps.
3156-
1. Use the [=Exchange MTU=] procedure to negotiate the largest
3157-
supported MTU. Ignore any errors from this step.
3158-
1. The UA MAY attempt to bond with the remote device using the <a>BR/EDR
3159-
Bonding Procedure</a> or the [=LE Bonding Procedure=].
3160-
3161-
Note: We would normally prefer to give the website control over
3162-
whether and when bonding happens, but the [Core
3163-
Bluetooth](https://developer.apple.com/library/content/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/AboutCoreBluetooth/Introduction.html)
3164-
platform API doesn't provide a way for UAs to implement such a knob.
3165-
Having a bond is more secure than not having one, so this
3166-
specification allows the UA to opportunistically create one on
3167-
platforms where that's possible. This may cause a user-visible
3168-
pairing dialog to appear when a connection is created, instead of
3169-
when a restricted characteristic is accessed.
3143+
1. If |global|'s [=Window/navigable=]'s
3144+
[=navigable/top-level traversable=]'s <a>simulated Bluetooth adapter</a>
3145+
is not empty, run the following steps:
3146+
1. [=Trigger a gatt connection attempted event=] given |global|'s
3147+
[=Window/navigable=] and [=this=].{{BluetoothRemoteGATTServer/device}}.
3148+
1. If [=this=].{{[[automatedGATTConnectionResponse]]}} is `"not-expected"`,
3149+
set it to `"expected"`.
3150+
1. If [=this=].{{[[automatedGATTConnectionResponse]]}} is `"expected"`,
3151+
wait for it to change.
3152+
1. Let |response| be [=this=].{{[[automatedGATTConnectionResponse]]}}.
3153+
1. Set [=this=].{{[[automatedGATTConnectionResponse]]}} to `"not-expected"`.
3154+
1. If |response| is not `0`, do the
3155+
following sub-steps:
3156+
1. Remove |promise| from [=this=].{{[[activeAlgorithms]]}}.
3157+
1. [=Queue a global task=] on the [=Bluetooth task source=] given
3158+
|global| to [=reject=] |promise| with a "{{NetworkError}}"
3159+
{{DOMException}} and abort these steps.
3160+
1. Otherwise, run the following steps:
3161+
1. If [=this=].{{BluetoothRemoteGATTServer/device}}.{{[[representedDevice]]}}
3162+
has no [=ATT Bearer=], do the following sub-steps:
3163+
1. <p id="create-an-att-bearer">Attempt to create an <a>ATT Bearer</a>
3164+
using the procedures described in "Connection Establishment" under
3165+
<a>GAP Interoperability Requirements</a>. Abort this attempt if
3166+
|promise| is removed from
3167+
[=this=].{{[[activeAlgorithms]]}}.</p>
3168+
3169+
<div class="note">
3170+
Note: These procedures can wait forever
3171+
if a connectable advertisement isn't received.
3172+
The website should call {{disconnect()}}
3173+
if it no longer wants to connect.
3174+
</div>
3175+
1. If this attempt was aborted because |promise| was removed from
3176+
[=this=].{{[[activeAlgorithms]]}}, [=queue a global task=] on
3177+
the [=Bluetooth task source=] given |global| to [=reject=]
3178+
|promise| with an "{{AbortError}}" {{DOMException}} and abort these
3179+
steps.
3180+
1. If this attempt failed for another reason, [=queue a global task=]
3181+
on the [=Bluetooth task source=] given |global| to [=reject=]
3182+
|promise| with a "{{NetworkError}}" {{DOMException}} and abort
3183+
these steps.
3184+
1. Use the [=Exchange MTU=] procedure to negotiate the largest
3185+
supported MTU. Ignore any errors from this step.
3186+
1. The UA MAY attempt to bond with the remote device using the <a>BR/EDR
3187+
Bonding Procedure</a> or the [=LE Bonding Procedure=].
3188+
3189+
Note: We would normally prefer to give the website control over
3190+
whether and when bonding happens, but the [Core
3191+
Bluetooth](https://developer.apple.com/library/content/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/AboutCoreBluetooth/Introduction.html)
3192+
platform API doesn't provide a way for UAs to implement such a knob.
3193+
Having a bond is more secure than not having one, so this
3194+
specification allows the UA to opportunistically create one on
3195+
platforms where that's possible. This may cause a user-visible
3196+
pairing dialog to appear when a connection is created, instead of
3197+
when a restricted characteristic is accessed.
31703198
1. [=Queue a global task=] on the [=Bluetooth task source=] given |global|
31713199
to perform the following sub-steps:
31723200
1. If |promise| is not in [=this=].{{[[activeAlgorithms]]}}, [=reject=]
@@ -5134,6 +5162,7 @@ BluetoothCommand = (
51345162
bluetooth.DisableSimulation //
51355163
bluetooth.SimulatePreconnectedPeripheral //
51365164
bluetooth.SimulateAdvertisement //
5165+
bluetooth.SimulateGattConnectionResponse //
51375166
)
51385167
</pre>
51395168

@@ -5215,7 +5244,7 @@ bluetooth.SimulateAdapterParameters = {
52155244
<div algorithm="remote end steps for bluetooth.simulateAdapter">
52165245
The [=remote end steps=] with command parameters |params| are:
52175246

5218-
1. Let |contextId| be |params|["context"].
5247+
1. Let |contextId| be |params|[`"context"`].
52195248
1. Let |navigable| be the result of [=trying=] to [=get a navigable=] with |contextId|.
52205249
1. If |navigable| is not a [=navigable/top-level traversable=], return [=error=] with [=error code=] [=invalid argument=].
52215250
1. Let |simulatedBluetoothAdapter| be |navigable|'s <a>simulated Bluetooth adapter</a>.
@@ -5278,7 +5307,7 @@ bluetooth.DisableSimulationParameters = {
52785307
<div algorithm="remote end steps for bluetooth.disableSimulation">
52795308
The [=remote end steps=] with command parameters |params| are:
52805309

5281-
1. Let |contextId| be |params|["context"].
5310+
1. Let |contextId| be |params|[`"context"`].
52825311
1. Let |navigable| be the result of [=trying=] to [=get a navigable=] with |contextId|.
52835312
1. If |navigable| is not a [=navigable/top-level traversable=], return [=error=] with [=error code=] [=invalid argument=].
52845313
1. Set |navigable|'s <a>simulated Bluetooth adapter</a> to empty.
@@ -5380,7 +5409,7 @@ bluetooth.SimulateAdvertisementScanEntryParameters = {
53805409
<div algorithm="remote end steps for bluetooth.simulateAdvertisement">
53815410
The [=remote end steps=] with command parameters |params| are:
53825411

5383-
1. Let |contextId| be params["context"].
5412+
1. Let |contextId| be |params|[`"context"`].
53845413
1. Let |topLevelNavigable| be the result of [=trying=] to [=get a navigable=] with |contextId|.
53855414
1. If |topLevelNavigable| is not a [=navigable/top-level traversable=], return [=error=] with [=error code=] [=invalid argument=].
53865415
1. Let |scanEntry| be |params|[`"scanEntry"`].
@@ -5429,6 +5458,58 @@ A [=local end=] could simulate a device advertisement by sending the following m
54295458
</pre>
54305459
</div>
54315460

5461+
#### The bluetooth.simulateGattConnectionResponse Command #### {#bluetooth-simulategattconnectionresponse-command}
5462+
5463+
<pre highlight="cddl" class="cddl remote-cddl local-cddl">
5464+
bluetooth.SimulateGattConnectionResponse = (
5465+
method: "bluetooth.simulateGattConnectionResponse",
5466+
params: bluetooth.SimulateGattConnectionResponseParameters,
5467+
)
5468+
5469+
bluetooth.SimulateGattConnectionResponseParameters = {
5470+
context: text,
5471+
address: text,
5472+
code: uint
5473+
}
5474+
5475+
</pre>
5476+
5477+
<div algorithm="remote end steps for bluetooth.simulateGattConnectionResponse">
5478+
The [=remote end steps=] with command parameters |params| are:
5479+
5480+
1. Let |contextId| be |params|[`"context"`].
5481+
1. Let |navigable| be the result of [=trying=] to [=get a navigable=] with |contextId|.
5482+
1. Let |deviceAddress| be |params|[`"address"`].
5483+
1. Let |simulatedBluetoothAdapter| be |navigable|'s <a>simulated Bluetooth adapter</a>.
5484+
1. If |simulatedBluetoothAdapter| is empty, return [=error=] with [=error code=] [=invalid argument=].
5485+
1. Let |deviceMapping| be |simulatedBluetoothAdapter|'s <a>simulated Bluetooth device mapping</a>.
5486+
1. If |deviceMapping|[|deviceAddress|] [=map/exists=], let |simulatedDevice| be |deviceMapping|[|deviceAddress|].
5487+
Otherwise, return [=error=] with [=error code=] [=invalid argument=].
5488+
1. Let |simulatedDeviceInstance| be the result of <a>get the <code>BluetoothDevice</code> representing</a>
5489+
|simulatedDevice| inside |navigable|'s <a>active window</a>'s <a spec=HTML>associated <code>Navigator</code></a>'s
5490+
[=associated Bluetooth=].
5491+
1. If |simulatedDeviceInstance|.{{[[gatt]]}}.{{[[automatedGATTConnectionResponse]]}} is `"expected"`,
5492+
set |simulatedDeviceInstance|.{{[[gatt]]}}.{{[[automatedGATTConnectionResponse]]}} to |params|[`"code"`].
5493+
1. Otherwise, return [=error=] with [=error code=] [=invalid element state=].
5494+
5495+
</div>
5496+
5497+
<div class="example">
5498+
A [=local end=] could simulate a device gatt connection response of success
5499+
(error code `0x00` according to <a>List of Error Codes</a>) by sending the following message:
5500+
5501+
<pre highlight="json">
5502+
{
5503+
"method": "bluetooth.simulateGattConnectionResponse",
5504+
"params": {
5505+
"context": "cxt-d03fdd81",
5506+
"address": "09:09:09:09:09:09",
5507+
"code": 0
5508+
}
5509+
}
5510+
</pre>
5511+
</div>
5512+
54325513
### Events ### {#bidi-events}
54335514

54345515
#### The bluetooth.requestDevicePromptUpdated Event #### {#bluetooth-requestdevicepromptupdated-event}
@@ -5453,10 +5534,39 @@ To <dfn>trigger a prompt updated event</dfn> given a [=navigable=] |navigable|,
54535534
1. Let |prompt| be the [=device prompt=] (|promptId|, |devices|).
54545535
1. Let |serialized devices| be the result of [=serialize prompt devices=] with |prompt|.
54555536
1. Set [=map of navigables to device prompts=][|navigableId|] to |prompt|.
5456-
1. Let |params| be a [=map=] matching the <code>bluetooth.RequestDevicePromptUpdatedParameters</code> production with the <code>context</code> field set to |navigableId|, the <code>prompt</code> field set to |promptId|, and the <code>devices</code> field set to |serialized devices|.
5537+
1. Let |params| be a [=map=] matching the <code>bluetooth.RequestDevicePromptUpdatedParameters</code> production with the <code>context</code> field set to |navigableId|, the <code>prompt</code> field set to |promptId|, and the <code>devices</code> field set to |serialized devices|.
54575538
1. Let |body| be a [=map=] matching the <code>bluetooth.RequestDevicePromptUpdated</code> production, with the <code>params</code> field set to |params|.
5458-
1. Let |related navigables| be a [=/set=] containing |navigable|.
5459-
1. For each |session| in the [=set of sessions for which an event is enabled=] given "<code>bluetooth.requestDevicePromptUpdated</code>" and |related navigables|:
5539+
1. Let |relatedNavigables| be a [=/set=] containing |navigable|.
5540+
1. For each |session| in the [=set of sessions for which an event is enabled=] given "<code>bluetooth.requestDevicePromptUpdated</code>" and |relatedNavigables|:
5541+
1. [=Emit an event=] with |session| and |body|.
5542+
5543+
</div>
5544+
5545+
#### The bluetooth.gattConnectionAttempted Event #### {#bluetooth-gattConnectionAttempted-event}
5546+
5547+
<pre highlight="cddl" class="cddl local-cddl">
5548+
bluetooth.GattConnectionAttempted = (
5549+
method: "bluetooth.gattConnectionAttempted",
5550+
params: bluetooth.GattConnectionAttemptedParameters
5551+
)
5552+
5553+
bluetooth.GattConnectionAttemptedParameters = {
5554+
context: text,
5555+
address: text
5556+
}
5557+
</pre>
5558+
5559+
<div algorithm="remote end event trigger for bluetooth.GattConnectionAttempted">
5560+
To <dfn>trigger a gatt connection attempted event</dfn> given a [=navigable=] |navigable| and a {{BluetoothDevice}} |device|:
5561+
5562+
1. Let |navigableId| be |navigable|'s [=navigable id=].
5563+
1. Let |params| be a [=map=] matching the <code>bluetooth.GattConnectionAttemptedParameters</code> production with the
5564+
<code>context</code> field set to |navigableId| and the <code>address</code> field set to |device|.{{[[representedDevice]]}}'s address.
5565+
1. Let |body| be a [=map=] matching the <code>bluetooth.GattConnectionAttempted</code> production, with the
5566+
<code>params</code> field set to |params|.
5567+
1. Let |relatedNavigables| be a [=/set=] containing |navigable|.
5568+
1. For each |session| in the [=set of sessions for which an event is enabled=] given
5569+
"<code>bluetooth.gattEventGenerated</code>" and |relatedNavigables|:
54605570
1. [=Emit an event=] with |session| and |body|.
54615571

54625572
</div>
@@ -5526,6 +5636,15 @@ This specification uses a read-only type that is similar to WebIDL's
55265636
</li>
55275637
<li value="2">Core System Package [BR/EDR Controller volume]
55285638
<ol type="A">
5639+
<li value="4">Error Codes
5640+
<ol>
5641+
<li value="1">Overview of Error Codes
5642+
<ol>
5643+
<li value="3"><dfn>List of Error Codes</dfn></li>
5644+
</ol>
5645+
</li>
5646+
</ol>
5647+
</li>
55295648
<li value="5">Host Controller Interface Functional Specification
55305649
<ol>
55315650
<li value="7">HCI Commands and Events

0 commit comments

Comments
 (0)