Extension syncing: Introduce a NeedsSync pref
[chromium-blink-merge.git] / components / proximity_auth / ble / bluetooth_low_energy_connection_finder.cc
blob5f9a59e0da137a88357da1a86b05298f7f45d855
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/thread_task_runner_handle.h"
15 #include "components/proximity_auth/ble/bluetooth_low_energy_connection.h"
16 #include "components/proximity_auth/ble/bluetooth_low_energy_device_whitelist.h"
17 #include "components/proximity_auth/connection.h"
18 #include "components/proximity_auth/logging/logging.h"
19 #include "device/bluetooth/bluetooth_adapter_factory.h"
20 #include "device/bluetooth/bluetooth_device.h"
21 #include "device/bluetooth/bluetooth_discovery_session.h"
22 #include "device/bluetooth/bluetooth_uuid.h"
24 using device::BluetoothAdapter;
25 using device::BluetoothDevice;
26 using device::BluetoothGattConnection;
27 using device::BluetoothDiscoveryFilter;
29 namespace proximity_auth {
30 namespace {
31 const int kMinDiscoveryRSSI = -90;
32 const int kDelayAfterGattConnectionMilliseconds = 1000;
33 } // namespace
35 BluetoothLowEnergyConnectionFinder::BluetoothLowEnergyConnectionFinder(
36 const std::string& remote_service_uuid,
37 const std::string& to_peripheral_char_uuid,
38 const std::string& from_peripheral_char_uuid,
39 const BluetoothLowEnergyDeviceWhitelist* device_whitelist,
40 int max_number_of_tries)
41 : remote_service_uuid_(device::BluetoothUUID(remote_service_uuid)),
42 to_peripheral_char_uuid_(device::BluetoothUUID(to_peripheral_char_uuid)),
43 from_peripheral_char_uuid_(
44 device::BluetoothUUID(from_peripheral_char_uuid)),
45 device_whitelist_(device_whitelist),
46 connected_(false),
47 max_number_of_tries_(max_number_of_tries),
48 delay_after_gatt_connection_(base::TimeDelta::FromMilliseconds(
49 kDelayAfterGattConnectionMilliseconds)),
50 weak_ptr_factory_(this) {
53 BluetoothLowEnergyConnectionFinder::~BluetoothLowEnergyConnectionFinder() {
54 if (discovery_session_) {
55 StopDiscoverySession();
58 if (connection_) {
59 connection_->RemoveObserver(this);
60 connection_.reset();
63 if (adapter_) {
64 adapter_->RemoveObserver(this);
65 adapter_ = NULL;
69 void BluetoothLowEnergyConnectionFinder::Find(
70 const ConnectionCallback& connection_callback) {
71 if (!device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable()) {
72 PA_LOG(WARNING) << "Bluetooth is unsupported on this platform. Aborting.";
73 return;
75 PA_LOG(INFO) << "Finding connection";
77 connection_callback_ = connection_callback;
79 device::BluetoothAdapterFactory::GetAdapter(
80 base::Bind(&BluetoothLowEnergyConnectionFinder::OnAdapterInitialized,
81 weak_ptr_factory_.GetWeakPtr()));
84 // It's not necessary to observe |AdapterPresentChanged| too. When |adapter_| is
85 // present, but not powered, it's not possible to scan for new devices.
86 void BluetoothLowEnergyConnectionFinder::AdapterPoweredChanged(
87 BluetoothAdapter* adapter,
88 bool powered) {
89 DCHECK_EQ(adapter_.get(), adapter);
90 PA_LOG(INFO) << "Adapter powered: " << powered;
92 // Important: do not rely on |adapter->IsDiscoverying()| to verify if there is
93 // an active discovery session. We need to create our own with an specific
94 // filter.
95 if (powered && (!discovery_session_ || !discovery_session_->IsActive()))
96 StartDiscoverySession();
99 void BluetoothLowEnergyConnectionFinder::DeviceAdded(BluetoothAdapter* adapter,
100 BluetoothDevice* device) {
101 DCHECK_EQ(adapter_.get(), adapter);
102 DCHECK(device);
103 PA_LOG(INFO) << "Device added: " << device->GetAddress();
105 // Note: Only consider |device| when it was actually added/updated during a
106 // scanning, otherwise the device is stale and the GATT connection will fail.
107 // For instance, when |adapter_| change status from unpowered to powered,
108 // |DeviceAdded| is called for each paired |device|.
109 if (adapter_->IsPowered() && discovery_session_ &&
110 discovery_session_->IsActive())
111 HandleDeviceUpdated(device);
114 void BluetoothLowEnergyConnectionFinder::DeviceChanged(
115 BluetoothAdapter* adapter,
116 BluetoothDevice* device) {
117 DCHECK_EQ(adapter_.get(), adapter);
118 DCHECK(device);
119 PA_LOG(INFO) << "Device changed: " << device->GetAddress();
121 // Note: Only consider |device| when it was actually added/updated during a
122 // scanning, otherwise the device is stale and the GATT connection will fail.
123 // For instance, when |adapter_| change status from unpowered to powered,
124 // |DeviceAdded| is called for each paired |device|.
125 if (adapter_->IsPowered() && discovery_session_ &&
126 discovery_session_->IsActive())
127 HandleDeviceUpdated(device);
130 void BluetoothLowEnergyConnectionFinder::HandleDeviceUpdated(
131 BluetoothDevice* device) {
132 if (connected_)
133 return;
134 const auto& i = pending_connections_.find(device);
135 if (i != pending_connections_.end()) {
136 PA_LOG(INFO) << "Pending connection to device " << device->GetAddress();
137 return;
139 if (device->IsPaired() &&
140 (HasService(device) ||
141 device_whitelist_->HasDeviceWithAddress(device->GetAddress()))) {
142 PA_LOG(INFO) << "Connecting to paired device " << device->GetAddress()
143 << " with service (" << HasService(device)
144 << ") or is whitelisted ("
145 << device_whitelist_->HasDeviceWithAddress(
146 device->GetAddress()) << ")";
147 pending_connections_.insert(device);
148 CreateGattConnection(device);
152 void BluetoothLowEnergyConnectionFinder::DeviceRemoved(
153 BluetoothAdapter* adapter,
154 BluetoothDevice* device) {
155 if (connected_)
156 return;
158 const auto& i = pending_connections_.find(device);
159 if (i != pending_connections_.end()) {
160 PA_LOG(INFO) << "Remove pending connection to " << device->GetAddress();
161 pending_connections_.erase(i);
165 void BluetoothLowEnergyConnectionFinder::OnAdapterInitialized(
166 scoped_refptr<BluetoothAdapter> adapter) {
167 PA_LOG(INFO) << "Adapter ready";
169 adapter_ = adapter;
170 adapter_->AddObserver(this);
172 // Note: it's not possible to connect with the paired directly, as the
173 // temporary MAC may not be resolved automatically (see crbug.com/495402). The
174 // Bluetooth adapter will fire |OnDeviceChanged| notifications for all
175 // Bluetooth Low Energy devices that are advertising.
176 std::vector<BluetoothDevice*> devices = adapter_->GetDevices();
177 for (auto* device : devices) {
178 PA_LOG(INFO) << "Ignoring device " << device->GetAddress()
179 << " present when adapter was initialized.";
182 StartDiscoverySession();
185 void BluetoothLowEnergyConnectionFinder::OnDiscoverySessionStarted(
186 scoped_ptr<device::BluetoothDiscoverySession> discovery_session) {
187 PA_LOG(INFO) << "Discovery session started";
188 discovery_session_ = discovery_session.Pass();
191 void BluetoothLowEnergyConnectionFinder::OnStartDiscoverySessionError() {
192 PA_LOG(WARNING) << "Error starting discovery session";
195 void BluetoothLowEnergyConnectionFinder::StartDiscoverySession() {
196 DCHECK(adapter_);
197 if (discovery_session_ && discovery_session_->IsActive()) {
198 PA_LOG(INFO) << "Discovery session already active";
199 return;
202 // Discover only low energy (LE) devices with strong enough signal.
203 scoped_ptr<BluetoothDiscoveryFilter> filter(new BluetoothDiscoveryFilter(
204 BluetoothDiscoveryFilter::Transport::TRANSPORT_LE));
205 filter->SetRSSI(kMinDiscoveryRSSI);
207 adapter_->StartDiscoverySessionWithFilter(
208 filter.Pass(),
209 base::Bind(&BluetoothLowEnergyConnectionFinder::OnDiscoverySessionStarted,
210 weak_ptr_factory_.GetWeakPtr()),
211 base::Bind(
212 &BluetoothLowEnergyConnectionFinder::OnStartDiscoverySessionError,
213 weak_ptr_factory_.GetWeakPtr()));
216 void BluetoothLowEnergyConnectionFinder::OnDiscoverySessionStopped() {
217 PA_LOG(INFO) << "Discovery session stopped";
218 discovery_session_.reset();
221 void BluetoothLowEnergyConnectionFinder::OnStopDiscoverySessionError() {
222 PA_LOG(WARNING) << "Error stopping discovery session";
225 void BluetoothLowEnergyConnectionFinder::StopDiscoverySession() {
226 PA_LOG(INFO) << "Stopping discovery sesison";
228 if (!adapter_) {
229 PA_LOG(WARNING) << "Adapter not initialized";
230 return;
232 if (!discovery_session_ || !discovery_session_->IsActive()) {
233 PA_LOG(INFO) << "No Active discovery session";
234 return;
237 discovery_session_->Stop(
238 base::Bind(&BluetoothLowEnergyConnectionFinder::OnDiscoverySessionStopped,
239 weak_ptr_factory_.GetWeakPtr()),
240 base::Bind(
241 &BluetoothLowEnergyConnectionFinder::OnStopDiscoverySessionError,
242 weak_ptr_factory_.GetWeakPtr()));
245 bool BluetoothLowEnergyConnectionFinder::HasService(
246 BluetoothDevice* remote_device) {
247 if (remote_device) {
248 PA_LOG(INFO) << "Device " << remote_device->GetAddress() << " has "
249 << remote_device->GetUUIDs().size() << " services.";
250 std::vector<device::BluetoothUUID> uuids = remote_device->GetUUIDs();
251 for (const auto& service_uuid : uuids) {
252 if (remote_service_uuid_ == service_uuid) {
253 return true;
257 return false;
260 void BluetoothLowEnergyConnectionFinder::OnCreateGattConnectionError(
261 std::string device_address,
262 BluetoothDevice::ConnectErrorCode error_code) {
263 PA_LOG(WARNING) << "Error creating connection to device " << device_address
264 << " : error code = " << error_code;
266 BluetoothDevice* device = GetDevice(device_address);
267 const auto& i = pending_connections_.find(device);
268 if (i != pending_connections_.end()) {
269 PA_LOG(INFO) << "Remove pending connection to " << device->GetAddress();
270 pending_connections_.erase(i);
274 void BluetoothLowEnergyConnectionFinder::OnGattConnectionCreated(
275 scoped_ptr<BluetoothGattConnection> gatt_connection) {
276 if (connected_) {
277 CloseGattConnection(gatt_connection.Pass());
278 return;
281 PA_LOG(INFO) << "GATT connection with " << gatt_connection->GetDeviceAddress()
282 << " created.";
283 connected_ = true;
284 pending_connections_.clear();
286 gatt_connection_ = gatt_connection.Pass();
288 // This is a workaround for crbug.com/498850. Currently, trying to write/read
289 // characteristics immediatelly after the GATT connection was established
290 // fails with the very informative GATT_ERROR_FAILED.
291 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
292 FROM_HERE,
293 base::Bind(&BluetoothLowEnergyConnectionFinder::CompleteConnection,
294 weak_ptr_factory_.GetWeakPtr()),
295 delay_after_gatt_connection_);
297 StopDiscoverySession();
300 void BluetoothLowEnergyConnectionFinder::CompleteConnection() {
301 connection_ = CreateConnection(gatt_connection_.Pass());
302 connection_->AddObserver(this);
303 connection_->Connect();
306 void BluetoothLowEnergyConnectionFinder::CreateGattConnection(
307 device::BluetoothDevice* remote_device) {
308 PA_LOG(INFO) << "Creating GATT connection with "
309 << remote_device->GetAddress();
310 remote_device->CreateGattConnection(
311 base::Bind(&BluetoothLowEnergyConnectionFinder::OnGattConnectionCreated,
312 weak_ptr_factory_.GetWeakPtr()),
313 base::Bind(
314 &BluetoothLowEnergyConnectionFinder::OnCreateGattConnectionError,
315 weak_ptr_factory_.GetWeakPtr(), remote_device->GetAddress()));
318 void BluetoothLowEnergyConnectionFinder::CloseGattConnection(
319 scoped_ptr<device::BluetoothGattConnection> gatt_connection) {
320 DCHECK(gatt_connection);
321 PA_LOG(INFO) << "Closing GATT connection with "
322 << gatt_connection->GetDeviceAddress();
324 // Destroying the BluetoothGattConnection also disconnects it.
325 gatt_connection.reset();
328 scoped_ptr<Connection> BluetoothLowEnergyConnectionFinder::CreateConnection(
329 scoped_ptr<BluetoothGattConnection> gatt_connection) {
330 remote_device_.bluetooth_address = gatt_connection->GetDeviceAddress();
332 return make_scoped_ptr(new BluetoothLowEnergyConnection(
333 remote_device_, adapter_, remote_service_uuid_, to_peripheral_char_uuid_,
334 from_peripheral_char_uuid_, gatt_connection.Pass(),
335 max_number_of_tries_));
338 void BluetoothLowEnergyConnectionFinder::OnConnectionStatusChanged(
339 Connection* connection,
340 Connection::Status old_status,
341 Connection::Status new_status) {
342 DCHECK_EQ(connection, connection_.get());
343 PA_LOG(INFO) << "OnConnectionStatusChanged: " << old_status << " -> "
344 << new_status;
346 if (!connection_callback_.is_null() && connection_->IsConnected()) {
347 adapter_->RemoveObserver(this);
348 connection_->RemoveObserver(this);
350 // Note: any observer of |connection_| added in |connection_callback_| will
351 // also receive this |OnConnectionStatusChanged| notification (IN_PROGRESS
352 // -> CONNECTED).
353 connection_callback_.Run(connection_.Pass());
354 connection_callback_.Reset();
355 } else if (old_status == Connection::IN_PROGRESS) {
356 PA_LOG(WARNING) << "Connection failed. Retrying.";
357 RestartDiscoverySessionWhenReady();
361 void BluetoothLowEnergyConnectionFinder::RestartDiscoverySessionWhenReady() {
362 PA_LOG(INFO) << "Trying to restart discovery.";
364 // To restart scanning for devices, it's necessary to ensure that:
365 // (i) the GATT connection to |remove_device_| is closed;
366 // (ii) there is no pending call to
367 // |device::BluetoothDiscoverySession::Stop()|.
368 // The second condition is satisfied when |OnDiscoveryStopped| is called and
369 // |discovery_session_| is reset.
370 if ((!gatt_connection_ || !gatt_connection_->IsConnected()) &&
371 !discovery_session_) {
372 PA_LOG(INFO) << "Ready to start discovery.";
373 connection_.reset();
374 connected_ = false;
375 StartDiscoverySession();
376 } else {
377 base::ThreadTaskRunnerHandle::Get()->PostTask(
378 FROM_HERE, base::Bind(&BluetoothLowEnergyConnectionFinder::
379 RestartDiscoverySessionWhenReady,
380 weak_ptr_factory_.GetWeakPtr()));
384 void BluetoothLowEnergyConnectionFinder::SetDelayForTesting(
385 base::TimeDelta delay) {
386 delay_after_gatt_connection_ = delay;
389 BluetoothDevice* BluetoothLowEnergyConnectionFinder::GetDevice(
390 std::string device_address) {
391 // It's not possible to simply use
392 // |adapter_->GetDevice(GetRemoteDeviceAddress())| to find the device with MAC
393 // address |GetRemoteDeviceAddress()|. For paired devices,
394 // BluetoothAdapter::GetDevice(XXX) searches for the temporary MAC address
395 // XXX, whereas |remote_device_.bluetooth_address| is the real MAC address.
396 // This is a bug in the way device::BluetoothAdapter is storing the devices
397 // (see crbug.com/497841).
398 std::vector<BluetoothDevice*> devices = adapter_->GetDevices();
399 for (const auto& device : devices) {
400 if (device->GetAddress() == device_address)
401 return device;
403 return nullptr;
406 } // namespace proximity_auth