| Index: content/browser/bluetooth/bluetooth_dispatcher_host.cc
|
| diff --git a/content/browser/bluetooth/bluetooth_dispatcher_host.cc b/content/browser/bluetooth/bluetooth_dispatcher_host.cc
|
| deleted file mode 100644
|
| index 763838b8ba9271868436bf6224409ce3c9004643..0000000000000000000000000000000000000000
|
| --- a/content/browser/bluetooth/bluetooth_dispatcher_host.cc
|
| +++ /dev/null
|
| @@ -1,779 +0,0 @@
|
| -// Copyright 2014 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -// ID Not In Map Note:
|
| -// A service, characteristic, or descriptor ID not in the corresponding
|
| -// BluetoothDispatcherHost map [service_to_device_, characteristic_to_service_,
|
| -// descriptor_to_characteristic_] implies a hostile renderer because a renderer
|
| -// obtains the corresponding ID from this class and it will be added to the map
|
| -// at that time.
|
| -
|
| -#include "content/browser/bluetooth/bluetooth_dispatcher_host.h"
|
| -
|
| -#include <stddef.h>
|
| -
|
| -#include <utility>
|
| -
|
| -#include "base/bind.h"
|
| -#include "base/single_thread_task_runner.h"
|
| -#include "base/strings/utf_string_conversions.h"
|
| -#include "base/threading/thread_task_runner_handle.h"
|
| -#include "content/browser/bluetooth/bluetooth_blacklist.h"
|
| -#include "content/browser/bluetooth/bluetooth_metrics.h"
|
| -#include "content/browser/bluetooth/cache_query_result.h"
|
| -#include "content/browser/bluetooth/first_device_bluetooth_chooser.h"
|
| -#include "content/browser/frame_host/render_frame_host_impl.h"
|
| -#include "content/browser/web_contents/web_contents_impl.h"
|
| -#include "content/public/browser/content_browser_client.h"
|
| -#include "content/public/browser/web_contents.h"
|
| -#include "content/public/browser/web_contents_delegate.h"
|
| -#include "device/bluetooth/bluetooth_adapter.h"
|
| -#include "device/bluetooth/bluetooth_adapter_factory.h"
|
| -#include "device/bluetooth/bluetooth_device.h"
|
| -#include "device/bluetooth/bluetooth_discovery_session.h"
|
| -
|
| -using blink::WebBluetoothError;
|
| -using device::BluetoothAdapter;
|
| -using device::BluetoothAdapterFactory;
|
| -using device::BluetoothUUID;
|
| -
|
| -namespace content {
|
| -
|
| -namespace {
|
| -
|
| -// TODO(ortuno): Once we have a chooser for scanning, a way to control that
|
| -// chooser from tests, and the right callback for discovered services we should
|
| -// delete these constants.
|
| -// https://crbug.com/436280 and https://crbug.com/484504
|
| -const int kDelayTime = 5; // 5 seconds for scanning and discovering
|
| -const int kTestingDelayTime = 0; // No need to wait during tests
|
| -const size_t kMaxLengthForDeviceName =
|
| - 29; // max length of device name in filter.
|
| -
|
| -bool IsEmptyOrInvalidFilter(const content::BluetoothScanFilter& filter) {
|
| - return filter.name.empty() && filter.namePrefix.empty() &&
|
| - filter.services.empty() &&
|
| - filter.name.length() > kMaxLengthForDeviceName &&
|
| - filter.namePrefix.length() > kMaxLengthForDeviceName;
|
| -}
|
| -
|
| -bool HasEmptyOrInvalidFilter(
|
| - const std::vector<content::BluetoothScanFilter>& filters) {
|
| - return filters.empty()
|
| - ? true
|
| - : filters.end() != std::find_if(filters.begin(), filters.end(),
|
| - IsEmptyOrInvalidFilter);
|
| -}
|
| -
|
| -// Defined at
|
| -// https://webbluetoothchrome.github.io/web-bluetooth/#dfn-matches-a-filter
|
| -bool MatchesFilter(const device::BluetoothDevice& device,
|
| - const content::BluetoothScanFilter& filter) {
|
| - DCHECK(!IsEmptyOrInvalidFilter(filter));
|
| -
|
| - const std::string device_name = base::UTF16ToUTF8(device.GetName());
|
| -
|
| - if (!filter.name.empty() && (device_name != filter.name)) {
|
| - return false;
|
| - }
|
| -
|
| - if (!filter.namePrefix.empty() &&
|
| - (!base::StartsWith(device_name, filter.namePrefix,
|
| - base::CompareCase::SENSITIVE))) {
|
| - return false;
|
| - }
|
| -
|
| - if (!filter.services.empty()) {
|
| - const auto& device_uuid_list = device.GetUUIDs();
|
| - const std::set<BluetoothUUID> device_uuids(device_uuid_list.begin(),
|
| - device_uuid_list.end());
|
| - for (const auto& service : filter.services) {
|
| - if (!ContainsKey(device_uuids, service)) {
|
| - return false;
|
| - }
|
| - }
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| -bool MatchesFilters(const device::BluetoothDevice& device,
|
| - const std::vector<content::BluetoothScanFilter>& filters) {
|
| - DCHECK(!HasEmptyOrInvalidFilter(filters));
|
| - for (const content::BluetoothScanFilter& filter : filters) {
|
| - if (MatchesFilter(device, filter)) {
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -void StopDiscoverySession(
|
| - std::unique_ptr<device::BluetoothDiscoverySession> discovery_session) {
|
| - // Nothing goes wrong if the discovery session fails to stop, and we don't
|
| - // need to wait for it before letting the user's script proceed, so we ignore
|
| - // the results here.
|
| - discovery_session->Stop(base::Bind(&base::DoNothing),
|
| - base::Bind(&base::DoNothing));
|
| -}
|
| -
|
| -UMARequestDeviceOutcome OutcomeFromChooserEvent(BluetoothChooser::Event event) {
|
| - switch (event) {
|
| - case BluetoothChooser::Event::DENIED_PERMISSION:
|
| - return UMARequestDeviceOutcome::BLUETOOTH_CHOOSER_DENIED_PERMISSION;
|
| - case BluetoothChooser::Event::CANCELLED:
|
| - return UMARequestDeviceOutcome::BLUETOOTH_CHOOSER_CANCELLED;
|
| - case BluetoothChooser::Event::SHOW_OVERVIEW_HELP:
|
| - return UMARequestDeviceOutcome::BLUETOOTH_OVERVIEW_HELP_LINK_PRESSED;
|
| - case BluetoothChooser::Event::SHOW_ADAPTER_OFF_HELP:
|
| - return UMARequestDeviceOutcome::ADAPTER_OFF_HELP_LINK_PRESSED;
|
| - case BluetoothChooser::Event::SHOW_NEED_LOCATION_HELP:
|
| - return UMARequestDeviceOutcome::NEED_LOCATION_HELP_LINK_PRESSED;
|
| - case BluetoothChooser::Event::SELECTED:
|
| - // We can't know if we are going to send a success message yet because
|
| - // the device could have vanished. This event should be histogramed
|
| - // manually after checking if the device is still around.
|
| - NOTREACHED();
|
| - return UMARequestDeviceOutcome::SUCCESS;
|
| - case BluetoothChooser::Event::RESCAN:
|
| - // Rescanning doesn't result in a IPC message for the request being sent
|
| - // so no need to histogram it.
|
| - NOTREACHED();
|
| - return UMARequestDeviceOutcome::SUCCESS;
|
| - }
|
| - NOTREACHED();
|
| - return UMARequestDeviceOutcome::SUCCESS;
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -BluetoothDispatcherHost::BluetoothDispatcherHost(int render_process_id)
|
| - : BrowserMessageFilter(BluetoothMsgStart),
|
| - render_process_id_(render_process_id),
|
| - current_delay_time_(kDelayTime),
|
| - discovery_session_timer_(
|
| - FROM_HERE,
|
| - // TODO(jyasskin): Add a way for tests to control the dialog
|
| - // directly, and change this to a reasonable discovery timeout.
|
| - base::TimeDelta::FromSecondsD(current_delay_time_),
|
| - base::Bind(&BluetoothDispatcherHost::StopDeviceDiscovery,
|
| - // base::Timer guarantees it won't call back after its
|
| - // destructor starts.
|
| - base::Unretained(this)),
|
| - /*is_repeating=*/false),
|
| - weak_ptr_factory_(this) {
|
| - DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| -
|
| - // Bind all future weak pointers to the UI thread.
|
| - weak_ptr_on_ui_thread_ = weak_ptr_factory_.GetWeakPtr();
|
| - weak_ptr_on_ui_thread_.get(); // Associates with UI thread.
|
| -}
|
| -
|
| -void BluetoothDispatcherHost::OnDestruct() const {
|
| - // See class comment: UI Thread Note.
|
| - BrowserThread::DeleteOnUIThread::Destruct(this);
|
| -}
|
| -
|
| -void BluetoothDispatcherHost::OverrideThreadForMessage(
|
| - const IPC::Message& message,
|
| - content::BrowserThread::ID* thread) {
|
| - // See class comment: UI Thread Note.
|
| - *thread = BrowserThread::UI;
|
| -}
|
| -
|
| -bool BluetoothDispatcherHost::OnMessageReceived(const IPC::Message& message) {
|
| - DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| - bool handled = true;
|
| - IPC_BEGIN_MESSAGE_MAP(BluetoothDispatcherHost, message)
|
| - IPC_MESSAGE_HANDLER(BluetoothHostMsg_RequestDevice, OnRequestDevice)
|
| - IPC_MESSAGE_UNHANDLED(handled = false)
|
| - IPC_END_MESSAGE_MAP()
|
| - return handled;
|
| -}
|
| -
|
| -void BluetoothDispatcherHost::SetBluetoothAdapterForTesting(
|
| - scoped_refptr<device::BluetoothAdapter> mock_adapter) {
|
| - DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| -
|
| - if (mock_adapter.get()) {
|
| - current_delay_time_ = kTestingDelayTime;
|
| - // Reset the discovery session timer to use the new delay time.
|
| - discovery_session_timer_.Start(
|
| - FROM_HERE, base::TimeDelta::FromSecondsD(current_delay_time_),
|
| - base::Bind(&BluetoothDispatcherHost::StopDeviceDiscovery,
|
| - // base::Timer guarantees it won't call back after its
|
| - // destructor starts.
|
| - base::Unretained(this)));
|
| - } else {
|
| - // The following data structures are used to store pending operations.
|
| - // They should never contain elements at the end of a test.
|
| - DCHECK(request_device_sessions_.IsEmpty());
|
| -
|
| - // The following data structures are cleaned up when a
|
| - // device/service/characteristic is removed.
|
| - // Since this can happen after the test is done and the cleanup function is
|
| - // called, we clean them here.
|
| - allowed_devices_map_ = BluetoothAllowedDevicesMap();
|
| - }
|
| -
|
| - set_adapter(std::move(mock_adapter));
|
| -}
|
| -
|
| -BluetoothDispatcherHost::~BluetoothDispatcherHost() {
|
| - DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| - // Clear adapter, releasing observer references.
|
| - set_adapter(scoped_refptr<device::BluetoothAdapter>());
|
| -}
|
| -
|
| -// Stores information associated with an in-progress requestDevice call. This
|
| -// will include the state of the active chooser dialog in a future patch.
|
| -struct BluetoothDispatcherHost::RequestDeviceSession {
|
| - public:
|
| - RequestDeviceSession(int thread_id,
|
| - int request_id,
|
| - url::Origin origin,
|
| - const std::vector<BluetoothScanFilter>& filters,
|
| - const std::vector<BluetoothUUID>& optional_services)
|
| - : thread_id(thread_id),
|
| - request_id(request_id),
|
| - origin(origin),
|
| - filters(filters),
|
| - optional_services(optional_services) {}
|
| -
|
| - void AddFilteredDevice(const device::BluetoothDevice& device) {
|
| - if (chooser && MatchesFilters(device, filters)) {
|
| - chooser->AddDevice(device.GetAddress(), device.GetName());
|
| - }
|
| - }
|
| -
|
| - std::unique_ptr<device::BluetoothDiscoveryFilter> ComputeScanFilter() const {
|
| - std::set<BluetoothUUID> services;
|
| - for (const BluetoothScanFilter& filter : filters) {
|
| - services.insert(filter.services.begin(), filter.services.end());
|
| - }
|
| - std::unique_ptr<device::BluetoothDiscoveryFilter> discovery_filter(
|
| - new device::BluetoothDiscoveryFilter(
|
| - device::BluetoothDiscoveryFilter::TRANSPORT_DUAL));
|
| - for (const BluetoothUUID& service : services) {
|
| - discovery_filter->AddUUID(service);
|
| - }
|
| - return discovery_filter;
|
| - }
|
| -
|
| - const int thread_id;
|
| - const int request_id;
|
| - const url::Origin origin;
|
| - const std::vector<BluetoothScanFilter> filters;
|
| - const std::vector<BluetoothUUID> optional_services;
|
| - std::unique_ptr<BluetoothChooser> chooser;
|
| - std::unique_ptr<device::BluetoothDiscoverySession> discovery_session;
|
| -};
|
| -
|
| -void BluetoothDispatcherHost::set_adapter(
|
| - scoped_refptr<device::BluetoothAdapter> adapter) {
|
| - DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| - if (adapter_.get()) {
|
| - adapter_->RemoveObserver(this);
|
| - for (device::BluetoothAdapter::Observer* observer : adapter_observers_) {
|
| - adapter_->RemoveObserver(observer);
|
| - }
|
| - }
|
| - adapter_ = adapter;
|
| - if (adapter_.get()) {
|
| - adapter_->AddObserver(this);
|
| - for (device::BluetoothAdapter::Observer* observer : adapter_observers_) {
|
| - adapter_->AddObserver(observer);
|
| - }
|
| - } else {
|
| - // Notify that the adapter has been removed and observers should clean up
|
| - // their state.
|
| - for (device::BluetoothAdapter::Observer* observer : adapter_observers_) {
|
| - observer->AdapterPresentChanged(nullptr, false);
|
| - }
|
| - }
|
| -}
|
| -
|
| -void BluetoothDispatcherHost::StartDeviceDiscovery(
|
| - RequestDeviceSession* session,
|
| - int chooser_id) {
|
| - DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| - if (session->discovery_session) {
|
| - // Already running; just increase the timeout.
|
| - discovery_session_timer_.Reset();
|
| - } else {
|
| - session->chooser->ShowDiscoveryState(
|
| - BluetoothChooser::DiscoveryState::DISCOVERING);
|
| - adapter_->StartDiscoverySessionWithFilter(
|
| - session->ComputeScanFilter(),
|
| - base::Bind(&BluetoothDispatcherHost::OnDiscoverySessionStarted,
|
| - weak_ptr_on_ui_thread_, chooser_id),
|
| - base::Bind(&BluetoothDispatcherHost::OnDiscoverySessionStartedError,
|
| - weak_ptr_on_ui_thread_, chooser_id));
|
| - }
|
| -}
|
| -
|
| -void BluetoothDispatcherHost::StopDeviceDiscovery() {
|
| - DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| - for (IDMap<RequestDeviceSession, IDMapOwnPointer>::iterator iter(
|
| - &request_device_sessions_);
|
| - !iter.IsAtEnd(); iter.Advance()) {
|
| - RequestDeviceSession* session = iter.GetCurrentValue();
|
| - if (session->discovery_session) {
|
| - StopDiscoverySession(std::move(session->discovery_session));
|
| - }
|
| - if (session->chooser) {
|
| - session->chooser->ShowDiscoveryState(
|
| - BluetoothChooser::DiscoveryState::IDLE);
|
| - }
|
| - }
|
| -}
|
| -
|
| -void BluetoothDispatcherHost::AdapterPoweredChanged(
|
| - device::BluetoothAdapter* adapter,
|
| - bool powered) {
|
| - DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| - const BluetoothChooser::AdapterPresence presence =
|
| - powered ? BluetoothChooser::AdapterPresence::POWERED_ON
|
| - : BluetoothChooser::AdapterPresence::POWERED_OFF;
|
| - for (IDMap<RequestDeviceSession, IDMapOwnPointer>::iterator iter(
|
| - &request_device_sessions_);
|
| - !iter.IsAtEnd(); iter.Advance()) {
|
| - RequestDeviceSession* session = iter.GetCurrentValue();
|
| -
|
| - // Stop ongoing discovery session if power is off.
|
| - if (!powered && session->discovery_session) {
|
| - StopDiscoverySession(std::move(session->discovery_session));
|
| - }
|
| -
|
| - if (session->chooser)
|
| - session->chooser->SetAdapterPresence(presence);
|
| - }
|
| -
|
| - // Stop the timer so that we don't change the state of the chooser
|
| - // when timer expires.
|
| - if (!powered) {
|
| - discovery_session_timer_.Stop();
|
| - }
|
| -}
|
| -
|
| -void BluetoothDispatcherHost::DeviceAdded(device::BluetoothAdapter* adapter,
|
| - device::BluetoothDevice* device) {
|
| - DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| - VLOG(1) << "Adding device to all choosers: " << device->GetAddress();
|
| - for (IDMap<RequestDeviceSession, IDMapOwnPointer>::iterator iter(
|
| - &request_device_sessions_);
|
| - !iter.IsAtEnd(); iter.Advance()) {
|
| - RequestDeviceSession* session = iter.GetCurrentValue();
|
| - session->AddFilteredDevice(*device);
|
| - }
|
| -}
|
| -
|
| -void BluetoothDispatcherHost::DeviceRemoved(device::BluetoothAdapter* adapter,
|
| - device::BluetoothDevice* device) {
|
| - DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| - VLOG(1) << "Marking device removed on all choosers: " << device->GetAddress();
|
| - for (IDMap<RequestDeviceSession, IDMapOwnPointer>::iterator iter(
|
| - &request_device_sessions_);
|
| - !iter.IsAtEnd(); iter.Advance()) {
|
| - RequestDeviceSession* session = iter.GetCurrentValue();
|
| - if (session->chooser) {
|
| - session->chooser->RemoveDevice(device->GetAddress());
|
| - }
|
| - }
|
| -}
|
| -
|
| -void BluetoothDispatcherHost::OnRequestDevice(
|
| - int thread_id,
|
| - int request_id,
|
| - int frame_routing_id,
|
| - const std::vector<BluetoothScanFilter>& filters,
|
| - const std::vector<BluetoothUUID>& optional_services) {
|
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| - RecordWebBluetoothFunctionCall(UMAWebBluetoothFunction::REQUEST_DEVICE);
|
| - RecordRequestDeviceArguments(filters, optional_services);
|
| -
|
| - if (!adapter_.get()) {
|
| - if (BluetoothAdapterFactory::IsBluetoothAdapterAvailable()) {
|
| - BluetoothAdapterFactory::GetAdapter(base::Bind(
|
| - &BluetoothDispatcherHost::OnGetAdapter, weak_ptr_on_ui_thread_,
|
| - base::Bind(&BluetoothDispatcherHost::OnRequestDeviceImpl,
|
| - weak_ptr_on_ui_thread_, thread_id, request_id,
|
| - frame_routing_id, filters, optional_services)));
|
| - return;
|
| - }
|
| - RecordRequestDeviceOutcome(UMARequestDeviceOutcome::NO_BLUETOOTH_ADAPTER);
|
| - Send(new BluetoothMsg_RequestDeviceError(
|
| - thread_id, request_id, WebBluetoothError::NO_BLUETOOTH_ADAPTER));
|
| - return;
|
| - }
|
| - OnRequestDeviceImpl(thread_id, request_id, frame_routing_id, filters,
|
| - optional_services);
|
| -}
|
| -
|
| -void BluetoothDispatcherHost::OnGetAdapter(
|
| - base::Closure continuation,
|
| - scoped_refptr<device::BluetoothAdapter> adapter) {
|
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| - set_adapter(adapter);
|
| - continuation.Run();
|
| -}
|
| -
|
| -void BluetoothDispatcherHost::OnRequestDeviceImpl(
|
| - int thread_id,
|
| - int request_id,
|
| - int frame_routing_id,
|
| - const std::vector<BluetoothScanFilter>& filters,
|
| - const std::vector<BluetoothUUID>& optional_services) {
|
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| -
|
| - VLOG(1) << "requestDevice called with the following filters: ";
|
| - for (const BluetoothScanFilter& filter : filters) {
|
| - VLOG(1) << "Name: " << filter.name;
|
| - VLOG(1) << "Name Prefix: " << filter.namePrefix;
|
| - VLOG(1) << "Services:";
|
| - VLOG(1) << "\t[";
|
| - for (const BluetoothUUID& service : filter.services)
|
| - VLOG(1) << "\t\t" << service.value();
|
| - VLOG(1) << "\t]";
|
| - }
|
| -
|
| - VLOG(1) << "requestDevice called with the following optional services: ";
|
| - for (const BluetoothUUID& service : optional_services)
|
| - VLOG(1) << "\t" << service.value();
|
| -
|
| - // Check blacklist to reject invalid filters and adjust optional_services.
|
| - if (BluetoothBlacklist::Get().IsExcluded(filters)) {
|
| - RecordRequestDeviceOutcome(
|
| - UMARequestDeviceOutcome::BLACKLISTED_SERVICE_IN_FILTER);
|
| - Send(new BluetoothMsg_RequestDeviceError(
|
| - thread_id, request_id,
|
| - WebBluetoothError::REQUEST_DEVICE_WITH_BLACKLISTED_UUID));
|
| - return;
|
| - }
|
| - std::vector<BluetoothUUID> optional_services_blacklist_filtered(
|
| - optional_services);
|
| - BluetoothBlacklist::Get().RemoveExcludedUuids(
|
| - &optional_services_blacklist_filtered);
|
| -
|
| - RenderFrameHostImpl* render_frame_host =
|
| - RenderFrameHostImpl::FromID(render_process_id_, frame_routing_id);
|
| - WebContents* web_contents =
|
| - WebContents::FromRenderFrameHost(render_frame_host);
|
| -
|
| - if (!render_frame_host || !web_contents) {
|
| - DLOG(WARNING) << "Got a requestDevice IPC without a matching "
|
| - << "RenderFrameHost or WebContents: " << render_process_id_
|
| - << ", " << frame_routing_id;
|
| - RecordRequestDeviceOutcome(UMARequestDeviceOutcome::NO_RENDER_FRAME);
|
| - Send(new BluetoothMsg_RequestDeviceError(
|
| - thread_id, request_id,
|
| - WebBluetoothError::REQUEST_DEVICE_WITHOUT_FRAME));
|
| - return;
|
| - }
|
| -
|
| - const url::Origin requesting_origin =
|
| - render_frame_host->GetLastCommittedOrigin();
|
| - const url::Origin embedding_origin =
|
| - web_contents->GetMainFrame()->GetLastCommittedOrigin();
|
| -
|
| - // TODO(crbug.com/518042): Enforce correctly-delegated permissions instead of
|
| - // matching origins. When relaxing this, take care to handle non-sandboxed
|
| - // unique origins.
|
| - if (!embedding_origin.IsSameOriginWith(requesting_origin)) {
|
| - Send(new BluetoothMsg_RequestDeviceError(
|
| - thread_id, request_id,
|
| - WebBluetoothError::REQUEST_DEVICE_FROM_CROSS_ORIGIN_IFRAME));
|
| - return;
|
| - }
|
| - // The above also excludes unique origins, which are not even same-origin with
|
| - // themselves.
|
| - DCHECK(!requesting_origin.unique());
|
| -
|
| - DCHECK(adapter_.get());
|
| -
|
| - if (!adapter_->IsPresent()) {
|
| - VLOG(1) << "Bluetooth Adapter not present. Can't serve requestDevice.";
|
| - RecordRequestDeviceOutcome(
|
| - UMARequestDeviceOutcome::BLUETOOTH_ADAPTER_NOT_PRESENT);
|
| - Send(new BluetoothMsg_RequestDeviceError(
|
| - thread_id, request_id, WebBluetoothError::NO_BLUETOOTH_ADAPTER));
|
| - return;
|
| - }
|
| -
|
| - // The renderer should never send empty filters.
|
| - if (HasEmptyOrInvalidFilter(filters)) {
|
| - bad_message::ReceivedBadMessage(this,
|
| - bad_message::BDH_EMPTY_OR_INVALID_FILTERS);
|
| - return;
|
| - }
|
| -
|
| - switch (GetContentClient()->browser()->AllowWebBluetooth(
|
| - web_contents->GetBrowserContext(), requesting_origin, embedding_origin)) {
|
| - case ContentBrowserClient::AllowWebBluetoothResult::BLOCK_POLICY: {
|
| - RecordRequestDeviceOutcome(
|
| - UMARequestDeviceOutcome::BLUETOOTH_CHOOSER_POLICY_DISABLED);
|
| - Send(new BluetoothMsg_RequestDeviceError(
|
| - thread_id, request_id,
|
| - WebBluetoothError::CHOOSER_NOT_SHOWN_API_LOCALLY_DISABLED));
|
| - return;
|
| - }
|
| - case ContentBrowserClient::AllowWebBluetoothResult::
|
| - BLOCK_GLOBALLY_DISABLED: {
|
| - // Log to the developer console.
|
| - web_contents->GetMainFrame()->AddMessageToConsole(
|
| - content::CONSOLE_MESSAGE_LEVEL_LOG,
|
| - "Bluetooth permission has been blocked.");
|
| - // Block requests.
|
| - RecordRequestDeviceOutcome(
|
| - UMARequestDeviceOutcome::BLUETOOTH_GLOBALLY_DISABLED);
|
| - Send(new BluetoothMsg_RequestDeviceError(
|
| - thread_id, request_id,
|
| - WebBluetoothError::CHOOSER_NOT_SHOWN_API_GLOBALLY_DISABLED));
|
| - return;
|
| - }
|
| - case ContentBrowserClient::AllowWebBluetoothResult::ALLOW:
|
| - break;
|
| - }
|
| -
|
| - // Create storage for the information that backs the chooser, and show the
|
| - // chooser.
|
| - RequestDeviceSession* const session =
|
| - new RequestDeviceSession(thread_id, request_id, requesting_origin,
|
| - filters, optional_services_blacklist_filtered);
|
| - int chooser_id = request_device_sessions_.Add(session);
|
| -
|
| - BluetoothChooser::EventHandler chooser_event_handler =
|
| - base::Bind(&BluetoothDispatcherHost::OnBluetoothChooserEvent,
|
| - weak_ptr_on_ui_thread_, chooser_id);
|
| - if (WebContentsDelegate* delegate = web_contents->GetDelegate()) {
|
| - session->chooser =
|
| - delegate->RunBluetoothChooser(render_frame_host, chooser_event_handler);
|
| - }
|
| - if (!session->chooser) {
|
| - LOG(WARNING)
|
| - << "No Bluetooth chooser implementation; falling back to first device.";
|
| - session->chooser.reset(
|
| - new FirstDeviceBluetoothChooser(chooser_event_handler));
|
| - }
|
| -
|
| - if (!session->chooser->CanAskForScanningPermission()) {
|
| - VLOG(1) << "Closing immediately because Chooser cannot obtain permission.";
|
| - OnBluetoothChooserEvent(chooser_id,
|
| - BluetoothChooser::Event::DENIED_PERMISSION, "");
|
| - return;
|
| - }
|
| -
|
| - // Populate the initial list of devices.
|
| - VLOG(1) << "Populating " << adapter_->GetDevices().size()
|
| - << " devices in chooser " << chooser_id;
|
| - for (const device::BluetoothDevice* device : adapter_->GetDevices()) {
|
| - VLOG(1) << "\t" << device->GetAddress();
|
| - session->AddFilteredDevice(*device);
|
| - }
|
| -
|
| - if (!session->chooser) {
|
| - // If the dialog's closing, no need to do any of the rest of this.
|
| - return;
|
| - }
|
| -
|
| - if (!adapter_->IsPowered()) {
|
| - session->chooser->SetAdapterPresence(
|
| - BluetoothChooser::AdapterPresence::POWERED_OFF);
|
| - return;
|
| - }
|
| -
|
| - StartDeviceDiscovery(session, chooser_id);
|
| -}
|
| -
|
| -void BluetoothDispatcherHost::OnDiscoverySessionStarted(
|
| - int chooser_id,
|
| - std::unique_ptr<device::BluetoothDiscoverySession> discovery_session) {
|
| - DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| - VLOG(1) << "Started discovery session for " << chooser_id;
|
| - if (RequestDeviceSession* session =
|
| - request_device_sessions_.Lookup(chooser_id)) {
|
| - session->discovery_session = std::move(discovery_session);
|
| -
|
| - // Arrange to stop discovery later.
|
| - discovery_session_timer_.Reset();
|
| - } else {
|
| - VLOG(1) << "Chooser " << chooser_id
|
| - << " was closed before the session finished starting. Stopping.";
|
| - StopDiscoverySession(std::move(discovery_session));
|
| - }
|
| -}
|
| -
|
| -void BluetoothDispatcherHost::OnDiscoverySessionStartedError(int chooser_id) {
|
| - DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| - VLOG(1) << "Failed to start discovery session for " << chooser_id;
|
| - if (RequestDeviceSession* session =
|
| - request_device_sessions_.Lookup(chooser_id)) {
|
| - if (session->chooser && !session->discovery_session) {
|
| - session->chooser->ShowDiscoveryState(
|
| - BluetoothChooser::DiscoveryState::FAILED_TO_START);
|
| - }
|
| - }
|
| - // Ignore discovery session start errors when the dialog was already closed by
|
| - // the time they happen.
|
| -}
|
| -
|
| -void BluetoothDispatcherHost::OnBluetoothChooserEvent(
|
| - int chooser_id,
|
| - BluetoothChooser::Event event,
|
| - const std::string& device_id) {
|
| - DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| - RequestDeviceSession* session = request_device_sessions_.Lookup(chooser_id);
|
| - DCHECK(session) << "Shouldn't receive an event (" << static_cast<int>(event)
|
| - << ") from a closed chooser.";
|
| - CHECK(session->chooser) << "Shouldn't receive an event ("
|
| - << static_cast<int>(event)
|
| - << ") from a closed chooser.";
|
| - switch (event) {
|
| - case BluetoothChooser::Event::RESCAN:
|
| - StartDeviceDiscovery(session, chooser_id);
|
| - // No need to close the chooser so we return.
|
| - return;
|
| - case BluetoothChooser::Event::DENIED_PERMISSION:
|
| - case BluetoothChooser::Event::CANCELLED:
|
| - case BluetoothChooser::Event::SELECTED:
|
| - break;
|
| - case BluetoothChooser::Event::SHOW_OVERVIEW_HELP:
|
| - VLOG(1) << "Overview Help link pressed.";
|
| - break;
|
| - case BluetoothChooser::Event::SHOW_ADAPTER_OFF_HELP:
|
| - VLOG(1) << "Adapter Off Help link pressed.";
|
| - break;
|
| - case BluetoothChooser::Event::SHOW_NEED_LOCATION_HELP:
|
| - VLOG(1) << "Need Location Help link pressed.";
|
| - break;
|
| - }
|
| -
|
| - // Synchronously ensure nothing else calls into the chooser after it has
|
| - // asked to be closed.
|
| - session->chooser.reset();
|
| -
|
| - // Yield to the event loop to make sure we don't destroy the session
|
| - // within a BluetoothDispatcherHost stack frame.
|
| - if (!base::ThreadTaskRunnerHandle::Get()->PostTask(
|
| - FROM_HERE,
|
| - base::Bind(&BluetoothDispatcherHost::FinishClosingChooser,
|
| - weak_ptr_on_ui_thread_, chooser_id, event, device_id))) {
|
| - LOG(WARNING) << "No TaskRunner; not closing requestDevice dialog.";
|
| - }
|
| -}
|
| -
|
| -void BluetoothDispatcherHost::FinishClosingChooser(
|
| - int chooser_id,
|
| - BluetoothChooser::Event event,
|
| - const std::string& device_id) {
|
| - DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| - RequestDeviceSession* session = request_device_sessions_.Lookup(chooser_id);
|
| - DCHECK(session) << "Session removed unexpectedly.";
|
| -
|
| - if ((event != BluetoothChooser::Event::DENIED_PERMISSION) &&
|
| - (event != BluetoothChooser::Event::SELECTED)) {
|
| - RecordRequestDeviceOutcome(OutcomeFromChooserEvent(event));
|
| - Send(new BluetoothMsg_RequestDeviceError(
|
| - session->thread_id, session->request_id,
|
| - WebBluetoothError::CHOOSER_CANCELLED));
|
| - request_device_sessions_.Remove(chooser_id);
|
| - return;
|
| - }
|
| - if (event == BluetoothChooser::Event::DENIED_PERMISSION) {
|
| - RecordRequestDeviceOutcome(
|
| - UMARequestDeviceOutcome::BLUETOOTH_CHOOSER_DENIED_PERMISSION);
|
| - VLOG(1) << "Bluetooth chooser denied permission";
|
| - Send(new BluetoothMsg_RequestDeviceError(
|
| - session->thread_id, session->request_id,
|
| - WebBluetoothError::CHOOSER_NOT_SHOWN_USER_DENIED_PERMISSION_TO_SCAN));
|
| - request_device_sessions_.Remove(chooser_id);
|
| - return;
|
| - }
|
| - DCHECK_EQ(static_cast<int>(event),
|
| - static_cast<int>(BluetoothChooser::Event::SELECTED));
|
| -
|
| - // |device_id| is the Device Address that RequestDeviceSession passed to
|
| - // chooser->AddDevice().
|
| - const device::BluetoothDevice* const device = adapter_->GetDevice(device_id);
|
| - if (device == nullptr) {
|
| - VLOG(1) << "Device " << device_id << " no longer in adapter";
|
| - RecordRequestDeviceOutcome(UMARequestDeviceOutcome::CHOSEN_DEVICE_VANISHED);
|
| - Send(new BluetoothMsg_RequestDeviceError(
|
| - session->thread_id, session->request_id,
|
| - WebBluetoothError::CHOSEN_DEVICE_VANISHED));
|
| - request_device_sessions_.Remove(chooser_id);
|
| - return;
|
| - }
|
| -
|
| - const std::string& device_id_for_origin = allowed_devices_map_.AddDevice(
|
| - session->origin, device->GetAddress(), session->filters,
|
| - session->optional_services);
|
| -
|
| - VLOG(1) << "Device: " << device->GetName();
|
| - VLOG(1) << "UUIDs: ";
|
| -
|
| - device::BluetoothDevice::UUIDList filtered_uuids;
|
| - for (BluetoothUUID uuid : device->GetUUIDs()) {
|
| - if (allowed_devices_map_.IsOriginAllowedToAccessService(
|
| - session->origin, device_id_for_origin, uuid.canonical_value())) {
|
| - VLOG(1) << "\t Allowed: " << uuid.canonical_value();
|
| - filtered_uuids.push_back(uuid);
|
| - } else {
|
| - VLOG(1) << "\t Not Allowed: " << uuid.canonical_value();
|
| - }
|
| - }
|
| -
|
| - content::BluetoothDevice device_ipc(
|
| - device_id_for_origin, // id
|
| - device->GetName(), // name
|
| - content::BluetoothDevice::UUIDsFromBluetoothUUIDs(
|
| - filtered_uuids)); // uuids
|
| - RecordRequestDeviceOutcome(UMARequestDeviceOutcome::SUCCESS);
|
| - Send(new BluetoothMsg_RequestDeviceSuccess(session->thread_id,
|
| - session->request_id, device_ipc));
|
| - request_device_sessions_.Remove(chooser_id);
|
| -}
|
| -
|
| -CacheQueryResult BluetoothDispatcherHost::QueryCacheForDevice(
|
| - const url::Origin& origin,
|
| - const std::string& device_id) {
|
| - const std::string& device_address =
|
| - allowed_devices_map_.GetDeviceAddress(origin, device_id);
|
| - if (device_address.empty()) {
|
| - bad_message::ReceivedBadMessage(
|
| - this, bad_message::BDH_DEVICE_NOT_ALLOWED_FOR_ORIGIN);
|
| - return CacheQueryResult(CacheQueryOutcome::BAD_RENDERER);
|
| - }
|
| -
|
| - CacheQueryResult result;
|
| - result.device = adapter_->GetDevice(device_address);
|
| -
|
| - // When a device can't be found in the BluetoothAdapter, that generally
|
| - // indicates that it's gone out of range. We reject with a NetworkError in
|
| - // that case.
|
| - // https://webbluetoothchrome.github.io/web-bluetooth/#dom-bluetoothdevice-connectgatt
|
| - if (result.device == nullptr) {
|
| - result.outcome = CacheQueryOutcome::NO_DEVICE;
|
| - }
|
| - return result;
|
| -}
|
| -
|
| -void BluetoothDispatcherHost::AddAdapterObserver(
|
| - device::BluetoothAdapter::Observer* observer) {
|
| - adapter_observers_.insert(observer);
|
| - if (adapter_) {
|
| - adapter_->AddObserver(observer);
|
| - }
|
| -}
|
| -
|
| -void BluetoothDispatcherHost::RemoveAdapterObserver(
|
| - device::BluetoothAdapter::Observer* observer) {
|
| - size_t removed = adapter_observers_.erase(observer);
|
| - DCHECK(removed);
|
| - if (adapter_) {
|
| - adapter_->RemoveObserver(observer);
|
| - }
|
| -}
|
| -
|
| -} // namespace content
|
|
|