diff options
8 files changed, 148 insertions, 31 deletions
diff --git a/content/browser/bluetooth/bluetooth_dispatcher_host.cc b/content/browser/bluetooth/bluetooth_dispatcher_host.cc index c1a2395..75556c0 100644 --- a/content/browser/bluetooth/bluetooth_dispatcher_host.cc +++ b/content/browser/bluetooth/bluetooth_dispatcher_host.cc @@ -243,6 +243,20 @@ struct BluetoothDispatcherHost::RequestDeviceSession { } } + scoped_ptr<device::BluetoothDiscoveryFilter> ComputeScanFilter() const { + std::set<BluetoothUUID> services; + for (const BluetoothScanFilter& filter : filters) { + services.insert(filter.services.begin(), filter.services.end()); + } + scoped_ptr<device::BluetoothDiscoveryFilter> discovery_filter( + new device::BluetoothDiscoveryFilter( + device::BluetoothDiscoveryFilter::TRANSPORT_DUAL)); + for (const BluetoothUUID& service : services) { + discovery_filter->AddUUID(service); + } + return discovery_filter.Pass(); + } + const int thread_id; const int request_id; const std::vector<BluetoothScanFilter> filters; @@ -261,6 +275,24 @@ void BluetoothDispatcherHost::set_adapter( adapter_->AddObserver(this); } +void BluetoothDispatcherHost::StartDeviceDiscovery( + RequestDeviceSession* session, + int chooser_id) { + 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_factory_.GetWeakPtr(), chooser_id), + base::Bind(&BluetoothDispatcherHost::OnDiscoverySessionStartedError, + weak_ptr_factory_.GetWeakPtr(), chooser_id)); + } +} + void BluetoothDispatcherHost::StopDeviceDiscovery() { for (IDMap<RequestDeviceSession, IDMapOwnPointer>::iterator iter( &request_device_sessions_); @@ -315,21 +347,6 @@ void BluetoothDispatcherHost::DeviceRemoved(device::BluetoothAdapter* adapter, } } -static scoped_ptr<device::BluetoothDiscoveryFilter> ComputeScanFilter( - const std::vector<BluetoothScanFilter>& filters) { - std::set<BluetoothUUID> services; - for (const BluetoothScanFilter& filter : filters) { - services.insert(filter.services.begin(), filter.services.end()); - } - scoped_ptr<device::BluetoothDiscoveryFilter> discovery_filter( - new device::BluetoothDiscoveryFilter( - device::BluetoothDiscoveryFilter::TRANSPORT_DUAL)); - for (const BluetoothUUID& service : services) { - discovery_filter->AddUUID(service); - } - return discovery_filter.Pass(); -} - void BluetoothDispatcherHost::OnRequestDevice( int thread_id, int request_id, @@ -425,15 +442,7 @@ void BluetoothDispatcherHost::OnRequestDevice( 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)); + StartDeviceDiscovery(session, chooser_id); } void BluetoothDispatcherHost::OnConnectGATT( @@ -721,18 +730,18 @@ void BluetoothDispatcherHost::OnBluetoothChooserEvent( int chooser_id, BluetoothChooser::Event event, const std::string& device_id) { + 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: - // TODO(jyasskin): Implement starting a new Bluetooth discovery session. - NOTIMPLEMENTED(); + StartDeviceDiscovery(session, chooser_id); break; 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(); diff --git a/content/browser/bluetooth/bluetooth_dispatcher_host.h b/content/browser/bluetooth/bluetooth_dispatcher_host.h index 7e3e7af4..cc6cd9d 100644 --- a/content/browser/bluetooth/bluetooth_dispatcher_host.h +++ b/content/browser/bluetooth/bluetooth_dispatcher_host.h @@ -59,6 +59,10 @@ class CONTENT_EXPORT BluetoothDispatcherHost final // releasing references to previous |adapter_|. void set_adapter(scoped_refptr<device::BluetoothAdapter> adapter); + // Makes sure a BluetoothDiscoverySession is active for |session|, and resets + // its timeout. + void StartDeviceDiscovery(RequestDeviceSession* session, int chooser_id); + // Stops all BluetoothDiscoverySessions being run for requestDevice() // choosers. void StopDeviceDiscovery(); diff --git a/content/shell/browser/blink_test_controller.cc b/content/shell/browser/blink_test_controller.cc index eb15b2d..b9e84e5 100644 --- a/content/shell/browser/blink_test_controller.cc +++ b/content/shell/browser/blink_test_controller.cc @@ -753,6 +753,8 @@ void BlinkTestController::OnSendBluetoothManualChooserEvent( event = BluetoothChooser::Event::CANCELLED; } else if (event_name == "selected") { event = BluetoothChooser::Event::SELECTED; + } else if (event_name == "rescan") { + event = BluetoothChooser::Event::RESCAN; } else { printer_->AddErrorMessage(base::StringPrintf( "FAIL: Unexpected sendBluetoothManualChooserEvent() event name '%s'.", diff --git a/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.cc b/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.cc index cbc26af..ed27741 100644 --- a/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.cc +++ b/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.cc @@ -4,8 +4,12 @@ #include "content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.h" +#include "base/bind.h" +#include "base/bind_helpers.h" #include "base/format_macros.h" +#include "base/location.h" #include "base/strings/stringprintf.h" +#include "base/thread_task_runner_handle.h" #include "device/bluetooth/bluetooth_adapter.h" #include "device/bluetooth/bluetooth_device.h" #include "device/bluetooth/bluetooth_discovery_session.h" @@ -122,6 +126,8 @@ LayoutTestBluetoothAdapterProvider::GetBluetoothAdapter( return GetFailingConnectionsAdapter(); else if (fake_adapter_name == "FailingGATTOperationsAdapter") return GetFailingGATTOperationsAdapter(); + else if (fake_adapter_name == "SecondDiscoveryFindsHeartRateAdapter") + return GetSecondDiscoveryFindsHeartRateAdapter(); else if (fake_adapter_name == "") return NULL; @@ -248,6 +254,40 @@ LayoutTestBluetoothAdapterProvider::GetGlucoseHeartRateAdapter() { return adapter.Pass(); } +// Adds a device to |adapter| and notifies all observers about that new device. +// Mocks can call this asynchronously to cause changes in the middle of a test. +static void AddDevice(scoped_refptr<NiceMockBluetoothAdapter> adapter, + scoped_ptr<NiceMockBluetoothDevice> new_device) { + NiceMockBluetoothDevice* new_device_ptr = new_device.get(); + adapter->AddMockDevice(new_device.Pass()); + FOR_EACH_OBSERVER(BluetoothAdapter::Observer, adapter->GetObservers(), + DeviceAdded(adapter.get(), new_device_ptr)); +} + +// static +scoped_refptr<NiceMockBluetoothAdapter> +LayoutTestBluetoothAdapterProvider::GetSecondDiscoveryFindsHeartRateAdapter() { + scoped_refptr<NiceMockBluetoothAdapter> adapter(GetPoweredAdapter()); + NiceMockBluetoothAdapter* adapter_ptr = adapter.get(); + + EXPECT_CALL(*adapter, StartDiscoverySessionWithFilterRaw(_, _, _)) + .WillOnce(RunCallbackWithResult<1 /* success_callback */>( + []() { return GetDiscoverySession(); })) + .WillOnce( + RunCallbackWithResult<1 /* success_callback */>([adapter_ptr]() { + // In the second discovery session, have the adapter discover a new + // device, shortly after the session starts. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(&AddDevice, make_scoped_refptr(adapter_ptr), + + base::Passed(GetHeartRateDevice(adapter_ptr)))); + return GetDiscoverySession(); + })); + + return adapter; +} + // static scoped_refptr<NiceMockBluetoothAdapter> LayoutTestBluetoothAdapterProvider::GetMissingServiceGenericAccessAdapter() { diff --git a/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.h b/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.h index 9d52de9..ac87afe 100644 --- a/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.h +++ b/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.h @@ -121,6 +121,15 @@ class LayoutTestBluetoothAdapterProvider { static scoped_refptr<testing::NiceMock<device::MockBluetoothAdapter>> GetGlucoseHeartRateAdapter(); + // |SecondDiscoveryFindsHeartRateAdapter| + // Inherits from |PoweredAdapter| + // Mock Functions: + // - StartDiscoverySessionWithFilter: + // Run success callback with |DiscoverySession|. + // After the first call, adds a |HeartRateDevice|. + static scoped_refptr<testing::NiceMock<device::MockBluetoothAdapter>> + GetSecondDiscoveryFindsHeartRateAdapter(); + // |MissingServiceGenericAccessAdapter| // Inherits from |EmptyAdapter| // Internal Structure: diff --git a/device/bluetooth/test/mock_bluetooth_adapter.cc b/device/bluetooth/test/mock_bluetooth_adapter.cc index 8054d69..26ae68b 100644 --- a/device/bluetooth/test/mock_bluetooth_adapter.cc +++ b/device/bluetooth/test/mock_bluetooth_adapter.cc @@ -8,10 +8,21 @@ namespace device { +using testing::Invoke; +using testing::_; + MockBluetoothAdapter::Observer::Observer() {} MockBluetoothAdapter::Observer::~Observer() {} MockBluetoothAdapter::MockBluetoothAdapter() { + ON_CALL(*this, AddObserver(_)) + .WillByDefault(Invoke([this](BluetoothAdapter::Observer* observer) { + this->BluetoothAdapter::AddObserver(observer); + })); + ON_CALL(*this, RemoveObserver(_)) + .WillByDefault(Invoke([this](BluetoothAdapter::Observer* observer) { + this->BluetoothAdapter::RemoveObserver(observer); + })); } MockBluetoothAdapter::~MockBluetoothAdapter() {} diff --git a/device/bluetooth/test/mock_bluetooth_adapter.h b/device/bluetooth/test/mock_bluetooth_adapter.h index 6d1ee73..775adee 100644 --- a/device/bluetooth/test/mock_bluetooth_adapter.h +++ b/device/bluetooth/test/mock_bluetooth_adapter.h @@ -109,6 +109,13 @@ class MockBluetoothAdapter : public BluetoothAdapter { BluetoothAdapter::ConstDeviceList GetConstMockDevices(); BluetoothAdapter::DeviceList GetMockDevices(); + // The observers are maintained by the default behavior of AddObserver() and + // RemoveObserver(). Test fakes can use this function to notify the observers + // about events. + base::ObserverList<device::BluetoothAdapter::Observer>& GetObservers() { + return observers_; + } + protected: void AddDiscoverySession( BluetoothDiscoveryFilter* discovery_filter, diff --git a/third_party/WebKit/LayoutTests/bluetooth/requestDevice.html b/third_party/WebKit/LayoutTests/bluetooth/requestDevice.html index efd10a4..525d6ab 100644 --- a/third_party/WebKit/LayoutTests/bluetooth/requestDevice.html +++ b/third_party/WebKit/LayoutTests/bluetooth/requestDevice.html @@ -191,4 +191,39 @@ promise_test(() => { filters: [{services: ['generic_access', 'battery_service']}] }), 'NotFoundError'); }, 'Too-strict filters do prevent matching.'); + +promise_test(() => { + testRunner.setBluetoothMockDataSet('SecondDiscoveryFindsHeartRateAdapter'); + testRunner.setBluetoothManualChooser(); + + // Open the chooser, looking for a Heart Rate device. + let requestDevicePromise = requestDeviceWithKeyDown({ + filters: [{services: ['heart_rate']}] + }); + + // The adapter finds nothing, so we just see discovery start and stop. + getBluetoothManualChooserEvents(3).then(events => { + assert_array_equals(events, + ['chooser-opened(file:///)', + 'discovering', + 'discovery-idle', + ]); + + // On the second discovery, the adapter finds the Heart Rate device. + testRunner.sendBluetoothManualChooserEvent('rescan', ''); + return getBluetoothManualChooserEvents(3); + }).then(events => { + assert_equals(events.length, 3, events); + assert_equals(events[0], 'discovering', 'events[0]'); + let idsByName = new AddDeviceEventSet(); + idsByName.assert_add_device_event(events[1]); + assert_true(idsByName.has('Heart Rate Device')); + assert_equals(events[2], 'discovery-idle'); + + // Select it and let the test complete. + testRunner.sendBluetoothManualChooserEvent('selected', + idsByName.get('Heart Rate Device')); + return requestDevicePromise; + }).then(device => assert_equals(device.name, 'Heart Rate Device')); +}, 'The chooser can restart the BT scan.'); </script> |