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
;
96 scoped_ptr
<Connection
> CreateConnection(
97 scoped_ptr
<device::BluetoothGattConnection
> gatt_connection
) override
{
98 return make_scoped_ptr(CreateConnectionProxy());
102 DISALLOW_COPY_AND_ASSIGN(MockBluetoothLowEnergyConnectionFinder
);
107 class ProximityAuthBluetoothLowEnergyConnectionFinderTest
108 : public testing::Test
{
110 ProximityAuthBluetoothLowEnergyConnectionFinderTest()
111 : adapter_(new NiceMock
<device::MockBluetoothAdapter
>),
112 connection_callback_(
113 base::Bind(&ProximityAuthBluetoothLowEnergyConnectionFinderTest::
115 base::Unretained(this))),
116 device_(new NiceMock
<device::MockBluetoothDevice
>(adapter_
.get(),
122 device::BluetoothAdapterFactory::SetAdapterForTesting(adapter_
);
124 ON_CALL(*adapter_
, IsPresent()).WillByDefault(Return(true));
125 ON_CALL(*adapter_
, IsPowered()).WillByDefault(Return(true));
128 void OnConnectionFound(scoped_ptr
<Connection
> connection
) {
129 last_found_connection_
= connection
.Pass();
132 void FindAndExpectStartDiscovery(
133 BluetoothLowEnergyConnectionFinder
& connection_finder
) {
134 device::BluetoothAdapter::DiscoverySessionCallback discovery_callback
;
135 scoped_ptr
<device::MockBluetoothDiscoverySession
> discovery_session(
136 new NiceMock
<device::MockBluetoothDiscoverySession
>());
137 device::MockBluetoothDiscoverySession
* discovery_session_alias
=
138 discovery_session
.get();
140 // Starting a discovery session. StartDiscoveryWithFilterRaw is a proxy for
141 // StartDiscoveryWithFilter.
142 EXPECT_CALL(*adapter_
, StartDiscoverySessionWithFilterRaw(_
, _
, _
))
143 .WillOnce(SaveArg
<1>(&discovery_callback
));
144 EXPECT_CALL(*adapter_
, AddObserver(_
));
145 ON_CALL(*discovery_session_alias
, IsActive()).WillByDefault(Return(true));
146 connection_finder
.Find(connection_callback_
);
147 ASSERT_FALSE(discovery_callback
.is_null());
148 discovery_callback
.Run(discovery_session
.Pass());
151 EXPECT_CALL(*discovery_session_alias
, Stop(_
, _
)).Times(AtLeast(1));
152 EXPECT_CALL(*adapter_
, RemoveObserver(_
));
155 // Prepare |device_| with |uuid|.
156 void PrepareDevice(const std::string
& uuid
) {
157 std::vector
<device::BluetoothUUID
> uuids
;
158 uuids
.push_back(device::BluetoothUUID(uuid
));
159 ON_CALL(*device_
, GetUUIDs()).WillByDefault(Return(uuids
));
162 // Prepare expectations to add/change a right device.
163 void PrepareForNewRightDevice(
164 const std::string
& uuid
,
165 device::BluetoothDevice::GattConnectionCallback
& callback
) {
167 ON_CALL(*device_
, IsPaired()).WillByDefault(Return(true));
168 EXPECT_CALL(*device_
, CreateGattConnection(_
, _
))
169 .WillOnce(SaveArg
<0>(&callback
));
172 // Prepare expectations to add/change a wrong device.
173 void PrepareForNewWrongDevice(const std::string
& uuid
) {
175 EXPECT_CALL(*device_
, CreateGattConnection(_
, _
)).Times(0);
178 scoped_refptr
<device::MockBluetoothAdapter
> adapter_
;
179 ConnectionFinder::ConnectionCallback connection_callback_
;
180 scoped_ptr
<device::MockBluetoothDevice
> device_
;
181 scoped_ptr
<Connection
> last_found_connection_
;
184 base::MessageLoop message_loop_
;
187 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
188 ConstructAndDestroyDoesntCrash
) {
189 // Destroying a BluetoothConnectionFinder for which Find() has not been called
191 BluetoothLowEnergyConnectionFinder
connection_finder(
192 kServiceUUID
, kToPeripheralCharUUID
, kFromPeripheralCharUUID
,
193 kMaxNumberOfAttempts
);
196 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
197 Find_StartsDiscoverySession
) {
198 BluetoothLowEnergyConnectionFinder
connection_finder(
199 kServiceUUID
, kToPeripheralCharUUID
, kFromPeripheralCharUUID
,
200 kMaxNumberOfAttempts
);
202 EXPECT_CALL(*adapter_
, StartDiscoverySessionWithFilterRaw(_
, _
, _
));
203 EXPECT_CALL(*adapter_
, AddObserver(_
));
204 connection_finder
.Find(connection_callback_
);
207 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
208 Find_StopsDiscoverySessionBeforeDestroying
) {
209 BluetoothLowEnergyConnectionFinder
connection_finder(
210 kServiceUUID
, kToPeripheralCharUUID
, kFromPeripheralCharUUID
,
211 kMaxNumberOfAttempts
);
213 device::BluetoothAdapter::DiscoverySessionCallback discovery_callback
;
214 scoped_ptr
<device::MockBluetoothDiscoverySession
> discovery_session(
215 new NiceMock
<device::MockBluetoothDiscoverySession
>());
216 device::MockBluetoothDiscoverySession
* discovery_session_alias
=
217 discovery_session
.get();
219 EXPECT_CALL(*adapter_
, StartDiscoverySessionWithFilterRaw(_
, _
, _
))
220 .WillOnce(SaveArg
<1>(&discovery_callback
));
221 ON_CALL(*discovery_session_alias
, IsActive()).WillByDefault(Return(true));
222 EXPECT_CALL(*adapter_
, AddObserver(_
));
223 connection_finder
.Find(connection_callback_
);
225 EXPECT_CALL(*discovery_session_alias
, Stop(_
, _
));
226 ASSERT_FALSE(discovery_callback
.is_null());
227 discovery_callback
.Run(discovery_session
.Pass());
229 EXPECT_CALL(*adapter_
, RemoveObserver(_
));
232 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
233 Find_CreatesGattConnectionWhenRightDeviceIsAdded
) {
234 BluetoothLowEnergyConnectionFinder
connection_finder(
235 kServiceUUID
, kToPeripheralCharUUID
, kFromPeripheralCharUUID
,
236 kMaxNumberOfAttempts
);
237 device::BluetoothDevice::GattConnectionCallback gatt_connection_callback
;
238 FindAndExpectStartDiscovery(connection_finder
);
240 PrepareForNewRightDevice(kServiceUUID
, gatt_connection_callback
);
241 connection_finder
.DeviceAdded(adapter_
.get(), device_
.get());
242 ASSERT_FALSE(gatt_connection_callback
.is_null());
245 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
246 Find_DoesntCreateGattConnectionWhenWrongDeviceIsAdded
) {
247 BluetoothLowEnergyConnectionFinder
connection_finder(
248 kServiceUUID
, kToPeripheralCharUUID
, kFromPeripheralCharUUID
,
249 kMaxNumberOfAttempts
);
250 FindAndExpectStartDiscovery(connection_finder
);
252 PrepareForNewWrongDevice(kOtherUUID
);
253 connection_finder
.DeviceAdded(adapter_
.get(), device_
.get());
256 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
257 Find_CreatesGattConnectionWhenRightDeviceIsChanged
) {
258 BluetoothLowEnergyConnectionFinder
connection_finder(
259 kServiceUUID
, kToPeripheralCharUUID
, kFromPeripheralCharUUID
,
260 kMaxNumberOfAttempts
);
261 device::BluetoothDevice::GattConnectionCallback gatt_connection_callback
;
262 FindAndExpectStartDiscovery(connection_finder
);
264 PrepareForNewRightDevice(kServiceUUID
, gatt_connection_callback
);
265 connection_finder
.DeviceChanged(adapter_
.get(), device_
.get());
266 ASSERT_FALSE(gatt_connection_callback
.is_null());
269 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
270 Find_DoesntCreateGattConnectionWhenWrongDeviceIsChanged
) {
271 BluetoothLowEnergyConnectionFinder
connection_finder(
272 kServiceUUID
, kToPeripheralCharUUID
, kFromPeripheralCharUUID
,
273 kMaxNumberOfAttempts
);
274 FindAndExpectStartDiscovery(connection_finder
);
276 PrepareForNewWrongDevice(kOtherUUID
);
277 connection_finder
.DeviceChanged(adapter_
.get(), device_
.get());
280 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
281 Find_CreatesTwoGattConnections
) {
282 StrictMock
<MockBluetoothLowEnergyConnectionFinder
> connection_finder
;
283 FindAndExpectStartDiscovery(connection_finder
);
284 connection_finder
.ExpectCreateConnection();
286 // Prepare to add |device_|.
287 device::BluetoothDevice::GattConnectionCallback gatt_connection_callback
;
288 PrepareForNewRightDevice(kServiceUUID
, gatt_connection_callback
);
290 // Prepare to add |other_device|.
291 device::BluetoothDevice::GattConnectionCallback
292 other_gatt_connection_callback
;
293 NiceMock
<device::MockBluetoothDevice
> other_device(
294 adapter_
.get(), 0, kDeviceName
, kOtherBluetoothAddress
, false, false);
295 std::vector
<device::BluetoothUUID
> uuids
;
296 uuids
.push_back(device::BluetoothUUID(kServiceUUID
));
297 ON_CALL(other_device
, IsPaired()).WillByDefault(Return(true));
298 ON_CALL(other_device
, GetUUIDs()).WillByDefault((Return(uuids
)));
299 EXPECT_CALL(other_device
, CreateGattConnection(_
, _
))
300 .WillOnce(SaveArg
<0>(&other_gatt_connection_callback
));
303 connection_finder
.DeviceAdded(adapter_
.get(), device_
.get());
304 connection_finder
.DeviceAdded(adapter_
.get(), &other_device
);
306 ASSERT_FALSE(gatt_connection_callback
.is_null());
307 ASSERT_FALSE(other_gatt_connection_callback
.is_null());
309 base::RunLoop run_loop
;
310 gatt_connection_callback
.Run(make_scoped_ptr(
311 new NiceMock
<device::MockBluetoothGattConnection
>(kBluetoothAddress
)));
312 run_loop
.RunUntilIdle();
314 // The second device should be forgetten
315 EXPECT_CALL(*adapter_
, GetDevice(std::string(kOtherBluetoothAddress
)))
316 .WillOnce(Return(&other_device
));
317 EXPECT_CALL(other_device
, Disconnect(_
, _
));
318 other_gatt_connection_callback
.Run(
319 make_scoped_ptr(new NiceMock
<device::MockBluetoothGattConnection
>(
320 kOtherBluetoothAddress
)));
323 TEST_F(ProximityAuthBluetoothLowEnergyConnectionFinderTest
,
324 Find_ConnectionSucceeds
) {
325 StrictMock
<MockBluetoothLowEnergyConnectionFinder
> connection_finder
;
327 // Starting discovery
328 FindAndExpectStartDiscovery(connection_finder
);
330 // Finding and creating a GATT connection to the right device
331 device::BluetoothDevice::GattConnectionCallback gatt_connection_callback
;
332 PrepareForNewRightDevice(kServiceUUID
, gatt_connection_callback
);
333 connection_finder
.DeviceAdded(adapter_
.get(), device_
.get());
335 // Creating a connection
336 MockConnection
* connection
= connection_finder
.ExpectCreateConnection();
337 ASSERT_FALSE(gatt_connection_callback
.is_null());
338 base::RunLoop run_loop
;
339 gatt_connection_callback
.Run(make_scoped_ptr(
340 new NiceMock
<device::MockBluetoothGattConnection
>(kBluetoothAddress
)));
341 run_loop
.RunUntilIdle();
342 ASSERT_FALSE(last_found_connection_
);
343 connection
->SetStatus(Connection::IN_PROGRESS
);
344 connection
->SetStatus(Connection::CONNECTED
);
345 ASSERT_FALSE(!last_found_connection_
);
348 } // namespace proximity_auth