chromium/chromeos/ash/services/quick_pair/quick_pair_process_manager_impl.cc

// Copyright 2021 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/quick_pair/quick_pair_process_manager_impl.h"

#include <memory>

#include "ash/quick_pair/common/quick_pair_browser_delegate.h"
#include "base/functional/bind.h"
#include "base/task/sequenced_task_runner.h"
#include "base/unguessable_token.h"
#include "chromeos/ash/services/quick_pair/public/mojom/fast_pair_data_parser.mojom.h"
#include "chromeos/ash/services/quick_pair/quick_pair_process_shutdown_controller.h"
#include "components/cross_device/logging/logging.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"

namespace ash {
namespace quick_pair {

QuickPairProcessManagerImpl::ProcessReferenceImpl::ProcessReferenceImpl(
    const mojo::SharedRemote<mojom::FastPairDataParser>& fast_pair_data_parser,
    base::OnceClosure destructor_callback)
    : fast_pair_data_parser_(fast_pair_data_parser),
      destructor_callback_(std::move(destructor_callback)) {}

QuickPairProcessManagerImpl::ProcessReferenceImpl::~ProcessReferenceImpl() {
  // Reset the SharedRemotes before the destructor callback is run to ensure
  // that all connections to the utility process are destroyed before we attempt
  // to tear the process down.
  fast_pair_data_parser_.reset();

  std::move(destructor_callback_).Run();
}

const mojo::SharedRemote<mojom::FastPairDataParser>&
QuickPairProcessManagerImpl::ProcessReferenceImpl::GetFastPairDataParser()
    const {
  return fast_pair_data_parser_;
}

QuickPairProcessManagerImpl::QuickPairProcessManagerImpl()
    : QuickPairProcessManagerImpl(
          std::make_unique<QuickPairProcessShutdownController>()) {}

QuickPairProcessManagerImpl::QuickPairProcessManagerImpl(
    std::unique_ptr<QuickPairProcessShutdownController> shutdown_controller)
    : process_shutdown_controller_(std::move(shutdown_controller)) {}

QuickPairProcessManagerImpl::~QuickPairProcessManagerImpl() = default;

std::unique_ptr<QuickPairProcessManager::ProcessReference>
QuickPairProcessManagerImpl::GetProcessReference(
    QuickPairProcessManager::ProcessStoppedCallback
        on_process_stopped_callback) {
  // Start the process if we don't have valid bound Remotes.
  if (!service_ || !fast_pair_data_parser_)
    BindToProcess();

  CD_LOG(VERBOSE, Feature::FP)
      << __func__ << ": New process reference requested.";

  // Ensure the process isn't shutdown (it's possible the controller has started
  // their procedure by here.)
  process_shutdown_controller_->Stop();

  auto reference_id = base::UnguessableToken::Create();
  id_to_process_stopped_callback_map_[reference_id] =
      std::move(on_process_stopped_callback);

  return std::make_unique<ProcessReferenceImpl>(
      fast_pair_data_parser_,
      base::BindOnce(&QuickPairProcessManagerImpl::OnReferenceDeleted,
                     weak_ptr_factory_.GetWeakPtr(), reference_id));
}

void QuickPairProcessManagerImpl::BindToProcess() {
  DCHECK(!service_ && !fast_pair_data_parser_);

  CD_LOG(INFO, Feature::FP) << "Starting up QuickPair utility process";

  QuickPairBrowserDelegate::Get()->RequestService(
      service_.BindNewPipeAndPassReceiver());

  service_.set_disconnect_handler(
      base::BindOnce(&QuickPairProcessManagerImpl::ShutdownProcess,
                     weak_ptr_factory_.GetWeakPtr(), ShutdownReason::kCrash));

  mojo::PendingRemote<mojom::FastPairDataParser> fast_pair_data_parser;
  mojo::PendingReceiver<mojom::FastPairDataParser>
      fast_pair_data_parser_receiver =
          fast_pair_data_parser.InitWithNewPipeAndPassReceiver();
  fast_pair_data_parser_.Bind(std::move(fast_pair_data_parser),
                              /*bind_task_runner=*/nullptr);
  fast_pair_data_parser_.set_disconnect_handler(
      base::BindOnce(&QuickPairProcessManagerImpl::ShutdownProcess,
                     weak_ptr_factory_.GetWeakPtr(),
                     ShutdownReason::kFastPairDataParserMojoPipeDisconnection),
      base::SequencedTaskRunner::GetCurrentDefault());

  service_->Connect(std::move(fast_pair_data_parser_receiver));
}

void QuickPairProcessManagerImpl::OnReferenceDeleted(
    base::UnguessableToken id) {
  auto it = id_to_process_stopped_callback_map_.find(id);
  DCHECK(it != id_to_process_stopped_callback_map_.end());

  // Do not call the callback because its owner has already explicitly deleted
  // its reference.
  id_to_process_stopped_callback_map_.erase(it);

  // If there are still active references, the process should be kept alive, so
  // return early.
  if (!id_to_process_stopped_callback_map_.empty())
    return;

  CD_LOG(VERBOSE, Feature::FP)
      << "All process references have been released. Starting shutdown timer";

  process_shutdown_controller_->Start(
      base::BindOnce(&QuickPairProcessManagerImpl::ShutdownProcess,
                     weak_ptr_factory_.GetWeakPtr(), ShutdownReason::kNormal));
}

void QuickPairProcessManagerImpl::ShutdownProcess(
    ShutdownReason shutdown_reason) {
  if (!service_ && !fast_pair_data_parser_)
    return;

  CD_LOG(WARNING, Feature::FP) << __func__ << ": " << shutdown_reason;

  // Ensure that we don't try to stop the process again.
  process_shutdown_controller_->Stop();

  // Prevent the Remotes' disconnect handler and the OnReferenceDeleted
  // callbacks from firing.
  weak_ptr_factory_.InvalidateWeakPtrs();

  service_.reset();
  fast_pair_data_parser_.reset();

  // Move the map to a local variable to ensure that the instance field is
  // empty before any callbacks are made.
  auto old_map = std::move(id_to_process_stopped_callback_map_);
  id_to_process_stopped_callback_map_.clear();

  // Invoke the "process stopped" callback for each client.
  for (auto& it : old_map)
    std::move(it.second).Run(shutdown_reason);
}

}  // namespace quick_pair
}  // namespace ash