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

Unified Diff: chromeos/components/tether/ble_connection_manager.cc

Issue 2697763002: [CrOS Tether]: Create BleConnectionManager, which manages secure connections between the current de… (Closed)
Patch Set: Add missing DEP. Created 3 years, 10 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: chromeos/components/tether/ble_connection_manager.cc
diff --git a/chromeos/components/tether/ble_connection_manager.cc b/chromeos/components/tether/ble_connection_manager.cc
new file mode 100644
index 0000000000000000000000000000000000000000..0b6c4cec022798ebcc043f9bbdea3a5dcc9a6318
--- /dev/null
+++ b/chromeos/components/tether/ble_connection_manager.cc
@@ -0,0 +1,479 @@
+// Copyright 2017 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 "chromeos/components/tether/ble_connection_manager.h"
+
+#include "chromeos/components/tether/ble_constants.h"
+#include "components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.h"
+#include "components/proximity_auth/logging/logging.h"
+#include "device/bluetooth/bluetooth_uuid.h"
+
+namespace chromeos {
+
+namespace tether {
+
+namespace {
+const char kTetherFeature[] = "magic_tether";
+} // namespace
+
+const int64_t BleConnectionManager::kAdvertisingTimeoutMillis = 12000;
+const int64_t BleConnectionManager::kFailImmediatelyTimeoutMillis = 0;
+
+// static
+std::string BleConnectionManager::MessageTypeToString(
+ const MessageType& reason) {
+ switch (reason) {
+ case MessageType::TETHER_AVAILABILITY_REQUEST:
+ return "[TetherAvailabilityRequest]";
+ case MessageType::CONNECT_TETHERING_REQUEST:
+ return "[ConnectTetheringRequest]";
+ case MessageType::KEEP_ALIVE_TICKLE:
+ return "[KeepAliveTickle]";
+ case MessageType::DISCONNECT_TETHERING_REQUEST:
+ return "[DisconnectTetheringRequest]";
+ default:
+ return "[invalid MessageType]";
+ }
+}
+
+BleConnectionManager::ConnectionMetadata::ConnectionMetadata(
+ const cryptauth::RemoteDevice remote_device,
+ std::shared_ptr<base::Timer> timer,
+ base::WeakPtr<BleConnectionManager> manager)
+ : remote_device_(remote_device),
+ connection_attempt_timeout_timer_(timer),
+ manager_(manager),
+ weak_ptr_factory_(this) {}
+
+BleConnectionManager::ConnectionMetadata::~ConnectionMetadata() {}
+
+void BleConnectionManager::ConnectionMetadata::RegisterConnectionReason(
+ const MessageType& connection_reason) {
+ active_connection_reasons_.insert(connection_reason);
+}
+
+void BleConnectionManager::ConnectionMetadata::UnregisterConnectionReason(
+ const MessageType& connection_reason) {
+ active_connection_reasons_.erase(connection_reason);
+}
+
+bool BleConnectionManager::ConnectionMetadata::HasReasonForConnection() const {
+ return !active_connection_reasons_.empty();
+}
+
+bool BleConnectionManager::ConnectionMetadata::HasEstablishedConnection()
+ const {
+ return secure_channel_.get();
+}
+
+cryptauth::SecureChannel::Status
+BleConnectionManager::ConnectionMetadata::GetStatus() const {
+ if (connection_attempt_timeout_timer_->IsRunning()) {
+ // If the timer is running, a connection attempt is in progress but a
+ // channel has not been established.
+ return cryptauth::SecureChannel::Status::CONNECTING;
+ } else if (!HasEstablishedConnection()) {
+ // If there is no timer and a channel has not been established, the channel
+ // is disconnected.
+ return cryptauth::SecureChannel::Status::DISCONNECTED;
+ }
+
+ // If a channel has been established, return its status.
+ return secure_channel_->status();
+}
+
+void BleConnectionManager::ConnectionMetadata::StartConnectionAttemptTimer(
+ bool fail_immediately) {
+ DCHECK(!secure_channel_);
+ DCHECK(!connection_attempt_timeout_timer_->IsRunning());
+
+ int64_t timeout_millis = fail_immediately ? kFailImmediatelyTimeoutMillis
+ : kAdvertisingTimeoutMillis;
+
+ connection_attempt_timeout_timer_->Start(
+ FROM_HERE, base::TimeDelta::FromMilliseconds(timeout_millis),
+ base::Bind(&ConnectionMetadata::OnConnectionAttemptTimeout,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void BleConnectionManager::ConnectionMetadata::OnConnectionAttemptTimeout() {
+ manager_->OnConnectionAttemptTimeout(remote_device_);
+}
+
+void BleConnectionManager::ConnectionMetadata::SetSecureChannel(
+ std::unique_ptr<cryptauth::SecureChannel> secure_channel) {
+ DCHECK(!secure_channel_);
+
+ // The connection has succeeded, so cancel the timeout.
+ connection_attempt_timeout_timer_->Stop();
+
+ secure_channel_ = std::move(secure_channel);
+ secure_channel_->AddObserver(this);
+ secure_channel_->Initialize();
+}
+
+void BleConnectionManager::ConnectionMetadata::SendMessage(
+ const std::string& payload) {
+ DCHECK(GetStatus() == cryptauth::SecureChannel::Status::AUTHENTICATED);
+ secure_channel_->SendMessage(std::string(kTetherFeature), payload);
+}
+
+void BleConnectionManager::ConnectionMetadata::OnSecureChannelStatusChanged(
+ cryptauth::SecureChannel* secure_channel,
+ const cryptauth::SecureChannel::Status& old_status,
+ const cryptauth::SecureChannel::Status& new_status) {
+ DCHECK(secure_channel_.get() == secure_channel);
+
+ if (new_status == cryptauth::SecureChannel::Status::CONNECTING) {
+ // BleConnectionManager already broadcasts "disconnected => connecting"
+ // status updates when a connection attempt begins, so there is no need to
+ // handle this case.
+ return;
+ }
+
+ // Make a copy of the two statuses. If |secure_channel_.reset()| is called
+ // below, the SecureChannel instance will be destroyed and |old_status| and
+ // |new_status| may refer to memory which has been deleted.
+ const cryptauth::SecureChannel::Status old_status_copy = old_status;
+ const cryptauth::SecureChannel::Status new_status_copy = new_status;
+
+ if (new_status == cryptauth::SecureChannel::Status::DISCONNECTED) {
+ secure_channel_->RemoveObserver(this);
+ secure_channel_.reset();
+ }
+
+ manager_->OnSecureChannelStatusChanged(remote_device_, old_status_copy,
+ new_status_copy);
+}
+
+void BleConnectionManager::ConnectionMetadata::OnMessageReceived(
+ cryptauth::SecureChannel* secure_channel,
+ const std::string& feature,
+ const std::string& payload) {
+ DCHECK(secure_channel_.get() == secure_channel);
+ if (feature != std::string(kTetherFeature)) {
+ // If the message received was not a tether feature, ignore it.
+ return;
+ }
+
+ manager_->SendMessageReceivedEvent(remote_device_, payload);
+}
+
+std::unique_ptr<base::Timer> BleConnectionManager::TimerFactory::CreateTimer() {
+ return base::MakeUnique<base::OneShotTimer>();
+}
+
+BleConnectionManager::BleConnectionManager(
+ std::unique_ptr<Delegate> delegate,
+ scoped_refptr<device::BluetoothAdapter> adapter,
+ const LocalDeviceDataProvider* local_device_data_provider,
+ const cryptauth::RemoteBeaconSeedFetcher* remote_beacon_seed_fetcher,
+ cryptauth::BluetoothThrottler* bluetooth_throttler)
+ : BleConnectionManager(
+ std::move(delegate),
+ adapter,
+ // TODO(khorimoto): Inject |adapter| into |BleScanner|.
+ base::MakeUnique<BleScanner>(local_device_data_provider),
+ base::MakeUnique<BleAdvertiser>(adapter,
+ local_device_data_provider,
+ remote_beacon_seed_fetcher),
+ base::MakeUnique<BleAdvertisementDeviceQueue>(),
+ base::WrapUnique<TimerFactory>(new TimerFactory()),
+ bluetooth_throttler) {}
+
+BleConnectionManager::BleConnectionManager(
+ std::unique_ptr<Delegate> delegate,
+ scoped_refptr<device::BluetoothAdapter> adapter,
+ std::unique_ptr<BleScanner> ble_scanner,
+ std::unique_ptr<BleAdvertiser> ble_advertiser,
+ std::unique_ptr<BleAdvertisementDeviceQueue> device_queue,
+ std::unique_ptr<TimerFactory> timer_factory,
+ cryptauth::BluetoothThrottler* bluetooth_throttler)
+ : delegate_(std::move(delegate)),
+ adapter_(adapter),
+ ble_scanner_(std::move(ble_scanner)),
+ ble_advertiser_(std::move(ble_advertiser)),
+ device_queue_(std::move(device_queue)),
+ timer_factory_(std::move(timer_factory)),
+ bluetooth_throttler_(bluetooth_throttler),
+ weak_ptr_factory_(this) {
+ ble_scanner_->AddObserver(this);
+}
+
+BleConnectionManager::~BleConnectionManager() {
+ ble_scanner_->RemoveObserver(this);
+}
+
+void BleConnectionManager::RegisterRemoteDevice(
+ const cryptauth::RemoteDevice& remote_device,
+ const MessageType& connection_reason) {
+ PA_LOG(INFO) << "Registering device with ID "
+ << remote_device.GetTruncatedDeviceIdForLogs() << " for reason "
+ << MessageTypeToString(connection_reason);
+
+ std::shared_ptr<ConnectionMetadata> connection_metadata =
+ GetConnectionMetadata(remote_device);
+ if (!connection_metadata) {
+ connection_metadata = AddMetadataForDevice(remote_device);
+ }
+
+ connection_metadata->RegisterConnectionReason(connection_reason);
+ UpdateConnectionAttempts();
+}
+
+void BleConnectionManager::UnregisterRemoteDevice(
+ const cryptauth::RemoteDevice& remote_device,
+ const MessageType& connection_reason) {
+ PA_LOG(INFO) << "Unregistering device with ID "
+ << remote_device.GetTruncatedDeviceIdForLogs() << " for reason "
+ << MessageTypeToString(connection_reason);
+
+ std::shared_ptr<ConnectionMetadata> connection_metadata =
+ GetConnectionMetadata(remote_device);
+ if (!connection_metadata) {
+ // If the device was not registered, there is nothing to do.
+ return;
+ }
+
+ connection_metadata->UnregisterConnectionReason(connection_reason);
+ if (!connection_metadata->HasReasonForConnection()) {
+ cryptauth::SecureChannel::Status status_before_disconnect =
+ connection_metadata->GetStatus();
+ device_to_metadata_map_.erase(remote_device);
+ if (status_before_disconnect ==
+ cryptauth::SecureChannel::Status::CONNECTING) {
+ StopConnectionAttemptAndMoveToEndOfQueue(remote_device);
+ }
+ if (status_before_disconnect !=
+ cryptauth::SecureChannel::Status::DISCONNECTED) {
+ // Send a status update for the disconnection.
+ SendSecureChannelStatusChangeEvent(
+ remote_device, status_before_disconnect,
+ cryptauth::SecureChannel::Status::DISCONNECTED);
+ }
+ }
+
+ UpdateConnectionAttempts();
+}
+
+void BleConnectionManager::SendMessage(
+ const cryptauth::RemoteDevice& remote_device,
+ const std::string& message) {
+ std::shared_ptr<ConnectionMetadata> connection_metadata =
+ GetConnectionMetadata(remote_device);
+ if (!connection_metadata ||
+ connection_metadata->GetStatus() !=
+ cryptauth::SecureChannel::Status::AUTHENTICATED) {
+ PA_LOG(ERROR) << "Attempted to send a message to device with ID "
+ << remote_device.GetTruncatedDeviceIdForLogs() << ". "
+ << "Message: \"" << message << "\"";
+ return;
+ }
+
+ PA_LOG(INFO) << "Sending message to device with ID "
+ << remote_device.GetTruncatedDeviceIdForLogs() << ". "
+ << "Message: \"" << message << "\"";
+ connection_metadata->SendMessage(message);
+}
+
+void BleConnectionManager::AddObserver(Observer* observer) {
+ observer_list_.AddObserver(observer);
+}
+
+void BleConnectionManager::RemoveObserver(Observer* observer) {
+ observer_list_.RemoveObserver(observer);
+}
+
+void BleConnectionManager::OnReceivedAdvertisementFromDevice(
+ const std::string& device_address,
+ cryptauth::RemoteDevice remote_device) {
+ std::shared_ptr<ConnectionMetadata> connection_metadata =
+ GetConnectionMetadata(remote_device);
+ if (!connection_metadata) {
+ // If an advertisement is received from a device that is not registered,
+ // ignore it.
+ PA_LOG(WARNING) << "Received an advertisement from a device which is not "
+ << "registered. Bluetooth address: " << device_address
+ << ", Remote Device ID: " << remote_device.GetDeviceId();
+ return;
+ }
+
+ PA_LOG(INFO) << "Received advertisement from device with ID "
+ << remote_device.GetTruncatedDeviceIdForLogs() << ". "
+ << "Starting authentication handshake to that device.";
+
+ // Create a connection to that device.
+ std::unique_ptr<cryptauth::Connection> connection =
+ cryptauth::weave::BluetoothLowEnergyWeaveClientConnection::Factory::
+ NewInstance(remote_device, device_address, adapter_,
+ device::BluetoothUUID(std::string(kGattServerUuid)),
+ bluetooth_throttler_);
+ std::unique_ptr<cryptauth::SecureChannel> secure_channel =
+ cryptauth::SecureChannel::Factory::NewInstance(
+ std::move(connection), delegate_->CreateSecureChannelDelegate());
+ connection_metadata->SetSecureChannel(std::move(secure_channel));
+
+ // Stop trying to connect to that device, since a connection already exists.
+ StopConnectionAttemptAndMoveToEndOfQueue(remote_device);
+ UpdateConnectionAttempts();
+}
+
+std::shared_ptr<BleConnectionManager::ConnectionMetadata>
+BleConnectionManager::GetConnectionMetadata(
+ const cryptauth::RemoteDevice& remote_device) {
+ const auto map_iter = device_to_metadata_map_.find(remote_device);
+ if (map_iter == device_to_metadata_map_.end()) {
+ return nullptr;
+ }
+
+ return map_iter->second;
+}
+
+std::shared_ptr<BleConnectionManager::ConnectionMetadata>
+BleConnectionManager::AddMetadataForDevice(
+ const cryptauth::RemoteDevice& remote_device) {
+ std::shared_ptr<ConnectionMetadata> existing_data =
+ GetConnectionMetadata(remote_device);
+ if (existing_data) {
+ return existing_data;
+ }
+
+ std::unique_ptr<base::Timer> timer = timer_factory_->CreateTimer();
+ device_to_metadata_map_.insert(
+ std::pair<cryptauth::RemoteDevice, std::shared_ptr<ConnectionMetadata>>(
+ remote_device,
+ std::shared_ptr<ConnectionMetadata>(
+ new ConnectionMetadata(remote_device, std::move(timer),
+ weak_ptr_factory_.GetWeakPtr()))));
+ return device_to_metadata_map_.at(remote_device);
+}
+
+void BleConnectionManager::UpdateConnectionAttempts() {
+ UpdateAdvertisementQueue();
+
+ std::vector<cryptauth::RemoteDevice> should_advertise_to =
+ device_queue_->GetDevicesToWhichToAdvertise();
+ DCHECK(should_advertise_to.size() <=
+ static_cast<size_t>(kMaxConcurrentAdvertisements));
+
+ for (const auto& remote_device : should_advertise_to) {
+ std::shared_ptr<ConnectionMetadata> associated_data =
+ GetConnectionMetadata(remote_device);
+ if (associated_data->GetStatus() !=
+ cryptauth::SecureChannel::Status::CONNECTING) {
+ // If there is no active attempt to connect to a device at the front of
+ // the queue, start a connection attempt.
+ StartConnectionAttempt(remote_device);
+ }
+ }
+}
+
+void BleConnectionManager::UpdateAdvertisementQueue() {
+ std::vector<cryptauth::RemoteDevice> devices_for_queue;
+ for (const auto& map_entry : device_to_metadata_map_) {
+ if (map_entry.second->HasEstablishedConnection()) {
+ // If there is already an active connection to the device, there is no
+ // need to advertise to the device to bootstrap a connection.
+ continue;
+ }
+
+ devices_for_queue.push_back(map_entry.first);
+ }
+
+ device_queue_->SetDevices(devices_for_queue);
+}
+
+void BleConnectionManager::StartConnectionAttempt(
+ const cryptauth::RemoteDevice& remote_device) {
+ std::shared_ptr<ConnectionMetadata> connection_metadata =
+ GetConnectionMetadata(remote_device);
+ DCHECK(connection_metadata);
+
+ PA_LOG(INFO) << "Starting connection attempt to device with ID "
+ << remote_device.GetTruncatedDeviceIdForLogs();
+
+ // Send a "disconnected => connecting" update to alert clients that a
+ // connection attempt for |remote_device| is underway.
+ SendSecureChannelStatusChangeEvent(
+ remote_device, cryptauth::SecureChannel::Status::DISCONNECTED,
+ cryptauth::SecureChannel::Status::CONNECTING);
+
+ bool success = ble_scanner_->RegisterScanFilterForDevice(remote_device) &&
+ ble_advertiser_->StartAdvertisingToDevice(remote_device);
+
+ // Start a timer; if a connection is unable to be created before the timer
+ // fires, a timeout occurs. Note that if this class is unable to start both
+ // the scanner and advertiser successfully (i.e., |success| is |false|), a
+ // the connection fails immediately insetad of waiting for a timeout, which
+ // has the effect of quickly sending out "disconnected => connecting =>
+ // disconnecting" status updates. The timer is used here instead of a special
+ // case in order to route all connection failures through the same code path.
+ connection_metadata->StartConnectionAttemptTimer(
+ !success /* fail_immediately */);
+}
+
+void BleConnectionManager::StopConnectionAttemptAndMoveToEndOfQueue(
+ const cryptauth::RemoteDevice& remote_device) {
+ PA_LOG(INFO) << "Stopping connection attempt to device with ID "
+ << remote_device.GetTruncatedDeviceIdForLogs();
+
+ ble_scanner_->UnregisterScanFilterForDevice(remote_device);
+ ble_advertiser_->StopAdvertisingToDevice(remote_device);
+
+ device_queue_->MoveDeviceToEnd(remote_device.GetDeviceId());
+}
+
+void BleConnectionManager::OnConnectionAttemptTimeout(
+ const cryptauth::RemoteDevice& remote_device) {
+ PA_LOG(INFO) << "Connection attempt timed out for device with ID "
+ << remote_device.GetTruncatedDeviceIdForLogs();
+
+ StopConnectionAttemptAndMoveToEndOfQueue(remote_device);
+
+ // Send a "connecting => disconnected" update to alert clients that a
+ // connection attempt for |remote_device| has failed.
+ SendSecureChannelStatusChangeEvent(
+ remote_device, cryptauth::SecureChannel::Status::CONNECTING,
+ cryptauth::SecureChannel::Status::DISCONNECTED);
+
+ UpdateConnectionAttempts();
+}
+
+void BleConnectionManager::OnSecureChannelStatusChanged(
+ const cryptauth::RemoteDevice& remote_device,
+ const cryptauth::SecureChannel::Status& old_status,
+ const cryptauth::SecureChannel::Status& new_status) {
+ SendSecureChannelStatusChangeEvent(remote_device, old_status, new_status);
+ UpdateConnectionAttempts();
+}
+
+void BleConnectionManager::SendMessageReceivedEvent(
+ const cryptauth::RemoteDevice& remote_device,
+ const std::string& payload) {
+ PA_LOG(INFO) << "Broadcasting message received event: "
+ << "Device ID \"" << remote_device.GetTruncatedDeviceIdForLogs()
+ << "\" received message \"" << payload << "\".";
+ for (auto& observer : observer_list_) {
+ observer.OnMessageReceived(remote_device, payload);
+ }
+}
+
+void BleConnectionManager::SendSecureChannelStatusChangeEvent(
+ const cryptauth::RemoteDevice& remote_device,
+ const cryptauth::SecureChannel::Status& old_status,
+ const cryptauth::SecureChannel::Status& new_status) {
+ PA_LOG(INFO) << "Broadcasting status change event: "
+ << "Device ID \"" << remote_device.GetTruncatedDeviceIdForLogs()
+ << "\": " << cryptauth::SecureChannel::StatusToString(old_status)
+ << " => "
+ << cryptauth::SecureChannel::StatusToString(new_status);
+ for (auto& observer : observer_list_) {
+ observer.OnSecureChannelStatusChanged(remote_device, old_status,
+ new_status);
+ }
+}
+
+} // namespace tether
+
+} // namespace chromeos
« no previous file with comments | « chromeos/components/tether/ble_connection_manager.h ('k') | chromeos/components/tether/ble_connection_manager_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698