Index: components/proximity_auth/ble/bluetooth_low_energy_connection.cc |
diff --git a/components/proximity_auth/ble/bluetooth_low_energy_connection.cc b/components/proximity_auth/ble/bluetooth_low_energy_connection.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..43c83bf9dbc284d987e16d36d8870dddad91ad8f |
--- /dev/null |
+++ b/components/proximity_auth/ble/bluetooth_low_energy_connection.cc |
@@ -0,0 +1,326 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "components/proximity_auth/ble/bluetooth_low_energy_connection.h" |
+ |
+#include "base/bind.h" |
+#include "base/memory/ref_counted.h" |
+#include "base/memory/weak_ptr.h" |
+#include "components/proximity_auth/ble/fake_wire_message.h" |
+#include "components/proximity_auth/connection_finder.h" |
+#include "components/proximity_auth/wire_message.h" |
+#include "device/bluetooth/bluetooth_adapter.h" |
+#include "device/bluetooth/bluetooth_device.h" |
+#include "device/bluetooth/bluetooth_gatt_characteristic.h" |
+#include "device/bluetooth/bluetooth_gatt_connection.h" |
+#include "device/bluetooth/bluetooth_gatt_notify_session.h" |
+#include "device/bluetooth/bluetooth_uuid.h" |
+ |
+using device::BluetoothAdapter; |
+using device::BluetoothDevice; |
+using device::BluetoothGattConnection; |
+using device::BluetoothGattService; |
+using device::BluetoothGattCharacteristic; |
+using device::BluetoothGattNotifySession; |
+using device::BluetoothUUID; |
+ |
+namespace proximity_auth { |
+ |
+BluetoothLowEnergyConnection::BluetoothLowEnergyConnection( |
+ const RemoteDevice& device, |
+ scoped_refptr<device::BluetoothAdapter> adapter, |
+ BluetoothUUID remote_service_uuid, |
+ const std::string& to_peripheral_char_uuid, |
+ const std::string& from_peripheral_char_uuid, |
+ scoped_ptr<BluetoothGattConnection> gatt_connection) |
+ : Connection(device), |
+ adapter_(adapter), |
+ remote_service_uuid_(remote_service_uuid), |
+ to_peripheral_char_uuid_(to_peripheral_char_uuid), |
+ from_peripheral_char_uuid_(from_peripheral_char_uuid), |
+ connection_(gatt_connection.Pass()), |
+ notify_session_pending_(false), |
+ connect_signal_response_pending_(false), |
+ weak_ptr_factory_(this) { |
+ DCHECK(connection_); |
+ DCHECK(adapter_); |
+ DCHECK(adapter_->IsInitialized()); |
+ |
+ SetStatus(IN_PROGRESS); |
+ adapter_->AddObserver(this); |
Tim Song
2015/05/11 08:02:39
Are all services going to be discovered as soon as
sacomoto
2015/05/11 12:04:04
Yes, you are right. We should scan the services a
|
+} |
+ |
+BluetoothLowEnergyConnection::~BluetoothLowEnergyConnection() { |
+ Disconnect(); |
+ if (adapter_) { |
+ adapter_->RemoveObserver(this); |
+ adapter_ = NULL; |
+ } |
+} |
+ |
+void BluetoothLowEnergyConnection::Connect() { |
+ NOTREACHED(); |
+} |
+ |
+// This actually forgets the remote BLE device. This is safe as long as we only |
+// connect to BLE devices advertising the SmartLock service (assuming this |
+// device has no other being used). |
+void BluetoothLowEnergyConnection::Disconnect() { |
+ StopNotifySession(); |
+ SetStatus(DISCONNECTED); |
+ if (connection_) { |
+ connection_.reset(); |
+ BluetoothDevice* device = GetRemoteDevice(); |
+ if (device) { |
+ VLOG(1) << "Forget device " << device->GetAddress(); |
+ device->Forget(base::Bind(&base::DoNothing)); |
+ } |
+ } |
+} |
+ |
+// TODO(sacomoto): Send a SmartLock BLE socket incoming signal. Implement a |
+// sender with full support for messages larger than a single characteristic |
+// value. |
+void BluetoothLowEnergyConnection::SendMessageImpl( |
+ scoped_ptr<WireMessage> message) { |
+ DCHECK(!GetGattCharacteristic(to_peripheral_char_id_)); |
+ VLOG(1) << "Sending message " << message->Serialize(); |
+ |
+ std::string serialized_message = message->Serialize(); |
+ std::vector<uint8> bytes(serialized_message.begin(), |
+ serialized_message.end()); |
+ |
+ GetGattCharacteristic(to_peripheral_char_id_) |
+ ->WriteRemoteCharacteristic( |
+ bytes, base::Bind(&base::DoNothing), |
+ base::Bind( |
+ &BluetoothLowEnergyConnection::OnWriteRemoteCharacteristicError, |
+ weak_ptr_factory_.GetWeakPtr())); |
+} |
+ |
+void BluetoothLowEnergyConnection::DeviceRemoved(BluetoothAdapter* adapter, |
+ BluetoothDevice* device) { |
+ if (device && device->GetAddress() == GetRemoteDeviceAddress()) { |
+ VLOG(1) << "Device removed " << GetRemoteDeviceAddress(); |
+ Disconnect(); |
+ } |
+} |
+ |
+void BluetoothLowEnergyConnection::GattDiscoveryCompleteForService( |
+ BluetoothAdapter* adapter, |
+ BluetoothGattService* service) { |
+ if (service && service->GetUUID() == remote_service_uuid_) { |
+ VLOG(1) << "All characteristics discovered for " |
+ << remote_service_uuid_.canonical_value(); |
+ |
+ std::vector<device::BluetoothGattCharacteristic*> characteristics = |
+ service->GetCharacteristics(); |
+ for (auto iter = characteristics.begin(); iter != characteristics.end(); |
+ iter++) { |
+ HandleCharacteristicUpdate(*iter); |
+ } |
+ |
+ if (to_peripheral_char_id_.empty() || from_peripheral_char_id_.empty()) { |
+ VLOG(1) << "Connection error, missing characteristics for SmartLock " |
+ "service.\n" |
+ << (to_peripheral_char_id_.empty() ? to_peripheral_char_uuid_ |
+ : "") |
+ << (from_peripheral_char_id_.empty() |
+ ? ", " + from_peripheral_char_uuid_ |
+ : "") << " not found."; |
+ Disconnect(); |
+ } |
+ } |
+} |
+ |
+void BluetoothLowEnergyConnection::GattCharacteristicAdded( |
+ BluetoothAdapter* adapter, |
+ BluetoothGattCharacteristic* characteristic) { |
+ VLOG(1) << "new char found: " << characteristic->GetUUID().canonical_value(); |
+ HandleCharacteristicUpdate(characteristic); |
Tim Song
2015/05/11 08:02:39
Is it correct to call HandleCharacteristicUpdate()
sacomoto
2015/05/11 12:04:04
This was intentional. The idea was to have the con
Tim Song
2015/05/12 03:02:46
Thanks for the explanation. In this case, do you e
sacomoto
2015/05/12 07:23:19
Yes, we still need to override GattDiscoveryComple
Tim Song
2015/05/13 02:36:55
Can we at least get rid of the for loop iterating
|
+} |
+ |
+// TODO(sacomoto): Parse the SmartLock BLE socket incoming signal. Implement a |
+// receiver with full suport for messages larger than a single characteristic |
+// value. |
+void BluetoothLowEnergyConnection::GattCharacteristicValueChanged( |
+ BluetoothAdapter* adapter, |
+ BluetoothGattCharacteristic* characteristic, |
+ const std::vector<uint8>& value) { |
+ DCHECK_EQ(adapter, adapter_.get()); |
+ |
+ VLOG(1) << "Characteristic value changed: " |
+ << characteristic->GetUUID().canonical_value(); |
+ |
+ if (characteristic->GetIdentifier() == from_peripheral_char_id_) { |
+ const ControlSignal signal = |
+ static_cast<ControlSignal>(ExtractUint32(value)); |
+ |
+ switch (signal) { |
+ case kInvitationResponseSignal: |
+ connect_signal_response_pending_ = false; |
+ CompleteConnection(); |
+ break; |
+ case kInviteToConnectSignal: |
+ case kSendSignal: |
+ // TODO(sacomoto): Actually handle the message and call OnBytesReceived |
+ // when complete. |
+ case kDisconnectSignal: |
+ Disconnect(); |
+ break; |
+ } |
+ } |
+} |
+ |
+scoped_ptr<WireMessage> BluetoothLowEnergyConnection::DeserializeWireMessage( |
+ bool* is_incomplete_message) { |
+ return FakeWireMessage::Deserialize(received_bytes(), is_incomplete_message); |
+} |
+ |
+void BluetoothLowEnergyConnection::HandleCharacteristicUpdate( |
+ BluetoothGattCharacteristic* characteristic) { |
+ // Checks if |characteristic| is equal to |from_peripheral_char_uuid_| or |
+ // |to_peripheral_char_uuid_|. |
+ UpdateCharacteristicsStatus(characteristic); |
+ |
+ // Starts a notify session for |from_peripheral_char_uuid_|. |
+ if (characteristic->GetIdentifier() == from_peripheral_char_id_) |
+ StartNotifySession(); |
+ |
+ // Sends a invite to connect signal if ready. |
+ SendInviteToConnectSignal(); |
+} |
+ |
+void BluetoothLowEnergyConnection::CompleteConnection() { |
+ if (status() == IN_PROGRESS && !connect_signal_response_pending_ && |
+ !to_peripheral_char_id_.empty() && !from_peripheral_char_id_.empty() && |
+ notify_session_) { |
+ VLOG(1) << "Connection completed"; |
+ SetStatus(CONNECTED); |
+ } |
+} |
+ |
+void BluetoothLowEnergyConnection::StartNotifySession() { |
+ BluetoothGattCharacteristic* characteristic = |
+ GetGattCharacteristic(from_peripheral_char_id_); |
+ if (!characteristic) { |
+ VLOG(1) << "Characteristic " << from_peripheral_char_uuid_ << " not found."; |
+ return; |
+ } |
+ |
+ if (notify_session_ || notify_session_pending_) { |
+ VLOG(1) << "Notify session already started."; |
+ return; |
+ } |
+ |
+ notify_session_pending_ = true; |
+ characteristic->StartNotifySession( |
+ base::Bind(&BluetoothLowEnergyConnection::OnNotifySessionStarted, |
+ weak_ptr_factory_.GetWeakPtr()), |
+ base::Bind(&BluetoothLowEnergyConnection::OnNotifySessionError, |
+ weak_ptr_factory_.GetWeakPtr())); |
+} |
+ |
+void BluetoothLowEnergyConnection::OnNotifySessionError( |
+ BluetoothGattService::GattErrorCode error) { |
+ VLOG(1) << "Error starting notification session: " << error; |
+ notify_session_pending_ = false; |
+} |
+ |
+void BluetoothLowEnergyConnection::OnNotifySessionStarted( |
+ scoped_ptr<BluetoothGattNotifySession> notify_session) { |
+ VLOG(1) << "Notification session started " |
+ << notify_session->GetCharacteristicIdentifier(); |
+ notify_session_ = notify_session.Pass(); |
+ notify_session_pending_ = false; |
+ |
+ // Sends an invite to connect signal if ready. |
+ SendInviteToConnectSignal(); |
+} |
+ |
+void BluetoothLowEnergyConnection::StopNotifySession() { |
+ if (notify_session_) { |
+ notify_session_->Stop(base::Bind(&base::DoNothing)); |
+ notify_session_.reset(); |
+ } |
+} |
+ |
+void BluetoothLowEnergyConnection::OnWriteRemoteCharacteristicError( |
+ BluetoothGattService::GattErrorCode error) { |
+ VLOG(1) << "Error writing characteristic" << to_peripheral_char_uuid_; |
+} |
+ |
+void BluetoothLowEnergyConnection::SendInviteToConnectSignal() { |
+ if (status() == IN_PROGRESS && !connect_signal_response_pending_ && |
+ !to_peripheral_char_id_.empty() && !from_peripheral_char_id_.empty() && |
+ notify_session_) { |
+ VLOG(1) << "Sending invite to connect signal"; |
+ connect_signal_response_pending_ = true; |
+ const char connect_signal[4] = {}; |
Tim Song
2015/05/11 08:02:39
You should use use the kInviteToConnectSignal valu
sacomoto
2015/05/11 12:04:04
Done.
|
+ |
+ // The connection status is not CONNECTED yet, so in order to bypass the |
+ // check in SendMessage implementation we need to send the message using the |
+ // private implementation. |
+ SendMessageImpl( |
+ scoped_ptr<FakeWireMessage>(new FakeWireMessage(connect_signal))); |
+ } |
+} |
+ |
+void BluetoothLowEnergyConnection::UpdateCharacteristicsStatus( |
+ BluetoothGattCharacteristic* characteristic) { |
+ if (characteristic) { |
+ std::string canonical_value = characteristic->GetUUID().canonical_value(); |
+ VLOG(1) << canonical_value; |
+ if (to_peripheral_char_uuid_ == canonical_value) { |
+ to_peripheral_char_id_ = characteristic->GetIdentifier(); |
+ } |
+ if (from_peripheral_char_uuid_ == canonical_value) { |
Tim Song
2015/05/11 08:02:39
nit : please be consistent and remove the braces f
sacomoto
2015/05/11 12:04:03
Done.
|
+ from_peripheral_char_id_ = characteristic->GetIdentifier(); |
+ } |
+ BluetoothGattService* service = characteristic->GetService(); |
+ if (service && service->GetUUID() == remote_service_uuid_) |
+ remote_service_id_ = service->GetIdentifier(); |
+ } |
+} |
+ |
+const std::string& BluetoothLowEnergyConnection::GetRemoteDeviceAddress() { |
+ return remote_device().bluetooth_address; |
+} |
+ |
+BluetoothDevice* BluetoothLowEnergyConnection::GetRemoteDevice() { |
+ if (!adapter_ || !adapter_->IsInitialized()) { |
+ VLOG(1) << "adapter not ready"; |
+ return NULL; |
+ } |
+ return adapter_->GetDevice(GetRemoteDeviceAddress()); |
+} |
+ |
+BluetoothGattService* BluetoothLowEnergyConnection::GetRemoteService() { |
+ BluetoothDevice* remote_device = GetRemoteDevice(); |
+ if (!remote_device) { |
+ VLOG(1) << "device not found"; |
+ return NULL; |
+ } |
+ return remote_device->GetGattService(remote_service_id_); |
+} |
+ |
+BluetoothGattCharacteristic* |
+BluetoothLowEnergyConnection::GetGattCharacteristic( |
+ const std::string& gatt_characteristic) { |
+ BluetoothGattService* remote_service = GetRemoteService(); |
+ if (!remote_service) { |
+ VLOG(1) << "service not found"; |
+ return NULL; |
+ } |
+ return remote_service->GetCharacteristic(gatt_characteristic); |
+} |
+ |
+// TODO(sacomoto): make this robust to byte ordering in both sides of the |
+// SmartLock BLE socket. |
+uint32 BluetoothLowEnergyConnection::ExtractUint32( |
+ const std::vector<uint8>& bytes) { |
+ return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24); |
+} |
+ |
+} // namespace proximity_auth |