Connecting only to paired devices in BLE connection finder.
[chromium-blink-merge.git] / components / proximity_auth / ble / bluetooth_low_energy_connection.cc
blobd77f9da2a22d87110716fa7d1c23fb8d9661a95a
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.h"
7 #include "base/bind.h"
8 #include "base/memory/ref_counted.h"
9 #include "base/memory/weak_ptr.h"
10 #include "base/time/time.h"
11 #include "components/proximity_auth/ble/bluetooth_low_energy_characteristics_finder.h"
12 #include "components/proximity_auth/ble/fake_wire_message.h"
13 #include "components/proximity_auth/connection_finder.h"
14 #include "components/proximity_auth/wire_message.h"
15 #include "device/bluetooth/bluetooth_adapter.h"
16 #include "device/bluetooth/bluetooth_device.h"
17 #include "device/bluetooth/bluetooth_gatt_characteristic.h"
18 #include "device/bluetooth/bluetooth_gatt_connection.h"
19 #include "device/bluetooth/bluetooth_gatt_notify_session.h"
20 #include "device/bluetooth/bluetooth_uuid.h"
22 using device::BluetoothAdapter;
23 using device::BluetoothDevice;
24 using device::BluetoothGattConnection;
25 using device::BluetoothGattService;
26 using device::BluetoothGattCharacteristic;
27 using device::BluetoothGattNotifySession;
28 using device::BluetoothUUID;
30 namespace proximity_auth {
31 namespace {
33 // Deprecated signal send as the first byte in send byte operations.
34 const int kFirstByteZero = 0;
36 // The maximum number of bytes written in a remote characteristic with a single
37 // request.
38 const int kMaxChunkSize = 100;
40 } // namespace
42 BluetoothLowEnergyConnection::BluetoothLowEnergyConnection(
43 const RemoteDevice& device,
44 scoped_refptr<device::BluetoothAdapter> adapter,
45 const BluetoothUUID remote_service_uuid,
46 const BluetoothUUID to_peripheral_char_uuid,
47 const BluetoothUUID from_peripheral_char_uuid,
48 scoped_ptr<BluetoothGattConnection> gatt_connection,
49 int max_number_of_write_attempts)
50 : Connection(device),
51 adapter_(adapter),
52 remote_service_({remote_service_uuid, ""}),
53 to_peripheral_char_({to_peripheral_char_uuid, ""}),
54 from_peripheral_char_({from_peripheral_char_uuid, ""}),
55 connection_(gatt_connection.Pass()),
56 sub_status_(SubStatus::DISCONNECTED),
57 receiving_bytes_(false),
58 write_remote_characteristic_pending_(false),
59 max_number_of_write_attempts_(max_number_of_write_attempts),
60 max_chunk_size_(kMaxChunkSize),
61 weak_ptr_factory_(this) {
62 DCHECK(adapter_);
63 DCHECK(adapter_->IsInitialized());
65 start_time_ = base::TimeTicks::Now();
66 adapter_->AddObserver(this);
69 BluetoothLowEnergyConnection::~BluetoothLowEnergyConnection() {
70 Disconnect();
71 if (adapter_) {
72 adapter_->RemoveObserver(this);
73 adapter_ = NULL;
77 void BluetoothLowEnergyConnection::Connect() {
78 if (connection_ && connection_->IsConnected()) {
79 OnGattConnectionCreated(connection_.Pass());
80 return;
83 BluetoothDevice* remote_device = GetRemoteDevice();
84 if (remote_device) {
85 SetSubStatus(SubStatus::WAITING_GATT_CONNECTION);
86 remote_device->CreateGattConnection(
87 base::Bind(&BluetoothLowEnergyConnection::OnGattConnectionCreated,
88 weak_ptr_factory_.GetWeakPtr()),
89 base::Bind(&BluetoothLowEnergyConnection::OnCreateGattConnectionError,
90 weak_ptr_factory_.GetWeakPtr()));
94 void BluetoothLowEnergyConnection::Disconnect() {
95 if (sub_status_ != SubStatus::DISCONNECTED) {
96 ClearWriteRequestsQueue();
97 StopNotifySession();
98 SetSubStatus(SubStatus::DISCONNECTED);
99 if (connection_) {
100 connection_.reset();
101 BluetoothDevice* device = GetRemoteDevice();
102 if (device) {
103 VLOG(1) << "Disconnect from device " << device->GetAddress();
104 device->Disconnect(
105 base::Bind(&BluetoothLowEnergyConnection::OnDisconnected,
106 weak_ptr_factory_.GetWeakPtr()),
107 base::Bind(&BluetoothLowEnergyConnection::OnDisconnectError,
108 weak_ptr_factory_.GetWeakPtr()));
114 void BluetoothLowEnergyConnection::OnDisconnected() {
115 VLOG(1) << "Disconnected.";
118 void BluetoothLowEnergyConnection::OnDisconnectError() {
119 VLOG(1) << "Disconnection failed.";
122 void BluetoothLowEnergyConnection::SetSubStatus(SubStatus new_sub_status) {
123 sub_status_ = new_sub_status;
125 // Sets the status of parent class proximity_auth::Connection accordingly.
126 if (new_sub_status == SubStatus::CONNECTED) {
127 SetStatus(CONNECTED);
128 } else if (new_sub_status == SubStatus::DISCONNECTED) {
129 SetStatus(DISCONNECTED);
130 } else {
131 SetStatus(IN_PROGRESS);
135 void BluetoothLowEnergyConnection::SendMessageImpl(
136 scoped_ptr<WireMessage> message) {
137 VLOG(1) << "Sending message " << message->Serialize();
139 std::string serialized_msg = message->Serialize();
141 WriteRequest write_request = BuildWriteRequest(
142 ToByteVector(static_cast<uint32>(ControlSignal::kSendSignal)),
143 ToByteVector(static_cast<uint32>(serialized_msg.size())), false);
144 WriteRemoteCharacteristic(write_request);
146 // Each chunk has to include a deprecated signal: |kFirstByteZero| as the
147 // first byte.
148 int chunk_size = max_chunk_size_ - 1;
149 std::vector<uint8> kFirstByteZeroVector;
150 kFirstByteZeroVector.push_back(static_cast<uint8>(kFirstByteZero));
152 int message_size = static_cast<int>(serialized_msg.size());
153 int start_index = 0;
154 while (start_index < message_size) {
155 int end_index = (start_index + chunk_size) <= message_size
156 ? (start_index + chunk_size)
157 : message_size;
158 bool is_last_write_request = (end_index == message_size);
159 write_request = BuildWriteRequest(
160 kFirstByteZeroVector,
161 std::vector<uint8>(serialized_msg.begin() + start_index,
162 serialized_msg.begin() + end_index),
163 is_last_write_request);
164 WriteRemoteCharacteristic(write_request);
165 start_index = end_index;
169 void BluetoothLowEnergyConnection::DeviceRemoved(BluetoothAdapter* adapter,
170 BluetoothDevice* device) {
171 if (device && device->GetAddress() == GetRemoteDeviceAddress()) {
172 VLOG(1) << "Device removed " << GetRemoteDeviceAddress();
173 Disconnect();
177 void BluetoothLowEnergyConnection::GattCharacteristicValueChanged(
178 BluetoothAdapter* adapter,
179 BluetoothGattCharacteristic* characteristic,
180 const std::vector<uint8>& value) {
181 DCHECK_EQ(adapter, adapter_.get());
183 VLOG(1) << "Characteristic value changed: "
184 << characteristic->GetUUID().canonical_value();
186 if (characteristic->GetIdentifier() == from_peripheral_char_.id) {
187 if (receiving_bytes_) {
188 // Ignoring the first byte, as it contains a deprecated signal.
189 const std::string bytes(value.begin() + 1, value.end());
190 incoming_bytes_buffer_.append(bytes);
191 if (incoming_bytes_buffer_.size() >= expected_number_of_incoming_bytes_) {
192 OnBytesReceived(incoming_bytes_buffer_);
193 receiving_bytes_ = false;
195 return;
198 if (value.size() < 4) {
199 VLOG(1) << "Incoming data corrupted, no signal found.";
200 return;
203 const ControlSignal signal = static_cast<ControlSignal>(ToUint32(value));
204 switch (signal) {
205 case ControlSignal::kInvitationResponseSignal:
206 if (sub_status() == SubStatus::WAITING_RESPONSE_SIGNAL)
207 CompleteConnection();
208 break;
209 case ControlSignal::kInviteToConnectSignal:
210 break;
211 case ControlSignal::kSendSignal: {
212 if (value.size() < 8) {
213 VLOG(1)
214 << "Incoming data corrupted, expected message size not found.";
215 return;
217 std::vector<uint8> size(value.begin() + 4, value.end());
218 expected_number_of_incoming_bytes_ =
219 static_cast<size_t>(ToUint32(size));
220 receiving_bytes_ = true;
221 incoming_bytes_buffer_.clear();
222 break;
224 case ControlSignal::kDisconnectSignal:
225 Disconnect();
226 break;
231 BluetoothLowEnergyConnection::WriteRequest::WriteRequest(
232 const std::vector<uint8>& val,
233 bool flag)
234 : value(val),
235 is_last_write_for_wire_message(flag),
236 number_of_failed_attempts(0) {
239 BluetoothLowEnergyConnection::WriteRequest::~WriteRequest() {
242 scoped_ptr<WireMessage> BluetoothLowEnergyConnection::DeserializeWireMessage(
243 bool* is_incomplete_message) {
244 return FakeWireMessage::Deserialize(received_bytes(), is_incomplete_message);
247 void BluetoothLowEnergyConnection::CompleteConnection() {
248 VLOG(1) << "Connection completed\n"
249 << "Time elapsed: " << base::TimeTicks::Now() - start_time_;
250 SetSubStatus(SubStatus::CONNECTED);
253 void BluetoothLowEnergyConnection::OnCreateGattConnectionError(
254 device::BluetoothDevice::ConnectErrorCode error_code) {
255 VLOG(1) << "Error creating GATT connection to "
256 << remote_device().bluetooth_address << "error code: " << error_code;
257 Disconnect();
260 void BluetoothLowEnergyConnection::OnGattConnectionCreated(
261 scoped_ptr<device::BluetoothGattConnection> gatt_connection) {
262 connection_ = gatt_connection.Pass();
263 SetSubStatus(SubStatus::WAITING_CHARACTERISTICS);
264 characteristic_finder_.reset(CreateCharacteristicsFinder(
265 base::Bind(&BluetoothLowEnergyConnection::OnCharacteristicsFound,
266 weak_ptr_factory_.GetWeakPtr()),
267 base::Bind(&BluetoothLowEnergyConnection::OnCharacteristicsFinderError,
268 weak_ptr_factory_.GetWeakPtr())));
271 BluetoothLowEnergyCharacteristicsFinder*
272 BluetoothLowEnergyConnection::CreateCharacteristicsFinder(
273 const BluetoothLowEnergyCharacteristicsFinder::SuccessCallback&
274 success_callback,
275 const BluetoothLowEnergyCharacteristicsFinder::ErrorCallback&
276 error_callback) {
277 return new BluetoothLowEnergyCharacteristicsFinder(
278 adapter_, GetRemoteDevice(), remote_service_, to_peripheral_char_,
279 from_peripheral_char_, success_callback, error_callback);
282 void BluetoothLowEnergyConnection::OnCharacteristicsFound(
283 const RemoteAttribute& service,
284 const RemoteAttribute& to_peripheral_char,
285 const RemoteAttribute& from_peripheral_char) {
286 remote_service_ = service;
287 to_peripheral_char_ = to_peripheral_char;
288 from_peripheral_char_ = from_peripheral_char;
290 SetSubStatus(SubStatus::CHARACTERISTICS_FOUND);
291 StartNotifySession();
294 void BluetoothLowEnergyConnection::OnCharacteristicsFinderError(
295 const RemoteAttribute& to_peripheral_char,
296 const RemoteAttribute& from_peripheral_char) {
297 VLOG(1) << "Connection error, missing characteristics for SmartLock "
298 "service.\n" << (to_peripheral_char.id.empty()
299 ? to_peripheral_char.uuid.canonical_value()
300 : "")
301 << (from_peripheral_char.id.empty()
302 ? ", " + from_peripheral_char.uuid.canonical_value()
303 : "") << " not found.";
305 Disconnect();
308 void BluetoothLowEnergyConnection::StartNotifySession() {
309 if (sub_status() == SubStatus::CHARACTERISTICS_FOUND) {
310 BluetoothGattCharacteristic* characteristic =
311 GetGattCharacteristic(from_peripheral_char_.id);
312 DCHECK(characteristic);
314 SetSubStatus(SubStatus::WAITING_NOTIFY_SESSION);
315 characteristic->StartNotifySession(
316 base::Bind(&BluetoothLowEnergyConnection::OnNotifySessionStarted,
317 weak_ptr_factory_.GetWeakPtr()),
318 base::Bind(&BluetoothLowEnergyConnection::OnNotifySessionError,
319 weak_ptr_factory_.GetWeakPtr()));
323 void BluetoothLowEnergyConnection::OnNotifySessionError(
324 BluetoothGattService::GattErrorCode error) {
325 VLOG(1) << "Error starting notification session: " << error;
326 Disconnect();
329 void BluetoothLowEnergyConnection::OnNotifySessionStarted(
330 scoped_ptr<BluetoothGattNotifySession> notify_session) {
331 VLOG(1) << "Notification session started "
332 << notify_session->GetCharacteristicIdentifier();
334 SetSubStatus(SubStatus::NOTIFY_SESSION_READY);
335 notify_session_ = notify_session.Pass();
337 // Sends an invite to connect signal if ready.
338 SendInviteToConnectSignal();
341 void BluetoothLowEnergyConnection::StopNotifySession() {
342 if (notify_session_) {
343 notify_session_->Stop(base::Bind(&base::DoNothing));
344 notify_session_.reset();
348 void BluetoothLowEnergyConnection::SendInviteToConnectSignal() {
349 if (sub_status() == SubStatus::NOTIFY_SESSION_READY) {
350 VLOG(1) << "Sending invite to connect signal";
351 SetSubStatus(SubStatus::WAITING_RESPONSE_SIGNAL);
353 WriteRequest write_request = BuildWriteRequest(
354 ToByteVector(
355 static_cast<uint32>(ControlSignal::kInviteToConnectSignal)),
356 std::vector<uint8>(), false);
358 WriteRemoteCharacteristic(write_request);
362 void BluetoothLowEnergyConnection::WriteRemoteCharacteristic(
363 WriteRequest request) {
364 write_requests_queue_.push(request);
365 ProcessNextWriteRequest();
368 void BluetoothLowEnergyConnection::ProcessNextWriteRequest() {
369 BluetoothGattCharacteristic* characteristic =
370 GetGattCharacteristic(to_peripheral_char_.id);
371 if (!write_requests_queue_.empty() && !write_remote_characteristic_pending_ &&
372 characteristic) {
373 write_remote_characteristic_pending_ = true;
374 WriteRequest next_request = write_requests_queue_.front();
375 characteristic->WriteRemoteCharacteristic(
376 next_request.value,
377 base::Bind(&BluetoothLowEnergyConnection::OnRemoteCharacteristicWritten,
378 weak_ptr_factory_.GetWeakPtr(),
379 next_request.is_last_write_for_wire_message),
380 base::Bind(
381 &BluetoothLowEnergyConnection::OnWriteRemoteCharacteristicError,
382 weak_ptr_factory_.GetWeakPtr(),
383 next_request.is_last_write_for_wire_message));
387 void BluetoothLowEnergyConnection::OnRemoteCharacteristicWritten(
388 bool run_did_send_message_callback) {
389 write_remote_characteristic_pending_ = false;
390 // TODO(sacomoto): Actually pass the current message to the observer.
391 if (run_did_send_message_callback)
392 OnDidSendMessage(FakeWireMessage(""), true);
394 // Removes the top of queue (already processed) and process the next request.
395 DCHECK(!write_requests_queue_.empty());
396 write_requests_queue_.pop();
397 ProcessNextWriteRequest();
400 void BluetoothLowEnergyConnection::OnWriteRemoteCharacteristicError(
401 bool run_did_send_message_callback,
402 BluetoothGattService::GattErrorCode error) {
403 VLOG(1) << "Error " << error << " writing characteristic: "
404 << to_peripheral_char_.uuid.canonical_value();
405 write_remote_characteristic_pending_ = false;
406 // TODO(sacomoto): Actually pass the current message to the observer.
407 if (run_did_send_message_callback)
408 OnDidSendMessage(FakeWireMessage(""), false);
410 // Increases the number of failed attempts and retry.
411 DCHECK(!write_requests_queue_.empty());
412 if (++write_requests_queue_.front().number_of_failed_attempts >=
413 max_number_of_write_attempts_) {
414 Disconnect();
415 return;
417 ProcessNextWriteRequest();
420 BluetoothLowEnergyConnection::WriteRequest
421 BluetoothLowEnergyConnection::BuildWriteRequest(
422 const std::vector<uint8>& signal,
423 const std::vector<uint8>& bytes,
424 bool is_last_write_for_wire_message) {
425 std::vector<uint8> value(signal.begin(), signal.end());
426 value.insert(value.end(), bytes.begin(), bytes.end());
427 return WriteRequest(value, is_last_write_for_wire_message);
430 void BluetoothLowEnergyConnection::ClearWriteRequestsQueue() {
431 while (!write_requests_queue_.empty())
432 write_requests_queue_.pop();
435 const std::string& BluetoothLowEnergyConnection::GetRemoteDeviceAddress() {
436 return remote_device().bluetooth_address;
439 BluetoothDevice* BluetoothLowEnergyConnection::GetRemoteDevice() {
440 // It's not possible to simply use
441 // |adapter_->GetDevice(GetRemoteDeviceAddress())| to find the device with MAC
442 // address |GetRemoteDeviceAddress()|. For paired devices,
443 // BluetoothAdapter::GetDevice(XXX) searches for the temporary MAC address
444 // XXX, whereas |GetRemoteDeviceAddress()| is the real MAC address. This is a
445 // bug in the way device::BluetoothAdapter is storing the devices (see
446 // crbug.com/497841).
447 std::vector<BluetoothDevice*> devices = adapter_->GetDevices();
448 for (const auto& device : devices) {
449 if (device->GetAddress() == GetRemoteDeviceAddress())
450 return device;
453 return nullptr;
456 BluetoothGattService* BluetoothLowEnergyConnection::GetRemoteService() {
457 BluetoothDevice* remote_device = GetRemoteDevice();
458 if (!remote_device) {
459 VLOG(1) << "device not found";
460 return NULL;
462 if (remote_service_.id.empty()) {
463 std::vector<BluetoothGattService*> services =
464 remote_device->GetGattServices();
465 for (const auto& service : services)
466 if (service->GetUUID() == remote_service_.uuid) {
467 remote_service_.id = service->GetIdentifier();
468 break;
471 return remote_device->GetGattService(remote_service_.id);
474 BluetoothGattCharacteristic*
475 BluetoothLowEnergyConnection::GetGattCharacteristic(
476 const std::string& gatt_characteristic) {
477 BluetoothGattService* remote_service = GetRemoteService();
478 if (!remote_service) {
479 VLOG(1) << "service not found";
480 return NULL;
482 return remote_service->GetCharacteristic(gatt_characteristic);
485 // TODO(sacomoto): make this robust to byte ordering in both sides of the
486 // SmartLock BLE socket.
487 uint32 BluetoothLowEnergyConnection::ToUint32(const std::vector<uint8>& bytes) {
488 return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24);
491 // TODO(sacomoto): make this robust to byte ordering in both sides of the
492 // SmartLock BLE socket.
493 const std::vector<uint8> BluetoothLowEnergyConnection::ToByteVector(
494 const uint32 value) {
495 std::vector<uint8> bytes(4, 0);
496 bytes[0] = static_cast<uint8>(value);
497 bytes[1] = static_cast<uint8>(value >> 8);
498 bytes[2] = static_cast<uint8>(value >> 16);
499 bytes[3] = static_cast<uint8>(value >> 24);
500 return bytes;
503 } // namespace proximity_auth