| 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
|
| index c6c49b127592f10e6292f9c8a5316e947bff7cb8..af5c8f4360970ab1e3a60e92ac0804400da473c3 100644
|
| --- a/content/browser/bluetooth/bluetooth_dispatcher_host.cc
|
| +++ b/content/browser/bluetooth/bluetooth_dispatcher_host.cc
|
| @@ -10,11 +10,17 @@
|
|
|
| #include "content/browser/bluetooth/bluetooth_dispatcher_host.h"
|
|
|
| +#include "base/bind.h"
|
| +#include "base/single_thread_task_runner.h"
|
| #include "base/strings/utf_string_conversions.h"
|
| +#include "base/thread_task_runner_handle.h"
|
| #include "content/browser/bad_message.h"
|
| #include "content/browser/bluetooth/bluetooth_metrics.h"
|
| +#include "content/browser/bluetooth/first_device_bluetooth_chooser.h"
|
| #include "content/browser/frame_host/render_frame_host_impl.h"
|
| #include "content/common/bluetooth/bluetooth_messages.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"
|
| @@ -33,8 +39,9 @@ namespace content {
|
|
|
| namespace {
|
|
|
| -// TODO(ortuno): Once we have a chooser for scanning and the right
|
| -// callback for discovered services we should delete these constants.
|
| +// 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
|
| @@ -137,14 +144,33 @@ blink::WebBluetoothError TranslateGATTError(
|
| return blink::WebBluetoothError::GATTUntranslatedErrorCode;
|
| }
|
|
|
| +void StopDiscoverySession(
|
| + scoped_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));
|
| +}
|
| +
|
| } // 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);
|
| - current_delay_time_ = kDelayTime;
|
| if (BluetoothAdapterFactory::IsBluetoothAdapterAvailable())
|
| BluetoothAdapterFactory::GetAdapter(
|
| base::Bind(&BluetoothDispatcherHost::set_adapter,
|
| @@ -182,6 +208,13 @@ void BluetoothDispatcherHost::SetBluetoothAdapterForTesting(
|
| scoped_refptr<device::BluetoothAdapter> mock_adapter) {
|
| DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| 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)));
|
| set_adapter(mock_adapter.Pass());
|
| }
|
|
|
| @@ -194,12 +227,28 @@ BluetoothDispatcherHost::~BluetoothDispatcherHost() {
|
| // 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 {
|
| - RequestDeviceSession(const std::vector<BluetoothScanFilter>& filters,
|
| + public:
|
| + RequestDeviceSession(int thread_id,
|
| + int request_id,
|
| + const std::vector<BluetoothScanFilter>& filters,
|
| const std::vector<BluetoothUUID>& optional_services)
|
| - : filters(filters), optional_services(optional_services) {}
|
| + : thread_id(thread_id),
|
| + request_id(request_id),
|
| + filters(filters),
|
| + optional_services(optional_services) {}
|
|
|
| - std::vector<BluetoothScanFilter> filters;
|
| - std::vector<BluetoothUUID> optional_services;
|
| + void AddFilteredDevice(const device::BluetoothDevice& device) {
|
| + if (chooser && MatchesFilters(device, filters)) {
|
| + chooser->AddDevice(device.GetIdentifier(), device.GetName());
|
| + }
|
| + }
|
| +
|
| + const int thread_id;
|
| + const int request_id;
|
| + const std::vector<BluetoothScanFilter> filters;
|
| + const std::vector<BluetoothUUID> optional_services;
|
| + scoped_ptr<BluetoothChooser> chooser;
|
| + scoped_ptr<device::BluetoothDiscoverySession> discovery_session;
|
| };
|
|
|
| void BluetoothDispatcherHost::set_adapter(
|
| @@ -212,6 +261,47 @@ void BluetoothDispatcherHost::set_adapter(
|
| adapter_->AddObserver(this);
|
| }
|
|
|
| +void BluetoothDispatcherHost::StopDeviceDiscovery() {
|
| + for (IDMap<RequestDeviceSession, IDMapOwnPointer>::iterator iter(
|
| + &request_device_sessions_);
|
| + !iter.IsAtEnd(); iter.Advance()) {
|
| + RequestDeviceSession* session = iter.GetCurrentValue();
|
| + if (session->discovery_session) {
|
| + StopDiscoverySession(session->discovery_session.Pass());
|
| + }
|
| + if (session->chooser) {
|
| + session->chooser->ShowDiscoveryState(
|
| + BluetoothChooser::DiscoveryState::IDLE);
|
| + }
|
| + }
|
| +}
|
| +
|
| +void BluetoothDispatcherHost::AdapterPoweredChanged(
|
| + device::BluetoothAdapter* adapter,
|
| + bool powered) {
|
| + 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();
|
| + if (session->chooser)
|
| + session->chooser->SetAdapterPresence(presence);
|
| + }
|
| +}
|
| +
|
| +void BluetoothDispatcherHost::DeviceAdded(device::BluetoothAdapter* adapter,
|
| + device::BluetoothDevice* device) {
|
| + VLOG(1) << "Adding device to all choosers: " << device->GetIdentifier();
|
| + for (IDMap<RequestDeviceSession, IDMapOwnPointer>::iterator iter(
|
| + &request_device_sessions_);
|
| + !iter.IsAtEnd(); iter.Advance()) {
|
| + RequestDeviceSession* session = iter.GetCurrentValue();
|
| + session->AddFilteredDevice(*device);
|
| + }
|
| +}
|
| +
|
| static scoped_ptr<device::BluetoothDiscoveryFilter> ComputeScanFilter(
|
| const std::vector<BluetoothScanFilter>& filters) {
|
| std::set<BluetoothUUID> services;
|
| @@ -262,56 +352,74 @@ void BluetoothDispatcherHost::OnRequestDevice(
|
| return;
|
| }
|
|
|
| - // TODO(scheib): Device selection UI: crbug.com/436280
|
| - // TODO(scheib): Utilize BluetoothAdapter::Observer::DeviceAdded/Removed.
|
| - if (adapter_.get()) {
|
| - if (!request_device_sessions_
|
| - .insert(std::make_pair(
|
| - std::make_pair(thread_id, request_id),
|
| - RequestDeviceSession(filters, optional_services)))
|
| - .second) {
|
| - LOG(ERROR) << "2 requestDevice() calls with the same thread_id ("
|
| - << thread_id << ") and request_id (" << request_id
|
| - << ") shouldn't arrive at the same BluetoothDispatcherHost.";
|
| - bad_message::ReceivedBadMessage(
|
| - this, bad_message::BDH_DUPLICATE_REQUEST_DEVICE_ID);
|
| - }
|
| - 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::NoBluetoothAdapter));
|
| - request_device_sessions_.erase(std::make_pair(thread_id, request_id));
|
| - return;
|
| - }
|
| - // TODO(jyasskin): Once the dialog is available, the dialog should check for
|
| - // the status of the adapter, i.e. check IsPowered() and
|
| - // BluetoothAdapter::Observer::PoweredChanged, and inform the user. But
|
| - // until the dialog is available we log/histogram the status and return
|
| - // with a message.
|
| - // https://crbug.com/517237
|
| - if (!adapter_->IsPowered()) {
|
| - RecordRequestDeviceOutcome(
|
| - UMARequestDeviceOutcome::BLUETOOTH_ADAPTER_OFF);
|
| - Send(new BluetoothMsg_RequestDeviceError(
|
| - thread_id, request_id, WebBluetoothError::BluetoothAdapterOff));
|
| - request_device_sessions_.erase(std::make_pair(thread_id, request_id));
|
| - return;
|
| - }
|
| - adapter_->StartDiscoverySessionWithFilter(
|
| - ComputeScanFilter(filters),
|
| - base::Bind(&BluetoothDispatcherHost::OnDiscoverySessionStarted,
|
| - weak_ptr_factory_.GetWeakPtr(), thread_id, request_id),
|
| - base::Bind(&BluetoothDispatcherHost::OnDiscoverySessionStartedError,
|
| - weak_ptr_factory_.GetWeakPtr(), thread_id, request_id));
|
| - } else {
|
| + if (!adapter_) {
|
| VLOG(1) << "No BluetoothAdapter. Can't serve requestDevice.";
|
| RecordRequestDeviceOutcome(UMARequestDeviceOutcome::NO_BLUETOOTH_ADAPTER);
|
| Send(new BluetoothMsg_RequestDeviceError(
|
| thread_id, request_id, WebBluetoothError::NoBluetoothAdapter));
|
| + return;
|
| }
|
| - return;
|
| +
|
| + 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::NoBluetoothAdapter));
|
| + return;
|
| + }
|
| +
|
| + // Create storage for the information that backs the chooser, and show the
|
| + // chooser.
|
| + RequestDeviceSession* const session = new RequestDeviceSession(
|
| + thread_id, request_id, filters, optional_services);
|
| + int chooser_id = request_device_sessions_.Add(session);
|
| +
|
| + BluetoothChooser::EventHandler chooser_event_handler =
|
| + base::Bind(&BluetoothDispatcherHost::OnBluetoothChooserEvent,
|
| + weak_ptr_factory_.GetWeakPtr(), chooser_id);
|
| + if (WebContents* web_contents =
|
| + WebContents::FromRenderFrameHost(render_frame_host)) {
|
| + if (WebContentsDelegate* delegate = web_contents->GetDelegate()) {
|
| + session->chooser = delegate->RunBluetoothChooser(
|
| + web_contents, chooser_event_handler,
|
| + render_frame_host->GetLastCommittedURL().GetOrigin());
|
| + }
|
| + }
|
| + if (!session->chooser) {
|
| + LOG(WARNING)
|
| + << "No Bluetooth chooser implementation; falling back to first device.";
|
| + session->chooser.reset(
|
| + new FirstDeviceBluetoothChooser(chooser_event_handler));
|
| + }
|
| +
|
| + // Populate the initial list of devices.
|
| + VLOG(1) << "Populating devices in chooser " << chooser_id;
|
| + for (const device::BluetoothDevice* device : adapter_->GetDevices()) {
|
| + VLOG(1) << "\t" << device->GetIdentifier();
|
| + 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;
|
| + }
|
| +
|
| + // Redundant with the chooser's default; just to be clear:
|
| + session->chooser->ShowDiscoveryState(
|
| + BluetoothChooser::DiscoveryState::DISCOVERING);
|
| + adapter_->StartDiscoverySessionWithFilter(
|
| + ComputeScanFilter(filters),
|
| + base::Bind(&BluetoothDispatcherHost::OnDiscoverySessionStarted,
|
| + weak_ptr_factory_.GetWeakPtr(), chooser_id),
|
| + base::Bind(&BluetoothDispatcherHost::OnDiscoverySessionStartedError,
|
| + weak_ptr_factory_.GetWeakPtr(), chooser_id));
|
| }
|
|
|
| void BluetoothDispatcherHost::OnConnectGATT(
|
| @@ -564,86 +672,117 @@ void BluetoothDispatcherHost::OnWriteValue(
|
| }
|
|
|
| void BluetoothDispatcherHost::OnDiscoverySessionStarted(
|
| - int thread_id,
|
| - int request_id,
|
| + int chooser_id,
|
| scoped_ptr<device::BluetoothDiscoverySession> discovery_session) {
|
| DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| - BrowserThread::PostDelayedTask(
|
| - BrowserThread::UI, FROM_HERE,
|
| - base::Bind(&BluetoothDispatcherHost::StopDiscoverySession,
|
| - weak_ptr_factory_.GetWeakPtr(), thread_id, request_id,
|
| - base::Passed(&discovery_session)),
|
| - base::TimeDelta::FromSeconds(current_delay_time_));
|
| + VLOG(1) << "Started discovery session for " << chooser_id;
|
| + if (RequestDeviceSession* session =
|
| + request_device_sessions_.Lookup(chooser_id)) {
|
| + session->discovery_session = discovery_session.Pass();
|
| +
|
| + // Arrange to stop discovery later.
|
| + discovery_session_timer_.Reset();
|
| + } else {
|
| + VLOG(1) << "Chooser " << chooser_id
|
| + << " was closed before the session finished starting. Stopping.";
|
| + StopDiscoverySession(discovery_session.Pass());
|
| + }
|
| }
|
|
|
| -void BluetoothDispatcherHost::OnDiscoverySessionStartedError(int thread_id,
|
| - int request_id) {
|
| +void BluetoothDispatcherHost::OnDiscoverySessionStartedError(int chooser_id) {
|
| DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| - DLOG(WARNING) << "BluetoothDispatcherHost::OnDiscoverySessionStartedError";
|
| - RecordRequestDeviceOutcome(UMARequestDeviceOutcome::DISCOVERY_START_FAILED);
|
| - Send(new BluetoothMsg_RequestDeviceError(
|
| - thread_id, request_id, WebBluetoothError::DiscoverySessionStartFailed));
|
| - request_device_sessions_.erase(std::make_pair(thread_id, request_id));
|
| -}
|
| -
|
| -void BluetoothDispatcherHost::StopDiscoverySession(
|
| - int thread_id,
|
| - int request_id,
|
| - scoped_ptr<device::BluetoothDiscoverySession> discovery_session) {
|
| - DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| - discovery_session->Stop(
|
| - base::Bind(&BluetoothDispatcherHost::OnDiscoverySessionStopped,
|
| - weak_ptr_factory_.GetWeakPtr(), thread_id, request_id),
|
| - base::Bind(&BluetoothDispatcherHost::OnDiscoverySessionStoppedError,
|
| - weak_ptr_factory_.GetWeakPtr(), thread_id, request_id));
|
| -}
|
| -
|
| -void BluetoothDispatcherHost::OnDiscoverySessionStopped(int thread_id,
|
| - int request_id) {
|
| - DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| - auto session =
|
| - request_device_sessions_.find(std::make_pair(thread_id, request_id));
|
| - CHECK(session != request_device_sessions_.end());
|
| - BluetoothAdapter::DeviceList devices = adapter_->GetDevices();
|
| - for (device::BluetoothDevice* device : devices) {
|
| - VLOG(1) << "Device: " << device->GetName();
|
| - VLOG(1) << "UUIDs: ";
|
| - for (BluetoothUUID uuid : device->GetUUIDs())
|
| - VLOG(1) << "\t" << uuid.canonical_value();
|
| - if (MatchesFilters(*device, session->second.filters)) {
|
| - content::BluetoothDevice device_ipc(
|
| - device->GetAddress(), // instance_id
|
| - device->GetName(), // name
|
| - device->GetBluetoothClass(), // device_class
|
| - device->GetVendorIDSource(), // vendor_id_source
|
| - device->GetVendorID(), // vendor_id
|
| - device->GetProductID(), // product_id
|
| - device->GetDeviceID(), // product_version
|
| - device->IsPaired(), // paired
|
| - content::BluetoothDevice::UUIDsFromBluetoothUUIDs(
|
| - device->GetUUIDs())); // uuids
|
| - RecordRequestDeviceOutcome(UMARequestDeviceOutcome::SUCCESS);
|
| - Send(new BluetoothMsg_RequestDeviceSuccess(thread_id, request_id,
|
| - device_ipc));
|
| - request_device_sessions_.erase(session);
|
| - return;
|
| + 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);
|
| }
|
| }
|
| - RecordRequestDeviceOutcome(
|
| - UMARequestDeviceOutcome::NO_MATCHING_DEVICES_FOUND);
|
| - Send(new BluetoothMsg_RequestDeviceError(thread_id, request_id,
|
| - WebBluetoothError::NoDevicesFound));
|
| - request_device_sessions_.erase(session);
|
| + // Ignore discovery session start errors when the dialog was already closed by
|
| + // the time they happen.
|
| }
|
|
|
| -void BluetoothDispatcherHost::OnDiscoverySessionStoppedError(int thread_id,
|
| - int request_id) {
|
| - DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| - DLOG(WARNING) << "BluetoothDispatcherHost::OnDiscoverySessionStoppedError";
|
| - RecordRequestDeviceOutcome(UMARequestDeviceOutcome::DISCOVERY_STOP_FAILED);
|
| - Send(new BluetoothMsg_RequestDeviceError(
|
| - thread_id, request_id, WebBluetoothError::DiscoverySessionStopFailed));
|
| - request_device_sessions_.erase(std::make_pair(thread_id, request_id));
|
| +void BluetoothDispatcherHost::OnBluetoothChooserEvent(
|
| + int chooser_id,
|
| + BluetoothChooser::Event event,
|
| + const std::string& device_id) {
|
| + switch (event) {
|
| + case BluetoothChooser::Event::CANCELLED:
|
| + case BluetoothChooser::Event::SELECTED:
|
| + RequestDeviceSession* session =
|
| + request_device_sessions_.Lookup(chooser_id);
|
| + DCHECK(session) << "Shouldn't close the dialog twice.";
|
| + CHECK(session->chooser) << "Shouldn't close the dialog twice.";
|
| +
|
| + // 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_factory_.GetWeakPtr(), chooser_id, event,
|
| + device_id))) {
|
| + LOG(WARNING) << "No TaskRunner; not closing requestDevice dialog.";
|
| + }
|
| + break;
|
| + }
|
| +}
|
| +
|
| +void BluetoothDispatcherHost::FinishClosingChooser(
|
| + int chooser_id,
|
| + BluetoothChooser::Event event,
|
| + const std::string& device_id) {
|
| + RequestDeviceSession* session = request_device_sessions_.Lookup(chooser_id);
|
| + DCHECK(session) << "Session removed unexpectedly.";
|
| +
|
| + if (event == BluetoothChooser::Event::CANCELLED) {
|
| + RecordRequestDeviceOutcome(
|
| + UMARequestDeviceOutcome::BLUETOOTH_CHOOSER_CANCELLED);
|
| + VLOG(1) << "Bluetooth chooser cancelled";
|
| + Send(new BluetoothMsg_RequestDeviceError(
|
| + session->thread_id, session->request_id,
|
| + WebBluetoothError::ChooserCancelled));
|
| + request_device_sessions_.Remove(chooser_id);
|
| + return;
|
| + }
|
| + DCHECK_EQ(static_cast<int>(event),
|
| + static_cast<int>(BluetoothChooser::Event::SELECTED));
|
| +
|
| + const device::BluetoothAdapter::DeviceList devices = adapter_->GetDevices();
|
| + const device::BluetoothDevice* const device = adapter_->GetDevice(device_id);
|
| + if (device == nullptr) {
|
| + RecordRequestDeviceOutcome(UMARequestDeviceOutcome::CHOSEN_DEVICE_VANISHED);
|
| + Send(new BluetoothMsg_RequestDeviceError(
|
| + session->thread_id, session->request_id,
|
| + WebBluetoothError::ChosenDeviceVanished));
|
| + request_device_sessions_.Remove(chooser_id);
|
| + return;
|
| + }
|
| +
|
| + VLOG(1) << "Device: " << device->GetName();
|
| + VLOG(1) << "UUIDs: ";
|
| + for (BluetoothUUID uuid : device->GetUUIDs())
|
| + VLOG(1) << "\t" << uuid.canonical_value();
|
| +
|
| + content::BluetoothDevice device_ipc(
|
| + device->GetAddress(), // instance_id
|
| + device->GetName(), // name
|
| + device->GetBluetoothClass(), // device_class
|
| + device->GetVendorIDSource(), // vendor_id_source
|
| + device->GetVendorID(), // vendor_id
|
| + device->GetProductID(), // product_id
|
| + device->GetDeviceID(), // product_version
|
| + device->IsPaired(), // paired
|
| + content::BluetoothDevice::UUIDsFromBluetoothUUIDs(
|
| + device->GetUUIDs())); // uuids
|
| + RecordRequestDeviceOutcome(UMARequestDeviceOutcome::SUCCESS);
|
| + Send(new BluetoothMsg_RequestDeviceSuccess(session->thread_id,
|
| + session->request_id, device_ipc));
|
| + request_device_sessions_.Remove(chooser_id);
|
| }
|
|
|
| void BluetoothDispatcherHost::OnGATTConnectionCreated(
|
|
|