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"
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
{
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
39 const int kMaxChunkSize
= 100;
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
)
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) {
64 DCHECK(adapter_
->IsInitialized());
66 start_time_
= base::TimeTicks::Now();
67 adapter_
->AddObserver(this);
70 BluetoothLowEnergyConnection::~BluetoothLowEnergyConnection() {
73 adapter_
->RemoveObserver(this);
78 void BluetoothLowEnergyConnection::Connect() {
79 if (gatt_connection_
&& gatt_connection_
->IsConnected()) {
80 OnGattConnectionCreated(gatt_connection_
.Pass());
84 BluetoothDevice
* remote_device
= GetRemoteDevice();
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();
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
);
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
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());
147 while (start_index
< message_size
) {
148 int end_index
= (start_index
+ chunk_size
) <= message_size
149 ? (start_index
+ chunk_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
)
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()
181 void BluetoothLowEnergyConnection::DeviceRemoved(BluetoothAdapter
* adapter
,
182 BluetoothDevice
* device
) {
183 if (sub_status_
== SubStatus::DISCONNECTED
)
186 if (device
&& device
->GetAddress() == GetRemoteDeviceAddress()) {
187 PA_LOG(INFO
) << "Device removed " << GetRemoteDeviceAddress();
192 void BluetoothLowEnergyConnection::GattCharacteristicValueChanged(
193 BluetoothAdapter
* adapter
,
194 BluetoothGattCharacteristic
* characteristic
,
195 const std::vector
<uint8
>& value
) {
196 if (sub_status_
== SubStatus::DISCONNECTED
)
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;
216 if (value
.size() < 4) {
217 PA_LOG(WARNING
) << "Incoming data corrupted, no signal found.";
221 const ControlSignal signal
= static_cast<ControlSignal
>(ToUint32(value
));
223 case ControlSignal::kInvitationResponseSignal
:
224 if (sub_status() == SubStatus::WAITING_RESPONSE_SIGNAL
)
225 CompleteConnection();
227 case ControlSignal::kInviteToConnectSignal
:
229 case ControlSignal::kSendSignal
: {
230 if (value
.size() < 8) {
232 << "Incoming data corrupted, expected message size not found.";
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();
242 case ControlSignal::kDisconnectSignal
:
249 BluetoothLowEnergyConnection::WriteRequest::WriteRequest(
250 const std::vector
<uint8
>& 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
;
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
&
294 const BluetoothLowEnergyCharacteristicsFinder::ErrorCallback
&
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 "
318 << (to_peripheral_char
.id
.empty()
319 ? to_peripheral_char
.uuid
.canonical_value()
321 << (from_peripheral_char
.id
.empty()
322 ? ", " + from_peripheral_char
.uuid
.canonical_value()
323 : "") << " not found.";
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();
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
;
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(
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_
&&
404 write_remote_characteristic_pending_
= true;
405 WriteRequest next_request
= write_requests_queue_
.front();
406 characteristic
->WriteRemoteCharacteristic(
408 base::Bind(&BluetoothLowEnergyConnection::OnRemoteCharacteristicWritten
,
409 weak_ptr_factory_
.GetWeakPtr(),
410 next_request
.is_last_write_for_wire_message
),
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_
) {
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())
487 BluetoothGattService
* BluetoothLowEnergyConnection::GetRemoteService() {
488 BluetoothDevice
* remote_device
= GetRemoteDevice();
489 if (!remote_device
) {
490 PA_LOG(WARNING
) << "Remote device not found.";
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();
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.";
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);
534 } // namespace proximity_auth