Extension syncing: Introduce a NeedsSync pref
[chromium-blink-merge.git] / components / proximity_auth / ble / bluetooth_low_energy_connection.cc
bloba788fa220467f9e88689402b0d1f9023778573c6
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/logging/logging.h"
15 #include "components/proximity_auth/wire_message.h"
16 #include "device/bluetooth/bluetooth_adapter.h"
17 #include "device/bluetooth/bluetooth_device.h"
18 #include "device/bluetooth/bluetooth_gatt_characteristic.h"
19 #include "device/bluetooth/bluetooth_gatt_connection.h"
20 #include "device/bluetooth/bluetooth_gatt_notify_session.h"
21 #include "device/bluetooth/bluetooth_uuid.h"
23 using device::BluetoothAdapter;
24 using device::BluetoothDevice;
25 using device::BluetoothGattConnection;
26 using device::BluetoothGattService;
27 using device::BluetoothGattCharacteristic;
28 using device::BluetoothGattNotifySession;
29 using device::BluetoothUUID;
31 namespace proximity_auth {
32 namespace {
34 // Deprecated signal send as the first byte in send byte operations.
35 const int kFirstByteZero = 0;
37 // The maximum number of bytes written in a remote characteristic with a single
38 // request.
39 const int kMaxChunkSize = 100;
41 } // namespace
43 BluetoothLowEnergyConnection::BluetoothLowEnergyConnection(
44 const RemoteDevice& device,
45 scoped_refptr<device::BluetoothAdapter> adapter,
46 const BluetoothUUID remote_service_uuid,
47 const BluetoothUUID to_peripheral_char_uuid,
48 const BluetoothUUID from_peripheral_char_uuid,
49 scoped_ptr<BluetoothGattConnection> gatt_connection,
50 int max_number_of_write_attempts)
51 : Connection(device),
52 adapter_(adapter),
53 remote_service_({remote_service_uuid, ""}),
54 to_peripheral_char_({to_peripheral_char_uuid, ""}),
55 from_peripheral_char_({from_peripheral_char_uuid, ""}),
56 gatt_connection_(gatt_connection.Pass()),
57 sub_status_(SubStatus::DISCONNECTED),
58 receiving_bytes_(false),
59 write_remote_characteristic_pending_(false),
60 max_number_of_write_attempts_(max_number_of_write_attempts),
61 max_chunk_size_(kMaxChunkSize),
62 weak_ptr_factory_(this) {
63 DCHECK(adapter_);
64 DCHECK(adapter_->IsInitialized());
66 start_time_ = base::TimeTicks::Now();
67 adapter_->AddObserver(this);
70 BluetoothLowEnergyConnection::~BluetoothLowEnergyConnection() {
71 Disconnect();
72 if (adapter_) {
73 adapter_->RemoveObserver(this);
74 adapter_ = NULL;
78 void BluetoothLowEnergyConnection::Connect() {
79 if (gatt_connection_ && gatt_connection_->IsConnected()) {
80 OnGattConnectionCreated(gatt_connection_.Pass());
81 return;
84 BluetoothDevice* remote_device = GetRemoteDevice();
85 if (remote_device) {
86 SetSubStatus(SubStatus::WAITING_GATT_CONNECTION);
87 remote_device->CreateGattConnection(
88 base::Bind(&BluetoothLowEnergyConnection::OnGattConnectionCreated,
89 weak_ptr_factory_.GetWeakPtr()),
90 base::Bind(&BluetoothLowEnergyConnection::OnCreateGattConnectionError,
91 weak_ptr_factory_.GetWeakPtr()));
95 void BluetoothLowEnergyConnection::Disconnect() {
96 if (sub_status_ != SubStatus::DISCONNECTED) {
97 ClearWriteRequestsQueue();
98 StopNotifySession();
99 characteristic_finder_.reset();
100 if (gatt_connection_) {
101 PA_LOG(INFO) << "Disconnect from device "
102 << gatt_connection_->GetDeviceAddress();
104 // Destroying BluetoothGattConnection also disconnects it.
105 gatt_connection_.reset();
108 // Only transition to the DISCONNECTED state after perfoming all necessary
109 // operations. Otherwise, it'll trigger observers that can pontentially
110 // destroy the current instance (causing a crash).
111 SetSubStatus(SubStatus::DISCONNECTED);
115 void BluetoothLowEnergyConnection::SetSubStatus(SubStatus new_sub_status) {
116 sub_status_ = new_sub_status;
118 // Sets the status of parent class proximity_auth::Connection accordingly.
119 if (new_sub_status == SubStatus::CONNECTED) {
120 SetStatus(CONNECTED);
121 } else if (new_sub_status == SubStatus::DISCONNECTED) {
122 SetStatus(DISCONNECTED);
123 } else {
124 SetStatus(IN_PROGRESS);
128 void BluetoothLowEnergyConnection::SendMessageImpl(
129 scoped_ptr<WireMessage> message) {
130 PA_LOG(INFO) << "Sending message " << message->Serialize();
132 std::string serialized_msg = message->Serialize();
134 WriteRequest write_request = BuildWriteRequest(
135 ToByteVector(static_cast<uint32>(ControlSignal::kSendSignal)),
136 ToByteVector(static_cast<uint32>(serialized_msg.size())), false);
137 WriteRemoteCharacteristic(write_request);
139 // Each chunk has to include a deprecated signal: |kFirstByteZero| as the
140 // first byte.
141 int chunk_size = max_chunk_size_ - 1;
142 std::vector<uint8> kFirstByteZeroVector;
143 kFirstByteZeroVector.push_back(static_cast<uint8>(kFirstByteZero));
145 int message_size = static_cast<int>(serialized_msg.size());
146 int start_index = 0;
147 while (start_index < message_size) {
148 int end_index = (start_index + chunk_size) <= message_size
149 ? (start_index + chunk_size)
150 : message_size;
151 bool is_last_write_request = (end_index == message_size);
152 write_request = BuildWriteRequest(
153 kFirstByteZeroVector,
154 std::vector<uint8>(serialized_msg.begin() + start_index,
155 serialized_msg.begin() + end_index),
156 is_last_write_request);
157 WriteRemoteCharacteristic(write_request);
158 start_index = end_index;
162 // Changes in the GATT connection with the remote device should be observed
163 // here. If the GATT connection is dropped, we should call Disconnect() anyway,
164 // so the object can notify its observers put itself in the right state.
165 void BluetoothLowEnergyConnection::DeviceChanged(BluetoothAdapter* adapter,
166 BluetoothDevice* device) {
167 if (sub_status_ == SubStatus::DISCONNECTED)
168 return;
170 if (device && device->GetAddress() == GetRemoteDeviceAddress() &&
171 !device->IsConnected()) {
172 PA_LOG(INFO) << "GATT connection dropped " << GetRemoteDeviceAddress()
173 << "\ndevice connected: " << device->IsConnected()
174 << "\ngatt connection: "
175 << (gatt_connection_ ? gatt_connection_->IsConnected()
176 : false);
177 Disconnect();
181 void BluetoothLowEnergyConnection::DeviceRemoved(BluetoothAdapter* adapter,
182 BluetoothDevice* device) {
183 if (sub_status_ == SubStatus::DISCONNECTED)
184 return;
186 if (device && device->GetAddress() == GetRemoteDeviceAddress()) {
187 PA_LOG(INFO) << "Device removed " << GetRemoteDeviceAddress();
188 Disconnect();
192 void BluetoothLowEnergyConnection::GattCharacteristicValueChanged(
193 BluetoothAdapter* adapter,
194 BluetoothGattCharacteristic* characteristic,
195 const std::vector<uint8>& value) {
196 if (sub_status_ == SubStatus::DISCONNECTED)
197 return;
199 DCHECK_EQ(adapter, adapter_.get());
201 PA_LOG(INFO) << "Characteristic value changed: "
202 << characteristic->GetUUID().canonical_value();
204 if (characteristic->GetIdentifier() == from_peripheral_char_.id) {
205 if (receiving_bytes_) {
206 // Ignoring the first byte, as it contains a deprecated signal.
207 const std::string bytes(value.begin() + 1, value.end());
208 incoming_bytes_buffer_.append(bytes);
209 if (incoming_bytes_buffer_.size() >= expected_number_of_incoming_bytes_) {
210 OnBytesReceived(incoming_bytes_buffer_);
211 receiving_bytes_ = false;
213 return;
216 if (value.size() < 4) {
217 PA_LOG(WARNING) << "Incoming data corrupted, no signal found.";
218 return;
221 const ControlSignal signal = static_cast<ControlSignal>(ToUint32(value));
222 switch (signal) {
223 case ControlSignal::kInvitationResponseSignal:
224 if (sub_status() == SubStatus::WAITING_RESPONSE_SIGNAL)
225 CompleteConnection();
226 break;
227 case ControlSignal::kInviteToConnectSignal:
228 break;
229 case ControlSignal::kSendSignal: {
230 if (value.size() < 8) {
231 PA_LOG(WARNING)
232 << "Incoming data corrupted, expected message size not found.";
233 return;
235 std::vector<uint8> size(value.begin() + 4, value.end());
236 expected_number_of_incoming_bytes_ =
237 static_cast<size_t>(ToUint32(size));
238 receiving_bytes_ = true;
239 incoming_bytes_buffer_.clear();
240 break;
242 case ControlSignal::kDisconnectSignal:
243 Disconnect();
244 break;
249 BluetoothLowEnergyConnection::WriteRequest::WriteRequest(
250 const std::vector<uint8>& val,
251 bool flag)
252 : value(val),
253 is_last_write_for_wire_message(flag),
254 number_of_failed_attempts(0) {
257 BluetoothLowEnergyConnection::WriteRequest::~WriteRequest() {
260 scoped_ptr<WireMessage> BluetoothLowEnergyConnection::DeserializeWireMessage(
261 bool* is_incomplete_message) {
262 return FakeWireMessage::Deserialize(received_bytes(), is_incomplete_message);
265 void BluetoothLowEnergyConnection::CompleteConnection() {
266 PA_LOG(INFO) << "Connection completed. Time elapsed: "
267 << base::TimeTicks::Now() - start_time_;
268 SetSubStatus(SubStatus::CONNECTED);
271 void BluetoothLowEnergyConnection::OnCreateGattConnectionError(
272 device::BluetoothDevice::ConnectErrorCode error_code) {
273 PA_LOG(WARNING) << "Error creating GATT connection to "
274 << remote_device().bluetooth_address
275 << "error code: " << error_code;
276 Disconnect();
279 void BluetoothLowEnergyConnection::OnGattConnectionCreated(
280 scoped_ptr<device::BluetoothGattConnection> gatt_connection) {
281 gatt_connection_ = gatt_connection.Pass();
282 SetSubStatus(SubStatus::WAITING_CHARACTERISTICS);
283 characteristic_finder_.reset(CreateCharacteristicsFinder(
284 base::Bind(&BluetoothLowEnergyConnection::OnCharacteristicsFound,
285 weak_ptr_factory_.GetWeakPtr()),
286 base::Bind(&BluetoothLowEnergyConnection::OnCharacteristicsFinderError,
287 weak_ptr_factory_.GetWeakPtr())));
290 BluetoothLowEnergyCharacteristicsFinder*
291 BluetoothLowEnergyConnection::CreateCharacteristicsFinder(
292 const BluetoothLowEnergyCharacteristicsFinder::SuccessCallback&
293 success_callback,
294 const BluetoothLowEnergyCharacteristicsFinder::ErrorCallback&
295 error_callback) {
296 return new BluetoothLowEnergyCharacteristicsFinder(
297 adapter_, GetRemoteDevice(), remote_service_, to_peripheral_char_,
298 from_peripheral_char_, success_callback, error_callback);
301 void BluetoothLowEnergyConnection::OnCharacteristicsFound(
302 const RemoteAttribute& service,
303 const RemoteAttribute& to_peripheral_char,
304 const RemoteAttribute& from_peripheral_char) {
305 remote_service_ = service;
306 to_peripheral_char_ = to_peripheral_char;
307 from_peripheral_char_ = from_peripheral_char;
309 SetSubStatus(SubStatus::CHARACTERISTICS_FOUND);
310 StartNotifySession();
313 void BluetoothLowEnergyConnection::OnCharacteristicsFinderError(
314 const RemoteAttribute& to_peripheral_char,
315 const RemoteAttribute& from_peripheral_char) {
316 PA_LOG(WARNING) << "Connection error, missing characteristics for SmartLock "
317 "service.\n"
318 << (to_peripheral_char.id.empty()
319 ? to_peripheral_char.uuid.canonical_value()
320 : "")
321 << (from_peripheral_char.id.empty()
322 ? ", " + from_peripheral_char.uuid.canonical_value()
323 : "") << " not found.";
325 Disconnect();
328 void BluetoothLowEnergyConnection::StartNotifySession() {
329 if (sub_status() == SubStatus::CHARACTERISTICS_FOUND) {
330 BluetoothGattCharacteristic* characteristic =
331 GetGattCharacteristic(from_peripheral_char_.id);
332 DCHECK(characteristic);
334 // This is a workaround for crbug.com/507325. If |characteristic| is already
335 // notifying |characteristic->StartNotifySession()| will fail with
336 // GATT_ERROR_FAILED.
337 if (characteristic->IsNotifying()) {
338 PA_LOG(INFO) << characteristic->GetUUID().canonical_value()
339 << " already notifying.";
340 SetSubStatus(SubStatus::NOTIFY_SESSION_READY);
341 SendInviteToConnectSignal();
342 return;
345 SetSubStatus(SubStatus::WAITING_NOTIFY_SESSION);
346 characteristic->StartNotifySession(
347 base::Bind(&BluetoothLowEnergyConnection::OnNotifySessionStarted,
348 weak_ptr_factory_.GetWeakPtr()),
349 base::Bind(&BluetoothLowEnergyConnection::OnNotifySessionError,
350 weak_ptr_factory_.GetWeakPtr()));
354 void BluetoothLowEnergyConnection::OnNotifySessionError(
355 BluetoothGattService::GattErrorCode error) {
356 PA_LOG(WARNING) << "Error starting notification session: " << error;
357 Disconnect();
360 void BluetoothLowEnergyConnection::OnNotifySessionStarted(
361 scoped_ptr<BluetoothGattNotifySession> notify_session) {
362 PA_LOG(INFO) << "Notification session started "
363 << notify_session->GetCharacteristicIdentifier();
365 SetSubStatus(SubStatus::NOTIFY_SESSION_READY);
366 notify_session_ = notify_session.Pass();
368 // Sends an invite to connect signal if ready.
369 SendInviteToConnectSignal();
372 void BluetoothLowEnergyConnection::StopNotifySession() {
373 if (notify_session_) {
374 notify_session_->Stop(base::Bind(&base::DoNothing));
375 notify_session_.reset();
379 void BluetoothLowEnergyConnection::SendInviteToConnectSignal() {
380 if (sub_status() == SubStatus::NOTIFY_SESSION_READY) {
381 PA_LOG(INFO) << "Sending invite to connect signal";
382 SetSubStatus(SubStatus::WAITING_RESPONSE_SIGNAL);
384 WriteRequest write_request = BuildWriteRequest(
385 ToByteVector(
386 static_cast<uint32>(ControlSignal::kInviteToConnectSignal)),
387 std::vector<uint8>(), false);
389 WriteRemoteCharacteristic(write_request);
393 void BluetoothLowEnergyConnection::WriteRemoteCharacteristic(
394 WriteRequest request) {
395 write_requests_queue_.push(request);
396 ProcessNextWriteRequest();
399 void BluetoothLowEnergyConnection::ProcessNextWriteRequest() {
400 BluetoothGattCharacteristic* characteristic =
401 GetGattCharacteristic(to_peripheral_char_.id);
402 if (!write_requests_queue_.empty() && !write_remote_characteristic_pending_ &&
403 characteristic) {
404 write_remote_characteristic_pending_ = true;
405 WriteRequest next_request = write_requests_queue_.front();
406 characteristic->WriteRemoteCharacteristic(
407 next_request.value,
408 base::Bind(&BluetoothLowEnergyConnection::OnRemoteCharacteristicWritten,
409 weak_ptr_factory_.GetWeakPtr(),
410 next_request.is_last_write_for_wire_message),
411 base::Bind(
412 &BluetoothLowEnergyConnection::OnWriteRemoteCharacteristicError,
413 weak_ptr_factory_.GetWeakPtr(),
414 next_request.is_last_write_for_wire_message));
418 void BluetoothLowEnergyConnection::OnRemoteCharacteristicWritten(
419 bool run_did_send_message_callback) {
420 write_remote_characteristic_pending_ = false;
421 // TODO(sacomoto): Actually pass the current message to the observer.
422 if (run_did_send_message_callback)
423 OnDidSendMessage(FakeWireMessage(""), true);
425 // Removes the top of queue (already processed) and process the next request.
426 DCHECK(!write_requests_queue_.empty());
427 write_requests_queue_.pop();
428 ProcessNextWriteRequest();
431 void BluetoothLowEnergyConnection::OnWriteRemoteCharacteristicError(
432 bool run_did_send_message_callback,
433 BluetoothGattService::GattErrorCode error) {
434 PA_LOG(WARNING) << "Error " << error << " writing characteristic: "
435 << to_peripheral_char_.uuid.canonical_value();
436 write_remote_characteristic_pending_ = false;
437 // TODO(sacomoto): Actually pass the current message to the observer.
438 if (run_did_send_message_callback)
439 OnDidSendMessage(FakeWireMessage(""), false);
441 // Increases the number of failed attempts and retry.
442 DCHECK(!write_requests_queue_.empty());
443 if (++write_requests_queue_.front().number_of_failed_attempts >=
444 max_number_of_write_attempts_) {
445 Disconnect();
446 return;
448 ProcessNextWriteRequest();
451 BluetoothLowEnergyConnection::WriteRequest
452 BluetoothLowEnergyConnection::BuildWriteRequest(
453 const std::vector<uint8>& signal,
454 const std::vector<uint8>& bytes,
455 bool is_last_write_for_wire_message) {
456 std::vector<uint8> value(signal.begin(), signal.end());
457 value.insert(value.end(), bytes.begin(), bytes.end());
458 return WriteRequest(value, is_last_write_for_wire_message);
461 void BluetoothLowEnergyConnection::ClearWriteRequestsQueue() {
462 while (!write_requests_queue_.empty())
463 write_requests_queue_.pop();
466 const std::string& BluetoothLowEnergyConnection::GetRemoteDeviceAddress() {
467 return remote_device().bluetooth_address;
470 BluetoothDevice* BluetoothLowEnergyConnection::GetRemoteDevice() {
471 // It's not possible to simply use
472 // |adapter_->GetDevice(GetRemoteDeviceAddress())| to find the device with MAC
473 // address |GetRemoteDeviceAddress()|. For paired devices,
474 // BluetoothAdapter::GetDevice(XXX) searches for the temporary MAC address
475 // XXX, whereas |GetRemoteDeviceAddress()| is the real MAC address. This is a
476 // bug in the way device::BluetoothAdapter is storing the devices (see
477 // crbug.com/497841).
478 std::vector<BluetoothDevice*> devices = adapter_->GetDevices();
479 for (const auto& device : devices) {
480 if (device->GetAddress() == GetRemoteDeviceAddress())
481 return device;
484 return nullptr;
487 BluetoothGattService* BluetoothLowEnergyConnection::GetRemoteService() {
488 BluetoothDevice* remote_device = GetRemoteDevice();
489 if (!remote_device) {
490 PA_LOG(WARNING) << "Remote device not found.";
491 return NULL;
493 if (remote_service_.id.empty()) {
494 std::vector<BluetoothGattService*> services =
495 remote_device->GetGattServices();
496 for (const auto& service : services)
497 if (service->GetUUID() == remote_service_.uuid) {
498 remote_service_.id = service->GetIdentifier();
499 break;
502 return remote_device->GetGattService(remote_service_.id);
505 BluetoothGattCharacteristic*
506 BluetoothLowEnergyConnection::GetGattCharacteristic(
507 const std::string& gatt_characteristic) {
508 BluetoothGattService* remote_service = GetRemoteService();
509 if (!remote_service) {
510 PA_LOG(WARNING) << "Remote service not found.";
511 return NULL;
513 return remote_service->GetCharacteristic(gatt_characteristic);
516 // TODO(sacomoto): make this robust to byte ordering in both sides of the
517 // SmartLock BLE socket.
518 uint32 BluetoothLowEnergyConnection::ToUint32(const std::vector<uint8>& bytes) {
519 return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24);
522 // TODO(sacomoto): make this robust to byte ordering in both sides of the
523 // SmartLock BLE socket.
524 const std::vector<uint8> BluetoothLowEnergyConnection::ToByteVector(
525 const uint32 value) {
526 std::vector<uint8> bytes(4, 0);
527 bytes[0] = static_cast<uint8>(value);
528 bytes[1] = static_cast<uint8>(value >> 8);
529 bytes[2] = static_cast<uint8>(value >> 16);
530 bytes[3] = static_cast<uint8>(value >> 24);
531 return bytes;
534 } // namespace proximity_auth