Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(383)

Side by Side Diff: content/browser/bluetooth/bluetooth_dispatcher_host.cc

Issue 1922923002: bluetooth: Move requestDevice to mojo (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@bluetooth-separate-tests-request-device
Patch Set: Change ref to pointer Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 // ID Not In Map Note:
6 // A service, characteristic, or descriptor ID not in the corresponding
7 // BluetoothDispatcherHost map [service_to_device_, characteristic_to_service_,
8 // descriptor_to_characteristic_] implies a hostile renderer because a renderer
9 // obtains the corresponding ID from this class and it will be added to the map
10 // at that time.
11
12 #include "content/browser/bluetooth/bluetooth_dispatcher_host.h"
13
14 #include <stddef.h>
15
16 #include <utility>
17
18 #include "base/bind.h"
19 #include "base/single_thread_task_runner.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/threading/thread_task_runner_handle.h"
22 #include "content/browser/bluetooth/bluetooth_blacklist.h"
23 #include "content/browser/bluetooth/bluetooth_metrics.h"
24 #include "content/browser/bluetooth/cache_query_result.h"
25 #include "content/browser/bluetooth/first_device_bluetooth_chooser.h"
26 #include "content/browser/frame_host/render_frame_host_impl.h"
27 #include "content/browser/web_contents/web_contents_impl.h"
28 #include "content/public/browser/content_browser_client.h"
29 #include "content/public/browser/web_contents.h"
30 #include "content/public/browser/web_contents_delegate.h"
31 #include "device/bluetooth/bluetooth_adapter.h"
32 #include "device/bluetooth/bluetooth_adapter_factory.h"
33 #include "device/bluetooth/bluetooth_device.h"
34 #include "device/bluetooth/bluetooth_discovery_session.h"
35
36 using blink::WebBluetoothError;
37 using device::BluetoothAdapter;
38 using device::BluetoothAdapterFactory;
39 using device::BluetoothUUID;
40
41 namespace content {
42
43 namespace {
44
45 // TODO(ortuno): Once we have a chooser for scanning, a way to control that
46 // chooser from tests, and the right callback for discovered services we should
47 // delete these constants.
48 // https://crbug.com/436280 and https://crbug.com/484504
49 const int kDelayTime = 5; // 5 seconds for scanning and discovering
50 const int kTestingDelayTime = 0; // No need to wait during tests
51 const size_t kMaxLengthForDeviceName =
52 29; // max length of device name in filter.
53
54 bool IsEmptyOrInvalidFilter(const content::BluetoothScanFilter& filter) {
55 return filter.name.empty() && filter.namePrefix.empty() &&
56 filter.services.empty() &&
57 filter.name.length() > kMaxLengthForDeviceName &&
58 filter.namePrefix.length() > kMaxLengthForDeviceName;
59 }
60
61 bool HasEmptyOrInvalidFilter(
62 const std::vector<content::BluetoothScanFilter>& filters) {
63 return filters.empty()
64 ? true
65 : filters.end() != std::find_if(filters.begin(), filters.end(),
66 IsEmptyOrInvalidFilter);
67 }
68
69 // Defined at
70 // https://webbluetoothchrome.github.io/web-bluetooth/#dfn-matches-a-filter
71 bool MatchesFilter(const device::BluetoothDevice& device,
72 const content::BluetoothScanFilter& filter) {
73 DCHECK(!IsEmptyOrInvalidFilter(filter));
74
75 const std::string device_name = base::UTF16ToUTF8(device.GetName());
76
77 if (!filter.name.empty() && (device_name != filter.name)) {
78 return false;
79 }
80
81 if (!filter.namePrefix.empty() &&
82 (!base::StartsWith(device_name, filter.namePrefix,
83 base::CompareCase::SENSITIVE))) {
84 return false;
85 }
86
87 if (!filter.services.empty()) {
88 const auto& device_uuid_list = device.GetUUIDs();
89 const std::set<BluetoothUUID> device_uuids(device_uuid_list.begin(),
90 device_uuid_list.end());
91 for (const auto& service : filter.services) {
92 if (!ContainsKey(device_uuids, service)) {
93 return false;
94 }
95 }
96 }
97
98 return true;
99 }
100
101 bool MatchesFilters(const device::BluetoothDevice& device,
102 const std::vector<content::BluetoothScanFilter>& filters) {
103 DCHECK(!HasEmptyOrInvalidFilter(filters));
104 for (const content::BluetoothScanFilter& filter : filters) {
105 if (MatchesFilter(device, filter)) {
106 return true;
107 }
108 }
109 return false;
110 }
111
112 void StopDiscoverySession(
113 std::unique_ptr<device::BluetoothDiscoverySession> discovery_session) {
114 // Nothing goes wrong if the discovery session fails to stop, and we don't
115 // need to wait for it before letting the user's script proceed, so we ignore
116 // the results here.
117 discovery_session->Stop(base::Bind(&base::DoNothing),
118 base::Bind(&base::DoNothing));
119 }
120
121 UMARequestDeviceOutcome OutcomeFromChooserEvent(BluetoothChooser::Event event) {
122 switch (event) {
123 case BluetoothChooser::Event::DENIED_PERMISSION:
124 return UMARequestDeviceOutcome::BLUETOOTH_CHOOSER_DENIED_PERMISSION;
125 case BluetoothChooser::Event::CANCELLED:
126 return UMARequestDeviceOutcome::BLUETOOTH_CHOOSER_CANCELLED;
127 case BluetoothChooser::Event::SHOW_OVERVIEW_HELP:
128 return UMARequestDeviceOutcome::BLUETOOTH_OVERVIEW_HELP_LINK_PRESSED;
129 case BluetoothChooser::Event::SHOW_ADAPTER_OFF_HELP:
130 return UMARequestDeviceOutcome::ADAPTER_OFF_HELP_LINK_PRESSED;
131 case BluetoothChooser::Event::SHOW_NEED_LOCATION_HELP:
132 return UMARequestDeviceOutcome::NEED_LOCATION_HELP_LINK_PRESSED;
133 case BluetoothChooser::Event::SELECTED:
134 // We can't know if we are going to send a success message yet because
135 // the device could have vanished. This event should be histogramed
136 // manually after checking if the device is still around.
137 NOTREACHED();
138 return UMARequestDeviceOutcome::SUCCESS;
139 case BluetoothChooser::Event::RESCAN:
140 // Rescanning doesn't result in a IPC message for the request being sent
141 // so no need to histogram it.
142 NOTREACHED();
143 return UMARequestDeviceOutcome::SUCCESS;
144 }
145 NOTREACHED();
146 return UMARequestDeviceOutcome::SUCCESS;
147 }
148
149 } // namespace
150
151 BluetoothDispatcherHost::BluetoothDispatcherHost(int render_process_id)
152 : BrowserMessageFilter(BluetoothMsgStart),
153 render_process_id_(render_process_id),
154 current_delay_time_(kDelayTime),
155 discovery_session_timer_(
156 FROM_HERE,
157 // TODO(jyasskin): Add a way for tests to control the dialog
158 // directly, and change this to a reasonable discovery timeout.
159 base::TimeDelta::FromSecondsD(current_delay_time_),
160 base::Bind(&BluetoothDispatcherHost::StopDeviceDiscovery,
161 // base::Timer guarantees it won't call back after its
162 // destructor starts.
163 base::Unretained(this)),
164 /*is_repeating=*/false),
165 weak_ptr_factory_(this) {
166 DCHECK_CURRENTLY_ON(BrowserThread::UI);
167
168 // Bind all future weak pointers to the UI thread.
169 weak_ptr_on_ui_thread_ = weak_ptr_factory_.GetWeakPtr();
170 weak_ptr_on_ui_thread_.get(); // Associates with UI thread.
171 }
172
173 void BluetoothDispatcherHost::OnDestruct() const {
174 // See class comment: UI Thread Note.
175 BrowserThread::DeleteOnUIThread::Destruct(this);
176 }
177
178 void BluetoothDispatcherHost::OverrideThreadForMessage(
179 const IPC::Message& message,
180 content::BrowserThread::ID* thread) {
181 // See class comment: UI Thread Note.
182 *thread = BrowserThread::UI;
183 }
184
185 bool BluetoothDispatcherHost::OnMessageReceived(const IPC::Message& message) {
186 DCHECK_CURRENTLY_ON(BrowserThread::UI);
187 bool handled = true;
188 IPC_BEGIN_MESSAGE_MAP(BluetoothDispatcherHost, message)
189 IPC_MESSAGE_HANDLER(BluetoothHostMsg_RequestDevice, OnRequestDevice)
190 IPC_MESSAGE_UNHANDLED(handled = false)
191 IPC_END_MESSAGE_MAP()
192 return handled;
193 }
194
195 void BluetoothDispatcherHost::SetBluetoothAdapterForTesting(
196 scoped_refptr<device::BluetoothAdapter> mock_adapter) {
197 DCHECK_CURRENTLY_ON(BrowserThread::UI);
198
199 if (mock_adapter.get()) {
200 current_delay_time_ = kTestingDelayTime;
201 // Reset the discovery session timer to use the new delay time.
202 discovery_session_timer_.Start(
203 FROM_HERE, base::TimeDelta::FromSecondsD(current_delay_time_),
204 base::Bind(&BluetoothDispatcherHost::StopDeviceDiscovery,
205 // base::Timer guarantees it won't call back after its
206 // destructor starts.
207 base::Unretained(this)));
208 } else {
209 // The following data structures are used to store pending operations.
210 // They should never contain elements at the end of a test.
211 DCHECK(request_device_sessions_.IsEmpty());
212
213 // The following data structures are cleaned up when a
214 // device/service/characteristic is removed.
215 // Since this can happen after the test is done and the cleanup function is
216 // called, we clean them here.
217 allowed_devices_map_ = BluetoothAllowedDevicesMap();
218 }
219
220 set_adapter(std::move(mock_adapter));
221 }
222
223 BluetoothDispatcherHost::~BluetoothDispatcherHost() {
224 DCHECK_CURRENTLY_ON(BrowserThread::UI);
225 // Clear adapter, releasing observer references.
226 set_adapter(scoped_refptr<device::BluetoothAdapter>());
227 }
228
229 // Stores information associated with an in-progress requestDevice call. This
230 // will include the state of the active chooser dialog in a future patch.
231 struct BluetoothDispatcherHost::RequestDeviceSession {
232 public:
233 RequestDeviceSession(int thread_id,
234 int request_id,
235 url::Origin origin,
236 const std::vector<BluetoothScanFilter>& filters,
237 const std::vector<BluetoothUUID>& optional_services)
238 : thread_id(thread_id),
239 request_id(request_id),
240 origin(origin),
241 filters(filters),
242 optional_services(optional_services) {}
243
244 void AddFilteredDevice(const device::BluetoothDevice& device) {
245 if (chooser && MatchesFilters(device, filters)) {
246 chooser->AddDevice(device.GetAddress(), device.GetName());
247 }
248 }
249
250 std::unique_ptr<device::BluetoothDiscoveryFilter> ComputeScanFilter() const {
251 std::set<BluetoothUUID> services;
252 for (const BluetoothScanFilter& filter : filters) {
253 services.insert(filter.services.begin(), filter.services.end());
254 }
255 std::unique_ptr<device::BluetoothDiscoveryFilter> discovery_filter(
256 new device::BluetoothDiscoveryFilter(
257 device::BluetoothDiscoveryFilter::TRANSPORT_DUAL));
258 for (const BluetoothUUID& service : services) {
259 discovery_filter->AddUUID(service);
260 }
261 return discovery_filter;
262 }
263
264 const int thread_id;
265 const int request_id;
266 const url::Origin origin;
267 const std::vector<BluetoothScanFilter> filters;
268 const std::vector<BluetoothUUID> optional_services;
269 std::unique_ptr<BluetoothChooser> chooser;
270 std::unique_ptr<device::BluetoothDiscoverySession> discovery_session;
271 };
272
273 void BluetoothDispatcherHost::set_adapter(
274 scoped_refptr<device::BluetoothAdapter> adapter) {
275 DCHECK_CURRENTLY_ON(BrowserThread::UI);
276 if (adapter_.get()) {
277 adapter_->RemoveObserver(this);
278 for (device::BluetoothAdapter::Observer* observer : adapter_observers_) {
279 adapter_->RemoveObserver(observer);
280 }
281 }
282 adapter_ = adapter;
283 if (adapter_.get()) {
284 adapter_->AddObserver(this);
285 for (device::BluetoothAdapter::Observer* observer : adapter_observers_) {
286 adapter_->AddObserver(observer);
287 }
288 } else {
289 // Notify that the adapter has been removed and observers should clean up
290 // their state.
291 for (device::BluetoothAdapter::Observer* observer : adapter_observers_) {
292 observer->AdapterPresentChanged(nullptr, false);
293 }
294 }
295 }
296
297 void BluetoothDispatcherHost::StartDeviceDiscovery(
298 RequestDeviceSession* session,
299 int chooser_id) {
300 DCHECK_CURRENTLY_ON(BrowserThread::UI);
301 if (session->discovery_session) {
302 // Already running; just increase the timeout.
303 discovery_session_timer_.Reset();
304 } else {
305 session->chooser->ShowDiscoveryState(
306 BluetoothChooser::DiscoveryState::DISCOVERING);
307 adapter_->StartDiscoverySessionWithFilter(
308 session->ComputeScanFilter(),
309 base::Bind(&BluetoothDispatcherHost::OnDiscoverySessionStarted,
310 weak_ptr_on_ui_thread_, chooser_id),
311 base::Bind(&BluetoothDispatcherHost::OnDiscoverySessionStartedError,
312 weak_ptr_on_ui_thread_, chooser_id));
313 }
314 }
315
316 void BluetoothDispatcherHost::StopDeviceDiscovery() {
317 DCHECK_CURRENTLY_ON(BrowserThread::UI);
318 for (IDMap<RequestDeviceSession, IDMapOwnPointer>::iterator iter(
319 &request_device_sessions_);
320 !iter.IsAtEnd(); iter.Advance()) {
321 RequestDeviceSession* session = iter.GetCurrentValue();
322 if (session->discovery_session) {
323 StopDiscoverySession(std::move(session->discovery_session));
324 }
325 if (session->chooser) {
326 session->chooser->ShowDiscoveryState(
327 BluetoothChooser::DiscoveryState::IDLE);
328 }
329 }
330 }
331
332 void BluetoothDispatcherHost::AdapterPoweredChanged(
333 device::BluetoothAdapter* adapter,
334 bool powered) {
335 DCHECK_CURRENTLY_ON(BrowserThread::UI);
336 const BluetoothChooser::AdapterPresence presence =
337 powered ? BluetoothChooser::AdapterPresence::POWERED_ON
338 : BluetoothChooser::AdapterPresence::POWERED_OFF;
339 for (IDMap<RequestDeviceSession, IDMapOwnPointer>::iterator iter(
340 &request_device_sessions_);
341 !iter.IsAtEnd(); iter.Advance()) {
342 RequestDeviceSession* session = iter.GetCurrentValue();
343
344 // Stop ongoing discovery session if power is off.
345 if (!powered && session->discovery_session) {
346 StopDiscoverySession(std::move(session->discovery_session));
347 }
348
349 if (session->chooser)
350 session->chooser->SetAdapterPresence(presence);
351 }
352
353 // Stop the timer so that we don't change the state of the chooser
354 // when timer expires.
355 if (!powered) {
356 discovery_session_timer_.Stop();
357 }
358 }
359
360 void BluetoothDispatcherHost::DeviceAdded(device::BluetoothAdapter* adapter,
361 device::BluetoothDevice* device) {
362 DCHECK_CURRENTLY_ON(BrowserThread::UI);
363 VLOG(1) << "Adding device to all choosers: " << device->GetAddress();
364 for (IDMap<RequestDeviceSession, IDMapOwnPointer>::iterator iter(
365 &request_device_sessions_);
366 !iter.IsAtEnd(); iter.Advance()) {
367 RequestDeviceSession* session = iter.GetCurrentValue();
368 session->AddFilteredDevice(*device);
369 }
370 }
371
372 void BluetoothDispatcherHost::DeviceRemoved(device::BluetoothAdapter* adapter,
373 device::BluetoothDevice* device) {
374 DCHECK_CURRENTLY_ON(BrowserThread::UI);
375 VLOG(1) << "Marking device removed on all choosers: " << device->GetAddress();
376 for (IDMap<RequestDeviceSession, IDMapOwnPointer>::iterator iter(
377 &request_device_sessions_);
378 !iter.IsAtEnd(); iter.Advance()) {
379 RequestDeviceSession* session = iter.GetCurrentValue();
380 if (session->chooser) {
381 session->chooser->RemoveDevice(device->GetAddress());
382 }
383 }
384 }
385
386 void BluetoothDispatcherHost::OnRequestDevice(
387 int thread_id,
388 int request_id,
389 int frame_routing_id,
390 const std::vector<BluetoothScanFilter>& filters,
391 const std::vector<BluetoothUUID>& optional_services) {
392 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
393 RecordWebBluetoothFunctionCall(UMAWebBluetoothFunction::REQUEST_DEVICE);
394 RecordRequestDeviceArguments(filters, optional_services);
395
396 if (!adapter_.get()) {
397 if (BluetoothAdapterFactory::IsBluetoothAdapterAvailable()) {
398 BluetoothAdapterFactory::GetAdapter(base::Bind(
399 &BluetoothDispatcherHost::OnGetAdapter, weak_ptr_on_ui_thread_,
400 base::Bind(&BluetoothDispatcherHost::OnRequestDeviceImpl,
401 weak_ptr_on_ui_thread_, thread_id, request_id,
402 frame_routing_id, filters, optional_services)));
403 return;
404 }
405 RecordRequestDeviceOutcome(UMARequestDeviceOutcome::NO_BLUETOOTH_ADAPTER);
406 Send(new BluetoothMsg_RequestDeviceError(
407 thread_id, request_id, WebBluetoothError::NO_BLUETOOTH_ADAPTER));
408 return;
409 }
410 OnRequestDeviceImpl(thread_id, request_id, frame_routing_id, filters,
411 optional_services);
412 }
413
414 void BluetoothDispatcherHost::OnGetAdapter(
415 base::Closure continuation,
416 scoped_refptr<device::BluetoothAdapter> adapter) {
417 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
418 set_adapter(adapter);
419 continuation.Run();
420 }
421
422 void BluetoothDispatcherHost::OnRequestDeviceImpl(
423 int thread_id,
424 int request_id,
425 int frame_routing_id,
426 const std::vector<BluetoothScanFilter>& filters,
427 const std::vector<BluetoothUUID>& optional_services) {
428 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
429
430 VLOG(1) << "requestDevice called with the following filters: ";
431 for (const BluetoothScanFilter& filter : filters) {
432 VLOG(1) << "Name: " << filter.name;
433 VLOG(1) << "Name Prefix: " << filter.namePrefix;
434 VLOG(1) << "Services:";
435 VLOG(1) << "\t[";
436 for (const BluetoothUUID& service : filter.services)
437 VLOG(1) << "\t\t" << service.value();
438 VLOG(1) << "\t]";
439 }
440
441 VLOG(1) << "requestDevice called with the following optional services: ";
442 for (const BluetoothUUID& service : optional_services)
443 VLOG(1) << "\t" << service.value();
444
445 // Check blacklist to reject invalid filters and adjust optional_services.
446 if (BluetoothBlacklist::Get().IsExcluded(filters)) {
447 RecordRequestDeviceOutcome(
448 UMARequestDeviceOutcome::BLACKLISTED_SERVICE_IN_FILTER);
449 Send(new BluetoothMsg_RequestDeviceError(
450 thread_id, request_id,
451 WebBluetoothError::REQUEST_DEVICE_WITH_BLACKLISTED_UUID));
452 return;
453 }
454 std::vector<BluetoothUUID> optional_services_blacklist_filtered(
455 optional_services);
456 BluetoothBlacklist::Get().RemoveExcludedUuids(
457 &optional_services_blacklist_filtered);
458
459 RenderFrameHostImpl* render_frame_host =
460 RenderFrameHostImpl::FromID(render_process_id_, frame_routing_id);
461 WebContents* web_contents =
462 WebContents::FromRenderFrameHost(render_frame_host);
463
464 if (!render_frame_host || !web_contents) {
465 DLOG(WARNING) << "Got a requestDevice IPC without a matching "
466 << "RenderFrameHost or WebContents: " << render_process_id_
467 << ", " << frame_routing_id;
468 RecordRequestDeviceOutcome(UMARequestDeviceOutcome::NO_RENDER_FRAME);
469 Send(new BluetoothMsg_RequestDeviceError(
470 thread_id, request_id,
471 WebBluetoothError::REQUEST_DEVICE_WITHOUT_FRAME));
472 return;
473 }
474
475 const url::Origin requesting_origin =
476 render_frame_host->GetLastCommittedOrigin();
477 const url::Origin embedding_origin =
478 web_contents->GetMainFrame()->GetLastCommittedOrigin();
479
480 // TODO(crbug.com/518042): Enforce correctly-delegated permissions instead of
481 // matching origins. When relaxing this, take care to handle non-sandboxed
482 // unique origins.
483 if (!embedding_origin.IsSameOriginWith(requesting_origin)) {
484 Send(new BluetoothMsg_RequestDeviceError(
485 thread_id, request_id,
486 WebBluetoothError::REQUEST_DEVICE_FROM_CROSS_ORIGIN_IFRAME));
487 return;
488 }
489 // The above also excludes unique origins, which are not even same-origin with
490 // themselves.
491 DCHECK(!requesting_origin.unique());
492
493 DCHECK(adapter_.get());
494
495 if (!adapter_->IsPresent()) {
496 VLOG(1) << "Bluetooth Adapter not present. Can't serve requestDevice.";
497 RecordRequestDeviceOutcome(
498 UMARequestDeviceOutcome::BLUETOOTH_ADAPTER_NOT_PRESENT);
499 Send(new BluetoothMsg_RequestDeviceError(
500 thread_id, request_id, WebBluetoothError::NO_BLUETOOTH_ADAPTER));
501 return;
502 }
503
504 // The renderer should never send empty filters.
505 if (HasEmptyOrInvalidFilter(filters)) {
506 bad_message::ReceivedBadMessage(this,
507 bad_message::BDH_EMPTY_OR_INVALID_FILTERS);
508 return;
509 }
510
511 switch (GetContentClient()->browser()->AllowWebBluetooth(
512 web_contents->GetBrowserContext(), requesting_origin, embedding_origin)) {
513 case ContentBrowserClient::AllowWebBluetoothResult::BLOCK_POLICY: {
514 RecordRequestDeviceOutcome(
515 UMARequestDeviceOutcome::BLUETOOTH_CHOOSER_POLICY_DISABLED);
516 Send(new BluetoothMsg_RequestDeviceError(
517 thread_id, request_id,
518 WebBluetoothError::CHOOSER_NOT_SHOWN_API_LOCALLY_DISABLED));
519 return;
520 }
521 case ContentBrowserClient::AllowWebBluetoothResult::
522 BLOCK_GLOBALLY_DISABLED: {
523 // Log to the developer console.
524 web_contents->GetMainFrame()->AddMessageToConsole(
525 content::CONSOLE_MESSAGE_LEVEL_LOG,
526 "Bluetooth permission has been blocked.");
527 // Block requests.
528 RecordRequestDeviceOutcome(
529 UMARequestDeviceOutcome::BLUETOOTH_GLOBALLY_DISABLED);
530 Send(new BluetoothMsg_RequestDeviceError(
531 thread_id, request_id,
532 WebBluetoothError::CHOOSER_NOT_SHOWN_API_GLOBALLY_DISABLED));
533 return;
534 }
535 case ContentBrowserClient::AllowWebBluetoothResult::ALLOW:
536 break;
537 }
538
539 // Create storage for the information that backs the chooser, and show the
540 // chooser.
541 RequestDeviceSession* const session =
542 new RequestDeviceSession(thread_id, request_id, requesting_origin,
543 filters, optional_services_blacklist_filtered);
544 int chooser_id = request_device_sessions_.Add(session);
545
546 BluetoothChooser::EventHandler chooser_event_handler =
547 base::Bind(&BluetoothDispatcherHost::OnBluetoothChooserEvent,
548 weak_ptr_on_ui_thread_, chooser_id);
549 if (WebContentsDelegate* delegate = web_contents->GetDelegate()) {
550 session->chooser =
551 delegate->RunBluetoothChooser(render_frame_host, chooser_event_handler);
552 }
553 if (!session->chooser) {
554 LOG(WARNING)
555 << "No Bluetooth chooser implementation; falling back to first device.";
556 session->chooser.reset(
557 new FirstDeviceBluetoothChooser(chooser_event_handler));
558 }
559
560 if (!session->chooser->CanAskForScanningPermission()) {
561 VLOG(1) << "Closing immediately because Chooser cannot obtain permission.";
562 OnBluetoothChooserEvent(chooser_id,
563 BluetoothChooser::Event::DENIED_PERMISSION, "");
564 return;
565 }
566
567 // Populate the initial list of devices.
568 VLOG(1) << "Populating " << adapter_->GetDevices().size()
569 << " devices in chooser " << chooser_id;
570 for (const device::BluetoothDevice* device : adapter_->GetDevices()) {
571 VLOG(1) << "\t" << device->GetAddress();
572 session->AddFilteredDevice(*device);
573 }
574
575 if (!session->chooser) {
576 // If the dialog's closing, no need to do any of the rest of this.
577 return;
578 }
579
580 if (!adapter_->IsPowered()) {
581 session->chooser->SetAdapterPresence(
582 BluetoothChooser::AdapterPresence::POWERED_OFF);
583 return;
584 }
585
586 StartDeviceDiscovery(session, chooser_id);
587 }
588
589 void BluetoothDispatcherHost::OnDiscoverySessionStarted(
590 int chooser_id,
591 std::unique_ptr<device::BluetoothDiscoverySession> discovery_session) {
592 DCHECK_CURRENTLY_ON(BrowserThread::UI);
593 VLOG(1) << "Started discovery session for " << chooser_id;
594 if (RequestDeviceSession* session =
595 request_device_sessions_.Lookup(chooser_id)) {
596 session->discovery_session = std::move(discovery_session);
597
598 // Arrange to stop discovery later.
599 discovery_session_timer_.Reset();
600 } else {
601 VLOG(1) << "Chooser " << chooser_id
602 << " was closed before the session finished starting. Stopping.";
603 StopDiscoverySession(std::move(discovery_session));
604 }
605 }
606
607 void BluetoothDispatcherHost::OnDiscoverySessionStartedError(int chooser_id) {
608 DCHECK_CURRENTLY_ON(BrowserThread::UI);
609 VLOG(1) << "Failed to start discovery session for " << chooser_id;
610 if (RequestDeviceSession* session =
611 request_device_sessions_.Lookup(chooser_id)) {
612 if (session->chooser && !session->discovery_session) {
613 session->chooser->ShowDiscoveryState(
614 BluetoothChooser::DiscoveryState::FAILED_TO_START);
615 }
616 }
617 // Ignore discovery session start errors when the dialog was already closed by
618 // the time they happen.
619 }
620
621 void BluetoothDispatcherHost::OnBluetoothChooserEvent(
622 int chooser_id,
623 BluetoothChooser::Event event,
624 const std::string& device_id) {
625 DCHECK_CURRENTLY_ON(BrowserThread::UI);
626 RequestDeviceSession* session = request_device_sessions_.Lookup(chooser_id);
627 DCHECK(session) << "Shouldn't receive an event (" << static_cast<int>(event)
628 << ") from a closed chooser.";
629 CHECK(session->chooser) << "Shouldn't receive an event ("
630 << static_cast<int>(event)
631 << ") from a closed chooser.";
632 switch (event) {
633 case BluetoothChooser::Event::RESCAN:
634 StartDeviceDiscovery(session, chooser_id);
635 // No need to close the chooser so we return.
636 return;
637 case BluetoothChooser::Event::DENIED_PERMISSION:
638 case BluetoothChooser::Event::CANCELLED:
639 case BluetoothChooser::Event::SELECTED:
640 break;
641 case BluetoothChooser::Event::SHOW_OVERVIEW_HELP:
642 VLOG(1) << "Overview Help link pressed.";
643 break;
644 case BluetoothChooser::Event::SHOW_ADAPTER_OFF_HELP:
645 VLOG(1) << "Adapter Off Help link pressed.";
646 break;
647 case BluetoothChooser::Event::SHOW_NEED_LOCATION_HELP:
648 VLOG(1) << "Need Location Help link pressed.";
649 break;
650 }
651
652 // Synchronously ensure nothing else calls into the chooser after it has
653 // asked to be closed.
654 session->chooser.reset();
655
656 // Yield to the event loop to make sure we don't destroy the session
657 // within a BluetoothDispatcherHost stack frame.
658 if (!base::ThreadTaskRunnerHandle::Get()->PostTask(
659 FROM_HERE,
660 base::Bind(&BluetoothDispatcherHost::FinishClosingChooser,
661 weak_ptr_on_ui_thread_, chooser_id, event, device_id))) {
662 LOG(WARNING) << "No TaskRunner; not closing requestDevice dialog.";
663 }
664 }
665
666 void BluetoothDispatcherHost::FinishClosingChooser(
667 int chooser_id,
668 BluetoothChooser::Event event,
669 const std::string& device_id) {
670 DCHECK_CURRENTLY_ON(BrowserThread::UI);
671 RequestDeviceSession* session = request_device_sessions_.Lookup(chooser_id);
672 DCHECK(session) << "Session removed unexpectedly.";
673
674 if ((event != BluetoothChooser::Event::DENIED_PERMISSION) &&
675 (event != BluetoothChooser::Event::SELECTED)) {
676 RecordRequestDeviceOutcome(OutcomeFromChooserEvent(event));
677 Send(new BluetoothMsg_RequestDeviceError(
678 session->thread_id, session->request_id,
679 WebBluetoothError::CHOOSER_CANCELLED));
680 request_device_sessions_.Remove(chooser_id);
681 return;
682 }
683 if (event == BluetoothChooser::Event::DENIED_PERMISSION) {
684 RecordRequestDeviceOutcome(
685 UMARequestDeviceOutcome::BLUETOOTH_CHOOSER_DENIED_PERMISSION);
686 VLOG(1) << "Bluetooth chooser denied permission";
687 Send(new BluetoothMsg_RequestDeviceError(
688 session->thread_id, session->request_id,
689 WebBluetoothError::CHOOSER_NOT_SHOWN_USER_DENIED_PERMISSION_TO_SCAN));
690 request_device_sessions_.Remove(chooser_id);
691 return;
692 }
693 DCHECK_EQ(static_cast<int>(event),
694 static_cast<int>(BluetoothChooser::Event::SELECTED));
695
696 // |device_id| is the Device Address that RequestDeviceSession passed to
697 // chooser->AddDevice().
698 const device::BluetoothDevice* const device = adapter_->GetDevice(device_id);
699 if (device == nullptr) {
700 VLOG(1) << "Device " << device_id << " no longer in adapter";
701 RecordRequestDeviceOutcome(UMARequestDeviceOutcome::CHOSEN_DEVICE_VANISHED);
702 Send(new BluetoothMsg_RequestDeviceError(
703 session->thread_id, session->request_id,
704 WebBluetoothError::CHOSEN_DEVICE_VANISHED));
705 request_device_sessions_.Remove(chooser_id);
706 return;
707 }
708
709 const std::string& device_id_for_origin = allowed_devices_map_.AddDevice(
710 session->origin, device->GetAddress(), session->filters,
711 session->optional_services);
712
713 VLOG(1) << "Device: " << device->GetName();
714 VLOG(1) << "UUIDs: ";
715
716 device::BluetoothDevice::UUIDList filtered_uuids;
717 for (BluetoothUUID uuid : device->GetUUIDs()) {
718 if (allowed_devices_map_.IsOriginAllowedToAccessService(
719 session->origin, device_id_for_origin, uuid.canonical_value())) {
720 VLOG(1) << "\t Allowed: " << uuid.canonical_value();
721 filtered_uuids.push_back(uuid);
722 } else {
723 VLOG(1) << "\t Not Allowed: " << uuid.canonical_value();
724 }
725 }
726
727 content::BluetoothDevice device_ipc(
728 device_id_for_origin, // id
729 device->GetName(), // name
730 content::BluetoothDevice::UUIDsFromBluetoothUUIDs(
731 filtered_uuids)); // uuids
732 RecordRequestDeviceOutcome(UMARequestDeviceOutcome::SUCCESS);
733 Send(new BluetoothMsg_RequestDeviceSuccess(session->thread_id,
734 session->request_id, device_ipc));
735 request_device_sessions_.Remove(chooser_id);
736 }
737
738 CacheQueryResult BluetoothDispatcherHost::QueryCacheForDevice(
739 const url::Origin& origin,
740 const std::string& device_id) {
741 const std::string& device_address =
742 allowed_devices_map_.GetDeviceAddress(origin, device_id);
743 if (device_address.empty()) {
744 bad_message::ReceivedBadMessage(
745 this, bad_message::BDH_DEVICE_NOT_ALLOWED_FOR_ORIGIN);
746 return CacheQueryResult(CacheQueryOutcome::BAD_RENDERER);
747 }
748
749 CacheQueryResult result;
750 result.device = adapter_->GetDevice(device_address);
751
752 // When a device can't be found in the BluetoothAdapter, that generally
753 // indicates that it's gone out of range. We reject with a NetworkError in
754 // that case.
755 // https://webbluetoothchrome.github.io/web-bluetooth/#dom-bluetoothdevice-con nectgatt
756 if (result.device == nullptr) {
757 result.outcome = CacheQueryOutcome::NO_DEVICE;
758 }
759 return result;
760 }
761
762 void BluetoothDispatcherHost::AddAdapterObserver(
763 device::BluetoothAdapter::Observer* observer) {
764 adapter_observers_.insert(observer);
765 if (adapter_) {
766 adapter_->AddObserver(observer);
767 }
768 }
769
770 void BluetoothDispatcherHost::RemoveAdapterObserver(
771 device::BluetoothAdapter::Observer* observer) {
772 size_t removed = adapter_observers_.erase(observer);
773 DCHECK(removed);
774 if (adapter_) {
775 adapter_->RemoveObserver(observer);
776 }
777 }
778
779 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/bluetooth/bluetooth_dispatcher_host.h ('k') | content/browser/bluetooth/bluetooth_metrics.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698