chromium/chromeos/ash/services/secure_channel/secure_channel_initializer.cc

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromeos/ash/services/secure_channel/secure_channel_initializer.h"

#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "chromeos/ash/components/multidevice/logging/logging.h"
#include "chromeos/ash/services/secure_channel/secure_channel_impl.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/dbus/bluez_dbus_manager.h"
#include "device/bluetooth/floss/floss_dbus_manager.h"
#include "device/bluetooth/floss/floss_features.h"
#include "mojo/public/cpp/bindings/pending_remote.h"

namespace ash::secure_channel {

// static
SecureChannelInitializer::Factory*
    SecureChannelInitializer::Factory::test_factory_ = nullptr;

// static
std::unique_ptr<SecureChannelBase> SecureChannelInitializer::Factory::Create(
    scoped_refptr<base::TaskRunner> task_runner) {
  if (test_factory_)
    return test_factory_->CreateInstance(std::move(task_runner));

  return base::WrapUnique(new SecureChannelInitializer(std::move(task_runner)));
}

// static
void SecureChannelInitializer::Factory::SetFactoryForTesting(
    Factory* test_factory) {
  test_factory_ = test_factory;
}

SecureChannelInitializer::Factory::~Factory() = default;

SecureChannelInitializer::ConnectionRequestArgs::ConnectionRequestArgs(
    const multidevice::RemoteDevice& device_to_connect,
    const multidevice::RemoteDevice& local_device,
    const std::string& feature,
    ConnectionMedium connection_medium,
    ConnectionPriority connection_priority,
    mojo::PendingRemote<mojom::ConnectionDelegate> delegate,
    mojo::PendingRemote<mojom::SecureChannelStructuredMetricsLogger>
        secure_channel_structured_metrics_logger,
    bool is_listen_request)
    : device_to_connect(device_to_connect),
      local_device(local_device),
      feature(feature),
      connection_medium(connection_medium),
      connection_priority(connection_priority),
      delegate(std::move(delegate)),
      secure_channel_structured_metrics_logger(
          std::move(secure_channel_structured_metrics_logger)),
      is_listen_request(is_listen_request) {}

SecureChannelInitializer::ConnectionRequestArgs::~ConnectionRequestArgs() =
    default;

SecureChannelInitializer::SecureChannelInitializer(
    scoped_refptr<base::TaskRunner> task_runner) {
  bool is_initialized = false;
  if (floss::features::IsFlossEnabled()) {
    is_initialized = floss::FlossDBusManager::IsInitialized();
  } else {
    is_initialized = bluez::BluezDBusManager::IsInitialized();
  }

  // May not be initialized in tests.
  if (!is_initialized)
    return;

  PA_LOG(VERBOSE) << "SecureChannelInitializer::SecureChannelInitializer(): "
                  << "Fetching Bluetooth adapter. All requests received before "
                  << "the adapter is fetched will be queued.";

  // device::BluetoothAdapterFactory::SetAdapterForTesting() causes the
  // GetAdapter() callback to return synchronously. Thus, post the GetAdapter()
  // call as a task to ensure that it is returned asynchronously, even in tests.
  auto* factory = device::BluetoothAdapterFactory::Get();
  task_runner->PostTask(
      FROM_HERE,
      base::BindOnce(
          &device::BluetoothAdapterFactory::GetAdapter,
          base::Unretained(factory),
          base::BindOnce(&SecureChannelInitializer::OnBluetoothAdapterReceived,
                         weak_ptr_factory_.GetWeakPtr())));
}

SecureChannelInitializer::~SecureChannelInitializer() = default;

void SecureChannelInitializer::ListenForConnectionFromDevice(
    const multidevice::RemoteDevice& device_to_connect,
    const multidevice::RemoteDevice& local_device,
    const std::string& feature,
    ConnectionMedium connection_medium,
    ConnectionPriority connection_priority,
    mojo::PendingRemote<mojom::ConnectionDelegate> delegate) {
  if (secure_channel_impl_) {
    secure_channel_impl_->ListenForConnectionFromDevice(
        device_to_connect, local_device, feature, connection_medium,
        connection_priority, std::move(delegate));
    return;
  }

  pending_args_.push(std::make_unique<ConnectionRequestArgs>(
      device_to_connect, local_device, feature, connection_medium,
      connection_priority, std::move(delegate), mojo::NullRemote(),
      true /* is_listen_request */));
}

void SecureChannelInitializer::InitiateConnectionToDevice(
    const multidevice::RemoteDevice& device_to_connect,
    const multidevice::RemoteDevice& local_device,
    const std::string& feature,
    ConnectionMedium connection_medium,
    ConnectionPriority connection_priority,
    mojo::PendingRemote<mojom::ConnectionDelegate> delegate,
    mojo::PendingRemote<mojom::SecureChannelStructuredMetricsLogger>
        secure_channel_structured_metrics_logger) {
  if (secure_channel_impl_) {
    secure_channel_impl_->InitiateConnectionToDevice(
        device_to_connect, local_device, feature, connection_medium,
        connection_priority, std::move(delegate),
        std::move(secure_channel_structured_metrics_logger));
    return;
  }

  pending_args_.push(std::make_unique<ConnectionRequestArgs>(
      device_to_connect, local_device, feature, connection_medium,
      connection_priority, std::move(delegate),
      std::move(secure_channel_structured_metrics_logger),
      false /* is_listen_request */));
}
void SecureChannelInitializer::GetLastSeenTimestamp(
    const std::string& remote_device_id,
    base::OnceCallback<void(std::optional<base::Time>)> callback) {
  if (secure_channel_impl_) {
    secure_channel_impl_->GetLastSeenTimestamp(remote_device_id,
                                               std::move(callback));
  }
}

void SecureChannelInitializer::SetNearbyConnector(
    mojo::PendingRemote<mojom::NearbyConnector> nearby_connector) {
  if (secure_channel_impl_) {
    secure_channel_impl_->SetNearbyConnector(std::move(nearby_connector));
    return;
  }

  nearby_connector_ = std::move(nearby_connector);
}

void SecureChannelInitializer::OnBluetoothAdapterReceived(
    scoped_refptr<device::BluetoothAdapter> bluetooth_adapter) {
  PA_LOG(VERBOSE) << "SecureChannelInitializer::OnBluetoothAdapterReceived(): "
                  << "Bluetooth adapter has been fetched. Passing all queued "
                  << "requests to the service.";

  secure_channel_impl_ = SecureChannelImpl::Factory::Create(bluetooth_adapter);

  if (nearby_connector_)
    secure_channel_impl_->SetNearbyConnector(std::move(nearby_connector_));

  while (!pending_args_.empty()) {
    std::unique_ptr<ConnectionRequestArgs> args_to_pass =
        std::move(pending_args_.front());
    pending_args_.pop();

    if (args_to_pass->is_listen_request) {
      secure_channel_impl_->ListenForConnectionFromDevice(
          args_to_pass->device_to_connect, args_to_pass->local_device,
          args_to_pass->feature, args_to_pass->connection_medium,
          args_to_pass->connection_priority, std::move(args_to_pass->delegate));
      continue;
    }

    secure_channel_impl_->InitiateConnectionToDevice(
        args_to_pass->device_to_connect, args_to_pass->local_device,
        args_to_pass->feature, args_to_pass->connection_medium,
        args_to_pass->connection_priority, std::move(args_to_pass->delegate),
        std::move(args_to_pass->secure_channel_structured_metrics_logger));
  }
}

}  // namespace ash::secure_channel