chromium/chromeos/ash/components/kiosk/vision/kiosk_vision.cc

// Copyright 2024 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/kiosk/vision/kiosk_vision.h"

#include <string>
#include <string_view>
#include <utility>

#include "base/check_deref.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_forward.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "chromeos/ash/components/dbus/dlcservice/dlcservice.pb.h"
#include "chromeos/ash/components/dbus/dlcservice/dlcservice_client.h"
#include "chromeos/ash/components/kiosk/vision/internal/camera_service_connector.h"
#include "chromeos/ash/components/kiosk/vision/internal/detection_processor.h"
#include "chromeos/ash/components/kiosk/vision/internal/pref_observer.h"
#include "chromeos/ash/components/kiosk/vision/internals_page_processor.h"
#include "chromeos/ash/components/kiosk/vision/pref_names.h"
#include "chromeos/ash/components/kiosk/vision/telemetry_processor.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "third_party/cros_system_api/dbus/dlcservice/dbus-constants.h"

namespace ash::kiosk_vision {

namespace {

void InstallDlc(base::OnceCallback<void(std::string dlc_root_path)> on_done,
                base::OnceClosure on_error) {
  auto& dlc_service = CHECK_DEREF(DlcserviceClient::Get());
  dlcservice::InstallRequest install_request;
  install_request.set_id(kKioskVisionDlcId);
  dlc_service.Install(
      install_request,
      base::BindOnce(
          [](base::OnceCallback<void(std::string)> on_done,
             base::OnceClosure on_error,
             const DlcserviceClient::InstallResult& result) {
            if (result.error != dlcservice::kErrorNone) {
              LOG(ERROR)
                  << "Kiosk Vision failed to install DLC, unable to proceed: "
                  << result.error;
              return std::move(on_error).Run();
            }

            std::move(on_done).Run(result.root_path);
          },
          std::move(on_done), std::move(on_error)),
      /*progress_callback=*/base::DoNothing());
}

void UninstallDlc() {
  auto& dlc_service = CHECK_DEREF(DlcserviceClient::Get());
  dlc_service.Uninstall(
      kKioskVisionDlcId, base::BindOnce([](std::string_view error) {
        if (error != dlcservice::kErrorNone) {
          LOG(WARNING) << "Kiosk Vision failed to uninstall DLC: " << error;
        }
      }));
}

}  // namespace

KioskVision::KioskVision(PrefService* pref_service)
    : pref_observer_(
          pref_service,
          /*on_enabled=*/
          base::BindRepeating(&KioskVision::Enable, base::Unretained(this)),
          /*on_disabled=*/
          base::BindRepeating(&KioskVision::Disable, base::Unretained(this))) {
  if (!IsTelemetryPrefEnabled(CHECK_DEREF(pref_service))) {
    // Only uninstalls the DLC during construction, not during pref changes.
    // This avoids uninstalling the DLC while camera service is using it.
    UninstallDlc();
  }
  pref_observer_.Start();
}

KioskVision::~KioskVision() = default;

void KioskVision::Enable() {
  InstallDlc(/*on_done=*/
             base::BindOnce(&KioskVision::InitializeProcessors,
                            weak_ptr_factory_.GetWeakPtr()),
             /*on_error=*/
             base::BindOnce(&KioskVision::OnDlcInstallError,
                            weak_ptr_factory_.GetWeakPtr()));
}

void KioskVision::Disable() {
  camera_connector_.reset();
  detection_observer_.reset();
  telemetry_processor_.reset();
  retry_timer_.Stop();
}

void KioskVision::InitializeProcessors(std::string dlc_path) {
  telemetry_processor_.emplace();
  DetectionProcessors ps = {&telemetry_processor_.value()};
  if (IsInternalsPageEnabled()) {
    internals_webui_processor_.emplace();
    ps.push_back(&internals_webui_processor_.value());
  }
  detection_observer_.emplace(std::move(ps));
  camera_connector_.emplace(std::move(dlc_path), &detection_observer_.value());
  camera_connector_->Start();
}

void KioskVision::OnDlcInstallError() {
  Disable();
  retry_timer_.Start(base::BindOnce(&KioskVision::Enable,
                                    // Safe because `this` owns `retry_timer_`.
                                    base::Unretained(this)));
}

TelemetryProcessor* KioskVision::GetTelemetryProcessor() {
  return telemetry_processor_.has_value() ? &telemetry_processor_.value()
                                          : nullptr;
}

InternalsPageProcessor* KioskVision::GetInternalsPageProcessor() {
  return internals_webui_processor_.has_value()
             ? &internals_webui_processor_.value()
             : nullptr;
}

const CameraServiceConnector* KioskVision::GetCameraConnectorForTesting()
    const {
  return camera_connector_.has_value() ? &camera_connector_.value() : nullptr;
}

void RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
  registry->RegisterBooleanPref(prefs::kKioskVisionTelemetryEnabled,
                                /*default_value=*/false);
  registry->RegisterTimeDeltaPref(prefs::kKioskVisionTelemetryFrequency,
                                  /*default_value=*/base::Minutes(2));
}

}  // namespace ash::kiosk_vision