1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/proximity_auth/ble/bluetooth_low_energy_connection_finder.h"
10 #include "base/memory/ref_counted.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/run_loop.h"
14 #include "base/time/time.h"
15 #include "components/proximity_auth/ble/bluetooth_low_energy_device_whitelist.h"
16 #include "components/proximity_auth/connection.h"
17 #include "components/proximity_auth/remote_device.h"
18 #include "components/proximity_auth/wire_message.h"
19 #include "device/bluetooth/bluetooth_adapter_factory.h"
20 #include "device/bluetooth/bluetooth_uuid.h"
21 #include "device/bluetooth/test/mock_bluetooth_adapter.h"
22 #include "device/bluetooth/test/mock_bluetooth_device.h"
23 #include "device/bluetooth/test/mock_bluetooth_discovery_session.h"
24 #include "device/bluetooth/test/mock_bluetooth_gatt_connection.h"
25 #include "testing/gmock/include/gmock/gmock.h"
26 #include "testing/gtest/include/gtest/gtest.h"
29 using testing::AtLeast
;
30 using testing::NiceMock
;
31 using testing::Return
;
32 using testing::StrictMock
;
33 using testing::SaveArg
;
35 namespace proximity_auth
{
38 const char kDeviceName
[] = "Device name";
39 const char kPublicKey
[] = "Public key";
40 const char kBluetoothAddress
[] = "11:22:33:44:55:66";
41 const char kPersistentSymmetricKey
[] = "PSK";
43 const char kServiceUUID
[] = "DEADBEEF-CAFE-FEED-FOOD-D15EA5EBEEEF";
44 const char kToPeripheralCharUUID
[] = "FBAE09F2-0482-11E5-8418-1697F925EC7B";
45 const char kFromPeripheralCharUUID
[] = "5539ED10-0483-11E5-8418-1697F925EC7B";
47 const char kOtherUUID
[] = "AAAAAAAA-AAAA-AAAA-AAAA-D15EA5EBEEEF";
48 const char kOtherBluetoothAddress
[] = "00:00:00:00:00:00";
50 const int kMaxNumberOfAttempts
= 2;
52 class MockConnection
: public Connection
{
54 MockConnection() : Connection(CreateRemoteDevice()) {}
55 ~MockConnection() override
{}
57 MOCK_METHOD0(Connect
, void());
59 using Connection::SetStatus
;
62 void Disconnect() override
{}
63 void SendMessageImpl(scoped_ptr
<WireMessage
> message
) override
{}
65 RemoteDevice
CreateRemoteDevice() {
66 return RemoteDevice(kDeviceName
, kPublicKey
, kBluetoothAddress
,
67 kPersistentSymmetricKey
);
70 DISALLOW_COPY_AND_ASSIGN(MockConnection
);
73 class MockBluetoothLowEnergyDeviceWhitelist
74 : public BluetoothLowEnergyDeviceWhitelist
{
76 MockBluetoothLowEnergyDeviceWhitelist()
77 : BluetoothLowEnergyDeviceWhitelist(nullptr) {}
78 ~MockBluetoothLowEnergyDeviceWhitelist() override
{}
80 MOCK_CONST_METHOD1(HasDeviceWithAddress
, bool(const std::string
&));
83 class MockBluetoothLowEnergyConnectionFinder
84 : public BluetoothLowEnergyConnectionFinder
{
86 MockBluetoothLowEnergyConnectionFinder(
87 const BluetoothLowEnergyDeviceWhitelist
* device_whitelist
)
88 : BluetoothLowEnergyConnectionFinder(kServiceUUID
,
89 kToPeripheralCharUUID
,
90 kFromPeripheralCharUUID
,
93 kMaxNumberOfAttempts
) {}
95 ~MockBluetoothLowEnergyConnectionFinder() override
{}
97 // Mock methods don't support return type scoped_ptr<>. This is a possible
98 // workaround: mock a proxy method to be called by the target overrided method
99 // (CreateConnection).
100 MOCK_METHOD0(CreateConnectionProxy
, Connection
*());
102 // Creates a mock connection and sets an expectation that the mock connection
103 // finder's CreateConnection() method will be called and will return the
104 // created connection. Returns a reference to the created connection.
105 // NOTE: The returned connection's lifetime is managed by the connection
107 MockConnection
* ExpectCreateConnection() {
108 scoped_ptr
<MockConnection
> connection(new NiceMock
<MockConnection
>());
109 MockConnection
* connection_alias
= connection
.get();
110 EXPECT_CALL(*this, CreateConnectionProxy())
111 .WillOnce(Return(connection
.release()));
112 return connection_alias
;
115 MOCK_METHOD0(CloseGattConnectionProxy
, void(void));
118 scoped_ptr
<Connection
> CreateConnection(
119 const std::string
& device_address
) override
{
120 return make_scoped_ptr(CreateConnectionProxy());
124 DISALLOW_COPY_AND_ASSIGN(MockBluetoothLowEnergyConnectionFinder
);
129 class ProximityAuthBluetoothLowEnergyConnectionFinderTest
130 : public testing::Test
{
132 ProximityAuthBluetoothLowEnergyConnectionFinderTest()
133 : adapter_(new NiceMock
<device::MockBluetoothAdapter
>),
134 connection_callback_(
135 base::Bind(&ProximityAuthBluetoothLowEnergyConnectionFinderTest::
137 base::Unretained(this))),
138 device_(new NiceMock
<device::MockBluetoothDevice
>(adapter_
.get(),
144 device_whitelist_(new MockBluetoothLowEnergyDeviceWhitelist()),
145 last_discovery_session_alias_(nullptr) {
146 device::BluetoothAdapterFactory::SetAdapterForTesting(adapter_
);
148 std::vector
<const device::BluetoothDevice
*> devices
;
149 ON_CALL(*adapter_
, GetDevices()).WillByDefault(Return(devices
));
151 ON_CALL(*adapter_
, IsPresent()).WillByDefault(Return(true));
152 ON_CALL(*adapter_
, IsPowered()).WillByDefault(Return(true));
154 ON_CALL(*device_whitelist_
, HasDeviceWithAddress(_
))
155 .WillByDefault(Return(false));
158 void OnConnectionFound(scoped_ptr
<Connection
> connection
) {
159 last_found_connection_
= connection
.Pass();
162 void FindAndExpectStartDiscovery(
163 BluetoothLowEnergyConnectionFinder
& connection_finder
) {
164 device::BluetoothAdapter::DiscoverySessionCallback discovery_callback
;
165 scoped_ptr
<device::MockBluetoothDiscoverySession
> discovery_session(
166 new NiceMock
<device::MockBluetoothDiscoverySession
>());
167 last_discovery_session_alias_
= discovery_session
.get();
169 // Starting a discovery session. StartDiscoveryWithFilterRaw is a proxy for
170 // StartDiscoveryWithFilter.
171 EXPECT_CALL(*adapter_
, StartDiscoverySessionWithFilterRaw(_
, _
, _
))
172 .WillOnce(SaveArg
<1>(&discovery_callback
));
173 EXPECT_CALL(*adapter_
, AddObserver(_
));
174 ON_CALL(*last_discovery_session_alias_
, IsActive())
175 .WillByDefault(Return(true));
176 connection_finder
.Find(connection_callback_
);
177 ASSERT_FALSE(discovery_callback
.is_null());
178 discovery_callback
.Run(discovery_session
.Pass());
181 void ExpectStopDiscoveryAndRemoveObserver() {
182 EXPECT_CALL(*last_discovery_session_alias_
, Stop(_
, _
)).Times(AtLeast(1));
183 EXPECT_CALL(*adapter_
, RemoveObserver(_
)).Times(AtLeast(1));
186 // Prepare |device_| with |uuid|.
187 void PrepareDevice(const std::string
& uuid
) {
188 std::vector
<device::BluetoothUUID
> uuids
;
189 uuids
.push_back(device::BluetoothUUID(uuid
));
190 ON_CALL(*device_
, GetUUIDs()).WillByDefault(Return(uuids
));
193 // Prepare expectations to add/change a right device.
194 void PrepareForNewRightDevice(const std::string
& uuid
) {
196 ON_CALL(*device_
, IsPaired()).WillByDefault(Return(true));
199 // Prepare expectations to add/change a wrong device.
200 void PrepareForNewWrongDevice(const std::string
& uuid
) {
202 ON_CALL(*device_
, IsPaired()).WillByDefault(Return(true));
205 scoped_refptr
<device::MockBluetoothAdapter
> adapter_
;
206 ConnectionFinder::ConnectionCallback connection_callback_
;
207 scoped_ptr
<device::MockBluetoothDevice
> device_
;
208 scoped_ptr
<Connection
> last_found_connection_
;
209 scoped_ptr
<MockBluetoothLowEnergyDeviceWhitelist
> device_whitelist_
;
210 device::MockBluetoothDiscoverySession
* last_discovery_session_alias_
;
213 base::MessageLoop message_loop_
;
216 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
217 ConstructAndDestroyDoesntCrash
) {
218 // Destroying a BluetoothConnectionFinder for which Find() has not been called
220 BluetoothLowEnergyConnectionFinder
connection_finder(
221 kServiceUUID
, kToPeripheralCharUUID
, kFromPeripheralCharUUID
,
222 device_whitelist_
.get(), nullptr, kMaxNumberOfAttempts
);
225 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
226 Find_StartsDiscoverySession
) {
227 BluetoothLowEnergyConnectionFinder
connection_finder(
228 kServiceUUID
, kToPeripheralCharUUID
, kFromPeripheralCharUUID
,
229 device_whitelist_
.get(), nullptr, kMaxNumberOfAttempts
);
231 EXPECT_CALL(*adapter_
, StartDiscoverySessionWithFilterRaw(_
, _
, _
));
232 EXPECT_CALL(*adapter_
, AddObserver(_
));
233 connection_finder
.Find(connection_callback_
);
236 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
237 Find_StopsDiscoverySessionBeforeDestroying
) {
238 BluetoothLowEnergyConnectionFinder
connection_finder(
239 kServiceUUID
, kToPeripheralCharUUID
, kFromPeripheralCharUUID
,
240 device_whitelist_
.get(), nullptr, kMaxNumberOfAttempts
);
242 device::BluetoothAdapter::DiscoverySessionCallback discovery_callback
;
243 scoped_ptr
<device::MockBluetoothDiscoverySession
> discovery_session(
244 new NiceMock
<device::MockBluetoothDiscoverySession
>());
245 device::MockBluetoothDiscoverySession
* discovery_session_alias
=
246 discovery_session
.get();
248 EXPECT_CALL(*adapter_
, StartDiscoverySessionWithFilterRaw(_
, _
, _
))
249 .WillOnce(SaveArg
<1>(&discovery_callback
));
250 ON_CALL(*discovery_session_alias
, IsActive()).WillByDefault(Return(true));
251 EXPECT_CALL(*adapter_
, AddObserver(_
));
252 connection_finder
.Find(connection_callback_
);
254 EXPECT_CALL(*discovery_session_alias
, Stop(_
, _
));
255 ASSERT_FALSE(discovery_callback
.is_null());
256 discovery_callback
.Run(discovery_session
.Pass());
258 EXPECT_CALL(*adapter_
, RemoveObserver(_
));
261 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
262 Find_CreatesConnectionWhenWhitelistedDeviceIsAdded
) {
263 StrictMock
<MockBluetoothLowEnergyConnectionFinder
> connection_finder(
264 device_whitelist_
.get());
265 FindAndExpectStartDiscovery(connection_finder
);
266 ExpectStopDiscoveryAndRemoveObserver();
268 std::vector
<device::BluetoothUUID
> uuids
;
269 ON_CALL(*device_
, GetUUIDs()).WillByDefault(Return(uuids
));
270 ON_CALL(*device_
, IsPaired()).WillByDefault(Return(true));
271 ON_CALL(*device_whitelist_
, HasDeviceWithAddress(_
))
272 .WillByDefault(Return(true));
274 connection_finder
.ExpectCreateConnection();
275 connection_finder
.DeviceAdded(adapter_
.get(), device_
.get());
278 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
279 Find_CreatesConnectionWhenRightDeviceIsAdded
) {
280 StrictMock
<MockBluetoothLowEnergyConnectionFinder
> connection_finder(
281 device_whitelist_
.get());
283 FindAndExpectStartDiscovery(connection_finder
);
284 ExpectStopDiscoveryAndRemoveObserver();
286 PrepareForNewRightDevice(kServiceUUID
);
287 connection_finder
.ExpectCreateConnection();
288 connection_finder
.DeviceAdded(adapter_
.get(), device_
.get());
291 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
292 Find_DoesntCreateConnectionWhenWrongDeviceIsAdded
) {
293 StrictMock
<MockBluetoothLowEnergyConnectionFinder
> connection_finder(
294 device_whitelist_
.get());
295 FindAndExpectStartDiscovery(connection_finder
);
296 ExpectStopDiscoveryAndRemoveObserver();
298 PrepareForNewWrongDevice(kOtherUUID
);
299 EXPECT_CALL(connection_finder
, CreateConnectionProxy()).Times(0);
300 connection_finder
.DeviceAdded(adapter_
.get(), device_
.get());
303 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
304 Find_CreatesConnectionWhenRightDeviceIsChanged
) {
305 StrictMock
<MockBluetoothLowEnergyConnectionFinder
> connection_finder(
306 device_whitelist_
.get());
308 FindAndExpectStartDiscovery(connection_finder
);
309 ExpectStopDiscoveryAndRemoveObserver();
311 PrepareForNewRightDevice(kServiceUUID
);
312 connection_finder
.ExpectCreateConnection();
313 connection_finder
.DeviceChanged(adapter_
.get(), device_
.get());
316 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
317 Find_DoesntCreateConnectionWhenWrongDeviceIsChanged
) {
318 StrictMock
<MockBluetoothLowEnergyConnectionFinder
> connection_finder(
319 device_whitelist_
.get());
321 FindAndExpectStartDiscovery(connection_finder
);
322 ExpectStopDiscoveryAndRemoveObserver();
324 PrepareForNewWrongDevice(kOtherUUID
);
325 EXPECT_CALL(connection_finder
, CreateConnectionProxy()).Times(0);
326 connection_finder
.DeviceChanged(adapter_
.get(), device_
.get());
329 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
330 Find_CreatesOnlyOneConnection
) {
331 StrictMock
<MockBluetoothLowEnergyConnectionFinder
> connection_finder(
332 device_whitelist_
.get());
333 FindAndExpectStartDiscovery(connection_finder
);
334 ExpectStopDiscoveryAndRemoveObserver();
336 // Prepare to add |device_|.
337 PrepareForNewRightDevice(kServiceUUID
);
339 // Prepare to add |other_device|.
340 NiceMock
<device::MockBluetoothDevice
> other_device(
341 adapter_
.get(), 0, kDeviceName
, kOtherBluetoothAddress
, false, false);
342 std::vector
<device::BluetoothUUID
> uuids
;
343 uuids
.push_back(device::BluetoothUUID(kServiceUUID
));
344 ON_CALL(other_device
, IsPaired()).WillByDefault(Return(true));
345 ON_CALL(other_device
, GetUUIDs()).WillByDefault((Return(uuids
)));
347 // Only one connection should be created.
348 connection_finder
.ExpectCreateConnection();
351 connection_finder
.DeviceAdded(adapter_
.get(), device_
.get());
352 connection_finder
.DeviceAdded(adapter_
.get(), &other_device
);
355 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
356 Find_ConnectionSucceeds
) {
357 StrictMock
<MockBluetoothLowEnergyConnectionFinder
> connection_finder(
358 device_whitelist_
.get());
359 // Starting discovery.
360 FindAndExpectStartDiscovery(connection_finder
);
361 ExpectStopDiscoveryAndRemoveObserver();
363 // Finding and creating a connection to the right device.
364 MockConnection
* connection
= connection_finder
.ExpectCreateConnection();
365 PrepareForNewRightDevice(kServiceUUID
);
366 connection_finder
.DeviceAdded(adapter_
.get(), device_
.get());
368 // Creating a connection.
369 base::RunLoop run_loop
;
370 EXPECT_FALSE(last_found_connection_
);
371 connection
->SetStatus(Connection::IN_PROGRESS
);
372 connection
->SetStatus(Connection::CONNECTED
);
373 run_loop
.RunUntilIdle();
374 EXPECT_TRUE(last_found_connection_
);
377 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
378 Find_ConnectionFails_RestartDiscoveryAndConnectionSucceeds
) {
379 StrictMock
<MockBluetoothLowEnergyConnectionFinder
> connection_finder(
380 device_whitelist_
.get());
382 // Starting discovery.
383 FindAndExpectStartDiscovery(connection_finder
);
384 base::Closure stop_discovery_session_callback
;
385 EXPECT_CALL(*last_discovery_session_alias_
, Stop(_
, _
))
386 .WillOnce(SaveArg
<0>(&stop_discovery_session_callback
));
388 // Preparing to create a GATT connection to the right device.
389 PrepareForNewRightDevice(kServiceUUID
);
390 MockConnection
* connection
= connection_finder
.ExpectCreateConnection();
392 // Trying to create a connection.
393 connection_finder
.DeviceAdded(adapter_
.get(), device_
.get());
394 ASSERT_FALSE(last_found_connection_
);
395 connection
->SetStatus(Connection::IN_PROGRESS
);
397 // Stopping the discovery session.
398 ASSERT_FALSE(stop_discovery_session_callback
.is_null());
399 stop_discovery_session_callback
.Run();
401 // Preparing to restart the discovery session.
402 device::BluetoothAdapter::DiscoverySessionCallback discovery_callback
;
403 std::vector
<const device::BluetoothDevice
*> devices
;
404 ON_CALL(*adapter_
, GetDevices()).WillByDefault(Return(devices
));
405 EXPECT_CALL(*adapter_
, StartDiscoverySessionWithFilterRaw(_
, _
, _
))
406 .WillOnce(SaveArg
<1>(&discovery_callback
));
409 connection
->SetStatus(Connection::DISCONNECTED
);
411 // Restarting the discovery session.
412 scoped_ptr
<device::MockBluetoothDiscoverySession
> discovery_session(
413 new NiceMock
<device::MockBluetoothDiscoverySession
>());
414 last_discovery_session_alias_
= discovery_session
.get();
415 ON_CALL(*last_discovery_session_alias_
, IsActive())
416 .WillByDefault(Return(true));
417 ASSERT_FALSE(discovery_callback
.is_null());
418 discovery_callback
.Run(discovery_session
.Pass());
420 // Preparing to create a GATT connection to the right device.
421 PrepareForNewRightDevice(kServiceUUID
);
422 connection
= connection_finder
.ExpectCreateConnection();
424 // Trying to create a connection.
425 connection_finder
.DeviceAdded(adapter_
.get(), device_
.get());
426 EXPECT_CALL(*last_discovery_session_alias_
, Stop(_
, _
)).Times(AtLeast(1));
428 // Completing the connection.
429 base::RunLoop run_loop
;
430 EXPECT_FALSE(last_found_connection_
);
431 connection
->SetStatus(Connection::IN_PROGRESS
);
432 connection
->SetStatus(Connection::CONNECTED
);
433 run_loop
.RunUntilIdle();
434 EXPECT_TRUE(last_found_connection_
);
437 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
438 Find_AdapterRemoved_RestartDiscoveryAndConnectionSucceeds
) {
439 StrictMock
<MockBluetoothLowEnergyConnectionFinder
> connection_finder(
440 device_whitelist_
.get());
442 // Starting discovery.
443 FindAndExpectStartDiscovery(connection_finder
);
445 // Removing the adapter.
446 ON_CALL(*adapter_
, IsPresent()).WillByDefault(Return(false));
447 ON_CALL(*adapter_
, IsPowered()).WillByDefault(Return(false));
448 ON_CALL(*last_discovery_session_alias_
, IsActive())
449 .WillByDefault(Return(false));
450 connection_finder
.AdapterPoweredChanged(adapter_
.get(), false);
451 connection_finder
.AdapterPresentChanged(adapter_
.get(), false);
453 // Adding the adapter.
454 ON_CALL(*adapter_
, IsPresent()).WillByDefault(Return(true));
455 ON_CALL(*adapter_
, IsPowered()).WillByDefault(Return(true));
457 device::BluetoothAdapter::DiscoverySessionCallback discovery_callback
;
458 scoped_ptr
<device::MockBluetoothDiscoverySession
> discovery_session(
459 new NiceMock
<device::MockBluetoothDiscoverySession
>());
460 last_discovery_session_alias_
= discovery_session
.get();
462 // Restarting the discovery session.
463 EXPECT_CALL(*adapter_
, StartDiscoverySessionWithFilterRaw(_
, _
, _
))
464 .WillOnce(SaveArg
<1>(&discovery_callback
));
465 connection_finder
.AdapterPresentChanged(adapter_
.get(), true);
466 connection_finder
.AdapterPoweredChanged(adapter_
.get(), true);
467 ON_CALL(*last_discovery_session_alias_
, IsActive())
468 .WillByDefault(Return(true));
470 ASSERT_FALSE(discovery_callback
.is_null());
471 discovery_callback
.Run(discovery_session
.Pass());
473 // Preparing to create a GATT connection to the right device.
474 PrepareForNewRightDevice(kServiceUUID
);
475 MockConnection
* connection
= connection_finder
.ExpectCreateConnection();
477 // Trying to create a connection.
478 connection_finder
.DeviceAdded(adapter_
.get(), device_
.get());
479 EXPECT_CALL(*last_discovery_session_alias_
, Stop(_
, _
)).Times(AtLeast(1));
481 // Completing the connection.
482 base::RunLoop run_loop
;
483 ASSERT_FALSE(last_found_connection_
);
484 connection
->SetStatus(Connection::IN_PROGRESS
);
485 connection
->SetStatus(Connection::CONNECTED
);
486 run_loop
.RunUntilIdle();
487 EXPECT_TRUE(last_found_connection_
);
490 } // namespace proximity_auth