[refactor] More post-NSS WebCrypto cleanups (utility functions).
[chromium-blink-merge.git] / content / browser / bluetooth / bluetooth_dispatcher_host.cc
blobc1a2395c4f2bb918ab826e0a4242535217dbd999
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.
5 // NETWORK_ERROR Note:
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
8 // case.
9 // https://webbluetoothchrome.github.io/web-bluetooth/#dom-bluetoothdevice-connectgatt
11 #include "content/browser/bluetooth/bluetooth_dispatcher_host.h"
13 #include "base/bind.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/thread_task_runner_handle.h"
17 #include "content/browser/bad_message.h"
18 #include "content/browser/bluetooth/bluetooth_metrics.h"
19 #include "content/browser/bluetooth/first_device_bluetooth_chooser.h"
20 #include "content/browser/frame_host/render_frame_host_impl.h"
21 #include "content/common/bluetooth/bluetooth_messages.h"
22 #include "content/public/browser/web_contents.h"
23 #include "content/public/browser/web_contents_delegate.h"
24 #include "device/bluetooth/bluetooth_adapter.h"
25 #include "device/bluetooth/bluetooth_adapter_factory.h"
26 #include "device/bluetooth/bluetooth_device.h"
27 #include "device/bluetooth/bluetooth_discovery_session.h"
28 #include "device/bluetooth/bluetooth_gatt_characteristic.h"
29 #include "device/bluetooth/bluetooth_gatt_service.h"
31 using blink::WebBluetoothError;
32 using device::BluetoothAdapter;
33 using device::BluetoothAdapterFactory;
34 using device::BluetoothGattCharacteristic;
35 using device::BluetoothGattService;
36 using device::BluetoothUUID;
38 namespace content {
40 namespace {
42 // TODO(ortuno): Once we have a chooser for scanning, a way to control that
43 // chooser from tests, and the right callback for discovered services we should
44 // delete these constants.
45 // https://crbug.com/436280 and https://crbug.com/484504
46 const int kDelayTime = 5; // 5 seconds for scanning and discovering
47 const int kTestingDelayTime = 0; // No need to wait during tests
49 // Defined at
50 // https://webbluetoothchrome.github.io/web-bluetooth/#dfn-matches-a-filter
51 bool MatchesFilter(const std::set<BluetoothUUID>& device_uuids,
52 const content::BluetoothScanFilter& filter) {
53 if (filter.services.empty())
54 return false;
55 for (const BluetoothUUID& service : filter.services) {
56 if (!ContainsKey(device_uuids, service)) {
57 return false;
60 return true;
63 bool MatchesFilters(const device::BluetoothDevice& device,
64 const std::vector<content::BluetoothScanFilter>& filters) {
65 const std::vector<BluetoothUUID>& device_uuid_list = device.GetUUIDs();
66 const std::set<BluetoothUUID> device_uuids(device_uuid_list.begin(),
67 device_uuid_list.end());
68 for (const content::BluetoothScanFilter& filter : filters) {
69 if (MatchesFilter(device_uuids, filter)) {
70 return true;
73 return false;
76 WebBluetoothError TranslateConnectError(
77 device::BluetoothDevice::ConnectErrorCode error_code) {
78 switch (error_code) {
79 case device::BluetoothDevice::ERROR_UNKNOWN:
80 RecordConnectGATTOutcome(UMAConnectGATTOutcome::UNKNOWN);
81 return WebBluetoothError::ConnectUnknownError;
82 case device::BluetoothDevice::ERROR_INPROGRESS:
83 RecordConnectGATTOutcome(UMAConnectGATTOutcome::IN_PROGRESS);
84 return WebBluetoothError::ConnectAlreadyInProgress;
85 case device::BluetoothDevice::ERROR_FAILED:
86 RecordConnectGATTOutcome(UMAConnectGATTOutcome::FAILED);
87 return WebBluetoothError::ConnectUnknownFailure;
88 case device::BluetoothDevice::ERROR_AUTH_FAILED:
89 RecordConnectGATTOutcome(UMAConnectGATTOutcome::AUTH_FAILED);
90 return WebBluetoothError::ConnectAuthFailed;
91 case device::BluetoothDevice::ERROR_AUTH_CANCELED:
92 RecordConnectGATTOutcome(UMAConnectGATTOutcome::AUTH_CANCELED);
93 return WebBluetoothError::ConnectAuthCanceled;
94 case device::BluetoothDevice::ERROR_AUTH_REJECTED:
95 RecordConnectGATTOutcome(UMAConnectGATTOutcome::AUTH_REJECTED);
96 return WebBluetoothError::ConnectAuthRejected;
97 case device::BluetoothDevice::ERROR_AUTH_TIMEOUT:
98 RecordConnectGATTOutcome(UMAConnectGATTOutcome::AUTH_TIMEOUT);
99 return WebBluetoothError::ConnectAuthTimeout;
100 case device::BluetoothDevice::ERROR_UNSUPPORTED_DEVICE:
101 RecordConnectGATTOutcome(UMAConnectGATTOutcome::UNSUPPORTED_DEVICE);
102 return WebBluetoothError::ConnectUnsupportedDevice;
104 NOTREACHED();
105 return WebBluetoothError::UntranslatedConnectErrorCode;
108 blink::WebBluetoothError TranslateGATTError(
109 BluetoothGattService::GattErrorCode error_code,
110 UMAGATTOperation operation) {
111 switch (error_code) {
112 case BluetoothGattService::GATT_ERROR_UNKNOWN:
113 RecordGATTOperationOutcome(operation, UMAGATTOperationOutcome::UNKNOWN);
114 return blink::WebBluetoothError::GATTUnknownError;
115 case BluetoothGattService::GATT_ERROR_FAILED:
116 RecordGATTOperationOutcome(operation, UMAGATTOperationOutcome::FAILED);
117 return blink::WebBluetoothError::GATTUnknownFailure;
118 case BluetoothGattService::GATT_ERROR_IN_PROGRESS:
119 RecordGATTOperationOutcome(operation,
120 UMAGATTOperationOutcome::IN_PROGRESS);
121 return blink::WebBluetoothError::GATTOperationInProgress;
122 case BluetoothGattService::GATT_ERROR_INVALID_LENGTH:
123 RecordGATTOperationOutcome(operation,
124 UMAGATTOperationOutcome::INVALID_LENGTH);
125 return blink::WebBluetoothError::GATTInvalidAttributeLength;
126 case BluetoothGattService::GATT_ERROR_NOT_PERMITTED:
127 RecordGATTOperationOutcome(operation,
128 UMAGATTOperationOutcome::NOT_PERMITTED);
129 return blink::WebBluetoothError::GATTNotPermitted;
130 case BluetoothGattService::GATT_ERROR_NOT_AUTHORIZED:
131 RecordGATTOperationOutcome(operation,
132 UMAGATTOperationOutcome::NOT_AUTHORIZED);
133 return blink::WebBluetoothError::GATTNotAuthorized;
134 case BluetoothGattService::GATT_ERROR_NOT_PAIRED:
135 RecordGATTOperationOutcome(operation,
136 UMAGATTOperationOutcome::NOT_PAIRED);
137 return blink::WebBluetoothError::GATTNotPaired;
138 case BluetoothGattService::GATT_ERROR_NOT_SUPPORTED:
139 RecordGATTOperationOutcome(operation,
140 UMAGATTOperationOutcome::NOT_SUPPORTED);
141 return blink::WebBluetoothError::GATTNotSupported;
143 NOTREACHED();
144 return blink::WebBluetoothError::GATTUntranslatedErrorCode;
147 void StopDiscoverySession(
148 scoped_ptr<device::BluetoothDiscoverySession> discovery_session) {
149 // Nothing goes wrong if the discovery session fails to stop, and we don't
150 // need to wait for it before letting the user's script proceed, so we ignore
151 // the results here.
152 discovery_session->Stop(base::Bind(&base::DoNothing),
153 base::Bind(&base::DoNothing));
156 } // namespace
158 BluetoothDispatcherHost::BluetoothDispatcherHost(int render_process_id)
159 : BrowserMessageFilter(BluetoothMsgStart),
160 render_process_id_(render_process_id),
161 current_delay_time_(kDelayTime),
162 discovery_session_timer_(
163 FROM_HERE,
164 // TODO(jyasskin): Add a way for tests to control the dialog
165 // directly, and change this to a reasonable discovery timeout.
166 base::TimeDelta::FromSecondsD(current_delay_time_),
167 base::Bind(&BluetoothDispatcherHost::StopDeviceDiscovery,
168 // base::Timer guarantees it won't call back after its
169 // destructor starts.
170 base::Unretained(this)),
171 /*is_repeating=*/false),
172 weak_ptr_factory_(this) {
173 DCHECK_CURRENTLY_ON(BrowserThread::UI);
174 if (BluetoothAdapterFactory::IsBluetoothAdapterAvailable())
175 BluetoothAdapterFactory::GetAdapter(
176 base::Bind(&BluetoothDispatcherHost::set_adapter,
177 weak_ptr_factory_.GetWeakPtr()));
180 void BluetoothDispatcherHost::OnDestruct() const {
181 // See class comment: UI Thread Note.
182 BrowserThread::DeleteOnUIThread::Destruct(this);
185 void BluetoothDispatcherHost::OverrideThreadForMessage(
186 const IPC::Message& message,
187 content::BrowserThread::ID* thread) {
188 // See class comment: UI Thread Note.
189 *thread = BrowserThread::UI;
192 bool BluetoothDispatcherHost::OnMessageReceived(const IPC::Message& message) {
193 DCHECK_CURRENTLY_ON(BrowserThread::UI);
194 bool handled = true;
195 IPC_BEGIN_MESSAGE_MAP(BluetoothDispatcherHost, message)
196 IPC_MESSAGE_HANDLER(BluetoothHostMsg_RequestDevice, OnRequestDevice)
197 IPC_MESSAGE_HANDLER(BluetoothHostMsg_ConnectGATT, OnConnectGATT)
198 IPC_MESSAGE_HANDLER(BluetoothHostMsg_GetPrimaryService, OnGetPrimaryService)
199 IPC_MESSAGE_HANDLER(BluetoothHostMsg_GetCharacteristic, OnGetCharacteristic)
200 IPC_MESSAGE_HANDLER(BluetoothHostMsg_ReadValue, OnReadValue)
201 IPC_MESSAGE_HANDLER(BluetoothHostMsg_WriteValue, OnWriteValue)
202 IPC_MESSAGE_UNHANDLED(handled = false)
203 IPC_END_MESSAGE_MAP()
204 return handled;
207 void BluetoothDispatcherHost::SetBluetoothAdapterForTesting(
208 scoped_refptr<device::BluetoothAdapter> mock_adapter) {
209 DCHECK_CURRENTLY_ON(BrowserThread::UI);
210 current_delay_time_ = kTestingDelayTime;
211 // Reset the discovery session timer to use the new delay time.
212 discovery_session_timer_.Start(
213 FROM_HERE, base::TimeDelta::FromSecondsD(current_delay_time_),
214 base::Bind(&BluetoothDispatcherHost::StopDeviceDiscovery,
215 // base::Timer guarantees it won't call back after its
216 // destructor starts.
217 base::Unretained(this)));
218 set_adapter(mock_adapter.Pass());
221 BluetoothDispatcherHost::~BluetoothDispatcherHost() {
222 DCHECK_CURRENTLY_ON(BrowserThread::UI);
223 // Clear adapter, releasing observer references.
224 set_adapter(scoped_refptr<device::BluetoothAdapter>());
227 // Stores information associated with an in-progress requestDevice call. This
228 // will include the state of the active chooser dialog in a future patch.
229 struct BluetoothDispatcherHost::RequestDeviceSession {
230 public:
231 RequestDeviceSession(int thread_id,
232 int request_id,
233 const std::vector<BluetoothScanFilter>& filters,
234 const std::vector<BluetoothUUID>& optional_services)
235 : thread_id(thread_id),
236 request_id(request_id),
237 filters(filters),
238 optional_services(optional_services) {}
240 void AddFilteredDevice(const device::BluetoothDevice& device) {
241 if (chooser && MatchesFilters(device, filters)) {
242 chooser->AddDevice(device.GetAddress(), device.GetName());
246 const int thread_id;
247 const int request_id;
248 const std::vector<BluetoothScanFilter> filters;
249 const std::vector<BluetoothUUID> optional_services;
250 scoped_ptr<BluetoothChooser> chooser;
251 scoped_ptr<device::BluetoothDiscoverySession> discovery_session;
254 void BluetoothDispatcherHost::set_adapter(
255 scoped_refptr<device::BluetoothAdapter> adapter) {
256 DCHECK_CURRENTLY_ON(BrowserThread::UI);
257 if (adapter_.get())
258 adapter_->RemoveObserver(this);
259 adapter_ = adapter;
260 if (adapter_.get())
261 adapter_->AddObserver(this);
264 void BluetoothDispatcherHost::StopDeviceDiscovery() {
265 for (IDMap<RequestDeviceSession, IDMapOwnPointer>::iterator iter(
266 &request_device_sessions_);
267 !iter.IsAtEnd(); iter.Advance()) {
268 RequestDeviceSession* session = iter.GetCurrentValue();
269 if (session->discovery_session) {
270 StopDiscoverySession(session->discovery_session.Pass());
272 if (session->chooser) {
273 session->chooser->ShowDiscoveryState(
274 BluetoothChooser::DiscoveryState::IDLE);
279 void BluetoothDispatcherHost::AdapterPoweredChanged(
280 device::BluetoothAdapter* adapter,
281 bool powered) {
282 const BluetoothChooser::AdapterPresence presence =
283 powered ? BluetoothChooser::AdapterPresence::POWERED_ON
284 : BluetoothChooser::AdapterPresence::POWERED_OFF;
285 for (IDMap<RequestDeviceSession, IDMapOwnPointer>::iterator iter(
286 &request_device_sessions_);
287 !iter.IsAtEnd(); iter.Advance()) {
288 RequestDeviceSession* session = iter.GetCurrentValue();
289 if (session->chooser)
290 session->chooser->SetAdapterPresence(presence);
294 void BluetoothDispatcherHost::DeviceAdded(device::BluetoothAdapter* adapter,
295 device::BluetoothDevice* device) {
296 VLOG(1) << "Adding device to all choosers: " << device->GetAddress();
297 for (IDMap<RequestDeviceSession, IDMapOwnPointer>::iterator iter(
298 &request_device_sessions_);
299 !iter.IsAtEnd(); iter.Advance()) {
300 RequestDeviceSession* session = iter.GetCurrentValue();
301 session->AddFilteredDevice(*device);
305 void BluetoothDispatcherHost::DeviceRemoved(device::BluetoothAdapter* adapter,
306 device::BluetoothDevice* device) {
307 VLOG(1) << "Marking device removed on all choosers: " << device->GetAddress();
308 for (IDMap<RequestDeviceSession, IDMapOwnPointer>::iterator iter(
309 &request_device_sessions_);
310 !iter.IsAtEnd(); iter.Advance()) {
311 RequestDeviceSession* session = iter.GetCurrentValue();
312 if (session->chooser) {
313 session->chooser->RemoveDevice(device->GetAddress());
318 static scoped_ptr<device::BluetoothDiscoveryFilter> ComputeScanFilter(
319 const std::vector<BluetoothScanFilter>& filters) {
320 std::set<BluetoothUUID> services;
321 for (const BluetoothScanFilter& filter : filters) {
322 services.insert(filter.services.begin(), filter.services.end());
324 scoped_ptr<device::BluetoothDiscoveryFilter> discovery_filter(
325 new device::BluetoothDiscoveryFilter(
326 device::BluetoothDiscoveryFilter::TRANSPORT_DUAL));
327 for (const BluetoothUUID& service : services) {
328 discovery_filter->AddUUID(service);
330 return discovery_filter.Pass();
333 void BluetoothDispatcherHost::OnRequestDevice(
334 int thread_id,
335 int request_id,
336 int frame_routing_id,
337 const std::vector<BluetoothScanFilter>& filters,
338 const std::vector<BluetoothUUID>& optional_services) {
339 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
340 RecordWebBluetoothFunctionCall(UMAWebBluetoothFunction::REQUEST_DEVICE);
341 RecordRequestDeviceArguments(filters, optional_services);
343 VLOG(1) << "requestDevice called with the following filters: ";
344 for (const BluetoothScanFilter& filter : filters) {
345 VLOG(1) << "\t[";
346 for (const BluetoothUUID& service : filter.services)
347 VLOG(1) << "\t\t" << service.value();
348 VLOG(1) << "\t]";
351 VLOG(1) << "requestDevice called with the following optional services: ";
352 for (const BluetoothUUID& service : optional_services)
353 VLOG(1) << "\t" << service.value();
355 RenderFrameHostImpl* render_frame_host =
356 RenderFrameHostImpl::FromID(render_process_id_, frame_routing_id);
358 if (!render_frame_host) {
359 DLOG(WARNING)
360 << "Got a requestDevice IPC without a matching RenderFrameHost: "
361 << render_process_id_ << ", " << frame_routing_id;
362 RecordRequestDeviceOutcome(UMARequestDeviceOutcome::NO_RENDER_FRAME);
363 Send(new BluetoothMsg_RequestDeviceError(
364 thread_id, request_id, WebBluetoothError::RequestDeviceWithoutFrame));
365 return;
368 if (!adapter_) {
369 VLOG(1) << "No BluetoothAdapter. Can't serve requestDevice.";
370 RecordRequestDeviceOutcome(UMARequestDeviceOutcome::NO_BLUETOOTH_ADAPTER);
371 Send(new BluetoothMsg_RequestDeviceError(
372 thread_id, request_id, WebBluetoothError::NoBluetoothAdapter));
373 return;
376 if (!adapter_->IsPresent()) {
377 VLOG(1) << "Bluetooth Adapter not present. Can't serve requestDevice.";
378 RecordRequestDeviceOutcome(
379 UMARequestDeviceOutcome::BLUETOOTH_ADAPTER_NOT_PRESENT);
380 Send(new BluetoothMsg_RequestDeviceError(
381 thread_id, request_id, WebBluetoothError::NoBluetoothAdapter));
382 return;
385 // Create storage for the information that backs the chooser, and show the
386 // chooser.
387 RequestDeviceSession* const session = new RequestDeviceSession(
388 thread_id, request_id, filters, optional_services);
389 int chooser_id = request_device_sessions_.Add(session);
391 BluetoothChooser::EventHandler chooser_event_handler =
392 base::Bind(&BluetoothDispatcherHost::OnBluetoothChooserEvent,
393 weak_ptr_factory_.GetWeakPtr(), chooser_id);
394 if (WebContents* web_contents =
395 WebContents::FromRenderFrameHost(render_frame_host)) {
396 if (WebContentsDelegate* delegate = web_contents->GetDelegate()) {
397 session->chooser = delegate->RunBluetoothChooser(
398 web_contents, chooser_event_handler,
399 render_frame_host->GetLastCommittedURL().GetOrigin());
402 if (!session->chooser) {
403 LOG(WARNING)
404 << "No Bluetooth chooser implementation; falling back to first device.";
405 session->chooser.reset(
406 new FirstDeviceBluetoothChooser(chooser_event_handler));
409 // Populate the initial list of devices.
410 VLOG(1) << "Populating " << adapter_->GetDevices().size()
411 << " devices in chooser " << chooser_id;
412 for (const device::BluetoothDevice* device : adapter_->GetDevices()) {
413 VLOG(1) << "\t" << device->GetAddress();
414 session->AddFilteredDevice(*device);
417 if (!session->chooser) {
418 // If the dialog's closing, no need to do any of the rest of this.
419 return;
422 if (!adapter_->IsPowered()) {
423 session->chooser->SetAdapterPresence(
424 BluetoothChooser::AdapterPresence::POWERED_OFF);
425 return;
428 // Redundant with the chooser's default; just to be clear:
429 session->chooser->ShowDiscoveryState(
430 BluetoothChooser::DiscoveryState::DISCOVERING);
431 adapter_->StartDiscoverySessionWithFilter(
432 ComputeScanFilter(filters),
433 base::Bind(&BluetoothDispatcherHost::OnDiscoverySessionStarted,
434 weak_ptr_factory_.GetWeakPtr(), chooser_id),
435 base::Bind(&BluetoothDispatcherHost::OnDiscoverySessionStartedError,
436 weak_ptr_factory_.GetWeakPtr(), chooser_id));
439 void BluetoothDispatcherHost::OnConnectGATT(
440 int thread_id,
441 int request_id,
442 const std::string& device_instance_id) {
443 DCHECK_CURRENTLY_ON(BrowserThread::UI);
444 RecordWebBluetoothFunctionCall(UMAWebBluetoothFunction::CONNECT_GATT);
445 const base::TimeTicks start_time = base::TimeTicks::Now();
447 // TODO(ortuno): Right now it's pointless to check if the domain has access to
448 // the device, because any domain can connect to any device. But once
449 // permissions are implemented we should check that the domain has access to
450 // the device. https://crbug.com/484745
451 device::BluetoothDevice* device = adapter_->GetDevice(device_instance_id);
452 if (device == nullptr) { // See "NETWORK_ERROR Note" above.
453 RecordConnectGATTOutcome(UMAConnectGATTOutcome::NO_DEVICE);
454 Send(new BluetoothMsg_ConnectGATTError(
455 thread_id, request_id, WebBluetoothError::DeviceNoLongerInRange));
456 return;
458 device->CreateGattConnection(
459 base::Bind(&BluetoothDispatcherHost::OnGATTConnectionCreated,
460 weak_ptr_factory_.GetWeakPtr(), thread_id, request_id,
461 device_instance_id, start_time),
462 base::Bind(&BluetoothDispatcherHost::OnCreateGATTConnectionError,
463 weak_ptr_factory_.GetWeakPtr(), thread_id, request_id,
464 device_instance_id, start_time));
467 void BluetoothDispatcherHost::OnGetPrimaryService(
468 int thread_id,
469 int request_id,
470 const std::string& device_instance_id,
471 const std::string& service_uuid) {
472 DCHECK_CURRENTLY_ON(BrowserThread::UI);
473 RecordWebBluetoothFunctionCall(UMAWebBluetoothFunction::GET_PRIMARY_SERVICE);
474 RecordGetPrimaryServiceService(BluetoothUUID(service_uuid));
476 // TODO(ortuno): Check if device_instance_id is in "allowed devices"
477 // https://crbug.com/493459
478 // TODO(ortuno): Check if service_uuid is in "allowed services"
479 // https://crbug.com/493460
480 // For now just wait a fixed time and call OnServiceDiscovered.
481 // TODO(ortuno): Use callback once it's implemented http://crbug.com/484504
482 BrowserThread::PostDelayedTask(
483 BrowserThread::UI, FROM_HERE,
484 base::Bind(&BluetoothDispatcherHost::OnServicesDiscovered,
485 weak_ptr_factory_.GetWeakPtr(), thread_id, request_id,
486 device_instance_id, service_uuid),
487 base::TimeDelta::FromSeconds(current_delay_time_));
490 void BluetoothDispatcherHost::OnGetCharacteristic(
491 int thread_id,
492 int request_id,
493 const std::string& service_instance_id,
494 const std::string& characteristic_uuid) {
495 DCHECK_CURRENTLY_ON(BrowserThread::UI);
496 RecordWebBluetoothFunctionCall(UMAWebBluetoothFunction::GET_CHARACTERISTIC);
497 RecordGetCharacteristicCharacteristic(characteristic_uuid);
499 auto device_iter = service_to_device_.find(service_instance_id);
500 // A service_instance_id not in the map implies a hostile renderer
501 // because a renderer obtains the service id from this class and
502 // it will be added to the map at that time.
503 if (device_iter == service_to_device_.end()) {
504 // Kill the renderer
505 bad_message::ReceivedBadMessage(this, bad_message::BDH_INVALID_SERVICE_ID);
506 return;
509 // TODO(ortuno): Check if domain has access to device.
510 // https://crbug.com/493459
511 device::BluetoothDevice* device =
512 adapter_->GetDevice(device_iter->second /* device_instance_id */);
514 if (device == nullptr) { // See "NETWORK_ERROR Note" above.
515 RecordGetCharacteristicOutcome(UMAGetCharacteristicOutcome::NO_DEVICE);
516 Send(new BluetoothMsg_GetCharacteristicError(
517 thread_id, request_id, WebBluetoothError::DeviceNoLongerInRange));
518 return;
521 // TODO(ortuno): Check if domain has access to service
522 // http://crbug.com/493460
523 device::BluetoothGattService* service =
524 device->GetGattService(service_instance_id);
525 if (!service) {
526 RecordGetCharacteristicOutcome(UMAGetCharacteristicOutcome::NO_SERVICE);
527 Send(new BluetoothMsg_GetCharacteristicError(
528 thread_id, request_id, WebBluetoothError::ServiceNoLongerExists));
529 return;
532 for (BluetoothGattCharacteristic* characteristic :
533 service->GetCharacteristics()) {
534 if (characteristic->GetUUID().canonical_value() == characteristic_uuid) {
535 const std::string& characteristic_instance_id =
536 characteristic->GetIdentifier();
538 auto insert_result = characteristic_to_service_.insert(
539 make_pair(characteristic_instance_id, service_instance_id));
541 // If value is already in map, DCHECK it's valid.
542 if (!insert_result.second)
543 DCHECK(insert_result.first->second == service_instance_id);
545 RecordGetCharacteristicOutcome(UMAGetCharacteristicOutcome::SUCCESS);
546 // TODO(ortuno): Use generated instance ID instead.
547 // https://crbug.com/495379
548 Send(new BluetoothMsg_GetCharacteristicSuccess(
549 thread_id, request_id, characteristic_instance_id));
550 return;
553 RecordGetCharacteristicOutcome(UMAGetCharacteristicOutcome::NOT_FOUND);
554 Send(new BluetoothMsg_GetCharacteristicError(
555 thread_id, request_id, WebBluetoothError::CharacteristicNotFound));
558 void BluetoothDispatcherHost::OnReadValue(
559 int thread_id,
560 int request_id,
561 const std::string& characteristic_instance_id) {
562 DCHECK_CURRENTLY_ON(BrowserThread::UI);
563 RecordWebBluetoothFunctionCall(
564 UMAWebBluetoothFunction::CHARACTERISTIC_READ_VALUE);
566 auto characteristic_iter =
567 characteristic_to_service_.find(characteristic_instance_id);
568 // A characteristic_instance_id not in the map implies a hostile renderer
569 // because a renderer obtains the characteristic id from this class and
570 // it will be added to the map at that time.
571 if (characteristic_iter == characteristic_to_service_.end()) {
572 // Kill the renderer
573 bad_message::ReceivedBadMessage(this,
574 bad_message::BDH_INVALID_CHARACTERISTIC_ID);
575 return;
577 const std::string& service_instance_id = characteristic_iter->second;
579 auto device_iter = service_to_device_.find(service_instance_id);
581 CHECK(device_iter != service_to_device_.end());
583 device::BluetoothDevice* device =
584 adapter_->GetDevice(device_iter->second /* device_instance_id */);
585 if (device == nullptr) { // See "NETWORK_ERROR Note" above.
586 RecordCharacteristicReadValueOutcome(UMAGATTOperationOutcome::NO_DEVICE);
587 Send(new BluetoothMsg_ReadCharacteristicValueError(
588 thread_id, request_id, WebBluetoothError::DeviceNoLongerInRange));
589 return;
592 BluetoothGattService* service = device->GetGattService(service_instance_id);
593 if (service == nullptr) {
594 RecordCharacteristicReadValueOutcome(UMAGATTOperationOutcome::NO_SERVICE);
595 Send(new BluetoothMsg_ReadCharacteristicValueError(
596 thread_id, request_id, WebBluetoothError::ServiceNoLongerExists));
597 return;
600 BluetoothGattCharacteristic* characteristic =
601 service->GetCharacteristic(characteristic_instance_id);
602 if (characteristic == nullptr) {
603 RecordCharacteristicReadValueOutcome(
604 UMAGATTOperationOutcome::NO_CHARACTERISTIC);
605 Send(new BluetoothMsg_ReadCharacteristicValueError(
606 thread_id, request_id,
607 WebBluetoothError::CharacteristicNoLongerExists));
608 return;
611 characteristic->ReadRemoteCharacteristic(
612 base::Bind(&BluetoothDispatcherHost::OnCharacteristicValueRead,
613 weak_ptr_factory_.GetWeakPtr(), thread_id, request_id),
614 base::Bind(&BluetoothDispatcherHost::OnCharacteristicReadValueError,
615 weak_ptr_factory_.GetWeakPtr(), thread_id, request_id));
618 void BluetoothDispatcherHost::OnWriteValue(
619 int thread_id,
620 int request_id,
621 const std::string& characteristic_instance_id,
622 const std::vector<uint8_t>& value) {
623 DCHECK_CURRENTLY_ON(BrowserThread::UI);
624 RecordWebBluetoothFunctionCall(
625 UMAWebBluetoothFunction::CHARACTERISTIC_WRITE_VALUE);
627 // Length check per step 3 of writeValue algorithm:
628 // https://webbluetoothchrome.github.io/web-bluetooth/#dom-bluetoothgattcharacteristic-writevalue
629 // We perform the length check on the renderer side. So if we
630 // get a value with length > 512, we can assume it's a hostile
631 // renderer and kill it.
632 if (value.size() > 512) {
633 bad_message::ReceivedBadMessage(
634 this, bad_message::BDH_INVALID_WRITE_VALUE_LENGTH);
635 return;
638 auto characteristic_iter =
639 characteristic_to_service_.find(characteristic_instance_id);
640 // A characteristic_instance_id not in the map implies a hostile renderer
641 // because a renderer obtains the characteristic id from this class and
642 // it will be added to the map at that time.
643 if (characteristic_iter == characteristic_to_service_.end()) {
644 bad_message::ReceivedBadMessage(this,
645 bad_message::BDH_INVALID_CHARACTERISTIC_ID);
646 return;
648 const std::string& service_instance_id = characteristic_iter->second;
650 auto device_iter = service_to_device_.find(service_instance_id);
652 CHECK(device_iter != service_to_device_.end());
654 device::BluetoothDevice* device =
655 adapter_->GetDevice(device_iter->second /* device_instance_id */);
656 if (device == nullptr) { // See "NETWORK_ERROR Note" above.
657 RecordCharacteristicWriteValueOutcome(UMAGATTOperationOutcome::NO_DEVICE);
658 Send(new BluetoothMsg_WriteCharacteristicValueError(
659 thread_id, request_id, WebBluetoothError::DeviceNoLongerInRange));
660 return;
663 BluetoothGattService* service = device->GetGattService(service_instance_id);
664 if (service == nullptr) {
665 RecordCharacteristicWriteValueOutcome(UMAGATTOperationOutcome::NO_SERVICE);
666 Send(new BluetoothMsg_WriteCharacteristicValueError(
667 thread_id, request_id, WebBluetoothError::ServiceNoLongerExists));
668 return;
671 BluetoothGattCharacteristic* characteristic =
672 service->GetCharacteristic(characteristic_instance_id);
673 if (characteristic == nullptr) {
674 RecordCharacteristicWriteValueOutcome(
675 UMAGATTOperationOutcome::NO_CHARACTERISTIC);
676 Send(new BluetoothMsg_WriteCharacteristicValueError(
677 thread_id, request_id,
678 WebBluetoothError::CharacteristicNoLongerExists));
679 return;
681 characteristic->WriteRemoteCharacteristic(
682 value, base::Bind(&BluetoothDispatcherHost::OnWriteValueSuccess,
683 weak_ptr_factory_.GetWeakPtr(), thread_id, request_id),
684 base::Bind(&BluetoothDispatcherHost::OnWriteValueFailed,
685 weak_ptr_factory_.GetWeakPtr(), thread_id, request_id));
688 void BluetoothDispatcherHost::OnDiscoverySessionStarted(
689 int chooser_id,
690 scoped_ptr<device::BluetoothDiscoverySession> discovery_session) {
691 DCHECK_CURRENTLY_ON(BrowserThread::UI);
692 VLOG(1) << "Started discovery session for " << chooser_id;
693 if (RequestDeviceSession* session =
694 request_device_sessions_.Lookup(chooser_id)) {
695 session->discovery_session = discovery_session.Pass();
697 // Arrange to stop discovery later.
698 discovery_session_timer_.Reset();
699 } else {
700 VLOG(1) << "Chooser " << chooser_id
701 << " was closed before the session finished starting. Stopping.";
702 StopDiscoverySession(discovery_session.Pass());
706 void BluetoothDispatcherHost::OnDiscoverySessionStartedError(int chooser_id) {
707 DCHECK_CURRENTLY_ON(BrowserThread::UI);
708 VLOG(1) << "Failed to start discovery session for " << chooser_id;
709 if (RequestDeviceSession* session =
710 request_device_sessions_.Lookup(chooser_id)) {
711 if (session->chooser && !session->discovery_session) {
712 session->chooser->ShowDiscoveryState(
713 BluetoothChooser::DiscoveryState::FAILED_TO_START);
716 // Ignore discovery session start errors when the dialog was already closed by
717 // the time they happen.
720 void BluetoothDispatcherHost::OnBluetoothChooserEvent(
721 int chooser_id,
722 BluetoothChooser::Event event,
723 const std::string& device_id) {
724 switch (event) {
725 case BluetoothChooser::Event::RESCAN:
726 // TODO(jyasskin): Implement starting a new Bluetooth discovery session.
727 NOTIMPLEMENTED();
728 break;
729 case BluetoothChooser::Event::CANCELLED:
730 case BluetoothChooser::Event::SELECTED: {
731 RequestDeviceSession* session =
732 request_device_sessions_.Lookup(chooser_id);
733 DCHECK(session) << "Shouldn't close the dialog twice.";
734 CHECK(session->chooser) << "Shouldn't close the dialog twice.";
736 // Synchronously ensure nothing else calls into the chooser after it has
737 // asked to be closed.
738 session->chooser.reset();
740 // Yield to the event loop to make sure we don't destroy the session
741 // within a BluetoothDispatcherHost stack frame.
742 if (!base::ThreadTaskRunnerHandle::Get()->PostTask(
743 FROM_HERE,
744 base::Bind(&BluetoothDispatcherHost::FinishClosingChooser,
745 weak_ptr_factory_.GetWeakPtr(), chooser_id, event,
746 device_id))) {
747 LOG(WARNING) << "No TaskRunner; not closing requestDevice dialog.";
749 break;
751 case BluetoothChooser::Event::SHOW_OVERVIEW_HELP:
752 ShowBluetoothOverviewLink();
753 break;
754 case BluetoothChooser::Event::SHOW_PAIRING_HELP:
755 ShowBluetoothPairingLink();
756 break;
757 case BluetoothChooser::Event::SHOW_ADAPTER_OFF_HELP:
758 ShowBluetoothAdapterOffLink();
759 break;
763 void BluetoothDispatcherHost::FinishClosingChooser(
764 int chooser_id,
765 BluetoothChooser::Event event,
766 const std::string& device_id) {
767 RequestDeviceSession* session = request_device_sessions_.Lookup(chooser_id);
768 DCHECK(session) << "Session removed unexpectedly.";
770 if (event == BluetoothChooser::Event::CANCELLED) {
771 RecordRequestDeviceOutcome(
772 UMARequestDeviceOutcome::BLUETOOTH_CHOOSER_CANCELLED);
773 VLOG(1) << "Bluetooth chooser cancelled";
774 Send(new BluetoothMsg_RequestDeviceError(
775 session->thread_id, session->request_id,
776 WebBluetoothError::ChooserCancelled));
777 request_device_sessions_.Remove(chooser_id);
778 return;
780 DCHECK_EQ(static_cast<int>(event),
781 static_cast<int>(BluetoothChooser::Event::SELECTED));
783 const device::BluetoothDevice* const device = adapter_->GetDevice(device_id);
784 if (device == nullptr) {
785 VLOG(1) << "Device " << device_id << " no longer in adapter";
786 RecordRequestDeviceOutcome(UMARequestDeviceOutcome::CHOSEN_DEVICE_VANISHED);
787 Send(new BluetoothMsg_RequestDeviceError(
788 session->thread_id, session->request_id,
789 WebBluetoothError::ChosenDeviceVanished));
790 request_device_sessions_.Remove(chooser_id);
791 return;
794 VLOG(1) << "Device: " << device->GetName();
795 VLOG(1) << "UUIDs: ";
796 for (BluetoothUUID uuid : device->GetUUIDs())
797 VLOG(1) << "\t" << uuid.canonical_value();
799 content::BluetoothDevice device_ipc(
800 device->GetAddress(), // instance_id
801 device->GetName(), // name
802 device->GetBluetoothClass(), // device_class
803 device->GetVendorIDSource(), // vendor_id_source
804 device->GetVendorID(), // vendor_id
805 device->GetProductID(), // product_id
806 device->GetDeviceID(), // product_version
807 device->IsPaired(), // paired
808 content::BluetoothDevice::UUIDsFromBluetoothUUIDs(
809 device->GetUUIDs())); // uuids
810 RecordRequestDeviceOutcome(UMARequestDeviceOutcome::SUCCESS);
811 Send(new BluetoothMsg_RequestDeviceSuccess(session->thread_id,
812 session->request_id, device_ipc));
813 request_device_sessions_.Remove(chooser_id);
816 void BluetoothDispatcherHost::OnGATTConnectionCreated(
817 int thread_id,
818 int request_id,
819 const std::string& device_instance_id,
820 base::TimeTicks start_time,
821 scoped_ptr<device::BluetoothGattConnection> connection) {
822 // TODO(ortuno): Save the BluetoothGattConnection so we can disconnect
823 // from it.
824 RecordConnectGATTTimeSuccess(base::TimeTicks::Now() - start_time);
825 RecordConnectGATTOutcome(UMAConnectGATTOutcome::SUCCESS);
826 Send(new BluetoothMsg_ConnectGATTSuccess(thread_id, request_id,
827 device_instance_id));
830 void BluetoothDispatcherHost::OnCreateGATTConnectionError(
831 int thread_id,
832 int request_id,
833 const std::string& device_instance_id,
834 base::TimeTicks start_time,
835 device::BluetoothDevice::ConnectErrorCode error_code) {
836 // There was an error creating the ATT Bearer so we reject with
837 // NetworkError.
838 // https://webbluetoothchrome.github.io/web-bluetooth/#dom-bluetoothdevice-connectgatt
839 RecordConnectGATTTimeFailed(base::TimeTicks::Now() - start_time);
840 // RecordConnectGATTOutcome is called by TranslateConnectError.
841 Send(new BluetoothMsg_ConnectGATTError(thread_id, request_id,
842 TranslateConnectError(error_code)));
845 void BluetoothDispatcherHost::OnServicesDiscovered(
846 int thread_id,
847 int request_id,
848 const std::string& device_instance_id,
849 const std::string& service_uuid) {
850 DCHECK_CURRENTLY_ON(BrowserThread::UI);
852 device::BluetoothDevice* device = adapter_->GetDevice(device_instance_id);
853 if (device == nullptr) { // See "NETWORK_ERROR Note" above.
854 RecordGetPrimaryServiceOutcome(UMAGetPrimaryServiceOutcome::NO_DEVICE);
855 Send(new BluetoothMsg_GetPrimaryServiceError(
856 thread_id, request_id, WebBluetoothError::DeviceNoLongerInRange));
857 return;
859 for (BluetoothGattService* service : device->GetGattServices()) {
860 if (service->GetUUID().canonical_value() == service_uuid) {
861 // TODO(ortuno): Use generated instance ID instead.
862 // https://crbug.com/495379
863 const std::string& service_identifier = service->GetIdentifier();
864 auto insert_result = service_to_device_.insert(
865 make_pair(service_identifier, device_instance_id));
867 // If a value is already in map, DCHECK it's valid.
868 if (!insert_result.second)
869 DCHECK(insert_result.first->second == device_instance_id);
871 RecordGetPrimaryServiceOutcome(UMAGetPrimaryServiceOutcome::SUCCESS);
872 Send(new BluetoothMsg_GetPrimaryServiceSuccess(thread_id, request_id,
873 service_identifier));
874 return;
877 RecordGetPrimaryServiceOutcome(UMAGetPrimaryServiceOutcome::NOT_FOUND);
878 Send(new BluetoothMsg_GetPrimaryServiceError(
879 thread_id, request_id, WebBluetoothError::ServiceNotFound));
882 void BluetoothDispatcherHost::OnCharacteristicValueRead(
883 int thread_id,
884 int request_id,
885 const std::vector<uint8>& value) {
886 RecordCharacteristicReadValueOutcome(UMAGATTOperationOutcome::SUCCESS);
887 Send(new BluetoothMsg_ReadCharacteristicValueSuccess(thread_id, request_id,
888 value));
891 void BluetoothDispatcherHost::OnCharacteristicReadValueError(
892 int thread_id,
893 int request_id,
894 device::BluetoothGattService::GattErrorCode error_code) {
895 // TranslateGATTError calls RecordGATTOperationOutcome.
896 Send(new BluetoothMsg_ReadCharacteristicValueError(
897 thread_id, request_id,
898 TranslateGATTError(error_code, UMAGATTOperation::CHARACTERISTIC_READ)));
901 void BluetoothDispatcherHost::OnWriteValueSuccess(int thread_id,
902 int request_id) {
903 RecordCharacteristicWriteValueOutcome(UMAGATTOperationOutcome::SUCCESS);
904 Send(new BluetoothMsg_WriteCharacteristicValueSuccess(thread_id, request_id));
907 void BluetoothDispatcherHost::OnWriteValueFailed(
908 int thread_id,
909 int request_id,
910 device::BluetoothGattService::GattErrorCode error_code) {
911 // TranslateGATTError calls RecordGATTOperationOutcome.
912 Send(new BluetoothMsg_WriteCharacteristicValueError(
913 thread_id, request_id,
914 TranslateGATTError(error_code, UMAGATTOperation::CHARACTERISTIC_WRITE)));
917 void BluetoothDispatcherHost::ShowBluetoothOverviewLink() {
918 NOTIMPLEMENTED();
921 void BluetoothDispatcherHost::ShowBluetoothPairingLink() {
922 NOTIMPLEMENTED();
925 void BluetoothDispatcherHost::ShowBluetoothAdapterOffLink() {
926 NOTIMPLEMENTED();
929 } // namespace content