Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(440)

Unified Diff: components/proximity_auth/ble/bluetooth_low_energy_connection.cc

Issue 1116963002: Bluetooth low energy connection. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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..13e11b0238501a4cecbc70c967e0ce66bb6261f5
--- /dev/null
+++ b/components/proximity_auth/ble/bluetooth_low_energy_connection.cc
@@ -0,0 +1,360 @@
+// 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 "base/time/time.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,
+ const BluetoothUUID remote_service_uuid,
+ const BluetoothUUID to_peripheral_char_uuid,
+ const BluetoothUUID from_peripheral_char_uuid,
+ scoped_ptr<BluetoothGattConnection> gatt_connection)
+ : Connection(device),
+ adapter_(adapter),
+ remote_service_({remote_service_uuid, ""}),
+ to_peripheral_char_({to_peripheral_char_uuid, ""}),
+ from_peripheral_char_({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());
+
+ start_time_ = base::TimeTicks::Now();
+ SetStatus(IN_PROGRESS);
+
+ // We should set the status to IN_PROGRESS before calling this function, as we
+ // can set the status to CONNECTED by an inner call of
+ // HandleCharacteristicUpdate().
+ ScanRemoteCharacteristics(GetRemoteService());
+
+ adapter_->AddObserver(this);
+}
+
+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();
+
+ 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.canonical_value()
+ : "")
+ << (from_peripheral_char_.id.empty()
+ ? ", " + from_peripheral_char_.uuid.canonical_value()
+ : "") << " not found.";
+ Disconnect();
+ }
+ }
+}
+
+void BluetoothLowEnergyConnection::GattCharacteristicAdded(
+ BluetoothAdapter* adapter,
+ BluetoothGattCharacteristic* characteristic) {
+ VLOG(1) << "New char found: " << characteristic->GetUUID().canonical_value();
+ HandleCharacteristicUpdate(characteristic);
+}
+
+// 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>(ToUint32(value));
+
+ switch (signal) {
+ case ControlSignal::kInvitationResponseSignal:
+ connect_signal_response_pending_ = false;
+ CompleteConnection();
+ break;
+ case ControlSignal::kInviteToConnectSignal:
+ case ControlSignal::kSendSignal:
+ // TODO(sacomoto): Actually handle the message and call OnBytesReceived
+ // when complete.
+ case ControlSignal::kDisconnectSignal:
+ Disconnect();
+ break;
+ }
+ }
+}
+
+scoped_ptr<WireMessage> BluetoothLowEnergyConnection::DeserializeWireMessage(
+ bool* is_incomplete_message) {
+ return FakeWireMessage::Deserialize(received_bytes(), is_incomplete_message);
+}
+
+void BluetoothLowEnergyConnection::ScanRemoteCharacteristics(
+ BluetoothGattService* service) {
+ if (service) {
+ std::vector<device::BluetoothGattCharacteristic*> characteristics =
+ service->GetCharacteristics();
+ for (auto iter = characteristics.begin(); iter != characteristics.end();
+ iter++) {
+ HandleCharacteristicUpdate(*iter);
+ }
+ }
+}
+
+void BluetoothLowEnergyConnection::HandleCharacteristicUpdate(
+ BluetoothGattCharacteristic* characteristic) {
+ // Checks if |characteristic| is equal to |from_peripheral_char_| or
+ // |to_peripheral_char_|.
+ UpdateCharacteristicsStatus(characteristic);
+
+ // Starts a notify session for |from_peripheral_char_|.
+ 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";
+ VLOG(1) << "Time elapsed: " << base::TimeTicks::Now() - start_time_;
+ SetStatus(CONNECTED);
+ }
+}
+
+void BluetoothLowEnergyConnection::StartNotifySession() {
+ BluetoothGattCharacteristic* characteristic =
+ GetGattCharacteristic(from_peripheral_char_.id);
+ if (!characteristic) {
+ VLOG(1) << "Characteristic " << from_peripheral_char_.uuid.canonical_value()
+ << " 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.canonical_value();
+}
+
+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;
+
+ // 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(
+ ToString(static_cast<uint32>(ControlSignal::kInviteToConnectSignal)))));
+ }
+}
+
+void BluetoothLowEnergyConnection::UpdateCharacteristicsStatus(
+ BluetoothGattCharacteristic* characteristic) {
+ if (characteristic) {
+ BluetoothUUID uuid = characteristic->GetUUID();
+ if (to_peripheral_char_.uuid == uuid)
+ to_peripheral_char_.id = characteristic->GetIdentifier();
+ if (from_peripheral_char_.uuid == uuid)
+ 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;
+ }
+ if (remote_service_.id.empty()) {
+ std::vector<BluetoothGattService*> services =
+ remote_device->GetGattServices();
+ for (auto iter = services.begin(); iter != services.end(); ++iter)
+ if ((*iter)->GetUUID() == remote_service_.uuid) {
+ remote_service_.id = (*iter)->GetIdentifier();
+ break;
+ }
+ }
+ 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::ToUint32(const std::vector<uint8>& bytes) {
+ return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24);
+}
+
+// TODO(sacomoto): make this robust to byte ordering in both sides of the
+// SmartLock BLE socket.
+const std::string BluetoothLowEnergyConnection::ToString(const uint32 value) {
+ char bytes[4] = {};
+
+ bytes[0] = static_cast<uint8>(value);
+ bytes[1] = static_cast<uint8>(value >> 8);
+ bytes[2] = static_cast<uint8>(value >> 12);
+ bytes[3] = static_cast<uint8>(value >> 24);
+
+ return std::string(bytes);
+}
+
+} // namespace proximity_auth

Powered by Google App Engine
This is Rietveld 408576698