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/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
{
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
38 const int kMaxChunkSize
= 100;
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
)
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) {
63 DCHECK(adapter_
->IsInitialized());
65 start_time_
= base::TimeTicks::Now();
66 adapter_
->AddObserver(this);
69 BluetoothLowEnergyConnection::~BluetoothLowEnergyConnection() {
72 adapter_
->RemoveObserver(this);
77 void BluetoothLowEnergyConnection::Connect() {
78 if (connection_
&& connection_
->IsConnected()) {
79 OnGattConnectionCreated(connection_
.Pass());
83 BluetoothDevice
* remote_device
= GetRemoteDevice();
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();
98 SetSubStatus(SubStatus::DISCONNECTED
);
101 BluetoothDevice
* device
= GetRemoteDevice();
103 VLOG(1) << "Disconnect from device " << device
->GetAddress();
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
);
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
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());
154 while (start_index
< message_size
) {
155 int end_index
= (start_index
+ chunk_size
) <= message_size
156 ? (start_index
+ chunk_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();
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;
198 if (value
.size() < 4) {
199 VLOG(1) << "Incoming data corrupted, no signal found.";
203 const ControlSignal signal
= static_cast<ControlSignal
>(ToUint32(value
));
205 case ControlSignal::kInvitationResponseSignal
:
206 if (sub_status() == SubStatus::WAITING_RESPONSE_SIGNAL
)
207 CompleteConnection();
209 case ControlSignal::kInviteToConnectSignal
:
211 case ControlSignal::kSendSignal
: {
212 if (value
.size() < 8) {
214 << "Incoming data corrupted, expected message size not found.";
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();
224 case ControlSignal::kDisconnectSignal
:
231 BluetoothLowEnergyConnection::WriteRequest::WriteRequest(
232 const std::vector
<uint8
>& 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
;
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
&
275 const BluetoothLowEnergyCharacteristicsFinder::ErrorCallback
&
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()
301 << (from_peripheral_char
.id
.empty()
302 ? ", " + from_peripheral_char
.uuid
.canonical_value()
303 : "") << " not found.";
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
;
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(
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_
&&
373 write_remote_characteristic_pending_
= true;
374 WriteRequest next_request
= write_requests_queue_
.front();
375 characteristic
->WriteRemoteCharacteristic(
377 base::Bind(&BluetoothLowEnergyConnection::OnRemoteCharacteristicWritten
,
378 weak_ptr_factory_
.GetWeakPtr(),
379 next_request
.is_last_write_for_wire_message
),
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_
) {
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())
456 BluetoothGattService
* BluetoothLowEnergyConnection::GetRemoteService() {
457 BluetoothDevice
* remote_device
= GetRemoteDevice();
458 if (!remote_device
) {
459 VLOG(1) << "device not found";
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();
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";
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);
503 } // namespace proximity_auth