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

Unified Diff: content/browser/bluetooth/bluetooth_dispatcher_host.cc

Issue 1286063002: Add a path for content/ to open and control a Bluetooth chooser dialog. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@lkcr
Patch Set: Clean up. Created 5 years, 4 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 side-by-side diff with in-line comments
Download patch
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 cb1cfaeac2e3eeeee57af7c5159dfe45dd165b3b..6aec23ecd412f997b205ff78fc1308ac80bac8da 100644
--- a/content/browser/bluetooth/bluetooth_dispatcher_host.cc
+++ b/content/browser/bluetooth/bluetooth_dispatcher_host.cc
@@ -10,7 +10,10 @@
#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/frame_host/render_frame_host_impl.h"
@@ -33,8 +36,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
@@ -126,14 +130,30 @@ 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),
ortuno 2015/08/17 21:12:58 Should we at least log or histogram the result? It
Jeffrey Yasskin 2015/08/17 23:38:15 We should, but I think not here. I've just filed h
+ 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, this),
ortuno 2015/08/17 21:12:58 Shouldn't you pass a weak pointer to the timer?
Jeffrey Yasskin 2015/08/17 23:38:15 Good catch: I need to explicitly pass an unretaine
+ /*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,
@@ -171,6 +191,10 @@ 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, this));
ortuno 2015/08/17 21:12:58 Same here. Wouldn't a weak pointer be better?
Jeffrey Yasskin 2015/08/17 23:38:16 Switched to Unretained here too.
set_adapter(mock_adapter.Pass());
}
@@ -183,12 +207,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) {
ortuno 2015/08/17 21:12:58 Why do you pass the device by reference if you alr
Jeffrey Yasskin 2015/08/17 23:38:16 To be clear that |device| can't be null inside thi
+ 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(
@@ -201,6 +241,46 @@ 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->ShowDiscovering(BluetoothChooser::DiscoveryState::IDLE);
ortuno 2015/08/17 21:12:58 See below. ShowDiscovering could remove the sessio
Jeffrey Yasskin 2015/08/17 23:38:16 See below.
+ }
+ }
+}
+
+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);
ortuno 2015/08/17 21:12:58 AddFilteredDevice could end up removing the sessio
Jeffrey Yasskin 2015/08/17 23:38:15 Yep, the PostTask there makes sure the object is o
+ }
+}
+
static scoped_ptr<device::BluetoothDiscoveryFilter> ComputeScanFilter(
const std::vector<BluetoothScanFilter>& filters) {
std::set<BluetoothUUID> services;
@@ -251,56 +331,58 @@ 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);
+
+ session->chooser = render_frame_host->RunBluetoothChooser(
+ this, chooser_id, render_frame_host->GetLastCommittedURL().GetOrigin());
ortuno 2015/08/17 21:12:58 If you call requestDevice() from an iframe would i
Jeffrey Yasskin 2015/08/17 23:38:15 Right now, it would show the frame's origin, since
+ if (!session->chooser) {
+ LOG(ERROR) << "No Bluetooth chooser implementation.";
+ RecordRequestDeviceOutcome(
+ UMARequestDeviceOutcome::NO_BLUETOOTH_CHOOSER_IMPLEMENTATION);
+ Send(new BluetoothMsg_RequestDeviceError(
+ thread_id, request_id, WebBluetoothError::NoBluetoothChooser));
+ request_device_sessions_.Remove(chooser_id);
+ return;
+ }
+
+ session->chooser->SetAdapterPresence(
ortuno 2015/08/17 21:12:58 Since this call could close the dialog shouldn't y
Jeffrey Yasskin 2015/08/17 23:38:15 Yielding to the event loop in CloseDialog() makes
+ adapter_->IsPowered() ? BluetoothChooser::AdapterPresence::POWERED_ON
+ : BluetoothChooser::AdapterPresence::POWERED_OFF);
+
+ // 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);
+ }
+
+ 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(
@@ -545,86 +627,113 @@ 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)) {
+ VLOG(1) << "Setting discovery session for " << chooser_id;
+ session->discovery_session = discovery_session.Pass();
+ discovery_session_timer_.Reset();
+ if (session->chooser) {
+ session->chooser->ShowDiscovering(
+ BluetoothChooser::DiscoveryState::DISCOVERING);
+ }
+ } else {
+ // Dialog was closed before the session finished starting. Stop the session.
+ 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->ShowDiscovering(
+ 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);
+void BluetoothDispatcherHost::CloseDialog(int chooser_id,
+ const base::Closure& callback) {
+ 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.";
ortuno 2015/08/17 21:12:58 Do you need this CHECK? You would get a null point
Jeffrey Yasskin 2015/08/17 23:38:16 session->chooser.reset() works if session->chooser
+ session->chooser.reset();
+ if (!base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback)) {
+ LOG(WARNING) << "No TaskRunner; not closing requestDevice dialog.";
+ }
+}
+
+void BluetoothDispatcherHost::DialogCancelled(int chooser_id) {
+ CloseDialog(chooser_id,
+ base::Bind(&BluetoothDispatcherHost::OnRequestDeviceCancelled,
+ weak_ptr_factory_.GetWeakPtr(), chooser_id));
+}
+
+void BluetoothDispatcherHost::DeviceSelected(int chooser_id,
+ const std::string& device_id) {
+ CloseDialog(
+ chooser_id,
+ base::Bind(&BluetoothDispatcherHost::OnRequestDeviceSelected,
+ weak_ptr_factory_.GetWeakPtr(), chooser_id, device_id));
+}
+
+void BluetoothDispatcherHost::OnRequestDeviceCancelled(int chooser_id) {
+ RequestDeviceSession* session = request_device_sessions_.Lookup(chooser_id);
+ DCHECK(session) << "Session removed unexpectedly.";
+ RecordRequestDeviceOutcome(
+ UMARequestDeviceOutcome::BLUETOOTH_CHOOSER_CANCELLED);
+ VLOG(1) << "Bluetooth chooser cancelled";
Send(new BluetoothMsg_RequestDeviceError(
- thread_id, request_id, WebBluetoothError::DiscoverySessionStopFailed));
- request_device_sessions_.erase(std::make_pair(thread_id, request_id));
+ session->thread_id, session->request_id,
+ WebBluetoothError::ChooserCancelled));
+ request_device_sessions_.Remove(chooser_id);
+}
+
+void BluetoothDispatcherHost::OnRequestDeviceSelected(
+ int chooser_id,
+ const std::string& device_id) {
+ RequestDeviceSession* session = request_device_sessions_.Lookup(chooser_id);
+ DCHECK(session) << "Session removed unexpectedly.";
+
+ 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(

Powered by Google App Engine
This is Rietveld 408576698