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/metrics/histogram.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "content/browser/bad_message.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
;
34 // These types of errors aren't as common. We log them to understand
35 // how common they are and if we need to investigate more.
36 enum class BluetoothGATTError
{
41 // Add errors above this line and update corresponding histograms.xml enum.
45 enum class UMARequestDeviceOutcome
{
47 NO_BLUETOOTH_ADAPTER
= 1,
49 DISCOVERY_START_FAILED
= 3,
50 DISCOVERY_STOP_FAILED
= 4,
51 NO_MATCHING_DEVICES_FOUND
= 5,
52 // NOTE: Add new requestDevice() outcomes immediately above this line. Make
53 // sure to update the enum list in tools/histogram/histograms.xml accordingly.
57 void RecordRequestDeviceOutcome(UMARequestDeviceOutcome outcome
) {
58 UMA_HISTOGRAM_ENUMERATION("Bluetooth.RequestDevice.Outcome",
59 static_cast<int>(outcome
),
60 static_cast<int>(UMARequestDeviceOutcome::COUNT
));
63 // TODO(ortuno): Once we have a chooser for scanning and the right
64 // callback for discovered services we should delete these constants.
65 // https://crbug.com/436280 and https://crbug.com/484504
66 const int kDelayTime
= 5; // 5 seconds for scanning and discovering
67 const int kTestingDelayTime
= 0; // No need to wait during tests
70 // https://webbluetoothchrome.github.io/web-bluetooth/#dfn-matches-a-filter
71 bool MatchesFilter(const std::set
<BluetoothUUID
>& device_uuids
,
72 const content::BluetoothScanFilter
& filter
) {
73 if (filter
.services
.empty())
75 for (const BluetoothUUID
& service
: filter
.services
) {
76 if (!ContainsKey(device_uuids
, service
)) {
83 bool MatchesFilters(const device::BluetoothDevice
& device
,
84 const std::vector
<content::BluetoothScanFilter
>& filters
) {
85 const std::vector
<BluetoothUUID
>& device_uuid_list
= device
.GetUUIDs();
86 const std::set
<BluetoothUUID
> device_uuids(device_uuid_list
.begin(),
87 device_uuid_list
.end());
88 for (const content::BluetoothScanFilter
& filter
: filters
) {
89 if (MatchesFilter(device_uuids
, filter
)) {
96 void AddToHistogram(BluetoothGATTError error
) {
97 UMA_HISTOGRAM_ENUMERATION("Bluetooth.GATTErrors", static_cast<int>(error
),
98 static_cast<int>(BluetoothGATTError::MAX_ERROR
));
101 WebBluetoothError
TranslateConnectError(
102 device::BluetoothDevice::ConnectErrorCode error_code
) {
103 switch (error_code
) {
104 case device::BluetoothDevice::ERROR_UNKNOWN
:
105 return WebBluetoothError::ConnectUnknownError
;
106 case device::BluetoothDevice::ERROR_INPROGRESS
:
107 return WebBluetoothError::ConnectAlreadyInProgress
;
108 case device::BluetoothDevice::ERROR_FAILED
:
109 return WebBluetoothError::ConnectUnknownFailure
;
110 case device::BluetoothDevice::ERROR_AUTH_FAILED
:
111 return WebBluetoothError::ConnectAuthFailed
;
112 case device::BluetoothDevice::ERROR_AUTH_CANCELED
:
113 return WebBluetoothError::ConnectAuthCanceled
;
114 case device::BluetoothDevice::ERROR_AUTH_REJECTED
:
115 return WebBluetoothError::ConnectAuthRejected
;
116 case device::BluetoothDevice::ERROR_AUTH_TIMEOUT
:
117 return WebBluetoothError::ConnectAuthTimeout
;
118 case device::BluetoothDevice::ERROR_UNSUPPORTED_DEVICE
:
119 return WebBluetoothError::ConnectUnsupportedDevice
;
122 return WebBluetoothError::UntranslatedConnectErrorCode
;
125 blink::WebBluetoothError
TranslateGATTError(
126 BluetoothGattService::GattErrorCode error_code
) {
127 switch (error_code
) {
128 case BluetoothGattService::GATT_ERROR_UNKNOWN
:
129 AddToHistogram(BluetoothGATTError::UNKNOWN
);
130 return blink::WebBluetoothError::GATTUnknownError
;
131 case BluetoothGattService::GATT_ERROR_FAILED
:
132 AddToHistogram(BluetoothGATTError::FAILED
);
133 return blink::WebBluetoothError::GATTUnknownFailure
;
134 case BluetoothGattService::GATT_ERROR_IN_PROGRESS
:
135 AddToHistogram(BluetoothGATTError::IN_PROGRESS
);
136 return blink::WebBluetoothError::GATTOperationInProgress
;
137 case BluetoothGattService::GATT_ERROR_INVALID_LENGTH
:
138 return blink::WebBluetoothError::GATTInvalidAttributeLength
;
139 case BluetoothGattService::GATT_ERROR_NOT_PERMITTED
:
140 return blink::WebBluetoothError::GATTNotPermitted
;
141 case BluetoothGattService::GATT_ERROR_NOT_AUTHORIZED
:
142 return blink::WebBluetoothError::GATTNotAuthorized
;
143 case BluetoothGattService::GATT_ERROR_NOT_PAIRED
:
144 AddToHistogram(BluetoothGATTError::NOT_PAIRED
);
145 return blink::WebBluetoothError::GATTNotPaired
;
146 case BluetoothGattService::GATT_ERROR_NOT_SUPPORTED
:
147 return blink::WebBluetoothError::GATTNotSupported
;
150 return blink::WebBluetoothError::GATTUntranslatedErrorCode
;
157 BluetoothDispatcherHost::BluetoothDispatcherHost(int render_process_id
)
158 : BrowserMessageFilter(BluetoothMsgStart
),
159 render_process_id_(render_process_id
),
160 weak_ptr_factory_(this) {
161 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
162 current_delay_time_
= kDelayTime
;
163 if (BluetoothAdapterFactory::IsBluetoothAdapterAvailable())
164 BluetoothAdapterFactory::GetAdapter(
165 base::Bind(&BluetoothDispatcherHost::set_adapter
,
166 weak_ptr_factory_
.GetWeakPtr()));
169 void BluetoothDispatcherHost::OnDestruct() const {
170 // See class comment: UI Thread Note.
171 BrowserThread::DeleteOnUIThread::Destruct(this);
174 void BluetoothDispatcherHost::OverrideThreadForMessage(
175 const IPC::Message
& message
,
176 content::BrowserThread::ID
* thread
) {
177 // See class comment: UI Thread Note.
178 *thread
= BrowserThread::UI
;
181 bool BluetoothDispatcherHost::OnMessageReceived(const IPC::Message
& message
) {
182 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
184 IPC_BEGIN_MESSAGE_MAP(BluetoothDispatcherHost
, message
)
185 IPC_MESSAGE_HANDLER(BluetoothHostMsg_RequestDevice
, OnRequestDevice
)
186 IPC_MESSAGE_HANDLER(BluetoothHostMsg_ConnectGATT
, OnConnectGATT
)
187 IPC_MESSAGE_HANDLER(BluetoothHostMsg_GetPrimaryService
, OnGetPrimaryService
)
188 IPC_MESSAGE_HANDLER(BluetoothHostMsg_GetCharacteristic
, OnGetCharacteristic
)
189 IPC_MESSAGE_HANDLER(BluetoothHostMsg_ReadValue
, OnReadValue
)
190 IPC_MESSAGE_HANDLER(BluetoothHostMsg_WriteValue
, OnWriteValue
)
191 IPC_MESSAGE_UNHANDLED(handled
= false)
192 IPC_END_MESSAGE_MAP()
196 void BluetoothDispatcherHost::SetBluetoothAdapterForTesting(
197 scoped_refptr
<device::BluetoothAdapter
> mock_adapter
) {
198 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
199 current_delay_time_
= kTestingDelayTime
;
200 set_adapter(mock_adapter
.Pass());
203 BluetoothDispatcherHost::~BluetoothDispatcherHost() {
204 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
205 // Clear adapter, releasing observer references.
206 set_adapter(scoped_refptr
<device::BluetoothAdapter
>());
209 // Stores information associated with an in-progress requestDevice call. This
210 // will include the state of the active chooser dialog in a future patch.
211 struct BluetoothDispatcherHost::RequestDeviceSession
{
212 RequestDeviceSession(const std::vector
<BluetoothScanFilter
>& filters
,
213 const std::vector
<BluetoothUUID
>& optional_services
)
214 : filters(filters
), optional_services(optional_services
) {}
216 std::vector
<BluetoothScanFilter
> filters
;
217 std::vector
<BluetoothUUID
> optional_services
;
220 void BluetoothDispatcherHost::set_adapter(
221 scoped_refptr
<device::BluetoothAdapter
> adapter
) {
222 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
224 adapter_
->RemoveObserver(this);
227 adapter_
->AddObserver(this);
230 static scoped_ptr
<device::BluetoothDiscoveryFilter
> ComputeScanFilter(
231 const std::vector
<BluetoothScanFilter
>& filters
) {
232 std::set
<BluetoothUUID
> services
;
233 for (const BluetoothScanFilter
& filter
: filters
) {
234 services
.insert(filter
.services
.begin(), filter
.services
.end());
236 scoped_ptr
<device::BluetoothDiscoveryFilter
> discovery_filter(
237 new device::BluetoothDiscoveryFilter(
238 device::BluetoothDiscoveryFilter::TRANSPORT_DUAL
));
239 for (const BluetoothUUID
& service
: services
) {
240 discovery_filter
->AddUUID(service
);
242 return discovery_filter
.Pass();
245 void BluetoothDispatcherHost::OnRequestDevice(
248 int frame_routing_id
,
249 const std::vector
<BluetoothScanFilter
>& filters
,
250 const std::vector
<BluetoothUUID
>& optional_services
) {
251 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
253 RenderFrameHostImpl
* render_frame_host
=
254 RenderFrameHostImpl::FromID(render_process_id_
, frame_routing_id
);
256 if (!render_frame_host
) {
258 << "Got a requestDevice IPC without a matching RenderFrameHost: "
259 << render_process_id_
<< ", " << frame_routing_id
;
260 RecordRequestDeviceOutcome(UMARequestDeviceOutcome::NO_RENDER_FRAME
);
261 Send(new BluetoothMsg_RequestDeviceError(
262 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 adapter_
->StartDiscoverySessionWithFilter(
280 ComputeScanFilter(filters
),
281 base::Bind(&BluetoothDispatcherHost::OnDiscoverySessionStarted
,
282 weak_ptr_factory_
.GetWeakPtr(), thread_id
, request_id
),
283 base::Bind(&BluetoothDispatcherHost::OnDiscoverySessionStartedError
,
284 weak_ptr_factory_
.GetWeakPtr(), thread_id
, request_id
));
286 DLOG(WARNING
) << "No BluetoothAdapter. Can't serve requestDevice.";
287 RecordRequestDeviceOutcome(UMARequestDeviceOutcome::NO_BLUETOOTH_ADAPTER
);
288 Send(new BluetoothMsg_RequestDeviceError(
289 thread_id
, request_id
, WebBluetoothError::NoBluetoothAdapter
));
294 void BluetoothDispatcherHost::OnConnectGATT(
297 const std::string
& device_instance_id
) {
298 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
299 // TODO(ortuno): Right now it's pointless to check if the domain has access to
300 // the device, because any domain can connect to any device. But once
301 // permissions are implemented we should check that the domain has access to
302 // the device. https://crbug.com/484745
303 device::BluetoothDevice
* device
= adapter_
->GetDevice(device_instance_id
);
304 if (device
== nullptr) { // See "NETWORK_ERROR Note" above.
305 Send(new BluetoothMsg_ConnectGATTError(
306 thread_id
, request_id
, WebBluetoothError::DeviceNoLongerInRange
));
309 device
->CreateGattConnection(
310 base::Bind(&BluetoothDispatcherHost::OnGATTConnectionCreated
,
311 weak_ptr_factory_
.GetWeakPtr(), thread_id
, request_id
,
313 base::Bind(&BluetoothDispatcherHost::OnCreateGATTConnectionError
,
314 weak_ptr_factory_
.GetWeakPtr(), thread_id
, request_id
,
315 device_instance_id
));
318 void BluetoothDispatcherHost::OnGetPrimaryService(
321 const std::string
& device_instance_id
,
322 const std::string
& service_uuid
) {
323 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
324 // TODO(ortuno): Check if device_instance_id is in "allowed devices"
325 // https://crbug.com/493459
326 // TODO(ortuno): Check if service_uuid is in "allowed services"
327 // https://crbug.com/493460
328 // For now just wait a fixed time and call OnServiceDiscovered.
329 // TODO(ortuno): Use callback once it's implemented http://crbug.com/484504
330 BrowserThread::PostDelayedTask(
331 BrowserThread::UI
, FROM_HERE
,
332 base::Bind(&BluetoothDispatcherHost::OnServicesDiscovered
,
333 weak_ptr_factory_
.GetWeakPtr(), thread_id
, request_id
,
334 device_instance_id
, service_uuid
),
335 base::TimeDelta::FromSeconds(current_delay_time_
));
338 void BluetoothDispatcherHost::OnGetCharacteristic(
341 const std::string
& service_instance_id
,
342 const std::string
& characteristic_uuid
) {
343 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
345 auto device_iter
= service_to_device_
.find(service_instance_id
);
346 // A service_instance_id not in the map implies a hostile renderer
347 // because a renderer obtains the service id from this class and
348 // it will be added to the map at that time.
349 if (device_iter
== service_to_device_
.end()) {
351 bad_message::ReceivedBadMessage(this, bad_message::BDH_INVALID_SERVICE_ID
);
355 // TODO(ortuno): Check if domain has access to device.
356 // https://crbug.com/493459
357 device::BluetoothDevice
* device
=
358 adapter_
->GetDevice(device_iter
->second
/* device_instance_id */);
360 if (device
== nullptr) { // See "NETWORK_ERROR Note" above.
361 Send(new BluetoothMsg_GetCharacteristicError(
362 thread_id
, request_id
, WebBluetoothError::DeviceNoLongerInRange
));
366 // TODO(ortuno): Check if domain has access to service
367 // http://crbug.com/493460
368 device::BluetoothGattService
* service
=
369 device
->GetGattService(service_instance_id
);
371 Send(new BluetoothMsg_GetCharacteristicError(
372 thread_id
, request_id
, WebBluetoothError::ServiceNoLongerExists
));
376 for (BluetoothGattCharacteristic
* characteristic
:
377 service
->GetCharacteristics()) {
378 if (characteristic
->GetUUID().canonical_value() == characteristic_uuid
) {
379 const std::string
& characteristic_instance_id
=
380 characteristic
->GetIdentifier();
382 auto insert_result
= characteristic_to_service_
.insert(
383 make_pair(characteristic_instance_id
, service_instance_id
));
385 // If value is already in map, DCHECK it's valid.
386 if (!insert_result
.second
)
387 DCHECK(insert_result
.first
->second
== service_instance_id
);
389 // TODO(ortuno): Use generated instance ID instead.
390 // https://crbug.com/495379
391 Send(new BluetoothMsg_GetCharacteristicSuccess(
392 thread_id
, request_id
, characteristic_instance_id
));
396 Send(new BluetoothMsg_GetCharacteristicError(
397 thread_id
, request_id
, WebBluetoothError::CharacteristicNotFound
));
400 void BluetoothDispatcherHost::OnReadValue(
403 const std::string
& characteristic_instance_id
) {
404 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
406 auto characteristic_iter
=
407 characteristic_to_service_
.find(characteristic_instance_id
);
408 // A characteristic_instance_id not in the map implies a hostile renderer
409 // because a renderer obtains the characteristic id from this class and
410 // it will be added to the map at that time.
411 if (characteristic_iter
== characteristic_to_service_
.end()) {
413 bad_message::ReceivedBadMessage(this,
414 bad_message::BDH_INVALID_CHARACTERISTIC_ID
);
417 const std::string
& service_instance_id
= characteristic_iter
->second
;
419 auto device_iter
= service_to_device_
.find(service_instance_id
);
421 CHECK(device_iter
!= service_to_device_
.end());
423 device::BluetoothDevice
* device
=
424 adapter_
->GetDevice(device_iter
->second
/* device_instance_id */);
425 if (device
== nullptr) { // See "NETWORK_ERROR Note" above.
426 Send(new BluetoothMsg_ReadCharacteristicValueError(
427 thread_id
, request_id
, WebBluetoothError::DeviceNoLongerInRange
));
431 BluetoothGattService
* service
= device
->GetGattService(service_instance_id
);
432 if (service
== nullptr) {
433 Send(new BluetoothMsg_ReadCharacteristicValueError(
434 thread_id
, request_id
, WebBluetoothError::ServiceNoLongerExists
));
438 BluetoothGattCharacteristic
* characteristic
=
439 service
->GetCharacteristic(characteristic_instance_id
);
440 if (characteristic
== nullptr) {
441 Send(new BluetoothMsg_ReadCharacteristicValueError(
442 thread_id
, request_id
,
443 WebBluetoothError::CharacteristicNoLongerExists
));
447 characteristic
->ReadRemoteCharacteristic(
448 base::Bind(&BluetoothDispatcherHost::OnCharacteristicValueRead
,
449 weak_ptr_factory_
.GetWeakPtr(), thread_id
, request_id
),
450 base::Bind(&BluetoothDispatcherHost::OnCharacteristicReadValueError
,
451 weak_ptr_factory_
.GetWeakPtr(), thread_id
, request_id
));
454 void BluetoothDispatcherHost::OnWriteValue(
457 const std::string
& characteristic_instance_id
,
458 const std::vector
<uint8_t>& value
) {
459 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
461 // Length check per step 3 of writeValue algorithm:
462 // https://webbluetoothchrome.github.io/web-bluetooth/#dom-bluetoothgattcharacteristic-writevalue
463 // We perform the length check on the renderer side. So if we
464 // get a value with length > 512, we can assume it's a hostile
465 // renderer and kill it.
466 if (value
.size() > 512) {
467 bad_message::ReceivedBadMessage(
468 this, bad_message::BDH_INVALID_WRITE_VALUE_LENGTH
);
472 auto characteristic_iter
=
473 characteristic_to_service_
.find(characteristic_instance_id
);
474 // A characteristic_instance_id not in the map implies a hostile renderer
475 // because a renderer obtains the characteristic id from this class and
476 // it will be added to the map at that time.
477 if (characteristic_iter
== characteristic_to_service_
.end()) {
478 bad_message::ReceivedBadMessage(this,
479 bad_message::BDH_INVALID_CHARACTERISTIC_ID
);
482 const std::string
& service_instance_id
= characteristic_iter
->second
;
484 auto device_iter
= service_to_device_
.find(service_instance_id
);
486 CHECK(device_iter
!= service_to_device_
.end());
488 device::BluetoothDevice
* device
=
489 adapter_
->GetDevice(device_iter
->second
/* device_instance_id */);
490 if (device
== nullptr) { // See "NETWORK_ERROR Note" above.
491 Send(new BluetoothMsg_WriteCharacteristicValueError(
492 thread_id
, request_id
, WebBluetoothError::DeviceNoLongerInRange
));
496 BluetoothGattService
* service
= device
->GetGattService(service_instance_id
);
497 if (service
== nullptr) {
498 Send(new BluetoothMsg_WriteCharacteristicValueError(
499 thread_id
, request_id
, WebBluetoothError::ServiceNoLongerExists
));
503 BluetoothGattCharacteristic
* characteristic
=
504 service
->GetCharacteristic(characteristic_instance_id
);
505 if (characteristic
== nullptr) {
506 Send(new BluetoothMsg_WriteCharacteristicValueError(
507 thread_id
, request_id
,
508 WebBluetoothError::CharacteristicNoLongerExists
));
511 characteristic
->WriteRemoteCharacteristic(
512 value
, base::Bind(&BluetoothDispatcherHost::OnWriteValueSuccess
,
513 weak_ptr_factory_
.GetWeakPtr(), thread_id
, request_id
),
514 base::Bind(&BluetoothDispatcherHost::OnWriteValueFailed
,
515 weak_ptr_factory_
.GetWeakPtr(), thread_id
, request_id
));
518 void BluetoothDispatcherHost::OnDiscoverySessionStarted(
521 scoped_ptr
<device::BluetoothDiscoverySession
> discovery_session
) {
522 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
523 BrowserThread::PostDelayedTask(
524 BrowserThread::UI
, FROM_HERE
,
525 base::Bind(&BluetoothDispatcherHost::StopDiscoverySession
,
526 weak_ptr_factory_
.GetWeakPtr(), thread_id
, request_id
,
527 base::Passed(&discovery_session
)),
528 base::TimeDelta::FromSeconds(current_delay_time_
));
531 void BluetoothDispatcherHost::OnDiscoverySessionStartedError(int thread_id
,
533 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
534 DLOG(WARNING
) << "BluetoothDispatcherHost::OnDiscoverySessionStartedError";
535 RecordRequestDeviceOutcome(UMARequestDeviceOutcome::DISCOVERY_START_FAILED
);
536 Send(new BluetoothMsg_RequestDeviceError(
537 thread_id
, request_id
, WebBluetoothError::DiscoverySessionStartFailed
));
538 request_device_sessions_
.erase(std::make_pair(thread_id
, request_id
));
541 void BluetoothDispatcherHost::StopDiscoverySession(
544 scoped_ptr
<device::BluetoothDiscoverySession
> discovery_session
) {
545 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
546 discovery_session
->Stop(
547 base::Bind(&BluetoothDispatcherHost::OnDiscoverySessionStopped
,
548 weak_ptr_factory_
.GetWeakPtr(), thread_id
, request_id
),
549 base::Bind(&BluetoothDispatcherHost::OnDiscoverySessionStoppedError
,
550 weak_ptr_factory_
.GetWeakPtr(), thread_id
, request_id
));
553 void BluetoothDispatcherHost::OnDiscoverySessionStopped(int thread_id
,
555 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
557 request_device_sessions_
.find(std::make_pair(thread_id
, request_id
));
558 CHECK(session
!= request_device_sessions_
.end());
559 BluetoothAdapter::DeviceList devices
= adapter_
->GetDevices();
560 for (device::BluetoothDevice
* device
: devices
) {
561 if (MatchesFilters(*device
, session
->second
.filters
)) {
562 content::BluetoothDevice
device_ipc(
563 device
->GetAddress(), // instance_id
564 device
->GetName(), // name
565 device
->GetBluetoothClass(), // device_class
566 device
->GetVendorIDSource(), // vendor_id_source
567 device
->GetVendorID(), // vendor_id
568 device
->GetProductID(), // product_id
569 device
->GetDeviceID(), // product_version
570 device
->IsPaired(), // paired
571 content::BluetoothDevice::UUIDsFromBluetoothUUIDs(
572 device
->GetUUIDs())); // uuids
573 RecordRequestDeviceOutcome(UMARequestDeviceOutcome::SUCCESS
);
574 Send(new BluetoothMsg_RequestDeviceSuccess(thread_id
, request_id
,
576 request_device_sessions_
.erase(session
);
580 RecordRequestDeviceOutcome(
581 UMARequestDeviceOutcome::NO_MATCHING_DEVICES_FOUND
);
582 Send(new BluetoothMsg_RequestDeviceError(thread_id
, request_id
,
583 WebBluetoothError::NoDevicesFound
));
584 request_device_sessions_
.erase(session
);
587 void BluetoothDispatcherHost::OnDiscoverySessionStoppedError(int thread_id
,
589 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
590 DLOG(WARNING
) << "BluetoothDispatcherHost::OnDiscoverySessionStoppedError";
591 RecordRequestDeviceOutcome(UMARequestDeviceOutcome::DISCOVERY_STOP_FAILED
);
592 Send(new BluetoothMsg_RequestDeviceError(
593 thread_id
, request_id
, WebBluetoothError::DiscoverySessionStopFailed
));
594 request_device_sessions_
.erase(std::make_pair(thread_id
, request_id
));
597 void BluetoothDispatcherHost::OnGATTConnectionCreated(
600 const std::string
& device_instance_id
,
601 scoped_ptr
<device::BluetoothGattConnection
> connection
) {
602 // TODO(ortuno): Save the BluetoothGattConnection so we can disconnect
604 Send(new BluetoothMsg_ConnectGATTSuccess(thread_id
, request_id
,
605 device_instance_id
));
608 void BluetoothDispatcherHost::OnCreateGATTConnectionError(
611 const std::string
& device_instance_id
,
612 device::BluetoothDevice::ConnectErrorCode error_code
) {
613 // There was an error creating the ATT Bearer so we reject with
615 // https://webbluetoothchrome.github.io/web-bluetooth/#dom-bluetoothdevice-connectgatt
616 Send(new BluetoothMsg_ConnectGATTError(thread_id
, request_id
,
617 TranslateConnectError(error_code
)));
620 void BluetoothDispatcherHost::OnServicesDiscovered(
623 const std::string
& device_instance_id
,
624 const std::string
& service_uuid
) {
625 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
627 device::BluetoothDevice
* device
= adapter_
->GetDevice(device_instance_id
);
628 if (device
== nullptr) { // See "NETWORK_ERROR Note" above.
629 Send(new BluetoothMsg_GetPrimaryServiceError(
630 thread_id
, request_id
, WebBluetoothError::DeviceNoLongerInRange
));
633 for (BluetoothGattService
* service
: device
->GetGattServices()) {
634 if (service
->GetUUID().canonical_value() == service_uuid
) {
635 // TODO(ortuno): Use generated instance ID instead.
636 // https://crbug.com/495379
637 const std::string
& service_identifier
= service
->GetIdentifier();
638 auto insert_result
= service_to_device_
.insert(
639 make_pair(service_identifier
, device_instance_id
));
641 // If a value is already in map, DCHECK it's valid.
642 if (!insert_result
.second
)
643 DCHECK(insert_result
.first
->second
== device_instance_id
);
645 Send(new BluetoothMsg_GetPrimaryServiceSuccess(thread_id
, request_id
,
646 service_identifier
));
650 Send(new BluetoothMsg_GetPrimaryServiceError(
651 thread_id
, request_id
, WebBluetoothError::ServiceNotFound
));
654 void BluetoothDispatcherHost::OnCharacteristicValueRead(
657 const std::vector
<uint8
>& value
) {
658 Send(new BluetoothMsg_ReadCharacteristicValueSuccess(thread_id
, request_id
,
662 void BluetoothDispatcherHost::OnCharacteristicReadValueError(
665 device::BluetoothGattService::GattErrorCode error_code
) {
666 Send(new BluetoothMsg_ReadCharacteristicValueError(
667 thread_id
, request_id
, TranslateGATTError(error_code
)));
670 void BluetoothDispatcherHost::OnWriteValueSuccess(int thread_id
,
672 Send(new BluetoothMsg_WriteCharacteristicValueSuccess(thread_id
, request_id
));
675 void BluetoothDispatcherHost::OnWriteValueFailed(
678 device::BluetoothGattService::GattErrorCode error_code
) {
679 Send(new BluetoothMsg_WriteCharacteristicValueError(
680 thread_id
, request_id
, TranslateGATTError(error_code
)));
683 } // namespace content