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"
8 #include "base/memory/ref_counted.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/run_loop.h"
12 #include "base/time/time.h"
13 #include "components/proximity_auth/connection.h"
14 #include "components/proximity_auth/remote_device.h"
15 #include "components/proximity_auth/wire_message.h"
16 #include "device/bluetooth/bluetooth_adapter_factory.h"
17 #include "device/bluetooth/bluetooth_uuid.h"
18 #include "device/bluetooth/test/mock_bluetooth_adapter.h"
19 #include "device/bluetooth/test/mock_bluetooth_device.h"
20 #include "device/bluetooth/test/mock_bluetooth_discovery_session.h"
21 #include "device/bluetooth/test/mock_bluetooth_gatt_connection.h"
22 #include "testing/gmock/include/gmock/gmock.h"
23 #include "testing/gtest/include/gtest/gtest.h"
26 using testing::AtLeast
;
27 using testing::NiceMock
;
28 using testing::Return
;
29 using testing::StrictMock
;
30 using testing::SaveArg
;
32 namespace proximity_auth
{
35 const char kDeviceName
[] = "Device name";
36 const char kBluetoothAddress
[] = "11:22:33:44:55:66";
37 const RemoteDevice kRemoteDevice
= {kDeviceName
, kBluetoothAddress
};
39 const char kServiceUUID
[] = "DEADBEEF-CAFE-FEED-FOOD-D15EA5EBEEEF";
40 const char kToPeripheralCharUUID
[] = "FBAE09F2-0482-11E5-8418-1697F925EC7B";
41 const char kFromPeripheralCharUUID
[] = "5539ED10-0483-11E5-8418-1697F925EC7B";
43 const char kOtherUUID
[] = "AAAAAAAA-AAAA-AAAA-AAAA-D15EA5EBEEEF";
44 const char kOtherBluetoothAddress
[] = "00:00:00:00:00:00";
46 const int kMaxNumberOfAttempts
= 2;
48 class MockConnection
: public Connection
{
50 MockConnection() : Connection(kRemoteDevice
) {}
51 ~MockConnection() override
{}
53 MOCK_METHOD0(Connect
, void());
55 using Connection::SetStatus
;
58 void Disconnect() override
{}
59 void SendMessageImpl(scoped_ptr
<WireMessage
> message
) override
{}
61 DISALLOW_COPY_AND_ASSIGN(MockConnection
);
64 class MockBluetoothLowEnergyConnectionFinder
65 : public BluetoothLowEnergyConnectionFinder
{
67 MockBluetoothLowEnergyConnectionFinder()
68 : BluetoothLowEnergyConnectionFinder(kServiceUUID
,
69 kToPeripheralCharUUID
,
70 kFromPeripheralCharUUID
,
71 kMaxNumberOfAttempts
) {
72 SetDelayForTesting(base::TimeDelta());
75 ~MockBluetoothLowEnergyConnectionFinder() override
{}
77 // Mock methods don't support return type scoped_ptr<>. This is a possible
78 // workaround: mock a proxy method to be called by the target overrided method
79 // (CreateConnection).
80 MOCK_METHOD0(CreateConnectionProxy
, Connection
*());
82 // Creates a mock connection and sets an expectation that the mock connection
83 // finder's CreateConnection() method will be called and will return the
84 // created connection. Returns a reference to the created connection.
85 // NOTE: The returned connection's lifetime is managed by the connection
87 MockConnection
* ExpectCreateConnection() {
88 scoped_ptr
<MockConnection
> connection(new NiceMock
<MockConnection
>());
89 MockConnection
* connection_alias
= connection
.get();
90 EXPECT_CALL(*this, CreateConnectionProxy())
91 .WillOnce(Return(connection
.release()));
92 return connection_alias
;
95 MOCK_METHOD0(CloseGattConnectionProxy
, void(void));
98 scoped_ptr
<Connection
> CreateConnection(
99 scoped_ptr
<device::BluetoothGattConnection
> gatt_connection
) override
{
100 return make_scoped_ptr(CreateConnectionProxy());
103 void CloseGattConnection(
104 scoped_ptr
<device::BluetoothGattConnection
> gatt_connection
) override
{
105 BluetoothLowEnergyConnectionFinder::CloseGattConnection(
106 gatt_connection
.Pass());
107 CloseGattConnectionProxy();
111 DISALLOW_COPY_AND_ASSIGN(MockBluetoothLowEnergyConnectionFinder
);
116 class ProximityAuthBluetoothLowEnergyConnectionFinderTest
117 : public testing::Test
{
119 ProximityAuthBluetoothLowEnergyConnectionFinderTest()
120 : adapter_(new NiceMock
<device::MockBluetoothAdapter
>),
121 connection_callback_(
122 base::Bind(&ProximityAuthBluetoothLowEnergyConnectionFinderTest::
124 base::Unretained(this))),
125 device_(new NiceMock
<device::MockBluetoothDevice
>(adapter_
.get(),
131 last_discovery_session_alias_(nullptr) {
132 device::BluetoothAdapterFactory::SetAdapterForTesting(adapter_
);
134 std::vector
<const device::BluetoothDevice
*> devices
;
135 ON_CALL(*adapter_
, GetDevices()).WillByDefault(Return(devices
));
137 ON_CALL(*adapter_
, IsPresent()).WillByDefault(Return(true));
138 ON_CALL(*adapter_
, IsPowered()).WillByDefault(Return(true));
141 void OnConnectionFound(scoped_ptr
<Connection
> connection
) {
142 last_found_connection_
= connection
.Pass();
145 void FindAndExpectStartDiscovery(
146 BluetoothLowEnergyConnectionFinder
& connection_finder
) {
147 device::BluetoothAdapter::DiscoverySessionCallback discovery_callback
;
148 scoped_ptr
<device::MockBluetoothDiscoverySession
> discovery_session(
149 new NiceMock
<device::MockBluetoothDiscoverySession
>());
150 last_discovery_session_alias_
= discovery_session
.get();
152 // Starting a discovery session. StartDiscoveryWithFilterRaw is a proxy for
153 // StartDiscoveryWithFilter.
154 EXPECT_CALL(*adapter_
, StartDiscoverySessionWithFilterRaw(_
, _
, _
))
155 .WillOnce(SaveArg
<1>(&discovery_callback
));
156 EXPECT_CALL(*adapter_
, AddObserver(_
));
157 ON_CALL(*last_discovery_session_alias_
, IsActive())
158 .WillByDefault(Return(true));
159 connection_finder
.Find(connection_callback_
);
160 ASSERT_FALSE(discovery_callback
.is_null());
161 discovery_callback
.Run(discovery_session
.Pass());
164 void ExpectStopDiscoveryAndRemoveObserver() {
165 EXPECT_CALL(*last_discovery_session_alias_
, Stop(_
, _
)).Times(AtLeast(1));
166 EXPECT_CALL(*adapter_
, RemoveObserver(_
)).Times(AtLeast(1));
169 // Prepare |device_| with |uuid|.
170 void PrepareDevice(const std::string
& uuid
) {
171 std::vector
<device::BluetoothUUID
> uuids
;
172 uuids
.push_back(device::BluetoothUUID(uuid
));
173 ON_CALL(*device_
, GetUUIDs()).WillByDefault(Return(uuids
));
176 // Prepare expectations to add/change a right device.
177 void PrepareForNewRightDevice(
178 const std::string
& uuid
,
179 device::BluetoothDevice::GattConnectionCallback
& callback
) {
181 ON_CALL(*device_
, IsPaired()).WillByDefault(Return(true));
182 EXPECT_CALL(*device_
, CreateGattConnection(_
, _
))
183 .WillOnce(SaveArg
<0>(&callback
));
186 // Prepare expectations to add/change a wrong device.
187 void PrepareForNewWrongDevice(const std::string
& uuid
) {
189 ON_CALL(*device_
, IsPaired()).WillByDefault(Return(true));
190 EXPECT_CALL(*device_
, CreateGattConnection(_
, _
)).Times(0);
193 scoped_refptr
<device::MockBluetoothAdapter
> adapter_
;
194 ConnectionFinder::ConnectionCallback connection_callback_
;
195 scoped_ptr
<device::MockBluetoothDevice
> device_
;
196 scoped_ptr
<Connection
> last_found_connection_
;
197 device::MockBluetoothDiscoverySession
* last_discovery_session_alias_
;
200 base::MessageLoop message_loop_
;
203 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
204 ConstructAndDestroyDoesntCrash
) {
205 // Destroying a BluetoothConnectionFinder for which Find() has not been called
207 BluetoothLowEnergyConnectionFinder
connection_finder(
208 kServiceUUID
, kToPeripheralCharUUID
, kFromPeripheralCharUUID
,
209 kMaxNumberOfAttempts
);
212 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
213 Find_StartsDiscoverySession
) {
214 BluetoothLowEnergyConnectionFinder
connection_finder(
215 kServiceUUID
, kToPeripheralCharUUID
, kFromPeripheralCharUUID
,
216 kMaxNumberOfAttempts
);
218 EXPECT_CALL(*adapter_
, StartDiscoverySessionWithFilterRaw(_
, _
, _
));
219 EXPECT_CALL(*adapter_
, AddObserver(_
));
220 connection_finder
.Find(connection_callback_
);
223 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
224 Find_StopsDiscoverySessionBeforeDestroying
) {
225 BluetoothLowEnergyConnectionFinder
connection_finder(
226 kServiceUUID
, kToPeripheralCharUUID
, kFromPeripheralCharUUID
,
227 kMaxNumberOfAttempts
);
229 device::BluetoothAdapter::DiscoverySessionCallback discovery_callback
;
230 scoped_ptr
<device::MockBluetoothDiscoverySession
> discovery_session(
231 new NiceMock
<device::MockBluetoothDiscoverySession
>());
232 device::MockBluetoothDiscoverySession
* discovery_session_alias
=
233 discovery_session
.get();
235 EXPECT_CALL(*adapter_
, StartDiscoverySessionWithFilterRaw(_
, _
, _
))
236 .WillOnce(SaveArg
<1>(&discovery_callback
));
237 ON_CALL(*discovery_session_alias
, IsActive()).WillByDefault(Return(true));
238 EXPECT_CALL(*adapter_
, AddObserver(_
));
239 connection_finder
.Find(connection_callback_
);
241 EXPECT_CALL(*discovery_session_alias
, Stop(_
, _
));
242 ASSERT_FALSE(discovery_callback
.is_null());
243 discovery_callback
.Run(discovery_session
.Pass());
245 EXPECT_CALL(*adapter_
, RemoveObserver(_
));
248 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
249 Find_CreatesGattConnectionWhenRightDeviceIsAdded
) {
250 BluetoothLowEnergyConnectionFinder
connection_finder(
251 kServiceUUID
, kToPeripheralCharUUID
, kFromPeripheralCharUUID
,
252 kMaxNumberOfAttempts
);
253 device::BluetoothDevice::GattConnectionCallback gatt_connection_callback
;
254 FindAndExpectStartDiscovery(connection_finder
);
255 ExpectStopDiscoveryAndRemoveObserver();
257 PrepareForNewRightDevice(kServiceUUID
, gatt_connection_callback
);
258 connection_finder
.DeviceAdded(adapter_
.get(), device_
.get());
259 ASSERT_FALSE(gatt_connection_callback
.is_null());
262 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
263 Find_DoesntCreateGattConnectionWhenWrongDeviceIsAdded
) {
264 BluetoothLowEnergyConnectionFinder
connection_finder(
265 kServiceUUID
, kToPeripheralCharUUID
, kFromPeripheralCharUUID
,
266 kMaxNumberOfAttempts
);
267 FindAndExpectStartDiscovery(connection_finder
);
268 ExpectStopDiscoveryAndRemoveObserver();
270 PrepareForNewWrongDevice(kOtherUUID
);
271 connection_finder
.DeviceAdded(adapter_
.get(), device_
.get());
274 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
275 Find_CreatesGattConnectionWhenRightDeviceIsChanged
) {
276 BluetoothLowEnergyConnectionFinder
connection_finder(
277 kServiceUUID
, kToPeripheralCharUUID
, kFromPeripheralCharUUID
,
278 kMaxNumberOfAttempts
);
279 device::BluetoothDevice::GattConnectionCallback gatt_connection_callback
;
280 FindAndExpectStartDiscovery(connection_finder
);
281 ExpectStopDiscoveryAndRemoveObserver();
283 PrepareForNewRightDevice(kServiceUUID
, gatt_connection_callback
);
284 connection_finder
.DeviceChanged(adapter_
.get(), device_
.get());
285 ASSERT_FALSE(gatt_connection_callback
.is_null());
288 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
289 Find_DoesntCreateGattConnectionWhenWrongDeviceIsChanged
) {
290 BluetoothLowEnergyConnectionFinder
connection_finder(
291 kServiceUUID
, kToPeripheralCharUUID
, kFromPeripheralCharUUID
,
292 kMaxNumberOfAttempts
);
293 FindAndExpectStartDiscovery(connection_finder
);
294 ExpectStopDiscoveryAndRemoveObserver();
296 PrepareForNewWrongDevice(kOtherUUID
);
297 connection_finder
.DeviceChanged(adapter_
.get(), device_
.get());
300 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
301 Find_CreatesTwoGattConnections
) {
302 StrictMock
<MockBluetoothLowEnergyConnectionFinder
> connection_finder
;
303 FindAndExpectStartDiscovery(connection_finder
);
304 ExpectStopDiscoveryAndRemoveObserver();
305 connection_finder
.ExpectCreateConnection();
307 // Prepare to add |device_|.
308 device::BluetoothDevice::GattConnectionCallback gatt_connection_callback
;
309 PrepareForNewRightDevice(kServiceUUID
, gatt_connection_callback
);
311 // Prepare to add |other_device|.
312 device::BluetoothDevice::GattConnectionCallback
313 other_gatt_connection_callback
;
314 NiceMock
<device::MockBluetoothDevice
> other_device(
315 adapter_
.get(), 0, kDeviceName
, kOtherBluetoothAddress
, false, false);
316 std::vector
<device::BluetoothUUID
> uuids
;
317 uuids
.push_back(device::BluetoothUUID(kServiceUUID
));
318 ON_CALL(other_device
, IsPaired()).WillByDefault(Return(true));
319 ON_CALL(other_device
, GetUUIDs()).WillByDefault((Return(uuids
)));
320 EXPECT_CALL(other_device
, CreateGattConnection(_
, _
))
321 .WillOnce(SaveArg
<0>(&other_gatt_connection_callback
));
324 connection_finder
.DeviceAdded(adapter_
.get(), device_
.get());
325 connection_finder
.DeviceAdded(adapter_
.get(), &other_device
);
327 ASSERT_FALSE(gatt_connection_callback
.is_null());
328 ASSERT_FALSE(other_gatt_connection_callback
.is_null());
330 base::RunLoop run_loop
;
331 gatt_connection_callback
.Run(make_scoped_ptr(
332 new NiceMock
<device::MockBluetoothGattConnection
>(kBluetoothAddress
)));
333 run_loop
.RunUntilIdle();
335 // The second device should be forgetten.
336 EXPECT_CALL(connection_finder
, CloseGattConnectionProxy());
337 other_gatt_connection_callback
.Run(
338 make_scoped_ptr(new NiceMock
<device::MockBluetoothGattConnection
>(
339 kOtherBluetoothAddress
)));
342 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
343 Find_ConnectionSucceeds
) {
344 StrictMock
<MockBluetoothLowEnergyConnectionFinder
> connection_finder
;
346 // Starting discovery.
347 FindAndExpectStartDiscovery(connection_finder
);
348 ExpectStopDiscoveryAndRemoveObserver();
350 // Finding and creating a GATT connection to the right device.
351 device::BluetoothDevice::GattConnectionCallback gatt_connection_callback
;
352 PrepareForNewRightDevice(kServiceUUID
, gatt_connection_callback
);
353 connection_finder
.DeviceAdded(adapter_
.get(), device_
.get());
355 // Creating a connection.
356 MockConnection
* connection
= connection_finder
.ExpectCreateConnection();
357 ASSERT_FALSE(gatt_connection_callback
.is_null());
358 base::RunLoop run_loop
;
359 gatt_connection_callback
.Run(make_scoped_ptr(
360 new NiceMock
<device::MockBluetoothGattConnection
>(kBluetoothAddress
)));
361 run_loop
.RunUntilIdle();
362 EXPECT_FALSE(last_found_connection_
);
363 connection
->SetStatus(Connection::IN_PROGRESS
);
364 connection
->SetStatus(Connection::CONNECTED
);
365 EXPECT_TRUE(last_found_connection_
);
368 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
369 Find_ConnectionFails_RestartDiscoveryAndConnectionSucceeds
) {
370 StrictMock
<MockBluetoothLowEnergyConnectionFinder
> connection_finder
;
372 // Starting discovery.
373 FindAndExpectStartDiscovery(connection_finder
);
374 base::Closure stop_discovery_session_callback
;
375 EXPECT_CALL(*last_discovery_session_alias_
, Stop(_
, _
))
376 .WillOnce(SaveArg
<0>(&stop_discovery_session_callback
));
378 // Preparing to create a GATT connection to the right device.
379 device::BluetoothDevice::GattConnectionCallback gatt_connection_callback
;
380 PrepareForNewRightDevice(kServiceUUID
, gatt_connection_callback
);
381 MockConnection
* connection
= connection_finder
.ExpectCreateConnection();
383 // Trying to create a connection.
384 connection_finder
.DeviceAdded(adapter_
.get(), device_
.get());
385 ASSERT_FALSE(gatt_connection_callback
.is_null());
386 base::RunLoop run_loop
;
387 gatt_connection_callback
.Run(make_scoped_ptr(
388 new NiceMock
<device::MockBluetoothGattConnection
>(kBluetoothAddress
)));
389 run_loop
.RunUntilIdle();
390 ASSERT_FALSE(last_found_connection_
);
391 connection
->SetStatus(Connection::IN_PROGRESS
);
393 // Stopping the discovery session.
394 ASSERT_FALSE(stop_discovery_session_callback
.is_null());
395 stop_discovery_session_callback
.Run();
397 // Preparing to restart the discovery session.
398 device::BluetoothAdapter::DiscoverySessionCallback discovery_callback
;
399 std::vector
<const device::BluetoothDevice
*> devices
;
400 ON_CALL(*adapter_
, GetDevices()).WillByDefault(Return(devices
));
401 EXPECT_CALL(*adapter_
, StartDiscoverySessionWithFilterRaw(_
, _
, _
))
402 .WillOnce(SaveArg
<1>(&discovery_callback
));
405 connection
->SetStatus(Connection::DISCONNECTED
);
407 // Restarting the discovery session.
408 scoped_ptr
<device::MockBluetoothDiscoverySession
> discovery_session(
409 new NiceMock
<device::MockBluetoothDiscoverySession
>());
410 last_discovery_session_alias_
= discovery_session
.get();
411 ON_CALL(*last_discovery_session_alias_
, IsActive())
412 .WillByDefault(Return(true));
413 ASSERT_FALSE(discovery_callback
.is_null());
414 discovery_callback
.Run(discovery_session
.Pass());
416 // Preparing to create a GATT connection to the right device.
417 PrepareForNewRightDevice(kServiceUUID
, gatt_connection_callback
);
418 connection
= connection_finder
.ExpectCreateConnection();
420 // Trying to create a connection.
421 connection_finder
.DeviceAdded(adapter_
.get(), device_
.get());
422 EXPECT_CALL(*last_discovery_session_alias_
, Stop(_
, _
)).Times(AtLeast(1));
423 ASSERT_FALSE(gatt_connection_callback
.is_null());
424 base::RunLoop other_run_loop
;
425 gatt_connection_callback
.Run(make_scoped_ptr(
426 new NiceMock
<device::MockBluetoothGattConnection
>(kBluetoothAddress
)));
427 other_run_loop
.RunUntilIdle();
429 // Completing the connection.
430 EXPECT_FALSE(last_found_connection_
);
431 connection
->SetStatus(Connection::IN_PROGRESS
);
432 connection
->SetStatus(Connection::CONNECTED
);
433 EXPECT_TRUE(last_found_connection_
);
436 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
437 Find_AdapterRemoved_RestartDiscoveryAndConnectionSucceeds
) {
438 StrictMock
<MockBluetoothLowEnergyConnectionFinder
> connection_finder
;
440 // Starting discovery.
441 FindAndExpectStartDiscovery(connection_finder
);
443 // Removing the adapter.
444 ON_CALL(*adapter_
, IsPresent()).WillByDefault(Return(false));
445 ON_CALL(*adapter_
, IsPowered()).WillByDefault(Return(false));
446 ON_CALL(*last_discovery_session_alias_
, IsActive())
447 .WillByDefault(Return(false));
448 connection_finder
.AdapterPoweredChanged(adapter_
.get(), false);
449 connection_finder
.AdapterPresentChanged(adapter_
.get(), false);
451 // Adding the adapter.
452 ON_CALL(*adapter_
, IsPresent()).WillByDefault(Return(true));
453 ON_CALL(*adapter_
, IsPowered()).WillByDefault(Return(true));
455 device::BluetoothAdapter::DiscoverySessionCallback discovery_callback
;
456 scoped_ptr
<device::MockBluetoothDiscoverySession
> discovery_session(
457 new NiceMock
<device::MockBluetoothDiscoverySession
>());
458 last_discovery_session_alias_
= discovery_session
.get();
460 // Restarting the discovery session.
461 EXPECT_CALL(*adapter_
, StartDiscoverySessionWithFilterRaw(_
, _
, _
))
462 .WillOnce(SaveArg
<1>(&discovery_callback
));
463 connection_finder
.AdapterPresentChanged(adapter_
.get(), true);
464 connection_finder
.AdapterPoweredChanged(adapter_
.get(), true);
465 ON_CALL(*last_discovery_session_alias_
, IsActive())
466 .WillByDefault(Return(true));
468 ASSERT_FALSE(discovery_callback
.is_null());
469 discovery_callback
.Run(discovery_session
.Pass());
471 // Preparing to create a GATT connection to the right device.
472 device::BluetoothDevice::GattConnectionCallback gatt_connection_callback
;
473 PrepareForNewRightDevice(kServiceUUID
, gatt_connection_callback
);
474 MockConnection
* connection
= connection_finder
.ExpectCreateConnection();
476 // Trying to create a connection.
477 connection_finder
.DeviceAdded(adapter_
.get(), device_
.get());
478 EXPECT_CALL(*last_discovery_session_alias_
, Stop(_
, _
)).Times(AtLeast(1));
479 ASSERT_FALSE(gatt_connection_callback
.is_null());
480 base::RunLoop run_loop
;
481 gatt_connection_callback
.Run(make_scoped_ptr(
482 new NiceMock
<device::MockBluetoothGattConnection
>(kBluetoothAddress
)));
483 run_loop
.RunUntilIdle();
485 // Completing the connection.
486 ASSERT_FALSE(last_found_connection_
);
487 connection
->SetStatus(Connection::IN_PROGRESS
);
488 connection
->SetStatus(Connection::CONNECTED
);
489 EXPECT_TRUE(last_found_connection_
);
492 } // namespace proximity_auth