Vectorize website settings icons in omnibox
[chromium-blink-merge.git] / components / proximity_auth / ble / bluetooth_low_energy_connection_finder.cc
blobef166cc0002de12a5dd18cd8bfbfa925f9313441
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/logging/logging.h"
18 #include "device/bluetooth/bluetooth_adapter_factory.h"
19 #include "device/bluetooth/bluetooth_device.h"
20 #include "device/bluetooth/bluetooth_discovery_session.h"
21 #include "device/bluetooth/bluetooth_uuid.h"
23 using device::BluetoothAdapter;
24 using device::BluetoothDevice;
25 using device::BluetoothGattConnection;
26 using device::BluetoothDiscoveryFilter;
28 namespace proximity_auth {
29 namespace {
30 const int kMinDiscoveryRSSI = -90;
31 } // namespace
33 class BluetoothThrottler;
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 BluetoothThrottler* bluetooth_throttler,
41 int max_number_of_tries)
42 : remote_service_uuid_(device::BluetoothUUID(remote_service_uuid)),
43 to_peripheral_char_uuid_(device::BluetoothUUID(to_peripheral_char_uuid)),
44 from_peripheral_char_uuid_(
45 device::BluetoothUUID(from_peripheral_char_uuid)),
46 device_whitelist_(device_whitelist),
47 bluetooth_throttler_(bluetooth_throttler),
48 max_number_of_tries_(max_number_of_tries),
49 weak_ptr_factory_(this) {}
51 BluetoothLowEnergyConnectionFinder::~BluetoothLowEnergyConnectionFinder() {
52 if (discovery_session_) {
53 StopDiscoverySession();
56 if (connection_) {
57 connection_->RemoveObserver(this);
58 connection_.reset();
61 if (adapter_) {
62 adapter_->RemoveObserver(this);
63 adapter_ = NULL;
67 void BluetoothLowEnergyConnectionFinder::Find(
68 const ConnectionCallback& connection_callback) {
69 if (!device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable()) {
70 PA_LOG(WARNING) << "Bluetooth is unsupported on this platform. Aborting.";
71 return;
73 PA_LOG(INFO) << "Finding connection";
75 connection_callback_ = connection_callback;
77 device::BluetoothAdapterFactory::GetAdapter(
78 base::Bind(&BluetoothLowEnergyConnectionFinder::OnAdapterInitialized,
79 weak_ptr_factory_.GetWeakPtr()));
82 // It's not necessary to observe |AdapterPresentChanged| too. When |adapter_| is
83 // present, but not powered, it's not possible to scan for new devices.
84 void BluetoothLowEnergyConnectionFinder::AdapterPoweredChanged(
85 BluetoothAdapter* adapter,
86 bool powered) {
87 DCHECK_EQ(adapter_.get(), adapter);
88 PA_LOG(INFO) << "Adapter powered: " << powered;
90 // Important: do not rely on |adapter->IsDiscoverying()| to verify if there is
91 // an active discovery session. We need to create our own with an specific
92 // filter.
93 if (powered && (!discovery_session_ || !discovery_session_->IsActive()))
94 StartDiscoverySession();
97 void BluetoothLowEnergyConnectionFinder::DeviceAdded(BluetoothAdapter* adapter,
98 BluetoothDevice* device) {
99 DCHECK_EQ(adapter_.get(), adapter);
100 DCHECK(device);
102 // Note: Only consider |device| when it was actually added/updated during a
103 // scanning, otherwise the device is stale and the GATT connection will fail.
104 // For instance, when |adapter_| change status from unpowered to powered,
105 // |DeviceAdded| is called for each paired |device|.
106 if (adapter_->IsPowered() && discovery_session_ &&
107 discovery_session_->IsActive())
108 HandleDeviceUpdated(device);
111 void BluetoothLowEnergyConnectionFinder::DeviceChanged(
112 BluetoothAdapter* adapter,
113 BluetoothDevice* device) {
114 DCHECK_EQ(adapter_.get(), adapter);
115 DCHECK(device);
117 // Note: Only consider |device| when it was actually added/updated during a
118 // scanning, otherwise the device is stale and the GATT connection will fail.
119 // For instance, when |adapter_| change status from unpowered to powered,
120 // |DeviceAdded| is called for each paired |device|.
121 if (adapter_->IsPowered() && discovery_session_ &&
122 discovery_session_->IsActive())
123 HandleDeviceUpdated(device);
126 void BluetoothLowEnergyConnectionFinder::HandleDeviceUpdated(
127 BluetoothDevice* device) {
128 // Ensuring only one call to |CreateConnection()| is made. A new |connection_|
129 // can be created only when the previous one disconnects, triggering a call to
130 // |OnConnectionStatusChanged|.
131 if (connection_ || !device->IsPaired())
132 return;
134 if (HasService(device) ||
135 device_whitelist_->HasDeviceWithAddress(device->GetAddress())) {
136 PA_LOG(INFO) << "Connecting to paired device " << device->GetAddress()
137 << " with service (" << HasService(device)
138 << ") or is whitelisted ("
139 << device_whitelist_->HasDeviceWithAddress(
140 device->GetAddress()) << ")";
142 connection_ = CreateConnection(device->GetAddress());
143 connection_->AddObserver(this);
144 connection_->Connect();
146 StopDiscoverySession();
150 void BluetoothLowEnergyConnectionFinder::OnAdapterInitialized(
151 scoped_refptr<BluetoothAdapter> adapter) {
152 PA_LOG(INFO) << "Adapter ready";
154 adapter_ = adapter;
155 adapter_->AddObserver(this);
157 // Note: it's not possible to connect with the paired directly, as the
158 // temporary MAC may not be resolved automatically (see crbug.com/495402). The
159 // Bluetooth adapter will fire |OnDeviceChanged| notifications for all
160 // Bluetooth Low Energy devices that are advertising.
161 StartDiscoverySession();
164 void BluetoothLowEnergyConnectionFinder::OnDiscoverySessionStarted(
165 scoped_ptr<device::BluetoothDiscoverySession> discovery_session) {
166 PA_LOG(INFO) << "Discovery session started";
167 discovery_session_ = discovery_session.Pass();
170 void BluetoothLowEnergyConnectionFinder::OnStartDiscoverySessionError() {
171 PA_LOG(WARNING) << "Error starting discovery session";
174 void BluetoothLowEnergyConnectionFinder::StartDiscoverySession() {
175 DCHECK(adapter_);
176 if (discovery_session_ && discovery_session_->IsActive()) {
177 PA_LOG(INFO) << "Discovery session already active";
178 return;
181 // Discover only low energy (LE) devices with strong enough signal.
182 scoped_ptr<BluetoothDiscoveryFilter> filter(new BluetoothDiscoveryFilter(
183 BluetoothDiscoveryFilter::Transport::TRANSPORT_LE));
184 filter->SetRSSI(kMinDiscoveryRSSI);
186 adapter_->StartDiscoverySessionWithFilter(
187 filter.Pass(),
188 base::Bind(&BluetoothLowEnergyConnectionFinder::OnDiscoverySessionStarted,
189 weak_ptr_factory_.GetWeakPtr()),
190 base::Bind(
191 &BluetoothLowEnergyConnectionFinder::OnStartDiscoverySessionError,
192 weak_ptr_factory_.GetWeakPtr()));
195 void BluetoothLowEnergyConnectionFinder::OnDiscoverySessionStopped() {
196 PA_LOG(INFO) << "Discovery session stopped";
197 discovery_session_.reset();
200 void BluetoothLowEnergyConnectionFinder::OnStopDiscoverySessionError() {
201 PA_LOG(WARNING) << "Error stopping discovery session";
204 void BluetoothLowEnergyConnectionFinder::StopDiscoverySession() {
205 PA_LOG(INFO) << "Stopping discovery sesison";
207 if (!adapter_) {
208 PA_LOG(WARNING) << "Adapter not initialized";
209 return;
211 if (!discovery_session_ || !discovery_session_->IsActive()) {
212 PA_LOG(INFO) << "No Active discovery session";
213 return;
216 discovery_session_->Stop(
217 base::Bind(&BluetoothLowEnergyConnectionFinder::OnDiscoverySessionStopped,
218 weak_ptr_factory_.GetWeakPtr()),
219 base::Bind(
220 &BluetoothLowEnergyConnectionFinder::OnStopDiscoverySessionError,
221 weak_ptr_factory_.GetWeakPtr()));
224 bool BluetoothLowEnergyConnectionFinder::HasService(
225 BluetoothDevice* remote_device) {
226 if (remote_device) {
227 PA_LOG(INFO) << "Device " << remote_device->GetAddress() << " has "
228 << remote_device->GetUUIDs().size() << " services.";
229 std::vector<device::BluetoothUUID> uuids = remote_device->GetUUIDs();
230 for (const auto& service_uuid : uuids) {
231 if (remote_service_uuid_ == service_uuid) {
232 return true;
236 return false;
239 scoped_ptr<Connection> BluetoothLowEnergyConnectionFinder::CreateConnection(
240 const std::string& device_address) {
241 RemoteDevice remote_device(std::string(), std::string(), device_address,
242 std::string());
244 return make_scoped_ptr(new BluetoothLowEnergyConnection(
245 remote_device, adapter_, remote_service_uuid_, to_peripheral_char_uuid_,
246 from_peripheral_char_uuid_, bluetooth_throttler_, max_number_of_tries_));
249 void BluetoothLowEnergyConnectionFinder::OnConnectionStatusChanged(
250 Connection* connection,
251 Connection::Status old_status,
252 Connection::Status new_status) {
253 DCHECK_EQ(connection, connection_.get());
254 PA_LOG(INFO) << "OnConnectionStatusChanged: " << old_status << " -> "
255 << new_status;
257 if (!connection_callback_.is_null() && connection_->IsConnected()) {
258 adapter_->RemoveObserver(this);
259 connection_->RemoveObserver(this);
261 // If we invoke the callback now, the callback function may install its own
262 // observer to |connection_|. Because we are in the ConnectionObserver
263 // callstack, this new observer will receive this connection event.
264 // Therefore, we need to invoke the callback asynchronously.
265 base::ThreadTaskRunnerHandle::Get()->PostTask(
266 FROM_HERE,
267 base::Bind(&BluetoothLowEnergyConnectionFinder::InvokeCallbackAsync,
268 weak_ptr_factory_.GetWeakPtr()));
269 } else if (old_status == Connection::IN_PROGRESS) {
270 PA_LOG(WARNING) << "Connection failed. Retrying.";
271 RestartDiscoverySessionWhenReady();
275 void BluetoothLowEnergyConnectionFinder::RestartDiscoverySessionWhenReady() {
276 PA_LOG(INFO) << "Trying to restart discovery.";
278 // To restart scanning for devices, it's necessary to ensure that:
279 // (i) the GATT connection to |remove_device_| is closed;
280 // (ii) there is no pending call to
281 // |device::BluetoothDiscoverySession::Stop()|.
282 // The second condition is satisfied when |OnDiscoveryStopped| is called and
283 // |discovery_session_| is reset.
284 if (!discovery_session_) {
285 PA_LOG(INFO) << "Ready to start discovery.";
286 connection_.reset();
287 StartDiscoverySession();
288 } else {
289 base::ThreadTaskRunnerHandle::Get()->PostTask(
290 FROM_HERE, base::Bind(&BluetoothLowEnergyConnectionFinder::
291 RestartDiscoverySessionWhenReady,
292 weak_ptr_factory_.GetWeakPtr()));
296 BluetoothDevice* BluetoothLowEnergyConnectionFinder::GetDevice(
297 std::string device_address) {
298 // It's not possible to simply use
299 // |adapter_->GetDevice(GetRemoteDeviceAddress())| to find the device with MAC
300 // address |GetRemoteDeviceAddress()|. For paired devices,
301 // BluetoothAdapter::GetDevice(XXX) searches for the temporary MAC address
302 // XXX, whereas |remote_device_.bluetooth_address| is the real MAC address.
303 // This is a bug in the way device::BluetoothAdapter is storing the devices
304 // (see crbug.com/497841).
305 std::vector<BluetoothDevice*> devices = adapter_->GetDevices();
306 for (const auto& device : devices) {
307 if (device->GetAddress() == device_address)
308 return device;
310 return nullptr;
313 void BluetoothLowEnergyConnectionFinder::InvokeCallbackAsync() {
314 connection_callback_.Run(connection_.Pass());
317 } // namespace proximity_auth