Don't preload rarely seen large images
[chromium-blink-merge.git] / components / proximity_auth / ble / bluetooth_low_energy_connection.cc
blob11b4a2fd69478aab6b3d6e525b92f2d33fe3bb50
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 SetSubStatus(SubStatus::DISCONNECTED);
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();
110 void BluetoothLowEnergyConnection::OnDisconnected() {
111 PA_LOG(INFO) << "Disconnected.";
114 void BluetoothLowEnergyConnection::OnDisconnectError() {
115 PA_LOG(WARNING) << "Disconnection failed.";
118 void BluetoothLowEnergyConnection::SetSubStatus(SubStatus new_sub_status) {
119 sub_status_ = new_sub_status;
121 // Sets the status of parent class proximity_auth::Connection accordingly.
122 if (new_sub_status == SubStatus::CONNECTED) {
123 SetStatus(CONNECTED);
124 } else if (new_sub_status == SubStatus::DISCONNECTED) {
125 SetStatus(DISCONNECTED);
126 } else {
127 SetStatus(IN_PROGRESS);
131 void BluetoothLowEnergyConnection::SendMessageImpl(
132 scoped_ptr<WireMessage> message) {
133 PA_LOG(INFO) << "Sending message " << message->Serialize();
135 std::string serialized_msg = message->Serialize();
137 WriteRequest write_request = BuildWriteRequest(
138 ToByteVector(static_cast<uint32>(ControlSignal::kSendSignal)),
139 ToByteVector(static_cast<uint32>(serialized_msg.size())), false);
140 WriteRemoteCharacteristic(write_request);
142 // Each chunk has to include a deprecated signal: |kFirstByteZero| as the
143 // first byte.
144 int chunk_size = max_chunk_size_ - 1;
145 std::vector<uint8> kFirstByteZeroVector;
146 kFirstByteZeroVector.push_back(static_cast<uint8>(kFirstByteZero));
148 int message_size = static_cast<int>(serialized_msg.size());
149 int start_index = 0;
150 while (start_index < message_size) {
151 int end_index = (start_index + chunk_size) <= message_size
152 ? (start_index + chunk_size)
153 : message_size;
154 bool is_last_write_request = (end_index == message_size);
155 write_request = BuildWriteRequest(
156 kFirstByteZeroVector,
157 std::vector<uint8>(serialized_msg.begin() + start_index,
158 serialized_msg.begin() + end_index),
159 is_last_write_request);
160 WriteRemoteCharacteristic(write_request);
161 start_index = end_index;
165 // Changes in the GATT connection with the remote device should be observed
166 // here. If the GATT connection is dropped, we should call Disconnect() anyway,
167 // so the object can notify its observers put itself in the right state.
168 void BluetoothLowEnergyConnection::DeviceChanged(BluetoothAdapter* adapter,
169 BluetoothDevice* device) {
170 if (device && device->GetAddress() == GetRemoteDeviceAddress() &&
171 !device->IsConnected()) {
172 PA_LOG(INFO) << "GATT connection dropped " << GetRemoteDeviceAddress();
173 Disconnect();
177 void BluetoothLowEnergyConnection::DeviceRemoved(BluetoothAdapter* adapter,
178 BluetoothDevice* device) {
179 if (device && device->GetAddress() == GetRemoteDeviceAddress()) {
180 PA_LOG(INFO) << "Device removed " << GetRemoteDeviceAddress();
181 Disconnect();
185 void BluetoothLowEnergyConnection::GattCharacteristicValueChanged(
186 BluetoothAdapter* adapter,
187 BluetoothGattCharacteristic* characteristic,
188 const std::vector<uint8>& value) {
189 DCHECK_EQ(adapter, adapter_.get());
191 PA_LOG(INFO) << "Characteristic value changed: "
192 << characteristic->GetUUID().canonical_value();
194 if (characteristic->GetIdentifier() == from_peripheral_char_.id) {
195 if (receiving_bytes_) {
196 // Ignoring the first byte, as it contains a deprecated signal.
197 const std::string bytes(value.begin() + 1, value.end());
198 incoming_bytes_buffer_.append(bytes);
199 if (incoming_bytes_buffer_.size() >= expected_number_of_incoming_bytes_) {
200 OnBytesReceived(incoming_bytes_buffer_);
201 receiving_bytes_ = false;
203 return;
206 if (value.size() < 4) {
207 PA_LOG(WARNING) << "Incoming data corrupted, no signal found.";
208 return;
211 const ControlSignal signal = static_cast<ControlSignal>(ToUint32(value));
212 switch (signal) {
213 case ControlSignal::kInvitationResponseSignal:
214 if (sub_status() == SubStatus::WAITING_RESPONSE_SIGNAL)
215 CompleteConnection();
216 break;
217 case ControlSignal::kInviteToConnectSignal:
218 break;
219 case ControlSignal::kSendSignal: {
220 if (value.size() < 8) {
221 PA_LOG(WARNING)
222 << "Incoming data corrupted, expected message size not found.";
223 return;
225 std::vector<uint8> size(value.begin() + 4, value.end());
226 expected_number_of_incoming_bytes_ =
227 static_cast<size_t>(ToUint32(size));
228 receiving_bytes_ = true;
229 incoming_bytes_buffer_.clear();
230 break;
232 case ControlSignal::kDisconnectSignal:
233 Disconnect();
234 break;
239 BluetoothLowEnergyConnection::WriteRequest::WriteRequest(
240 const std::vector<uint8>& val,
241 bool flag)
242 : value(val),
243 is_last_write_for_wire_message(flag),
244 number_of_failed_attempts(0) {
247 BluetoothLowEnergyConnection::WriteRequest::~WriteRequest() {
250 scoped_ptr<WireMessage> BluetoothLowEnergyConnection::DeserializeWireMessage(
251 bool* is_incomplete_message) {
252 return FakeWireMessage::Deserialize(received_bytes(), is_incomplete_message);
255 void BluetoothLowEnergyConnection::CompleteConnection() {
256 PA_LOG(INFO) << "Connection completed. Time elapsed: "
257 << base::TimeTicks::Now() - start_time_;
258 SetSubStatus(SubStatus::CONNECTED);
261 void BluetoothLowEnergyConnection::OnCreateGattConnectionError(
262 device::BluetoothDevice::ConnectErrorCode error_code) {
263 PA_LOG(WARNING) << "Error creating GATT connection to "
264 << remote_device().bluetooth_address
265 << "error code: " << error_code;
266 Disconnect();
269 void BluetoothLowEnergyConnection::OnGattConnectionCreated(
270 scoped_ptr<device::BluetoothGattConnection> gatt_connection) {
271 gatt_connection_ = gatt_connection.Pass();
272 SetSubStatus(SubStatus::WAITING_CHARACTERISTICS);
273 characteristic_finder_.reset(CreateCharacteristicsFinder(
274 base::Bind(&BluetoothLowEnergyConnection::OnCharacteristicsFound,
275 weak_ptr_factory_.GetWeakPtr()),
276 base::Bind(&BluetoothLowEnergyConnection::OnCharacteristicsFinderError,
277 weak_ptr_factory_.GetWeakPtr())));
280 BluetoothLowEnergyCharacteristicsFinder*
281 BluetoothLowEnergyConnection::CreateCharacteristicsFinder(
282 const BluetoothLowEnergyCharacteristicsFinder::SuccessCallback&
283 success_callback,
284 const BluetoothLowEnergyCharacteristicsFinder::ErrorCallback&
285 error_callback) {
286 return new BluetoothLowEnergyCharacteristicsFinder(
287 adapter_, GetRemoteDevice(), remote_service_, to_peripheral_char_,
288 from_peripheral_char_, success_callback, error_callback);
291 void BluetoothLowEnergyConnection::OnCharacteristicsFound(
292 const RemoteAttribute& service,
293 const RemoteAttribute& to_peripheral_char,
294 const RemoteAttribute& from_peripheral_char) {
295 remote_service_ = service;
296 to_peripheral_char_ = to_peripheral_char;
297 from_peripheral_char_ = from_peripheral_char;
299 SetSubStatus(SubStatus::CHARACTERISTICS_FOUND);
300 StartNotifySession();
303 void BluetoothLowEnergyConnection::OnCharacteristicsFinderError(
304 const RemoteAttribute& to_peripheral_char,
305 const RemoteAttribute& from_peripheral_char) {
306 PA_LOG(WARNING) << "Connection error, missing characteristics for SmartLock "
307 "service.\n"
308 << (to_peripheral_char.id.empty()
309 ? to_peripheral_char.uuid.canonical_value()
310 : "")
311 << (from_peripheral_char.id.empty()
312 ? ", " + from_peripheral_char.uuid.canonical_value()
313 : "") << " not found.";
315 Disconnect();
318 void BluetoothLowEnergyConnection::StartNotifySession() {
319 if (sub_status() == SubStatus::CHARACTERISTICS_FOUND) {
320 BluetoothGattCharacteristic* characteristic =
321 GetGattCharacteristic(from_peripheral_char_.id);
322 DCHECK(characteristic);
324 SetSubStatus(SubStatus::WAITING_NOTIFY_SESSION);
325 characteristic->StartNotifySession(
326 base::Bind(&BluetoothLowEnergyConnection::OnNotifySessionStarted,
327 weak_ptr_factory_.GetWeakPtr()),
328 base::Bind(&BluetoothLowEnergyConnection::OnNotifySessionError,
329 weak_ptr_factory_.GetWeakPtr()));
333 void BluetoothLowEnergyConnection::OnNotifySessionError(
334 BluetoothGattService::GattErrorCode error) {
335 PA_LOG(WARNING) << "Error starting notification session: " << error;
336 Disconnect();
339 void BluetoothLowEnergyConnection::OnNotifySessionStarted(
340 scoped_ptr<BluetoothGattNotifySession> notify_session) {
341 PA_LOG(INFO) << "Notification session started "
342 << notify_session->GetCharacteristicIdentifier();
344 SetSubStatus(SubStatus::NOTIFY_SESSION_READY);
345 notify_session_ = notify_session.Pass();
347 // Sends an invite to connect signal if ready.
348 SendInviteToConnectSignal();
351 void BluetoothLowEnergyConnection::StopNotifySession() {
352 if (notify_session_) {
353 notify_session_->Stop(base::Bind(&base::DoNothing));
354 notify_session_.reset();
358 void BluetoothLowEnergyConnection::SendInviteToConnectSignal() {
359 if (sub_status() == SubStatus::NOTIFY_SESSION_READY) {
360 PA_LOG(INFO) << "Sending invite to connect signal";
361 SetSubStatus(SubStatus::WAITING_RESPONSE_SIGNAL);
363 WriteRequest write_request = BuildWriteRequest(
364 ToByteVector(
365 static_cast<uint32>(ControlSignal::kInviteToConnectSignal)),
366 std::vector<uint8>(), false);
368 WriteRemoteCharacteristic(write_request);
372 void BluetoothLowEnergyConnection::WriteRemoteCharacteristic(
373 WriteRequest request) {
374 write_requests_queue_.push(request);
375 ProcessNextWriteRequest();
378 void BluetoothLowEnergyConnection::ProcessNextWriteRequest() {
379 BluetoothGattCharacteristic* characteristic =
380 GetGattCharacteristic(to_peripheral_char_.id);
381 if (!write_requests_queue_.empty() && !write_remote_characteristic_pending_ &&
382 characteristic) {
383 write_remote_characteristic_pending_ = true;
384 WriteRequest next_request = write_requests_queue_.front();
385 characteristic->WriteRemoteCharacteristic(
386 next_request.value,
387 base::Bind(&BluetoothLowEnergyConnection::OnRemoteCharacteristicWritten,
388 weak_ptr_factory_.GetWeakPtr(),
389 next_request.is_last_write_for_wire_message),
390 base::Bind(
391 &BluetoothLowEnergyConnection::OnWriteRemoteCharacteristicError,
392 weak_ptr_factory_.GetWeakPtr(),
393 next_request.is_last_write_for_wire_message));
397 void BluetoothLowEnergyConnection::OnRemoteCharacteristicWritten(
398 bool run_did_send_message_callback) {
399 write_remote_characteristic_pending_ = false;
400 // TODO(sacomoto): Actually pass the current message to the observer.
401 if (run_did_send_message_callback)
402 OnDidSendMessage(FakeWireMessage(""), true);
404 // Removes the top of queue (already processed) and process the next request.
405 DCHECK(!write_requests_queue_.empty());
406 write_requests_queue_.pop();
407 ProcessNextWriteRequest();
410 void BluetoothLowEnergyConnection::OnWriteRemoteCharacteristicError(
411 bool run_did_send_message_callback,
412 BluetoothGattService::GattErrorCode error) {
413 PA_LOG(WARNING) << "Error " << error << " writing characteristic: "
414 << to_peripheral_char_.uuid.canonical_value();
415 write_remote_characteristic_pending_ = false;
416 // TODO(sacomoto): Actually pass the current message to the observer.
417 if (run_did_send_message_callback)
418 OnDidSendMessage(FakeWireMessage(""), false);
420 // Increases the number of failed attempts and retry.
421 DCHECK(!write_requests_queue_.empty());
422 if (++write_requests_queue_.front().number_of_failed_attempts >=
423 max_number_of_write_attempts_) {
424 Disconnect();
425 return;
427 ProcessNextWriteRequest();
430 BluetoothLowEnergyConnection::WriteRequest
431 BluetoothLowEnergyConnection::BuildWriteRequest(
432 const std::vector<uint8>& signal,
433 const std::vector<uint8>& bytes,
434 bool is_last_write_for_wire_message) {
435 std::vector<uint8> value(signal.begin(), signal.end());
436 value.insert(value.end(), bytes.begin(), bytes.end());
437 return WriteRequest(value, is_last_write_for_wire_message);
440 void BluetoothLowEnergyConnection::ClearWriteRequestsQueue() {
441 while (!write_requests_queue_.empty())
442 write_requests_queue_.pop();
445 const std::string& BluetoothLowEnergyConnection::GetRemoteDeviceAddress() {
446 return remote_device().bluetooth_address;
449 BluetoothDevice* BluetoothLowEnergyConnection::GetRemoteDevice() {
450 // It's not possible to simply use
451 // |adapter_->GetDevice(GetRemoteDeviceAddress())| to find the device with MAC
452 // address |GetRemoteDeviceAddress()|. For paired devices,
453 // BluetoothAdapter::GetDevice(XXX) searches for the temporary MAC address
454 // XXX, whereas |GetRemoteDeviceAddress()| is the real MAC address. This is a
455 // bug in the way device::BluetoothAdapter is storing the devices (see
456 // crbug.com/497841).
457 std::vector<BluetoothDevice*> devices = adapter_->GetDevices();
458 for (const auto& device : devices) {
459 if (device->GetAddress() == GetRemoteDeviceAddress())
460 return device;
463 return nullptr;
466 BluetoothGattService* BluetoothLowEnergyConnection::GetRemoteService() {
467 BluetoothDevice* remote_device = GetRemoteDevice();
468 if (!remote_device) {
469 PA_LOG(WARNING) << "Remote device not found.";
470 return NULL;
472 if (remote_service_.id.empty()) {
473 std::vector<BluetoothGattService*> services =
474 remote_device->GetGattServices();
475 for (const auto& service : services)
476 if (service->GetUUID() == remote_service_.uuid) {
477 remote_service_.id = service->GetIdentifier();
478 break;
481 return remote_device->GetGattService(remote_service_.id);
484 BluetoothGattCharacteristic*
485 BluetoothLowEnergyConnection::GetGattCharacteristic(
486 const std::string& gatt_characteristic) {
487 BluetoothGattService* remote_service = GetRemoteService();
488 if (!remote_service) {
489 PA_LOG(WARNING) << "Remote service not found.";
490 return NULL;
492 return remote_service->GetCharacteristic(gatt_characteristic);
495 // TODO(sacomoto): make this robust to byte ordering in both sides of the
496 // SmartLock BLE socket.
497 uint32 BluetoothLowEnergyConnection::ToUint32(const std::vector<uint8>& bytes) {
498 return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24);
501 // TODO(sacomoto): make this robust to byte ordering in both sides of the
502 // SmartLock BLE socket.
503 const std::vector<uint8> BluetoothLowEnergyConnection::ToByteVector(
504 const uint32 value) {
505 std::vector<uint8> bytes(4, 0);
506 bytes[0] = static_cast<uint8>(value);
507 bytes[1] = static_cast<uint8>(value >> 8);
508 bytes[2] = static_cast<uint8>(value >> 16);
509 bytes[3] = static_cast<uint8>(value >> 24);
510 return bytes;
513 } // namespace proximity_auth