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 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
);
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
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());
150 while (start_index
< message_size
) {
151 int end_index
= (start_index
+ chunk_size
) <= message_size
152 ? (start_index
+ chunk_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();
177 void BluetoothLowEnergyConnection::DeviceRemoved(BluetoothAdapter
* adapter
,
178 BluetoothDevice
* device
) {
179 if (device
&& device
->GetAddress() == GetRemoteDeviceAddress()) {
180 PA_LOG(INFO
) << "Device removed " << GetRemoteDeviceAddress();
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;
206 if (value
.size() < 4) {
207 PA_LOG(WARNING
) << "Incoming data corrupted, no signal found.";
211 const ControlSignal signal
= static_cast<ControlSignal
>(ToUint32(value
));
213 case ControlSignal::kInvitationResponseSignal
:
214 if (sub_status() == SubStatus::WAITING_RESPONSE_SIGNAL
)
215 CompleteConnection();
217 case ControlSignal::kInviteToConnectSignal
:
219 case ControlSignal::kSendSignal
: {
220 if (value
.size() < 8) {
222 << "Incoming data corrupted, expected message size not found.";
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();
232 case ControlSignal::kDisconnectSignal
:
239 BluetoothLowEnergyConnection::WriteRequest::WriteRequest(
240 const std::vector
<uint8
>& 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
;
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
&
284 const BluetoothLowEnergyCharacteristicsFinder::ErrorCallback
&
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 "
308 << (to_peripheral_char
.id
.empty()
309 ? to_peripheral_char
.uuid
.canonical_value()
311 << (from_peripheral_char
.id
.empty()
312 ? ", " + from_peripheral_char
.uuid
.canonical_value()
313 : "") << " not found.";
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
;
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(
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_
&&
383 write_remote_characteristic_pending_
= true;
384 WriteRequest next_request
= write_requests_queue_
.front();
385 characteristic
->WriteRemoteCharacteristic(
387 base::Bind(&BluetoothLowEnergyConnection::OnRemoteCharacteristicWritten
,
388 weak_ptr_factory_
.GetWeakPtr(),
389 next_request
.is_last_write_for_wire_message
),
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_
) {
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())
466 BluetoothGattService
* BluetoothLowEnergyConnection::GetRemoteService() {
467 BluetoothDevice
* remote_device
= GetRemoteDevice();
468 if (!remote_device
) {
469 PA_LOG(WARNING
) << "Remote device not found.";
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();
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.";
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);
513 } // namespace proximity_auth