1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 // When a device can't be found in the BluetoothAdapter, that generally
7 // indicates that it's gone out of range. We reject with a NetworkError in that
9 // https://webbluetoothchrome.github.io/web-bluetooth/#dom-bluetoothdevice-connectgatt
11 #include "content/browser/bluetooth/bluetooth_dispatcher_host.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "content/browser/bad_message.h"
15 #include "content/browser/bluetooth/bluetooth_metrics.h"
16 #include "content/browser/frame_host/render_frame_host_impl.h"
17 #include "content/common/bluetooth/bluetooth_messages.h"
18 #include "device/bluetooth/bluetooth_adapter.h"
19 #include "device/bluetooth/bluetooth_adapter_factory.h"
20 #include "device/bluetooth/bluetooth_device.h"
21 #include "device/bluetooth/bluetooth_discovery_session.h"
22 #include "device/bluetooth/bluetooth_gatt_characteristic.h"
23 #include "device/bluetooth/bluetooth_gatt_service.h"
25 using blink::WebBluetoothError
;
26 using device::BluetoothAdapter
;
27 using device::BluetoothAdapterFactory
;
28 using device::BluetoothGattCharacteristic
;
29 using device::BluetoothGattService
;
30 using device::BluetoothUUID
;
36 // TODO(ortuno): Once we have a chooser for scanning and the right
37 // callback for discovered services we should delete these constants.
38 // https://crbug.com/436280 and https://crbug.com/484504
39 const int kDelayTime
= 5; // 5 seconds for scanning and discovering
40 const int kTestingDelayTime
= 0; // No need to wait during tests
43 // https://webbluetoothchrome.github.io/web-bluetooth/#dfn-matches-a-filter
44 bool MatchesFilter(const std::set
<BluetoothUUID
>& device_uuids
,
45 const content::BluetoothScanFilter
& filter
) {
46 if (filter
.services
.empty())
48 for (const BluetoothUUID
& service
: filter
.services
) {
49 if (!ContainsKey(device_uuids
, service
)) {
56 bool MatchesFilters(const device::BluetoothDevice
& device
,
57 const std::vector
<content::BluetoothScanFilter
>& filters
) {
58 const std::vector
<BluetoothUUID
>& device_uuid_list
= device
.GetUUIDs();
59 const std::set
<BluetoothUUID
> device_uuids(device_uuid_list
.begin(),
60 device_uuid_list
.end());
61 for (const content::BluetoothScanFilter
& filter
: filters
) {
62 if (MatchesFilter(device_uuids
, filter
)) {
69 WebBluetoothError
TranslateConnectError(
70 device::BluetoothDevice::ConnectErrorCode error_code
) {
72 case device::BluetoothDevice::ERROR_UNKNOWN
:
73 RecordConnectGATTOutcome(UMAConnectGATTOutcome::UNKNOWN
);
74 return WebBluetoothError::ConnectUnknownError
;
75 case device::BluetoothDevice::ERROR_INPROGRESS
:
76 RecordConnectGATTOutcome(UMAConnectGATTOutcome::IN_PROGRESS
);
77 return WebBluetoothError::ConnectAlreadyInProgress
;
78 case device::BluetoothDevice::ERROR_FAILED
:
79 RecordConnectGATTOutcome(UMAConnectGATTOutcome::FAILED
);
80 return WebBluetoothError::ConnectUnknownFailure
;
81 case device::BluetoothDevice::ERROR_AUTH_FAILED
:
82 RecordConnectGATTOutcome(UMAConnectGATTOutcome::AUTH_FAILED
);
83 return WebBluetoothError::ConnectAuthFailed
;
84 case device::BluetoothDevice::ERROR_AUTH_CANCELED
:
85 RecordConnectGATTOutcome(UMAConnectGATTOutcome::AUTH_CANCELED
);
86 return WebBluetoothError::ConnectAuthCanceled
;
87 case device::BluetoothDevice::ERROR_AUTH_REJECTED
:
88 RecordConnectGATTOutcome(UMAConnectGATTOutcome::AUTH_REJECTED
);
89 return WebBluetoothError::ConnectAuthRejected
;
90 case device::BluetoothDevice::ERROR_AUTH_TIMEOUT
:
91 RecordConnectGATTOutcome(UMAConnectGATTOutcome::AUTH_TIMEOUT
);
92 return WebBluetoothError::ConnectAuthTimeout
;
93 case device::BluetoothDevice::ERROR_UNSUPPORTED_DEVICE
:
94 RecordConnectGATTOutcome(UMAConnectGATTOutcome::UNSUPPORTED_DEVICE
);
95 return WebBluetoothError::ConnectUnsupportedDevice
;
98 return WebBluetoothError::UntranslatedConnectErrorCode
;
101 blink::WebBluetoothError
TranslateGATTError(
102 BluetoothGattService::GattErrorCode error_code
,
103 UMAGATTOperation operation
) {
104 switch (error_code
) {
105 case BluetoothGattService::GATT_ERROR_UNKNOWN
:
106 RecordGATTOperationOutcome(operation
, UMAGATTOperationOutcome::UNKNOWN
);
107 return blink::WebBluetoothError::GATTUnknownError
;
108 case BluetoothGattService::GATT_ERROR_FAILED
:
109 RecordGATTOperationOutcome(operation
, UMAGATTOperationOutcome::FAILED
);
110 return blink::WebBluetoothError::GATTUnknownFailure
;
111 case BluetoothGattService::GATT_ERROR_IN_PROGRESS
:
112 RecordGATTOperationOutcome(operation
,
113 UMAGATTOperationOutcome::IN_PROGRESS
);
114 return blink::WebBluetoothError::GATTOperationInProgress
;
115 case BluetoothGattService::GATT_ERROR_INVALID_LENGTH
:
116 RecordGATTOperationOutcome(operation
,
117 UMAGATTOperationOutcome::INVALID_LENGTH
);
118 return blink::WebBluetoothError::GATTInvalidAttributeLength
;
119 case BluetoothGattService::GATT_ERROR_NOT_PERMITTED
:
120 RecordGATTOperationOutcome(operation
,
121 UMAGATTOperationOutcome::NOT_PERMITTED
);
122 return blink::WebBluetoothError::GATTNotPermitted
;
123 case BluetoothGattService::GATT_ERROR_NOT_AUTHORIZED
:
124 RecordGATTOperationOutcome(operation
,
125 UMAGATTOperationOutcome::NOT_AUTHORIZED
);
126 return blink::WebBluetoothError::GATTNotAuthorized
;
127 case BluetoothGattService::GATT_ERROR_NOT_PAIRED
:
128 RecordGATTOperationOutcome(operation
,
129 UMAGATTOperationOutcome::NOT_PAIRED
);
130 return blink::WebBluetoothError::GATTNotPaired
;
131 case BluetoothGattService::GATT_ERROR_NOT_SUPPORTED
:
132 RecordGATTOperationOutcome(operation
,
133 UMAGATTOperationOutcome::NOT_SUPPORTED
);
134 return blink::WebBluetoothError::GATTNotSupported
;
137 return blink::WebBluetoothError::GATTUntranslatedErrorCode
;
142 BluetoothDispatcherHost::BluetoothDispatcherHost(int render_process_id
)
143 : BrowserMessageFilter(BluetoothMsgStart
),
144 render_process_id_(render_process_id
),
145 weak_ptr_factory_(this) {
146 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
147 current_delay_time_
= kDelayTime
;
148 if (BluetoothAdapterFactory::IsBluetoothAdapterAvailable())
149 BluetoothAdapterFactory::GetAdapter(
150 base::Bind(&BluetoothDispatcherHost::set_adapter
,
151 weak_ptr_factory_
.GetWeakPtr()));
154 void BluetoothDispatcherHost::OnDestruct() const {
155 // See class comment: UI Thread Note.
156 BrowserThread::DeleteOnUIThread::Destruct(this);
159 void BluetoothDispatcherHost::OverrideThreadForMessage(
160 const IPC::Message
& message
,
161 content::BrowserThread::ID
* thread
) {
162 // See class comment: UI Thread Note.
163 *thread
= BrowserThread::UI
;
166 bool BluetoothDispatcherHost::OnMessageReceived(const IPC::Message
& message
) {
167 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
169 IPC_BEGIN_MESSAGE_MAP(BluetoothDispatcherHost
, message
)
170 IPC_MESSAGE_HANDLER(BluetoothHostMsg_RequestDevice
, OnRequestDevice
)
171 IPC_MESSAGE_HANDLER(BluetoothHostMsg_ConnectGATT
, OnConnectGATT
)
172 IPC_MESSAGE_HANDLER(BluetoothHostMsg_GetPrimaryService
, OnGetPrimaryService
)
173 IPC_MESSAGE_HANDLER(BluetoothHostMsg_GetCharacteristic
, OnGetCharacteristic
)
174 IPC_MESSAGE_HANDLER(BluetoothHostMsg_ReadValue
, OnReadValue
)
175 IPC_MESSAGE_HANDLER(BluetoothHostMsg_WriteValue
, OnWriteValue
)
176 IPC_MESSAGE_UNHANDLED(handled
= false)
177 IPC_END_MESSAGE_MAP()
181 void BluetoothDispatcherHost::SetBluetoothAdapterForTesting(
182 scoped_refptr
<device::BluetoothAdapter
> mock_adapter
) {
183 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
184 current_delay_time_
= kTestingDelayTime
;
185 set_adapter(mock_adapter
.Pass());
188 BluetoothDispatcherHost::~BluetoothDispatcherHost() {
189 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
190 // Clear adapter, releasing observer references.
191 set_adapter(scoped_refptr
<device::BluetoothAdapter
>());
194 // Stores information associated with an in-progress requestDevice call. This
195 // will include the state of the active chooser dialog in a future patch.
196 struct BluetoothDispatcherHost::RequestDeviceSession
{
197 RequestDeviceSession(const std::vector
<BluetoothScanFilter
>& filters
,
198 const std::vector
<BluetoothUUID
>& optional_services
)
199 : filters(filters
), optional_services(optional_services
) {}
201 std::vector
<BluetoothScanFilter
> filters
;
202 std::vector
<BluetoothUUID
> optional_services
;
205 void BluetoothDispatcherHost::set_adapter(
206 scoped_refptr
<device::BluetoothAdapter
> adapter
) {
207 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
209 adapter_
->RemoveObserver(this);
212 adapter_
->AddObserver(this);
215 static scoped_ptr
<device::BluetoothDiscoveryFilter
> ComputeScanFilter(
216 const std::vector
<BluetoothScanFilter
>& filters
) {
217 std::set
<BluetoothUUID
> services
;
218 for (const BluetoothScanFilter
& filter
: filters
) {
219 services
.insert(filter
.services
.begin(), filter
.services
.end());
221 scoped_ptr
<device::BluetoothDiscoveryFilter
> discovery_filter(
222 new device::BluetoothDiscoveryFilter(
223 device::BluetoothDiscoveryFilter::TRANSPORT_DUAL
));
224 for (const BluetoothUUID
& service
: services
) {
225 discovery_filter
->AddUUID(service
);
227 return discovery_filter
.Pass();
230 void BluetoothDispatcherHost::OnRequestDevice(
233 int frame_routing_id
,
234 const std::vector
<BluetoothScanFilter
>& filters
,
235 const std::vector
<BluetoothUUID
>& optional_services
) {
236 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
237 RecordWebBluetoothFunctionCall(UMAWebBluetoothFunction::REQUEST_DEVICE
);
238 RecordRequestDeviceArguments(filters
, optional_services
);
240 VLOG(1) << "requestDevice called with the following filters: ";
241 for (const BluetoothScanFilter
& filter
: filters
) {
243 for (const BluetoothUUID
& service
: filter
.services
)
244 VLOG(1) << "\t\t" << service
.value();
248 VLOG(1) << "requestDevice called with the following optional services: ";
249 for (const BluetoothUUID
& service
: optional_services
)
250 VLOG(1) << "\t" << service
.value();
252 RenderFrameHostImpl
* render_frame_host
=
253 RenderFrameHostImpl::FromID(render_process_id_
, frame_routing_id
);
255 if (!render_frame_host
) {
257 << "Got a requestDevice IPC without a matching RenderFrameHost: "
258 << render_process_id_
<< ", " << frame_routing_id
;
259 RecordRequestDeviceOutcome(UMARequestDeviceOutcome::NO_RENDER_FRAME
);
260 Send(new BluetoothMsg_RequestDeviceError(
261 thread_id
, request_id
, WebBluetoothError::RequestDeviceWithoutFrame
));
265 // TODO(scheib): Device selection UI: crbug.com/436280
266 // TODO(scheib): Utilize BluetoothAdapter::Observer::DeviceAdded/Removed.
267 if (adapter_
.get()) {
268 if (!request_device_sessions_
269 .insert(std::make_pair(
270 std::make_pair(thread_id
, request_id
),
271 RequestDeviceSession(filters
, optional_services
)))
273 LOG(ERROR
) << "2 requestDevice() calls with the same thread_id ("
274 << thread_id
<< ") and request_id (" << request_id
275 << ") shouldn't arrive at the same BluetoothDispatcherHost.";
276 bad_message::ReceivedBadMessage(
277 this, bad_message::BDH_DUPLICATE_REQUEST_DEVICE_ID
);
279 if (!adapter_
->IsPresent()) {
280 VLOG(1) << "Bluetooth Adapter not present. Can't serve requestDevice.";
281 RecordRequestDeviceOutcome(
282 UMARequestDeviceOutcome::BLUETOOTH_ADAPTER_NOT_PRESENT
);
283 Send(new BluetoothMsg_RequestDeviceError(
284 thread_id
, request_id
, WebBluetoothError::NoBluetoothAdapter
));
285 request_device_sessions_
.erase(std::make_pair(thread_id
, request_id
));
288 // TODO(jyasskin): Once the dialog is available, the dialog should check for
289 // the status of the adapter, i.e. check IsPowered() and
290 // BluetoothAdapter::Observer::PoweredChanged, and inform the user. But
291 // until the dialog is available we log/histogram the status and return
293 // https://crbug.com/517237
294 if (!adapter_
->IsPowered()) {
295 RecordRequestDeviceOutcome(
296 UMARequestDeviceOutcome::BLUETOOTH_ADAPTER_OFF
);
297 Send(new BluetoothMsg_RequestDeviceError(
298 thread_id
, request_id
, WebBluetoothError::BluetoothAdapterOff
));
299 request_device_sessions_
.erase(std::make_pair(thread_id
, request_id
));
302 adapter_
->StartDiscoverySessionWithFilter(
303 ComputeScanFilter(filters
),
304 base::Bind(&BluetoothDispatcherHost::OnDiscoverySessionStarted
,
305 weak_ptr_factory_
.GetWeakPtr(), thread_id
, request_id
),
306 base::Bind(&BluetoothDispatcherHost::OnDiscoverySessionStartedError
,
307 weak_ptr_factory_
.GetWeakPtr(), thread_id
, request_id
));
309 VLOG(1) << "No BluetoothAdapter. Can't serve requestDevice.";
310 RecordRequestDeviceOutcome(UMARequestDeviceOutcome::NO_BLUETOOTH_ADAPTER
);
311 Send(new BluetoothMsg_RequestDeviceError(
312 thread_id
, request_id
, WebBluetoothError::NoBluetoothAdapter
));
317 void BluetoothDispatcherHost::OnConnectGATT(
320 const std::string
& device_instance_id
) {
321 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
322 RecordWebBluetoothFunctionCall(UMAWebBluetoothFunction::CONNECT_GATT
);
323 const base::TimeTicks start_time
= base::TimeTicks::Now();
325 // TODO(ortuno): Right now it's pointless to check if the domain has access to
326 // the device, because any domain can connect to any device. But once
327 // permissions are implemented we should check that the domain has access to
328 // the device. https://crbug.com/484745
329 device::BluetoothDevice
* device
= adapter_
->GetDevice(device_instance_id
);
330 if (device
== nullptr) { // See "NETWORK_ERROR Note" above.
331 RecordConnectGATTOutcome(UMAConnectGATTOutcome::NO_DEVICE
);
332 Send(new BluetoothMsg_ConnectGATTError(
333 thread_id
, request_id
, WebBluetoothError::DeviceNoLongerInRange
));
336 device
->CreateGattConnection(
337 base::Bind(&BluetoothDispatcherHost::OnGATTConnectionCreated
,
338 weak_ptr_factory_
.GetWeakPtr(), thread_id
, request_id
,
339 device_instance_id
, start_time
),
340 base::Bind(&BluetoothDispatcherHost::OnCreateGATTConnectionError
,
341 weak_ptr_factory_
.GetWeakPtr(), thread_id
, request_id
,
342 device_instance_id
, start_time
));
345 void BluetoothDispatcherHost::OnGetPrimaryService(
348 const std::string
& device_instance_id
,
349 const std::string
& service_uuid
) {
350 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
351 RecordWebBluetoothFunctionCall(UMAWebBluetoothFunction::GET_PRIMARY_SERVICE
);
352 RecordGetPrimaryServiceService(BluetoothUUID(service_uuid
));
354 // TODO(ortuno): Check if device_instance_id is in "allowed devices"
355 // https://crbug.com/493459
356 // TODO(ortuno): Check if service_uuid is in "allowed services"
357 // https://crbug.com/493460
358 // For now just wait a fixed time and call OnServiceDiscovered.
359 // TODO(ortuno): Use callback once it's implemented http://crbug.com/484504
360 BrowserThread::PostDelayedTask(
361 BrowserThread::UI
, FROM_HERE
,
362 base::Bind(&BluetoothDispatcherHost::OnServicesDiscovered
,
363 weak_ptr_factory_
.GetWeakPtr(), thread_id
, request_id
,
364 device_instance_id
, service_uuid
),
365 base::TimeDelta::FromSeconds(current_delay_time_
));
368 void BluetoothDispatcherHost::OnGetCharacteristic(
371 const std::string
& service_instance_id
,
372 const std::string
& characteristic_uuid
) {
373 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
374 RecordWebBluetoothFunctionCall(UMAWebBluetoothFunction::GET_CHARACTERISTIC
);
375 RecordGetCharacteristicCharacteristic(characteristic_uuid
);
377 auto device_iter
= service_to_device_
.find(service_instance_id
);
378 // A service_instance_id not in the map implies a hostile renderer
379 // because a renderer obtains the service id from this class and
380 // it will be added to the map at that time.
381 if (device_iter
== service_to_device_
.end()) {
383 bad_message::ReceivedBadMessage(this, bad_message::BDH_INVALID_SERVICE_ID
);
387 // TODO(ortuno): Check if domain has access to device.
388 // https://crbug.com/493459
389 device::BluetoothDevice
* device
=
390 adapter_
->GetDevice(device_iter
->second
/* device_instance_id */);
392 if (device
== nullptr) { // See "NETWORK_ERROR Note" above.
393 RecordGetCharacteristicOutcome(UMAGetCharacteristicOutcome::NO_DEVICE
);
394 Send(new BluetoothMsg_GetCharacteristicError(
395 thread_id
, request_id
, WebBluetoothError::DeviceNoLongerInRange
));
399 // TODO(ortuno): Check if domain has access to service
400 // http://crbug.com/493460
401 device::BluetoothGattService
* service
=
402 device
->GetGattService(service_instance_id
);
404 RecordGetCharacteristicOutcome(UMAGetCharacteristicOutcome::NO_SERVICE
);
405 Send(new BluetoothMsg_GetCharacteristicError(
406 thread_id
, request_id
, WebBluetoothError::ServiceNoLongerExists
));
410 for (BluetoothGattCharacteristic
* characteristic
:
411 service
->GetCharacteristics()) {
412 if (characteristic
->GetUUID().canonical_value() == characteristic_uuid
) {
413 const std::string
& characteristic_instance_id
=
414 characteristic
->GetIdentifier();
416 auto insert_result
= characteristic_to_service_
.insert(
417 make_pair(characteristic_instance_id
, service_instance_id
));
419 // If value is already in map, DCHECK it's valid.
420 if (!insert_result
.second
)
421 DCHECK(insert_result
.first
->second
== service_instance_id
);
423 RecordGetCharacteristicOutcome(UMAGetCharacteristicOutcome::SUCCESS
);
424 // TODO(ortuno): Use generated instance ID instead.
425 // https://crbug.com/495379
426 Send(new BluetoothMsg_GetCharacteristicSuccess(
427 thread_id
, request_id
, characteristic_instance_id
));
431 RecordGetCharacteristicOutcome(UMAGetCharacteristicOutcome::NOT_FOUND
);
432 Send(new BluetoothMsg_GetCharacteristicError(
433 thread_id
, request_id
, WebBluetoothError::CharacteristicNotFound
));
436 void BluetoothDispatcherHost::OnReadValue(
439 const std::string
& characteristic_instance_id
) {
440 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
441 RecordWebBluetoothFunctionCall(
442 UMAWebBluetoothFunction::CHARACTERISTIC_READ_VALUE
);
444 auto characteristic_iter
=
445 characteristic_to_service_
.find(characteristic_instance_id
);
446 // A characteristic_instance_id not in the map implies a hostile renderer
447 // because a renderer obtains the characteristic id from this class and
448 // it will be added to the map at that time.
449 if (characteristic_iter
== characteristic_to_service_
.end()) {
451 bad_message::ReceivedBadMessage(this,
452 bad_message::BDH_INVALID_CHARACTERISTIC_ID
);
455 const std::string
& service_instance_id
= characteristic_iter
->second
;
457 auto device_iter
= service_to_device_
.find(service_instance_id
);
459 CHECK(device_iter
!= service_to_device_
.end());
461 device::BluetoothDevice
* device
=
462 adapter_
->GetDevice(device_iter
->second
/* device_instance_id */);
463 if (device
== nullptr) { // See "NETWORK_ERROR Note" above.
464 RecordCharacteristicReadValueOutcome(UMAGATTOperationOutcome::NO_DEVICE
);
465 Send(new BluetoothMsg_ReadCharacteristicValueError(
466 thread_id
, request_id
, WebBluetoothError::DeviceNoLongerInRange
));
470 BluetoothGattService
* service
= device
->GetGattService(service_instance_id
);
471 if (service
== nullptr) {
472 RecordCharacteristicReadValueOutcome(UMAGATTOperationOutcome::NO_SERVICE
);
473 Send(new BluetoothMsg_ReadCharacteristicValueError(
474 thread_id
, request_id
, WebBluetoothError::ServiceNoLongerExists
));
478 BluetoothGattCharacteristic
* characteristic
=
479 service
->GetCharacteristic(characteristic_instance_id
);
480 if (characteristic
== nullptr) {
481 RecordCharacteristicReadValueOutcome(
482 UMAGATTOperationOutcome::NO_CHARACTERISTIC
);
483 Send(new BluetoothMsg_ReadCharacteristicValueError(
484 thread_id
, request_id
,
485 WebBluetoothError::CharacteristicNoLongerExists
));
489 characteristic
->ReadRemoteCharacteristic(
490 base::Bind(&BluetoothDispatcherHost::OnCharacteristicValueRead
,
491 weak_ptr_factory_
.GetWeakPtr(), thread_id
, request_id
),
492 base::Bind(&BluetoothDispatcherHost::OnCharacteristicReadValueError
,
493 weak_ptr_factory_
.GetWeakPtr(), thread_id
, request_id
));
496 void BluetoothDispatcherHost::OnWriteValue(
499 const std::string
& characteristic_instance_id
,
500 const std::vector
<uint8_t>& value
) {
501 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
502 RecordWebBluetoothFunctionCall(
503 UMAWebBluetoothFunction::CHARACTERISTIC_WRITE_VALUE
);
505 // Length check per step 3 of writeValue algorithm:
506 // https://webbluetoothchrome.github.io/web-bluetooth/#dom-bluetoothgattcharacteristic-writevalue
507 // We perform the length check on the renderer side. So if we
508 // get a value with length > 512, we can assume it's a hostile
509 // renderer and kill it.
510 if (value
.size() > 512) {
511 bad_message::ReceivedBadMessage(
512 this, bad_message::BDH_INVALID_WRITE_VALUE_LENGTH
);
516 auto characteristic_iter
=
517 characteristic_to_service_
.find(characteristic_instance_id
);
518 // A characteristic_instance_id not in the map implies a hostile renderer
519 // because a renderer obtains the characteristic id from this class and
520 // it will be added to the map at that time.
521 if (characteristic_iter
== characteristic_to_service_
.end()) {
522 bad_message::ReceivedBadMessage(this,
523 bad_message::BDH_INVALID_CHARACTERISTIC_ID
);
526 const std::string
& service_instance_id
= characteristic_iter
->second
;
528 auto device_iter
= service_to_device_
.find(service_instance_id
);
530 CHECK(device_iter
!= service_to_device_
.end());
532 device::BluetoothDevice
* device
=
533 adapter_
->GetDevice(device_iter
->second
/* device_instance_id */);
534 if (device
== nullptr) { // See "NETWORK_ERROR Note" above.
535 RecordCharacteristicWriteValueOutcome(UMAGATTOperationOutcome::NO_DEVICE
);
536 Send(new BluetoothMsg_WriteCharacteristicValueError(
537 thread_id
, request_id
, WebBluetoothError::DeviceNoLongerInRange
));
541 BluetoothGattService
* service
= device
->GetGattService(service_instance_id
);
542 if (service
== nullptr) {
543 RecordCharacteristicWriteValueOutcome(UMAGATTOperationOutcome::NO_SERVICE
);
544 Send(new BluetoothMsg_WriteCharacteristicValueError(
545 thread_id
, request_id
, WebBluetoothError::ServiceNoLongerExists
));
549 BluetoothGattCharacteristic
* characteristic
=
550 service
->GetCharacteristic(characteristic_instance_id
);
551 if (characteristic
== nullptr) {
552 RecordCharacteristicWriteValueOutcome(
553 UMAGATTOperationOutcome::NO_CHARACTERISTIC
);
554 Send(new BluetoothMsg_WriteCharacteristicValueError(
555 thread_id
, request_id
,
556 WebBluetoothError::CharacteristicNoLongerExists
));
559 characteristic
->WriteRemoteCharacteristic(
560 value
, base::Bind(&BluetoothDispatcherHost::OnWriteValueSuccess
,
561 weak_ptr_factory_
.GetWeakPtr(), thread_id
, request_id
),
562 base::Bind(&BluetoothDispatcherHost::OnWriteValueFailed
,
563 weak_ptr_factory_
.GetWeakPtr(), thread_id
, request_id
));
566 void BluetoothDispatcherHost::OnDiscoverySessionStarted(
569 scoped_ptr
<device::BluetoothDiscoverySession
> discovery_session
) {
570 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
571 BrowserThread::PostDelayedTask(
572 BrowserThread::UI
, FROM_HERE
,
573 base::Bind(&BluetoothDispatcherHost::StopDiscoverySession
,
574 weak_ptr_factory_
.GetWeakPtr(), thread_id
, request_id
,
575 base::Passed(&discovery_session
)),
576 base::TimeDelta::FromSeconds(current_delay_time_
));
579 void BluetoothDispatcherHost::OnDiscoverySessionStartedError(int thread_id
,
581 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
582 DLOG(WARNING
) << "BluetoothDispatcherHost::OnDiscoverySessionStartedError";
583 RecordRequestDeviceOutcome(UMARequestDeviceOutcome::DISCOVERY_START_FAILED
);
584 Send(new BluetoothMsg_RequestDeviceError(
585 thread_id
, request_id
, WebBluetoothError::DiscoverySessionStartFailed
));
586 request_device_sessions_
.erase(std::make_pair(thread_id
, request_id
));
589 void BluetoothDispatcherHost::StopDiscoverySession(
592 scoped_ptr
<device::BluetoothDiscoverySession
> discovery_session
) {
593 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
594 discovery_session
->Stop(
595 base::Bind(&BluetoothDispatcherHost::OnDiscoverySessionStopped
,
596 weak_ptr_factory_
.GetWeakPtr(), thread_id
, request_id
),
597 base::Bind(&BluetoothDispatcherHost::OnDiscoverySessionStoppedError
,
598 weak_ptr_factory_
.GetWeakPtr(), thread_id
, request_id
));
601 void BluetoothDispatcherHost::OnDiscoverySessionStopped(int thread_id
,
603 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
605 request_device_sessions_
.find(std::make_pair(thread_id
, request_id
));
606 CHECK(session
!= request_device_sessions_
.end());
607 BluetoothAdapter::DeviceList devices
= adapter_
->GetDevices();
608 for (device::BluetoothDevice
* device
: devices
) {
609 VLOG(1) << "Device: " << device
->GetName();
610 VLOG(1) << "UUIDs: ";
611 for (BluetoothUUID uuid
: device
->GetUUIDs())
612 VLOG(1) << "\t" << uuid
.canonical_value();
613 if (MatchesFilters(*device
, session
->second
.filters
)) {
614 content::BluetoothDevice
device_ipc(
615 device
->GetAddress(), // instance_id
616 device
->GetName(), // name
617 device
->GetBluetoothClass(), // device_class
618 device
->GetVendorIDSource(), // vendor_id_source
619 device
->GetVendorID(), // vendor_id
620 device
->GetProductID(), // product_id
621 device
->GetDeviceID(), // product_version
622 device
->IsPaired(), // paired
623 content::BluetoothDevice::UUIDsFromBluetoothUUIDs(
624 device
->GetUUIDs())); // uuids
625 RecordRequestDeviceOutcome(UMARequestDeviceOutcome::SUCCESS
);
626 Send(new BluetoothMsg_RequestDeviceSuccess(thread_id
, request_id
,
628 request_device_sessions_
.erase(session
);
632 RecordRequestDeviceOutcome(
633 UMARequestDeviceOutcome::NO_MATCHING_DEVICES_FOUND
);
634 Send(new BluetoothMsg_RequestDeviceError(thread_id
, request_id
,
635 WebBluetoothError::NoDevicesFound
));
636 request_device_sessions_
.erase(session
);
639 void BluetoothDispatcherHost::OnDiscoverySessionStoppedError(int thread_id
,
641 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
642 DLOG(WARNING
) << "BluetoothDispatcherHost::OnDiscoverySessionStoppedError";
643 RecordRequestDeviceOutcome(UMARequestDeviceOutcome::DISCOVERY_STOP_FAILED
);
644 Send(new BluetoothMsg_RequestDeviceError(
645 thread_id
, request_id
, WebBluetoothError::DiscoverySessionStopFailed
));
646 request_device_sessions_
.erase(std::make_pair(thread_id
, request_id
));
649 void BluetoothDispatcherHost::OnGATTConnectionCreated(
652 const std::string
& device_instance_id
,
653 base::TimeTicks start_time
,
654 scoped_ptr
<device::BluetoothGattConnection
> connection
) {
655 // TODO(ortuno): Save the BluetoothGattConnection so we can disconnect
657 RecordConnectGATTTimeSuccess(base::TimeTicks::Now() - start_time
);
658 RecordConnectGATTOutcome(UMAConnectGATTOutcome::SUCCESS
);
659 Send(new BluetoothMsg_ConnectGATTSuccess(thread_id
, request_id
,
660 device_instance_id
));
663 void BluetoothDispatcherHost::OnCreateGATTConnectionError(
666 const std::string
& device_instance_id
,
667 base::TimeTicks start_time
,
668 device::BluetoothDevice::ConnectErrorCode error_code
) {
669 // There was an error creating the ATT Bearer so we reject with
671 // https://webbluetoothchrome.github.io/web-bluetooth/#dom-bluetoothdevice-connectgatt
672 RecordConnectGATTTimeFailed(base::TimeTicks::Now() - start_time
);
673 // RecordConnectGATTOutcome is called by TranslateConnectError.
674 Send(new BluetoothMsg_ConnectGATTError(thread_id
, request_id
,
675 TranslateConnectError(error_code
)));
678 void BluetoothDispatcherHost::OnServicesDiscovered(
681 const std::string
& device_instance_id
,
682 const std::string
& service_uuid
) {
683 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
685 device::BluetoothDevice
* device
= adapter_
->GetDevice(device_instance_id
);
686 if (device
== nullptr) { // See "NETWORK_ERROR Note" above.
687 RecordGetPrimaryServiceOutcome(UMAGetPrimaryServiceOutcome::NO_DEVICE
);
688 Send(new BluetoothMsg_GetPrimaryServiceError(
689 thread_id
, request_id
, WebBluetoothError::DeviceNoLongerInRange
));
692 for (BluetoothGattService
* service
: device
->GetGattServices()) {
693 if (service
->GetUUID().canonical_value() == service_uuid
) {
694 // TODO(ortuno): Use generated instance ID instead.
695 // https://crbug.com/495379
696 const std::string
& service_identifier
= service
->GetIdentifier();
697 auto insert_result
= service_to_device_
.insert(
698 make_pair(service_identifier
, device_instance_id
));
700 // If a value is already in map, DCHECK it's valid.
701 if (!insert_result
.second
)
702 DCHECK(insert_result
.first
->second
== device_instance_id
);
704 RecordGetPrimaryServiceOutcome(UMAGetPrimaryServiceOutcome::SUCCESS
);
705 Send(new BluetoothMsg_GetPrimaryServiceSuccess(thread_id
, request_id
,
706 service_identifier
));
710 RecordGetPrimaryServiceOutcome(UMAGetPrimaryServiceOutcome::NOT_FOUND
);
711 Send(new BluetoothMsg_GetPrimaryServiceError(
712 thread_id
, request_id
, WebBluetoothError::ServiceNotFound
));
715 void BluetoothDispatcherHost::OnCharacteristicValueRead(
718 const std::vector
<uint8
>& value
) {
719 RecordCharacteristicReadValueOutcome(UMAGATTOperationOutcome::SUCCESS
);
720 Send(new BluetoothMsg_ReadCharacteristicValueSuccess(thread_id
, request_id
,
724 void BluetoothDispatcherHost::OnCharacteristicReadValueError(
727 device::BluetoothGattService::GattErrorCode error_code
) {
728 // TranslateGATTError calls RecordGATTOperationOutcome.
729 Send(new BluetoothMsg_ReadCharacteristicValueError(
730 thread_id
, request_id
,
731 TranslateGATTError(error_code
, UMAGATTOperation::CHARACTERISTIC_READ
)));
734 void BluetoothDispatcherHost::OnWriteValueSuccess(int thread_id
,
736 RecordCharacteristicWriteValueOutcome(UMAGATTOperationOutcome::SUCCESS
);
737 Send(new BluetoothMsg_WriteCharacteristicValueSuccess(thread_id
, request_id
));
740 void BluetoothDispatcherHost::OnWriteValueFailed(
743 device::BluetoothGattService::GattErrorCode error_code
) {
744 // TranslateGATTError calls RecordGATTOperationOutcome.
745 Send(new BluetoothMsg_WriteCharacteristicValueError(
746 thread_id
, request_id
,
747 TranslateGATTError(error_code
, UMAGATTOperation::CHARACTERISTIC_WRITE
)));
750 } // namespace content