Connecting only to paired devices in BLE connection finder.
[chromium-blink-merge.git] / components / proximity_auth / ble / bluetooth_low_energy_connection_finder.cc
blob153158e18d92aaabc92786d37b1daa3e55554f63
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"
7 #include <string>
9 #include "base/bind.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 {
28 namespace {
29 const int kMinDiscoveryRSSI = -100;
30 const int kDelayAfterGattConnectionMilliseconds = 1000;
31 } // namespace
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)),
42 connected_(false),
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();
54 if (connection_) {
55 connection_->RemoveObserver(this);
56 connection_.reset();
59 if (adapter_) {
60 adapter_->RemoveObserver(this);
61 adapter_ = NULL;
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.";
69 return;
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) {
82 DCHECK(device);
83 VLOG(1) << "Device added: " << device->GetAddress();
84 HandleDeviceUpdated(device);
87 void BluetoothLowEnergyConnectionFinder::DeviceChanged(
88 BluetoothAdapter* adapter,
89 BluetoothDevice* device) {
90 DCHECK(device);
91 VLOG(1) << "Device changed: " << device->GetAddress();
92 HandleDeviceUpdated(device);
95 void BluetoothLowEnergyConnectionFinder::HandleDeviceUpdated(
96 BluetoothDevice* device) {
97 if (connected_)
98 return;
99 const auto& i = pending_connections_.find(device);
100 if (i != pending_connections_.end()) {
101 VLOG(1) << "Pending connection to device " << device->GetAddress();
102 return;
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) {
114 if (connected_)
115 return;
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";
128 adapter_ = adapter;
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.
135 if (VLOG_IS_ON(1)) {
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() {
157 DCHECK(adapter_);
158 if (discovery_session_ && discovery_session_->IsActive()) {
159 VLOG(1) << "Discovery session already active";
160 return;
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(
169 filter.Pass(),
170 base::Bind(&BluetoothLowEnergyConnectionFinder::OnDiscoverySessionStarted,
171 weak_ptr_factory_.GetWeakPtr()),
172 base::Bind(
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";
189 if (!adapter_) {
190 VLOG(1) << "Adapter not initialized";
191 return;
193 if (!discovery_session_ || !discovery_session_->IsActive()) {
194 VLOG(1) << "No Active discovery session";
195 return;
198 discovery_session_->Stop(
199 base::Bind(&BluetoothLowEnergyConnectionFinder::OnDiscoverySessionStopped,
200 weak_ptr_factory_.GetWeakPtr()),
201 base::Bind(
202 &BluetoothLowEnergyConnectionFinder::OnStopDiscoverySessionError,
203 weak_ptr_factory_.GetWeakPtr()));
206 bool BluetoothLowEnergyConnectionFinder::HasService(
207 BluetoothDevice* remote_device) {
208 if (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) {
214 return true;
218 return false;
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) {
230 if (connected_) {
231 CloseGattConnection(gatt_connection.Pass());
232 return;
235 VLOG(1) << "Connection created";
236 connected_ = true;
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(
245 FROM_HERE,
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()),
268 base::Bind(
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);
279 if (device) {
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