blob: 80bee9ef31b5bf1277678ddb775e5bd6a20e71a6 [file] [log] [blame]
// Copyright 2018 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/services/secure_channel/pending_connection_manager_impl.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/no_destructor.h"
#include "base/stl_util.h"
#include "chromeos/services/secure_channel/authenticated_channel.h"
#include "chromeos/services/secure_channel/ble_initiator_connection_attempt.h"
#include "chromeos/services/secure_channel/ble_listener_connection_attempt.h"
#include "chromeos/services/secure_channel/pending_ble_initiator_connection_request.h"
#include "chromeos/services/secure_channel/pending_ble_listener_connection_request.h"
namespace chromeos {
namespace secure_channel {
// static
PendingConnectionManagerImpl::Factory*
PendingConnectionManagerImpl::Factory::test_factory_ = nullptr;
// static
PendingConnectionManagerImpl::Factory*
PendingConnectionManagerImpl::Factory::Get() {
if (test_factory_)
return test_factory_;
static base::NoDestructor<Factory> factory;
return factory.get();
}
// static
void PendingConnectionManagerImpl::Factory::SetFactoryForTesting(
Factory* test_factory) {
test_factory_ = test_factory;
}
PendingConnectionManagerImpl::Factory::~Factory() = default;
std::unique_ptr<PendingConnectionManager>
PendingConnectionManagerImpl::Factory::BuildInstance(
Delegate* delegate,
BleConnectionManager* ble_connection_manager,
scoped_refptr<device::BluetoothAdapter> bluetooth_adapter) {
return base::WrapUnique(new PendingConnectionManagerImpl(
delegate, ble_connection_manager, bluetooth_adapter));
}
PendingConnectionManagerImpl::PendingConnectionManagerImpl(
Delegate* delegate,
BleConnectionManager* ble_connection_manager,
scoped_refptr<device::BluetoothAdapter> bluetooth_adapter)
: PendingConnectionManager(delegate),
ble_connection_manager_(ble_connection_manager),
bluetooth_adapter_(bluetooth_adapter) {}
PendingConnectionManagerImpl::~PendingConnectionManagerImpl() = default;
void PendingConnectionManagerImpl::HandleConnectionRequest(
const ConnectionAttemptDetails& connection_attempt_details,
std::unique_ptr<ClientConnectionParameters> client_connection_parameters,
ConnectionPriority connection_priority) {
DCHECK_EQ(ConnectionMedium::kBluetoothLowEnergy,
connection_attempt_details.connection_medium());
// If the client has canceled the request, it does not need to be processed.
if (!client_connection_parameters->IsClientWaitingForResponse()) {
PA_LOG(VERBOSE)
<< "PendingConnectionManagerImpl::HandleConnectionRequest(): "
<< "Request was canceled by the client before being passed to "
<< "PendingConnectionManager; ignoring. Details: "
<< connection_attempt_details
<< ", Parameters: " << *client_connection_parameters
<< ", Priority: " << connection_priority;
return;
}
// Insert the entry into the ConnectionDetails to ConnectionAttemptDetails
// map.
ConnectionDetails connection_details =
connection_attempt_details.GetAssociatedConnectionDetails();
details_to_attempt_details_map_[connection_details].insert(
connection_attempt_details);
// Process the role-specific details.
switch (connection_attempt_details.connection_role()) {
case ConnectionRole::kInitiatorRole:
HandleBleInitiatorRequest(connection_attempt_details,
std::move(client_connection_parameters),
connection_priority);
break;
case ConnectionRole::kListenerRole:
HandleBleListenerRequest(connection_attempt_details,
std::move(client_connection_parameters),
connection_priority);
break;
}
}
void PendingConnectionManagerImpl::OnConnectionAttemptSucceeded(
const ConnectionDetails& connection_details,
std::unique_ptr<AuthenticatedChannel> authenticated_channel) {
if (!base::ContainsKey(details_to_attempt_details_map_, connection_details)) {
PA_LOG(ERROR) << "PendingConnectionManagerImpl::"
<< "OnConnectionAttemptSucceeded(): Attempt succeeded, but "
<< "there was no corresponding map entry. "
<< "Details: " << connection_details;
NOTREACHED();
}
std::vector<std::unique_ptr<ClientConnectionParameters>> all_clients;
// Make a copy of the ConnectionAttemptDetails to process to prevent modifying
// the set during iteration.
base::flat_set<ConnectionAttemptDetails> to_process =
details_to_attempt_details_map_[connection_details];
// For each associated ConnectionAttemptDetails, extract clients from the
// connection attempt, add them to |all_clients|, and remove the associated
// map entries.
for (const auto& connection_attempt_details : to_process) {
std::vector<std::unique_ptr<ClientConnectionParameters>> clients_in_loop;
switch (connection_attempt_details.connection_role()) {
case ConnectionRole::kInitiatorRole:
clients_in_loop = ConnectionAttempt<BleInitiatorFailureType>::
ExtractClientConnectionParameters(
std::move(id_pair_to_ble_initiator_connection_attempts_
[connection_attempt_details.device_id_pair()]));
break;
case ConnectionRole::kListenerRole:
clients_in_loop = ConnectionAttempt<BleListenerFailureType>::
ExtractClientConnectionParameters(
std::move(id_pair_to_ble_listener_connection_attempts_
[connection_attempt_details.device_id_pair()]));
break;
}
// Move elements in |clients_in_list| to |all_clients|.
all_clients.insert(all_clients.end(),
std::make_move_iterator(clients_in_loop.begin()),
std::make_move_iterator(clients_in_loop.end()));
RemoveMapEntriesForFinishedConnectionAttempt(connection_attempt_details);
}
NotifyOnConnection(std::move(authenticated_channel), std::move(all_clients),
connection_details);
}
void PendingConnectionManagerImpl::OnConnectionAttemptFinishedWithoutConnection(
const ConnectionAttemptDetails& connection_attempt_details) {
RemoveMapEntriesForFinishedConnectionAttempt(connection_attempt_details);
}
void PendingConnectionManagerImpl::HandleBleInitiatorRequest(
const ConnectionAttemptDetails& connection_attempt_details,
std::unique_ptr<ClientConnectionParameters> client_connection_parameters,
ConnectionPriority connection_priority) {
// If no ConnectionAttempt exists to this device in the initiator role, create
// one.
if (!base::ContainsKey(id_pair_to_ble_initiator_connection_attempts_,
connection_attempt_details.device_id_pair())) {
id_pair_to_ble_initiator_connection_attempts_[connection_attempt_details
.device_id_pair()] =
BleInitiatorConnectionAttempt::Factory::Get()->BuildInstance(
ble_connection_manager_, this /* delegate */,
connection_attempt_details);
}
auto& connection_attempt =
id_pair_to_ble_initiator_connection_attempts_[connection_attempt_details
.device_id_pair()];
bool success = connection_attempt->AddPendingConnectionRequest(
PendingBleInitiatorConnectionRequest::Factory::Get()->BuildInstance(
std::move(client_connection_parameters), connection_priority,
connection_attempt.get() /* delegate */, bluetooth_adapter_));
if (!success) {
PA_LOG(ERROR) << "PendingConnectionManagerImpl::"
<< "HandleBleInitiatorRequest(): Not able to handle request. "
<< "Details: " << connection_attempt_details
<< ", Client parameters: " << *client_connection_parameters;
NOTREACHED();
}
}
void PendingConnectionManagerImpl::HandleBleListenerRequest(
const ConnectionAttemptDetails& connection_attempt_details,
std::unique_ptr<ClientConnectionParameters> client_connection_parameters,
ConnectionPriority connection_priority) {
// If no ConnectionAttempt exists to this device in the listener role, create
// one.
if (!base::ContainsKey(id_pair_to_ble_listener_connection_attempts_,
connection_attempt_details.device_id_pair())) {
id_pair_to_ble_listener_connection_attempts_[connection_attempt_details
.device_id_pair()] =
BleListenerConnectionAttempt::Factory::Get()->BuildInstance(
ble_connection_manager_, this /* delegate */,
connection_attempt_details);
}
auto& connection_attempt =
id_pair_to_ble_listener_connection_attempts_[connection_attempt_details
.device_id_pair()];
bool success = connection_attempt->AddPendingConnectionRequest(
PendingBleListenerConnectionRequest::Factory::Get()->BuildInstance(
std::move(client_connection_parameters), connection_priority,
connection_attempt.get() /* delegate */, bluetooth_adapter_));
if (!success) {
PA_LOG(ERROR) << "PendingConnectionManagerImpl::"
<< "HandleBleListenerRequest(): Not able to handle request. "
<< "Details: " << connection_attempt_details
<< ", Client parameters: " << *client_connection_parameters;
NOTREACHED();
}
}
void PendingConnectionManagerImpl::RemoveMapEntriesForFinishedConnectionAttempt(
const ConnectionAttemptDetails& connection_attempt_details) {
// Make a copy of |connection_attempt_details|, since it belongs to the
// ConnectionAttempt which is about to be deleted below.
ConnectionAttemptDetails connection_attempt_details_copy =
connection_attempt_details;
switch (connection_attempt_details_copy.connection_role()) {
case ConnectionRole::kInitiatorRole: {
size_t num_deleted = id_pair_to_ble_initiator_connection_attempts_.erase(
connection_attempt_details_copy.device_id_pair());
if (num_deleted != 1u) {
PA_LOG(ERROR) << "PendingConnectionManagerImpl::"
<< "RemoveMapEntriesForFinishedConnectionAttempt(): "
<< "Tried to remove failed initiator ConnectionAttempt, "
<< "but it was not present in the map. Details: "
<< connection_attempt_details_copy;
NOTREACHED();
}
break;
}
case ConnectionRole::kListenerRole: {
size_t num_deleted = id_pair_to_ble_listener_connection_attempts_.erase(
connection_attempt_details_copy.device_id_pair());
if (num_deleted != 1u) {
PA_LOG(ERROR) << "PendingConnectionManagerImpl::"
<< "RemoveMapEntriesForFinishedConnectionAttempt(): "
<< "Tried to remove failed listener ConnectionAttempt, "
<< "but it was not present in the map. Details: "
<< connection_attempt_details_copy;
NOTREACHED();
}
break;
}
}
ConnectionDetails connection_details =
connection_attempt_details_copy.GetAssociatedConnectionDetails();
size_t num_deleted =
details_to_attempt_details_map_[connection_details].erase(
connection_attempt_details_copy);
if (num_deleted != 1u) {
PA_LOG(ERROR) << "PendingConnectionManagerImpl::"
<< "RemoveMapEntriesForFinishedConnectionAttempt(): "
<< "Tried to remove ConnectionAttemptDetails, but they were"
<< "not present in the map. Details: "
<< connection_attempt_details_copy;
NOTREACHED();
}
// If |connection_attempt_details_copy| was the last entry, remove the entire
// set.
if (details_to_attempt_details_map_[connection_details].empty())
details_to_attempt_details_map_.erase(connection_details);
}
} // namespace secure_channel
} // namespace chromeos