chromium/chromeos/ash/components/telemetry_extension/routines/telemetry_diagnostic_routine_service_ash.cc

// Copyright 2023 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/components/telemetry_extension/routines/telemetry_diagnostic_routine_service_ash.h"

#include <memory>
#include <utility>

#include "base/functional/bind.h"
#include "base/memory/weak_ptr.h"
#include "chromeos/ash/components/telemetry_extension/common/self_owned_mojo_proxy.h"
#include "chromeos/ash/components/telemetry_extension/common/telemetry_extension_converters.h"
#include "chromeos/ash/components/telemetry_extension/routines/routine_control.h"
#include "chromeos/ash/components/telemetry_extension/routines/routine_converters.h"
#include "chromeos/ash/components/telemetry_extension/routines/routine_events_forwarder.h"
#include "chromeos/ash/services/cros_healthd/public/cpp/service_connection.h"
#include "chromeos/ash/services/cros_healthd/public/mojom/cros_healthd_routines.mojom.h"
#include "chromeos/crosapi/mojom/telemetry_diagnostic_routine_service.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"

namespace ash {

namespace {

namespace crosapi = crosapi::mojom;
namespace healthd = cros_healthd::mojom;

using RoutineControlProxy =
    SelfOwnedMojoProxy<healthd::RoutineControl,
                       crosapi::TelemetryDiagnosticRoutineControl,
                       CrosHealthdRoutineControl>;
using RoutineObserverProxy =
    SelfOwnedMojoProxy<crosapi::TelemetryDiagnosticRoutineObserver,
                       healthd::RoutineObserver,
                       CrosHealthdRoutineEventsForwarder>;

}  // namespace

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

// static
std::unique_ptr<crosapi::TelemetryDiagnosticRoutinesService>
TelemetryDiagnosticsRoutineServiceAsh::Factory::Create(
    mojo::PendingReceiver<crosapi::TelemetryDiagnosticRoutinesService>
        receiver) {
  if (test_factory_) {
    return test_factory_->CreateInstance(std::move(receiver));
  }

  auto routine_service =
      std::make_unique<TelemetryDiagnosticsRoutineServiceAsh>();
  routine_service->BindReceiver(std::move(receiver));
  return routine_service;
}

// static
void TelemetryDiagnosticsRoutineServiceAsh::Factory::SetForTesting(
    Factory* test_factory) {
  test_factory_ = test_factory;
}

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

TelemetryDiagnosticsRoutineServiceAsh::TelemetryDiagnosticsRoutineServiceAsh() =
    default;

TelemetryDiagnosticsRoutineServiceAsh::
    ~TelemetryDiagnosticsRoutineServiceAsh() {
  for (auto&& proxy : routine_controls_and_observers_) {
    if (proxy) {
      proxy->OnServiceDestroyed();
    }
  }
  routine_controls_and_observers_.clear();
}

void TelemetryDiagnosticsRoutineServiceAsh::BindReceiver(
    mojo::PendingReceiver<crosapi::TelemetryDiagnosticRoutinesService>
        receiver) {
  receivers_.Add(this, std::move(receiver));
}

void TelemetryDiagnosticsRoutineServiceAsh::CreateRoutine(
    crosapi::TelemetryDiagnosticRoutineArgumentPtr routine_argument,
    mojo::PendingReceiver<crosapi::TelemetryDiagnosticRoutineControl>
        routine_receiver,
    mojo::PendingRemote<crosapi::TelemetryDiagnosticRoutineObserver> observer) {
  // Setup the RoutineControl.
  mojo::PendingRemote<healthd::RoutineControl> cros_healthd_remote;
  auto cros_healthd_receiver =
      cros_healthd_remote.InitWithNewPipeAndPassReceiver();

  // SAFETY: We can use `base::Unretained` here since we signal the
  // `SelfOwnedMojoProxy` in the destructor.
  auto control_delete_cb =
      base::BindOnce(&TelemetryDiagnosticsRoutineServiceAsh::OnConnectionClosed,
                     base::Unretained(this));
  auto routine_control = RoutineControlProxy::Create(
      std::move(routine_receiver), std::move(cros_healthd_remote),
      std::move(control_delete_cb));
  routine_controls_and_observers_.insert(std::move(routine_control));

  // Setup the RoutineObserver.
  mojo::PendingRemote<healthd::RoutineObserver> cros_healthd_observer;
  if (observer.is_valid()) {
    // SAFETY: We can use `base::Unretained` here since we signal the
    // `SelfOwnedMojoProxy` in the destructor.
    auto observer_delete_cb = base::BindOnce(
        &TelemetryDiagnosticsRoutineServiceAsh::OnConnectionClosed,
        base::Unretained(this));
    auto routine_observer = RoutineObserverProxy::Create(
        cros_healthd_observer.InitWithNewPipeAndPassReceiver(),
        std::move(observer), std::move(observer_delete_cb));
    routine_controls_and_observers_.insert(std::move(routine_observer));
  }

  // Register the two objects with cros_healthd.
  cros_healthd::ServiceConnection::GetInstance()
      ->GetRoutinesService()
      ->CreateRoutine(
          converters::ConvertRoutinePtr(std::move(routine_argument)),
          std::move(cros_healthd_receiver), std::move(cros_healthd_observer));
}

void TelemetryDiagnosticsRoutineServiceAsh::IsRoutineArgumentSupported(
    crosapi::TelemetryDiagnosticRoutineArgumentPtr arg,
    IsRoutineArgumentSupportedCallback callback) {
  cros_healthd::ServiceConnection::GetInstance()
      ->GetRoutinesService()
      ->IsRoutineArgumentSupported(
          converters::ConvertRoutinePtr(std::move(arg)),
          base::BindOnce(
              [](IsRoutineArgumentSupportedCallback callback,
                 healthd::SupportStatusPtr status) {
                std::move(callback).Run(
                    converters::ConvertCommonPtr(std::move(status)));
              },
              std::move(callback)));
}

void TelemetryDiagnosticsRoutineServiceAsh::OnConnectionClosed(
    base::WeakPtr<SelfOwnedMojoProxyInterface> closed_connection) {
  routine_controls_and_observers_.erase(closed_connection);
}

}  // namespace ash