From fd7c534b719de832eb7ce1e3aa9043ba063f68c4 Mon Sep 17 00:00:00 2001 From: isherman Date: Thu, 23 Apr 2015 17:43:53 -0700 Subject: [Easy Unlock] Port the BluetoothThrottler class to native code. BUG=424419 TEST=components_unittests R=tengs@chromium.org Review URL: https://codereview.chromium.org/663693002 Cr-Commit-Position: refs/heads/master@{#326706} --- components/components_tests.gyp | 2 + components/proximity_auth.gypi | 5 + components/proximity_auth/BUILD.gn | 7 ++ .../proximity_auth/bluetooth_connection_finder.cc | 4 +- .../proximity_auth/bluetooth_connection_finder.h | 2 +- components/proximity_auth/bluetooth_throttler.h | 38 ++++++ .../proximity_auth/bluetooth_throttler_impl.cc | 66 ++++++++++ .../proximity_auth/bluetooth_throttler_impl.h | 69 +++++++++++ .../bluetooth_throttler_impl_unittest.cc | 88 ++++++++++++++ components/proximity_auth/client.cc | 4 +- components/proximity_auth/client.h | 2 +- components/proximity_auth/connection.cc | 5 +- components/proximity_auth/connection_finder.h | 4 - components/proximity_auth/connection_observer.h | 4 +- components/proximity_auth/connection_unittest.cc | 9 +- .../throttled_bluetooth_connection_finder.cc | 55 +++++++++ .../throttled_bluetooth_connection_finder.h | 60 ++++++++++ ...rottled_bluetooth_connection_finder_unittest.cc | 133 +++++++++++++++++++++ 18 files changed, 537 insertions(+), 20 deletions(-) create mode 100644 components/proximity_auth/bluetooth_throttler.h create mode 100644 components/proximity_auth/bluetooth_throttler_impl.cc create mode 100644 components/proximity_auth/bluetooth_throttler_impl.h create mode 100644 components/proximity_auth/bluetooth_throttler_impl_unittest.cc create mode 100644 components/proximity_auth/throttled_bluetooth_connection_finder.cc create mode 100644 components/proximity_auth/throttled_bluetooth_connection_finder.h create mode 100644 components/proximity_auth/throttled_bluetooth_connection_finder_unittest.cc (limited to 'components') diff --git a/components/components_tests.gyp b/components/components_tests.gyp index f48690f..05383f1 100644 --- a/components/components_tests.gyp +++ b/components/components_tests.gyp @@ -405,6 +405,7 @@ 'proximity_auth_unittest_sources': [ 'proximity_auth/bluetooth_connection_finder_unittest.cc', 'proximity_auth/bluetooth_connection_unittest.cc', + 'proximity_auth/bluetooth_throttler_impl_unittest.cc', 'proximity_auth/client_unittest.cc', 'proximity_auth/connection_unittest.cc', 'proximity_auth/cryptauth/base64url_unittest.cc', @@ -415,6 +416,7 @@ 'proximity_auth/cryptauth/fake_secure_message_delegate_unittest.cc', 'proximity_auth/proximity_auth_system_unittest.cc', 'proximity_auth/remote_status_update_unittest.cc', + 'proximity_auth/throttled_bluetooth_connection_finder_unittest.cc', 'proximity_auth/wire_message_unittest.cc', ], 'query_parser_unittest_sources': [ diff --git a/components/proximity_auth.gypi b/components/proximity_auth.gypi index ba8abc4..8a5526c 100644 --- a/components/proximity_auth.gypi +++ b/components/proximity_auth.gypi @@ -20,6 +20,9 @@ "proximity_auth/bluetooth_connection.h", "proximity_auth/bluetooth_connection_finder.cc", "proximity_auth/bluetooth_connection_finder.h", + "proximity_auth/bluetooth_throttler.h", + "proximity_auth/bluetooth_throttler_impl.cc", + "proximity_auth/bluetooth_throttler_impl.h", "proximity_auth/bluetooth_util.cc", "proximity_auth/bluetooth_util.h", "proximity_auth/bluetooth_util_chromeos.cc", @@ -40,6 +43,8 @@ "proximity_auth/secure_context.h", "proximity_auth/switches.cc", "proximity_auth/switches.h", + "proximity_auth/throttled_bluetooth_connection_finder.cc", + "proximity_auth/throttled_bluetooth_connection_finder.h", "proximity_auth/wire_message.cc", "proximity_auth/wire_message.h", ], diff --git a/components/proximity_auth/BUILD.gn b/components/proximity_auth/BUILD.gn index 380459e..7d31f67 100644 --- a/components/proximity_auth/BUILD.gn +++ b/components/proximity_auth/BUILD.gn @@ -10,6 +10,9 @@ source_set("proximity_auth") { "bluetooth_connection.h", "bluetooth_connection_finder.cc", "bluetooth_connection_finder.h", + "bluetooth_throttler.h", + "bluetooth_throttler_impl.cc", + "bluetooth_throttler_impl.h", "bluetooth_util.cc", "bluetooth_util.h", "bluetooth_util_chromeos.cc", @@ -28,6 +31,8 @@ source_set("proximity_auth") { "secure_context.h", "switches.cc", "switches.h", + "throttled_bluetooth_connection_finder.cc", + "throttled_bluetooth_connection_finder.h", "wire_message.cc", "wire_message.h", ] @@ -44,10 +49,12 @@ source_set("unit_tests") { sources = [ "bluetooth_connection_finder_unittest.cc", "bluetooth_connection_unittest.cc", + "bluetooth_throttler_impl_unittest.cc", "client_unittest.cc", "connection_unittest.cc", "proximity_auth_system_unittest.cc", "remote_status_update_unittest.cc", + "throttled_bluetooth_connection_finder_unittest.cc", "wire_message_unittest.cc", ] diff --git a/components/proximity_auth/bluetooth_connection_finder.cc b/components/proximity_auth/bluetooth_connection_finder.cc index 3fe6567..1f26d2f 100644 --- a/components/proximity_auth/bluetooth_connection_finder.cc +++ b/components/proximity_auth/bluetooth_connection_finder.cc @@ -119,10 +119,10 @@ void BluetoothConnectionFinder::AdapterPoweredChanged(BluetoothAdapter* adapter, } void BluetoothConnectionFinder::OnConnectionStatusChanged( - const Connection& connection, + Connection* connection, Connection::Status old_status, Connection::Status new_status) { - DCHECK_EQ(&connection, connection_.get()); + DCHECK_EQ(connection, connection_.get()); if (connection_->IsConnected()) { base::TimeDelta elapsed = base::TimeTicks::Now() - start_time_; diff --git a/components/proximity_auth/bluetooth_connection_finder.h b/components/proximity_auth/bluetooth_connection_finder.h index 40b28dd..cca88f63 100644 --- a/components/proximity_auth/bluetooth_connection_finder.h +++ b/components/proximity_auth/bluetooth_connection_finder.h @@ -64,7 +64,7 @@ class BluetoothConnectionFinder : public ConnectionFinder, void OnAdapterInitialized(scoped_refptr adapter); // ConnectionObserver: - void OnConnectionStatusChanged(const Connection& connection, + void OnConnectionStatusChanged(Connection* connection, Connection::Status old_status, Connection::Status new_status) override; diff --git a/components/proximity_auth/bluetooth_throttler.h b/components/proximity_auth/bluetooth_throttler.h new file mode 100644 index 0000000..1e428fe --- /dev/null +++ b/components/proximity_auth/bluetooth_throttler.h @@ -0,0 +1,38 @@ +// Copyright 2015 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. + +#ifndef COMPONENTS_PROXIMITY_AUTH_BLUETOOTH_THROTTLER_H +#define COMPONENTS_PROXIMITY_AUTH_BLUETOOTH_THROTTLER_H + +namespace base { +class TimeDelta; +} + +namespace proximity_auth { + +class Connection; + +// An interface for throttling repeated connection attempts to the same device. +// This throttling is necessary to prevent a kernel race condition when +// connecting before the previous connection fully closes, putting the +// connection in a corrupted, and unrecoverable state. http://crbug.com/345232 +class BluetoothThrottler { + public: + virtual ~BluetoothThrottler() {} + + // Returns the current delay that must be respected prior to reattempting to + // establish a connection with the remote device. The returned value is 0 if + // no delay is needed. + virtual base::TimeDelta GetDelay() const = 0; + + // Should be called when a connection to the remote device is established. + // Note that the |connection| is passed as a weak reference. The throttler + // will ensure, by registering as an observer, that it never attempts to use + // the connection after it has been destroyed. + virtual void OnConnection(Connection* connection) = 0; +}; + +} // namespace proximity_auth + +#endif // COMPONENTS_PROXIMITY_AUTH_BLUETOOTH_THROTTLER_H diff --git a/components/proximity_auth/bluetooth_throttler_impl.cc b/components/proximity_auth/bluetooth_throttler_impl.cc new file mode 100644 index 0000000..9963fb0 --- /dev/null +++ b/components/proximity_auth/bluetooth_throttler_impl.cc @@ -0,0 +1,66 @@ +// Copyright 2015 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. + +#include "components/proximity_auth/bluetooth_throttler_impl.h" + +#include "base/stl_util.h" +#include "base/time/tick_clock.h" +#include "components/proximity_auth/connection.h" + +namespace proximity_auth { +namespace { + +// Time to wait after disconnect before reconnecting. +const int kCooldownTimeSecs = 7; + +} // namespace + +BluetoothThrottlerImpl::BluetoothThrottlerImpl( + scoped_ptr clock) + : clock_(clock.Pass()) { +} + +BluetoothThrottlerImpl::~BluetoothThrottlerImpl() { + for (Connection* connection : connections_) { + connection->RemoveObserver(this); + } +} + +base::TimeDelta BluetoothThrottlerImpl::GetDelay() const { + if (last_disconnect_time_.is_null()) + return base::TimeDelta(); + + base::TimeTicks now = clock_->NowTicks(); + base::TimeTicks throttled_start_time = + last_disconnect_time_ + GetCooldownTimeDelta(); + if (now >= throttled_start_time) + return base::TimeDelta(); + + return throttled_start_time - now; +} + +void BluetoothThrottlerImpl::OnConnection(Connection* connection) { + DCHECK(!ContainsKey(connections_, connection)); + connections_.insert(connection); + connection->AddObserver(this); +} + +base::TimeDelta BluetoothThrottlerImpl::GetCooldownTimeDelta() const { + return base::TimeDelta::FromSeconds(kCooldownTimeSecs); +} + +void BluetoothThrottlerImpl::OnConnectionStatusChanged( + Connection* connection, + Connection::Status old_status, + Connection::Status new_status) { + DCHECK(ContainsKey(connections_, connection)); + if (old_status == Connection::CONNECTED && + new_status == Connection::DISCONNECTED) { + last_disconnect_time_ = clock_->NowTicks(); + connection->RemoveObserver(this); + connections_.erase(connection); + } +} + +} // namespace proximity_auth diff --git a/components/proximity_auth/bluetooth_throttler_impl.h b/components/proximity_auth/bluetooth_throttler_impl.h new file mode 100644 index 0000000..3ba71541 --- /dev/null +++ b/components/proximity_auth/bluetooth_throttler_impl.h @@ -0,0 +1,69 @@ +// Copyright 2015 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. + +#ifndef COMPONENTS_PROXIMITY_AUTH_BLUETOOTH_THROTTLER_IMPL_H +#define COMPONENTS_PROXIMITY_AUTH_BLUETOOTH_THROTTLER_IMPL_H + +#include + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/time/time.h" +#include "components/proximity_auth/bluetooth_throttler.h" +#include "components/proximity_auth/connection_observer.h" + +namespace base { +class TickClock; +} + +namespace proximity_auth { + +class Connection; + +// This class throttles repeated connection attempts to the same device. This +// throttling is necessary to prevent a kernel race condition when connecting +// before the previous connection fully closes, putting the connection in a +// corrupted, and unrecoverable state. http://crbug.com/345232 +class BluetoothThrottlerImpl : public BluetoothThrottler, + public ConnectionObserver { + public: + // Creates a throttler for connections to a remote device, using the |clock| + // as a time source. + explicit BluetoothThrottlerImpl(scoped_ptr clock); + ~BluetoothThrottlerImpl() override; + + // BluetoothThrottler: + base::TimeDelta GetDelay() const override; + void OnConnection(Connection* connection) override; + + protected: + // Returns the duration to wait, after disconnecting, before reattempting a + // connection to the remote device. Exposed for testing. + base::TimeDelta GetCooldownTimeDelta() const; + + private: + // ConnectionObserver: + void OnConnectionStatusChanged(Connection* connection, + Connection::Status old_status, + Connection::Status new_status) override; + + // Tracks the last seen disconnect time for the |remote_device_|. + base::TimeTicks last_disconnect_time_; + + // The time source. + scoped_ptr clock_; + + // The currently connected connections. + // Each connection is stored as a weak reference, which is safe because |this| + // instance is registered as an observer, and will unregister when the + // connection disconnects, which is guaranteed to occur before the connection + // is destroyed. + std::set connections_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothThrottlerImpl); +}; + +} // namespace proximity_auth + +#endif // COMPONENTS_PROXIMITY_AUTH_BLUETOOTH_THROTTLER_IMPL_H diff --git a/components/proximity_auth/bluetooth_throttler_impl_unittest.cc b/components/proximity_auth/bluetooth_throttler_impl_unittest.cc new file mode 100644 index 0000000..1bca940 --- /dev/null +++ b/components/proximity_auth/bluetooth_throttler_impl_unittest.cc @@ -0,0 +1,88 @@ +// Copyright 2015 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. + +#include "components/proximity_auth/bluetooth_throttler_impl.h" + +#include "base/test/simple_test_tick_clock.h" +#include "base/time/time.h" +#include "components/proximity_auth/wire_message.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace proximity_auth { +namespace { + +class StubConnection : public Connection { + public: + StubConnection() : Connection(RemoteDevice()) {} + ~StubConnection() override {} + + void Connect() override {} + void Disconnect() override {} + void SendMessageImpl(scoped_ptr message) override {} + + private: + DISALLOW_COPY_AND_ASSIGN(StubConnection); +}; + +class TestBluetoothThrottler : public BluetoothThrottlerImpl { + public: + explicit TestBluetoothThrottler(scoped_ptr clock) + : BluetoothThrottlerImpl(clock.Pass()) {} + ~TestBluetoothThrottler() override {} + + // Increase visibility for testing. + using BluetoothThrottlerImpl::GetCooldownTimeDelta; + + private: + DISALLOW_COPY_AND_ASSIGN(TestBluetoothThrottler); +}; + +} // namespace + +class ProximityAuthBluetoothThrottlerImplTest : public testing::Test { + public: + ProximityAuthBluetoothThrottlerImplTest() + : clock_(new base::SimpleTestTickClock), + throttler_(make_scoped_ptr(clock_)) { + // The throttler treats null times as special, so start with a non-null + // time. + clock_->Advance(base::TimeDelta::FromSeconds(1)); + } + + protected: + // The clock is owned by the |throttler_|. + base::SimpleTestTickClock* clock_; + TestBluetoothThrottler throttler_; +}; + +TEST_F(ProximityAuthBluetoothThrottlerImplTest, + GetDelay_FirstConnectionIsNotThrottled) { + EXPECT_EQ(base::TimeDelta(), throttler_.GetDelay()); +} + +TEST_F(ProximityAuthBluetoothThrottlerImplTest, + GetDelay_ConnectionAfterDisconnectIsThrottled) { + // Simulate a connection followed by a disconnection. + StubConnection connection; + throttler_.OnConnection(&connection); + static_cast(&throttler_) + ->OnConnectionStatusChanged(&connection, Connection::CONNECTED, + Connection::DISCONNECTED); + EXPECT_GT(throttler_.GetDelay(), base::TimeDelta()); +} + +TEST_F(ProximityAuthBluetoothThrottlerImplTest, + GetDelay_DelayedConnectionAfterDisconnectIsNotThrottled) { + // Simulate a connection followed by a disconnection, then allow the cooldown + // period to elapse. + StubConnection connection; + throttler_.OnConnection(&connection); + static_cast(&throttler_) + ->OnConnectionStatusChanged(&connection, Connection::CONNECTED, + Connection::DISCONNECTED); + clock_->Advance(throttler_.GetCooldownTimeDelta()); + EXPECT_EQ(base::TimeDelta(), throttler_.GetDelay()); +} + +} // namespace proximity_auth diff --git a/components/proximity_auth/client.cc b/components/proximity_auth/client.cc index 016c505..a0b6a06 100644 --- a/components/proximity_auth/client.cc +++ b/components/proximity_auth/client.cc @@ -177,10 +177,10 @@ void Client::HandleUnlockResponseMessage(const base::DictionaryValue& message) { FOR_EACH_OBSERVER(ClientObserver, observers_, OnUnlockResponse(true)); } -void Client::OnConnectionStatusChanged(const Connection& connection, +void Client::OnConnectionStatusChanged(Connection* connection, Connection::Status old_status, Connection::Status new_status) { - DCHECK_EQ(&connection, connection_.get()); + DCHECK_EQ(connection, connection_.get()); if (new_status != Connection::CONNECTED) { VLOG(1) << "[Client] Secure channel disconnected..."; connection_->RemoveObserver(this); diff --git a/components/proximity_auth/client.h b/components/proximity_auth/client.h index 5cd5107..3b66e8e 100644 --- a/components/proximity_auth/client.h +++ b/components/proximity_auth/client.h @@ -91,7 +91,7 @@ class Client : public ConnectionObserver { void HandleUnlockResponseMessage(const base::DictionaryValue& message); // ConnectionObserver: - void OnConnectionStatusChanged(const Connection& connection, + void OnConnectionStatusChanged(Connection* connection, Connection::Status old_status, Connection::Status new_status) override; void OnMessageReceived(const Connection& connection, diff --git a/components/proximity_auth/connection.cc b/components/proximity_auth/connection.cc index 65f6dd6..437be60 100644 --- a/components/proximity_auth/connection.cc +++ b/components/proximity_auth/connection.cc @@ -54,9 +54,8 @@ void Connection::SetStatus(Status status) { Status old_status = status_; status_ = status; - FOR_EACH_OBSERVER(ConnectionObserver, - observers_, - OnConnectionStatusChanged(*this, old_status, status_)); + FOR_EACH_OBSERVER(ConnectionObserver, observers_, + OnConnectionStatusChanged(this, old_status, status_)); } void Connection::OnDidSendMessage(const WireMessage& message, bool success) { diff --git a/components/proximity_auth/connection_finder.h b/components/proximity_auth/connection_finder.h index 51292da..246a9a0 100644 --- a/components/proximity_auth/connection_finder.h +++ b/components/proximity_auth/connection_finder.h @@ -16,7 +16,6 @@ class Connection; // Interface for finding a connection to a remote device. class ConnectionFinder { public: - ConnectionFinder() {} virtual ~ConnectionFinder() {} // Attempts to find a connection to a remote device. The finder will try to @@ -27,9 +26,6 @@ class ConnectionFinder { typedef base::Callback connection)> ConnectionCallback; virtual void Find(const ConnectionCallback& connection_callback) = 0; - - private: - DISALLOW_COPY_AND_ASSIGN(ConnectionFinder); }; } // namespace proximity_auth diff --git a/components/proximity_auth/connection_observer.h b/components/proximity_auth/connection_observer.h index 89bf974..27cfb35 100644 --- a/components/proximity_auth/connection_observer.h +++ b/components/proximity_auth/connection_observer.h @@ -15,8 +15,8 @@ class WireMessage; class ConnectionObserver { public: // Called when the |connection|'s status changes from |old_status| to - // |new_status|. - virtual void OnConnectionStatusChanged(const Connection& connection, + // |new_status|. The |connectoin| is guaranteed to be non-null. + virtual void OnConnectionStatusChanged(Connection* connection, Connection::Status old_status, Connection::Status new_status) {} diff --git a/components/proximity_auth/connection_unittest.cc b/components/proximity_auth/connection_unittest.cc index 8fb1593..3fb5f50 100644 --- a/components/proximity_auth/connection_unittest.cc +++ b/components/proximity_auth/connection_unittest.cc @@ -59,7 +59,7 @@ class MockConnectionObserver : public ConnectionObserver { virtual ~MockConnectionObserver() {} MOCK_METHOD3(OnConnectionStatusChanged, - void(const Connection& connection, + void(Connection* connection, Connection::Status old_status, Connection::Status new_status)); MOCK_METHOD2(OnMessageReceived, @@ -143,10 +143,9 @@ TEST(ProximityAuthConnectionTest, SetStatus_NotifiesObserversOfStatusChange) { StrictMock observer; connection.AddObserver(&observer); - EXPECT_CALL( - observer, - OnConnectionStatusChanged( - Ref(connection), Connection::DISCONNECTED, Connection::CONNECTED)); + EXPECT_CALL(observer, + OnConnectionStatusChanged(&connection, Connection::DISCONNECTED, + Connection::CONNECTED)); connection.SetStatus(Connection::CONNECTED); } diff --git a/components/proximity_auth/throttled_bluetooth_connection_finder.cc b/components/proximity_auth/throttled_bluetooth_connection_finder.cc new file mode 100644 index 0000000..252a47f --- /dev/null +++ b/components/proximity_auth/throttled_bluetooth_connection_finder.cc @@ -0,0 +1,55 @@ +// Copyright 2015 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. + +#include "components/proximity_auth/throttled_bluetooth_connection_finder.h" + +#include "base/bind.h" +#include "base/location.h" +#include "base/task_runner.h" +#include "base/time/time.h" +#include "components/proximity_auth/bluetooth_connection_finder.h" +#include "components/proximity_auth/bluetooth_throttler.h" + +namespace proximity_auth { + +ThrottledBluetoothConnectionFinder::ThrottledBluetoothConnectionFinder( + scoped_ptr connection_finder, + scoped_refptr task_runner, + BluetoothThrottler* throttler) + : connection_finder_(connection_finder.Pass()), + task_runner_(task_runner), + throttler_(throttler), + weak_ptr_factory_(this) { +} + +ThrottledBluetoothConnectionFinder::~ThrottledBluetoothConnectionFinder() { +} + +void ThrottledBluetoothConnectionFinder::Find( + const ConnectionCallback& connection_callback) { + const base::TimeDelta delay = throttler_->GetDelay(); + + // Wait, if needed. + if (delay != base::TimeDelta()) { + task_runner_->PostDelayedTask( + FROM_HERE, + base::Bind(&ThrottledBluetoothConnectionFinder::Find, + weak_ptr_factory_.GetWeakPtr(), connection_callback), + delay); + return; + } + + connection_finder_->Find( + base::Bind(&ThrottledBluetoothConnectionFinder::OnConnection, + weak_ptr_factory_.GetWeakPtr(), connection_callback)); +} + +void ThrottledBluetoothConnectionFinder::OnConnection( + const ConnectionCallback& connection_callback, + scoped_ptr connection) { + throttler_->OnConnection(connection.get()); + connection_callback.Run(connection.Pass()); +} + +} // namespace proximity_auth diff --git a/components/proximity_auth/throttled_bluetooth_connection_finder.h b/components/proximity_auth/throttled_bluetooth_connection_finder.h new file mode 100644 index 0000000..c107bb3 --- /dev/null +++ b/components/proximity_auth/throttled_bluetooth_connection_finder.h @@ -0,0 +1,60 @@ +// Copyright 2015 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. + +#ifndef COMPONENTS_PROXIMITY_AUTH_THROTTLED_BLUETOOTH_CONNECTION_FINDER_H +#define COMPONENTS_PROXIMITY_AUTH_THROTTLED_BLUETOOTH_CONNECTION_FINDER_H + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "components/proximity_auth/connection_finder.h" + +namespace base { +class TaskRunner; +} + +namespace proximity_auth { + +class BluetoothConnectionFinder; +class BluetoothThrottler; +class Connection; + +// A Bluetooth connection finder that delays Find() requests according to the +// throttler's cooldown period. +class ThrottledBluetoothConnectionFinder : public ConnectionFinder { + public: + // Note: The |throttler| is not owned, and must outlive |this| instance. + ThrottledBluetoothConnectionFinder( + scoped_ptr connection_finder, + scoped_refptr task_runner, + BluetoothThrottler* throttler); + ~ThrottledBluetoothConnectionFinder() override; + + // ConnectionFinder: + void Find(const ConnectionCallback& connection_callback) override; + + private: + // Callback to be called when a connection is found. + void OnConnection(const ConnectionCallback& connection_callback, + scoped_ptr connection); + + // The underlying connection finder. + scoped_ptr connection_finder_; + + // The task runner used for posting delayed messages. + scoped_refptr task_runner_; + + // The throttler managing this connection finder. The throttler is not owned, + // and must outlive |this| instance. + BluetoothThrottler* throttler_; + + base::WeakPtrFactory weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(ThrottledBluetoothConnectionFinder); +}; + +} // namespace proximity_auth + +#endif // COMPONENTS_PROXIMITY_AUTH_THROTTLED_BLUETOOTH_CONNECTION_FINDER_H diff --git a/components/proximity_auth/throttled_bluetooth_connection_finder_unittest.cc b/components/proximity_auth/throttled_bluetooth_connection_finder_unittest.cc new file mode 100644 index 0000000..cf2ca7b --- /dev/null +++ b/components/proximity_auth/throttled_bluetooth_connection_finder_unittest.cc @@ -0,0 +1,133 @@ +// Copyright 2015 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. + +#include "components/proximity_auth/throttled_bluetooth_connection_finder.h" + +#include "base/bind.h" +#include "base/test/test_simple_task_runner.h" +#include "base/time/time.h" +#include "components/proximity_auth/bluetooth_connection_finder.h" +#include "components/proximity_auth/bluetooth_throttler.h" +#include "components/proximity_auth/connection.h" +#include "components/proximity_auth/remote_device.h" +#include "components/proximity_auth/wire_message.h" +#include "device/bluetooth/bluetooth_uuid.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::NiceMock; +using testing::Return; +using testing::_; + +namespace proximity_auth { +namespace { + +const int kPollingIntervalSeconds = 7; +const char kUuid[] = "DEADBEEF-CAFE-FEED-FOOD-D15EA5EBEEF"; + +// A callback that stores a found |connection| into |out|. +void SaveConnection(scoped_ptr* out, + scoped_ptr connection) { + *out = connection.Pass(); +} + +class StubConnection : public Connection { + public: + StubConnection() : Connection(RemoteDevice()) {} + ~StubConnection() override {} + + void Connect() override {} + void Disconnect() override {} + void SendMessageImpl(scoped_ptr message) override {} + + private: + DISALLOW_COPY_AND_ASSIGN(StubConnection); +}; + +class MockBluetoothThrottler : public BluetoothThrottler { + public: + MockBluetoothThrottler() {} + ~MockBluetoothThrottler() override {} + + MOCK_CONST_METHOD0(GetDelay, base::TimeDelta()); + MOCK_METHOD1(OnConnection, void(Connection* connection)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockBluetoothThrottler); +}; + +class FakeBluetoothConnectionFinder : public BluetoothConnectionFinder { + public: + FakeBluetoothConnectionFinder() + : BluetoothConnectionFinder( + RemoteDevice(), + device::BluetoothUUID(kUuid), + base::TimeDelta::FromSeconds(kPollingIntervalSeconds)) {} + ~FakeBluetoothConnectionFinder() override {} + + void Find(const ConnectionCallback& connection_callback) override { + connection_callback.Run(make_scoped_ptr(new StubConnection)); + } + + private: + DISALLOW_COPY_AND_ASSIGN(FakeBluetoothConnectionFinder); +}; + +} // namespace + +class ProximityAuthThrottledBluetoothConnectionFinderTest + : public testing::Test { + public: + ProximityAuthThrottledBluetoothConnectionFinderTest() + : task_runner_(new base::TestSimpleTaskRunner) {} + + protected: + scoped_refptr task_runner_; + NiceMock throttler_; +}; + +TEST_F(ProximityAuthThrottledBluetoothConnectionFinderTest, + Find_ExecutesImmediatelyWhenUnthrottled) { + ON_CALL(throttler_, GetDelay()).WillByDefault(Return(base::TimeDelta())); + + ThrottledBluetoothConnectionFinder connection_finder( + make_scoped_ptr(new FakeBluetoothConnectionFinder), task_runner_, + &throttler_); + scoped_ptr connection; + connection_finder.Find(base::Bind(&SaveConnection, &connection)); + EXPECT_TRUE(connection); +} + +TEST_F(ProximityAuthThrottledBluetoothConnectionFinderTest, + Find_ExecutesAfterADelayWhenThrottled) { + ON_CALL(throttler_, GetDelay()) + .WillByDefault(Return(base::TimeDelta::FromSeconds(1))); + + ThrottledBluetoothConnectionFinder connection_finder( + make_scoped_ptr(new FakeBluetoothConnectionFinder), task_runner_, + &throttler_); + scoped_ptr connection; + connection_finder.Find(base::Bind(&SaveConnection, &connection)); + EXPECT_FALSE(connection); + + // The connection should be found once the throttling period has elapsed. + ON_CALL(throttler_, GetDelay()).WillByDefault(Return(base::TimeDelta())); + task_runner_->RunUntilIdle(); + EXPECT_TRUE(connection); +} + +TEST_F(ProximityAuthThrottledBluetoothConnectionFinderTest, + OnConnection_ForwardsNotificationToThrottler) { + ON_CALL(throttler_, GetDelay()).WillByDefault(Return(base::TimeDelta())); + + ThrottledBluetoothConnectionFinder connection_finder( + make_scoped_ptr(new FakeBluetoothConnectionFinder), task_runner_, + &throttler_); + scoped_ptr connection; + EXPECT_CALL(throttler_, OnConnection(_)); + connection_finder.Find(base::Bind(&SaveConnection, &connection)); + EXPECT_TRUE(connection); +} + +} // namespace proximity_auth -- cgit v1.1