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..bb671b1a04b45c7881a5d1835e7a93b5c9920107 |
--- /dev/null |
+++ b/components/proximity_auth/ble/bluetooth_low_energy_connection.cc |
@@ -0,0 +1,274 @@ |
+// 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/connection_finder.h" |
+#include "components/proximity_auth/ble/fake_wire_message.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, |
+ scoped_ptr<BluetoothGattConnection> gatt_connection) |
+ : Connection(device), |
+ adapter_(adapter), |
+ remote_service_uuid_(remote_service_uuid), |
+ connection_(gatt_connection.Pass()), |
+ notify_session_pending_(false), |
+ weak_ptr_factory_(this) { |
+ if (connection_) { |
msarda
2015/05/05 11:56:13
Can |connection_| be null? If not, then it is bett
sacomoto
2015/05/06 13:47:58
Done.
|
+ SetStatus(IN_PROGRESS); |
+ } |
+ |
+ if (adapter_) { |
msarda
2015/05/05 11:56:14
DCHECK(adapter_)?
sacomoto
2015/05/06 13:47:58
Done.
|
+ adapter_->AddObserver(this); |
+ } |
+} |
+ |
+BluetoothLowEnergyConnection::~BluetoothLowEnergyConnection() { |
+ Disconnect(); |
+ if (adapter_) { |
+ adapter_->RemoveObserver(this); |
+ adapter_ = NULL; |
+ } |
+} |
+ |
+void BluetoothLowEnergyConnection::Connect() { |
+ NOTREACHED(); |
+} |
+ |
+void BluetoothLowEnergyConnection::Disconnect() { |
+ StopNotifySession(); |
+ if (connection_) { |
+ connection_.reset(); |
+ BluetoothDevice* device = GetRemoteDevice(); |
+ if (device) { |
+ VLOG(1) << "Forget device " << device->GetAddress(); |
+ device->Forget(base::Bind(&base::DoNothing)); |
msarda
2015/05/05 11:56:14
Please add a comment that disconnect actually forg
sacomoto
2015/05/06 13:47:58
Done.
|
+ } |
+ } |
+} |
+ |
+// TODO(sacomoto): Send a Socketeer incoming signal. Implement a sender with |
msarda
2015/05/05 11:56:13
Remove all reference to Socketeer.
sacomoto
2015/05/06 13:47:58
Done.
|
+// 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() == remote_device_address()) { |
+ VLOG(1) << "device removed " << remote_device_address(); |
msarda
2015/05/05 11:56:13
Log should state with CAPS:
s/device/Device
sacomoto
2015/05/06 13:47:57
Done.
|
+ 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 service."; |
msarda
2015/05/05 11:56:13
s/service/Smart Lock service
msarda
2015/05/05 11:56:13
Please also log which one of |to_peripheral_char_i
sacomoto
2015/05/06 13:47:58
Done.
sacomoto
2015/05/06 13:47:58
Done.
|
+ Disconnect(); |
+ } |
+ } |
+} |
+ |
+void BluetoothLowEnergyConnection::GattCharacteristicAdded( |
+ BluetoothAdapter* adapter, |
+ BluetoothGattCharacteristic* characteristic) { |
+ VLOG(1) << "new char found: " << characteristic->GetUUID().canonical_value(); |
+ HandleCharacteristicUpdate(characteristic); |
+} |
+ |
+// TODO(sacomoto): Parse the Sockeeter incoming signal. Implement a receiver |
msarda
2015/05/05 11:56:13
Remove Socketeer.
sacomoto
2015/05/06 13:47:58
Done.
|
+// with full suport for messages larger than a single characteristic value. |
+void BluetoothLowEnergyConnection::GattCharacteristicValueChanged( |
+ BluetoothAdapter* adapter, |
+ BluetoothGattCharacteristic* characteristic, |
+ const std::vector<uint8>& value) { |
+ VLOG(1) << "Characteristic value changed: " |
+ << characteristic->GetUUID().canonical_value(); |
+ if (characteristic->GetIdentifier() == from_peripheral_char_id_) { |
+ std::string message(value.begin(), value.end()); |
+ VLOG(1) << "value: " << message; |
+ // TODO(sacomoto): Actually handle the message and call OnBytesReceived when |
+ // complete. |
+ } |
+} |
+ |
+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_| or |
+ // |to_peripheral_char_| are present. |
+ UpdateCharacteristicsStatus(characteristic); |
+ |
+ // Starts a notify session for |from_peripheral_char_|. |
+ if (characteristic->GetIdentifier() == from_peripheral_char_id_) |
+ StartNotifySession(); |
+ |
+ // Checks if the connection is complete. |
+ CompleteConnection(); |
+} |
+ |
+void BluetoothLowEnergyConnection::CompleteConnection() { |
+ if (status() == IN_PROGRESS && !to_peripheral_char_id_.empty() && |
+ !from_peripheral_char_id_.empty() && notify_session_) { |
+ VLOG(1) << "Connection completed"; |
+ SetStatus(CONNECTED); |
+ SendInviteToConnectSignal(); |
msarda
2015/05/05 11:56:13
Should we wait for a reply to invite signal before
sacomoto
2015/05/06 13:47:58
Done.
|
+ } |
+} |
+ |
+void BluetoothLowEnergyConnection::StartNotifySession() { |
+ BluetoothGattCharacteristic* characteristic = |
+ GetGattCharacteristic(from_peripheral_char_id_); |
+ if (!characteristic) { |
+ VLOG(1) << "Characteristic " << from_peripheral_char_ << " 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; |
+ |
+ // Checks if the connection is complete. |
+ CompleteConnection(); |
+} |
+ |
+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_; |
+} |
+ |
+void BluetoothLowEnergyConnection::SendInviteToConnectSignal() { |
+ // From Socketeer library: connect signal is 0 (32-bit int). |
+ const char connect_signal[4] = {}; |
+ SendMessage(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_ == canonical_value) { |
+ to_peripheral_char_id_ = characteristic->GetIdentifier(); |
+ } |
+ if (from_peripheral_char_ == canonical_value) { |
+ from_peripheral_char_id_ = characteristic->GetIdentifier(); |
+ } |
+ BluetoothGattService* service = characteristic->GetService(); |
+ if (service && service->GetUUID() == remote_service_uuid_) |
+ remote_service_id_ = service->GetIdentifier(); |
+ } |
+} |
+ |
+BluetoothDevice* BluetoothLowEnergyConnection::GetRemoteDevice() { |
+ if (!adapter_ || !adapter_->IsInitialized()) { |
+ VLOG(1) << "adapter not ready"; |
+ return NULL; |
+ } |
+ return adapter_->GetDevice(remote_device_address()); |
+} |
+ |
+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); |
+} |
+ |
+} // namespace proximity_auth |