Make USB permissions work in the new permission message system
[chromium-blink-merge.git] / content / browser / bluetooth / bluetooth_dispatcher_host.cc
blobc6c49b127592f10e6292f9c8a5316e947bff7cb8
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/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;
32 namespace content {
34 namespace {
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
42 // Defined at
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())
47 return false;
48 for (const BluetoothUUID& service : filter.services) {
49 if (!ContainsKey(device_uuids, service)) {
50 return false;
53 return true;
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)) {
63 return true;
66 return false;
69 WebBluetoothError TranslateConnectError(
70 device::BluetoothDevice::ConnectErrorCode error_code) {
71 switch (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;
97 NOTREACHED();
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;
136 NOTREACHED();
137 return blink::WebBluetoothError::GATTUntranslatedErrorCode;
140 } // namespace
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);
168 bool handled = true;
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()
178 return handled;
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);
208 if (adapter_.get())
209 adapter_->RemoveObserver(this);
210 adapter_ = adapter;
211 if (adapter_.get())
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(
231 int thread_id,
232 int request_id,
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) {
242 VLOG(1) << "\t[";
243 for (const BluetoothUUID& service : filter.services)
244 VLOG(1) << "\t\t" << service.value();
245 VLOG(1) << "\t]";
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) {
256 DLOG(WARNING)
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));
262 return;
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)))
272 .second) {
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));
286 return;
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
292 // with a message.
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));
300 return;
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));
308 } else {
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));
314 return;
317 void BluetoothDispatcherHost::OnConnectGATT(
318 int thread_id,
319 int request_id,
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));
334 return;
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(
346 int thread_id,
347 int request_id,
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(
369 int thread_id,
370 int request_id,
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()) {
382 // Kill the renderer
383 bad_message::ReceivedBadMessage(this, bad_message::BDH_INVALID_SERVICE_ID);
384 return;
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));
396 return;
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);
403 if (!service) {
404 RecordGetCharacteristicOutcome(UMAGetCharacteristicOutcome::NO_SERVICE);
405 Send(new BluetoothMsg_GetCharacteristicError(
406 thread_id, request_id, WebBluetoothError::ServiceNoLongerExists));
407 return;
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));
428 return;
431 RecordGetCharacteristicOutcome(UMAGetCharacteristicOutcome::NOT_FOUND);
432 Send(new BluetoothMsg_GetCharacteristicError(
433 thread_id, request_id, WebBluetoothError::CharacteristicNotFound));
436 void BluetoothDispatcherHost::OnReadValue(
437 int thread_id,
438 int request_id,
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()) {
450 // Kill the renderer
451 bad_message::ReceivedBadMessage(this,
452 bad_message::BDH_INVALID_CHARACTERISTIC_ID);
453 return;
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));
467 return;
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));
475 return;
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));
486 return;
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(
497 int thread_id,
498 int request_id,
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);
513 return;
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);
524 return;
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));
538 return;
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));
546 return;
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));
557 return;
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(
567 int thread_id,
568 int request_id,
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,
580 int request_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(
590 int thread_id,
591 int request_id,
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,
602 int request_id) {
603 DCHECK_CURRENTLY_ON(BrowserThread::UI);
604 auto session =
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,
627 device_ipc));
628 request_device_sessions_.erase(session);
629 return;
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,
640 int request_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(
650 int thread_id,
651 int request_id,
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
656 // from it.
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(
664 int thread_id,
665 int request_id,
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
670 // NetworkError.
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(
679 int thread_id,
680 int request_id,
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));
690 return;
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));
707 return;
710 RecordGetPrimaryServiceOutcome(UMAGetPrimaryServiceOutcome::NOT_FOUND);
711 Send(new BluetoothMsg_GetPrimaryServiceError(
712 thread_id, request_id, WebBluetoothError::ServiceNotFound));
715 void BluetoothDispatcherHost::OnCharacteristicValueRead(
716 int thread_id,
717 int request_id,
718 const std::vector<uint8>& value) {
719 RecordCharacteristicReadValueOutcome(UMAGATTOperationOutcome::SUCCESS);
720 Send(new BluetoothMsg_ReadCharacteristicValueSuccess(thread_id, request_id,
721 value));
724 void BluetoothDispatcherHost::OnCharacteristicReadValueError(
725 int thread_id,
726 int request_id,
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,
735 int request_id) {
736 RecordCharacteristicWriteValueOutcome(UMAGATTOperationOutcome::SUCCESS);
737 Send(new BluetoothMsg_WriteCharacteristicValueSuccess(thread_id, request_id));
740 void BluetoothDispatcherHost::OnWriteValueFailed(
741 int thread_id,
742 int request_id,
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