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/bind_helpers.h"
11 #include "base/location.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/message_loop/message_loop_proxy.h"
15 #include "components/proximity_auth/ble/bluetooth_low_energy_connection.h"
16 #include "components/proximity_auth/connection.h"
17 #include "device/bluetooth/bluetooth_adapter_factory.h"
18 #include "device/bluetooth/bluetooth_device.h"
19 #include "device/bluetooth/bluetooth_discovery_session.h"
20 #include "device/bluetooth/bluetooth_uuid.h"
22 using device::BluetoothAdapter
;
23 using device::BluetoothDevice
;
24 using device::BluetoothGattConnection
;
25 using device::BluetoothDiscoveryFilter
;
27 namespace proximity_auth
{
29 const int kMinDiscoveryRSSI
= -100;
30 const int kDelayAfterGattConnectionMilliseconds
= 1000;
33 BluetoothLowEnergyConnectionFinder::BluetoothLowEnergyConnectionFinder(
34 const std::string
& remote_service_uuid
,
35 const std::string
& to_peripheral_char_uuid
,
36 const std::string
& from_peripheral_char_uuid
,
37 int max_number_of_tries
)
38 : remote_service_uuid_(device::BluetoothUUID(remote_service_uuid
)),
39 to_peripheral_char_uuid_(device::BluetoothUUID(to_peripheral_char_uuid
)),
40 from_peripheral_char_uuid_(
41 device::BluetoothUUID(from_peripheral_char_uuid
)),
43 max_number_of_tries_(max_number_of_tries
),
44 delay_after_gatt_connection_(base::TimeDelta::FromMilliseconds(
45 kDelayAfterGattConnectionMilliseconds
)),
46 weak_ptr_factory_(this) {
49 BluetoothLowEnergyConnectionFinder::~BluetoothLowEnergyConnectionFinder() {
50 if (discovery_session_
) {
51 StopDiscoverySession();
55 connection_
->RemoveObserver(this);
60 adapter_
->RemoveObserver(this);
65 void BluetoothLowEnergyConnectionFinder::Find(
66 const ConnectionCallback
& connection_callback
) {
67 if (!device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable()) {
68 VLOG(1) << "[BCF] Bluetooth is unsupported on this platform. Aborting.";
71 VLOG(1) << "Finding connection";
73 connection_callback_
= connection_callback
;
75 device::BluetoothAdapterFactory::GetAdapter(
76 base::Bind(&BluetoothLowEnergyConnectionFinder::OnAdapterInitialized
,
77 weak_ptr_factory_
.GetWeakPtr()));
80 void BluetoothLowEnergyConnectionFinder::DeviceAdded(BluetoothAdapter
* adapter
,
81 BluetoothDevice
* device
) {
83 VLOG(1) << "Device added: " << device
->GetAddress();
84 HandleDeviceUpdated(device
);
87 void BluetoothLowEnergyConnectionFinder::DeviceChanged(
88 BluetoothAdapter
* adapter
,
89 BluetoothDevice
* device
) {
91 VLOG(1) << "Device changed: " << device
->GetAddress();
92 HandleDeviceUpdated(device
);
95 void BluetoothLowEnergyConnectionFinder::HandleDeviceUpdated(
96 BluetoothDevice
* device
) {
99 const auto& i
= pending_connections_
.find(device
);
100 if (i
!= pending_connections_
.end()) {
101 VLOG(1) << "Pending connection to device " << device
->GetAddress();
104 if (HasService(device
) && device
->IsPaired()) {
105 VLOG(1) << "Connecting to paired device " << device
->GetAddress();
106 pending_connections_
.insert(device
);
107 CreateGattConnection(device
);
111 void BluetoothLowEnergyConnectionFinder::DeviceRemoved(
112 BluetoothAdapter
* adapter
,
113 BluetoothDevice
* device
) {
117 const auto& i
= pending_connections_
.find(device
);
118 if (i
!= pending_connections_
.end()) {
119 VLOG(1) << "Remove pending connection to " << device
->GetAddress();
120 pending_connections_
.erase(i
);
124 void BluetoothLowEnergyConnectionFinder::OnAdapterInitialized(
125 scoped_refptr
<BluetoothAdapter
> adapter
) {
126 VLOG(1) << "Adapter ready";
129 adapter_
->AddObserver(this);
131 // Note: it's not possible to connect with the paired directly, as the
132 // temporary MAC may not be resolved automatically (see crbug.com/495402). The
133 // Bluetooth adapter will fire |OnDeviceChanged| notifications for all
134 // Bluetooth Low Energy devices that are advertising.
136 std::vector
<BluetoothDevice
*> devices
= adapter_
->GetDevices();
137 for (auto* device
: devices
) {
138 VLOG(1) << "Ignoring device " << device
->GetAddress()
139 << " present when adapter was initialized.";
143 StartDiscoverySession();
146 void BluetoothLowEnergyConnectionFinder::OnDiscoverySessionStarted(
147 scoped_ptr
<device::BluetoothDiscoverySession
> discovery_session
) {
148 VLOG(1) << "Discovery session started";
149 discovery_session_
= discovery_session
.Pass();
152 void BluetoothLowEnergyConnectionFinder::OnStartDiscoverySessionError() {
153 VLOG(1) << "Error starting discovery session";
156 void BluetoothLowEnergyConnectionFinder::StartDiscoverySession() {
158 if (discovery_session_
&& discovery_session_
->IsActive()) {
159 VLOG(1) << "Discovery session already active";
163 // Discover only low energy (LE) devices with strong enough signal.
164 scoped_ptr
<BluetoothDiscoveryFilter
> filter(new BluetoothDiscoveryFilter(
165 BluetoothDiscoveryFilter::Transport::TRANSPORT_LE
));
166 filter
->SetRSSI(kMinDiscoveryRSSI
);
168 adapter_
->StartDiscoverySessionWithFilter(
170 base::Bind(&BluetoothLowEnergyConnectionFinder::OnDiscoverySessionStarted
,
171 weak_ptr_factory_
.GetWeakPtr()),
173 &BluetoothLowEnergyConnectionFinder::OnStartDiscoverySessionError
,
174 weak_ptr_factory_
.GetWeakPtr()));
177 void BluetoothLowEnergyConnectionFinder::OnDiscoverySessionStopped() {
178 VLOG(1) << "Discovery session stopped";
179 discovery_session_
.reset();
182 void BluetoothLowEnergyConnectionFinder::OnStopDiscoverySessionError() {
183 VLOG(1) << "Error stopping discovery session";
186 void BluetoothLowEnergyConnectionFinder::StopDiscoverySession() {
187 VLOG(1) << "Stopping discovery sesison";
190 VLOG(1) << "Adapter not initialized";
193 if (!discovery_session_
|| !discovery_session_
->IsActive()) {
194 VLOG(1) << "No Active discovery session";
198 discovery_session_
->Stop(
199 base::Bind(&BluetoothLowEnergyConnectionFinder::OnDiscoverySessionStopped
,
200 weak_ptr_factory_
.GetWeakPtr()),
202 &BluetoothLowEnergyConnectionFinder::OnStopDiscoverySessionError
,
203 weak_ptr_factory_
.GetWeakPtr()));
206 bool BluetoothLowEnergyConnectionFinder::HasService(
207 BluetoothDevice
* remote_device
) {
209 VLOG(1) << "Device " << remote_device
->GetAddress() << " has "
210 << remote_device
->GetUUIDs().size() << " services.";
211 std::vector
<device::BluetoothUUID
> uuids
= remote_device
->GetUUIDs();
212 for (const auto& service_uuid
: uuids
) {
213 if (remote_service_uuid_
== service_uuid
) {
221 void BluetoothLowEnergyConnectionFinder::OnCreateGattConnectionError(
222 std::string device_address
,
223 BluetoothDevice::ConnectErrorCode error_code
) {
224 VLOG(1) << "Error creating connection to device " << device_address
225 << " : error code = " << error_code
;
228 void BluetoothLowEnergyConnectionFinder::OnGattConnectionCreated(
229 scoped_ptr
<BluetoothGattConnection
> gatt_connection
) {
231 CloseGattConnection(gatt_connection
.Pass());
235 VLOG(1) << "Connection created";
237 pending_connections_
.clear();
239 gatt_connection_
= gatt_connection
.Pass();
241 // This is a workaround for crbug.com/498850. Currently, trying to write/read
242 // characteristics immediatelly after the GATT connection was established
243 // fails with the very informative GATT_ERROR_FAILED.
244 base::MessageLoopProxy::current()->PostDelayedTask(
246 base::Bind(&BluetoothLowEnergyConnectionFinder::CompleteConnection
,
247 weak_ptr_factory_
.GetWeakPtr()),
248 delay_after_gatt_connection_
);
250 StopDiscoverySession();
253 void BluetoothLowEnergyConnectionFinder::CompleteConnection() {
254 connection_
= CreateConnection(gatt_connection_
.Pass());
255 connection_
->AddObserver(this);
256 connection_
->Connect();
259 void BluetoothLowEnergyConnectionFinder::CreateGattConnection(
260 device::BluetoothDevice
* remote_device
) {
261 VLOG(1) << "SmartLock service found ("
262 << remote_service_uuid_
.canonical_value() << ")\n"
263 << "device = " << remote_device
->GetAddress()
264 << ", name = " << remote_device
->GetName();
265 remote_device
->CreateGattConnection(
266 base::Bind(&BluetoothLowEnergyConnectionFinder::OnGattConnectionCreated
,
267 weak_ptr_factory_
.GetWeakPtr()),
269 &BluetoothLowEnergyConnectionFinder::OnCreateGattConnectionError
,
270 weak_ptr_factory_
.GetWeakPtr(), remote_device
->GetAddress()));
273 void BluetoothLowEnergyConnectionFinder::CloseGattConnection(
274 scoped_ptr
<device::BluetoothGattConnection
> gatt_connection
) {
275 DCHECK(gatt_connection
);
276 std::string device_address
= gatt_connection
->GetDeviceAddress();
277 gatt_connection
.reset();
278 BluetoothDevice
* device
= adapter_
->GetDevice(device_address
);
280 VLOG(1) << "Disconnect from device " << device
->GetAddress();
281 device
->Disconnect(base::Bind(&base::DoNothing
),
282 base::Bind(&base::DoNothing
));
286 scoped_ptr
<Connection
> BluetoothLowEnergyConnectionFinder::CreateConnection(
287 scoped_ptr
<BluetoothGattConnection
> gatt_connection
) {
288 RemoteDevice remote_device
;
289 remote_device
.bluetooth_address
= gatt_connection
->GetDeviceAddress();
291 return make_scoped_ptr(new BluetoothLowEnergyConnection(
292 remote_device
, adapter_
, remote_service_uuid_
, to_peripheral_char_uuid_
,
293 from_peripheral_char_uuid_
, gatt_connection
.Pass(),
294 max_number_of_tries_
));
297 void BluetoothLowEnergyConnectionFinder::OnConnectionStatusChanged(
298 Connection
* connection
,
299 Connection::Status old_status
,
300 Connection::Status new_status
) {
301 DCHECK_EQ(connection
, connection_
.get());
303 if (!connection_callback_
.is_null() && connection_
->IsConnected()) {
304 connection_
->RemoveObserver(this);
305 connection_callback_
.Run(connection_
.Pass());
306 connection_callback_
.Reset();
310 void BluetoothLowEnergyConnectionFinder::SetDelayForTesting(
311 base::TimeDelta delay
) {
312 delay_after_gatt_connection_
= delay
;
315 } // namespace proximity_auth