chromium/ash/ambient/util/time_of_day_utils.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 "ash/ambient/util/time_of_day_utils.h"

#include <string>
#include <utility>

#include "ash/ambient/metrics/ambient_metrics.h"
#include "ash/constants/ambient_time_of_day_constants.h"
#include "ash/constants/ash_features.h"
#include "base/check.h"
#include "base/containers/flat_map.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/no_destructor.h"
#include "base/strings/stringprintf.h"
#include "chromeos/ash/components/dbus/dlcservice/dlcservice.pb.h"
#include "chromeos/ash/components/dbus/dlcservice/dlcservice_client.h"
#include "third_party/cros_system_api/dbus/dlcservice/dbus-constants.h"

namespace ash {

namespace {

DlcError ConvertToDlcErrorEnum(const std::string& error_str) {
  const base::flat_map<std::string, DlcError> error_mapping = {
      {dlcservice::kErrorNone, DlcError::kNone},
      {dlcservice::kErrorInternal, DlcError::kInternal},
      {dlcservice::kErrorBusy, DlcError::kBusy},
      {dlcservice::kErrorNone, DlcError::kNone},
      {dlcservice::kErrorNeedReboot, DlcError::kNeedReboot},
      {dlcservice::kErrorInvalidDlc, DlcError::kInvalidDlc},
      {dlcservice::kErrorAllocation, DlcError::kAllocation},
      {dlcservice::kErrorNoImageFound, DlcError::kNoImageFound}};
  auto error_found_iter = error_mapping.find(error_str);
  if (error_found_iter != error_mapping.end()) {
    return error_found_iter->second;
  }
  // Return unknown if we can't recognize the error.
  LOG(ERROR) << "Wrong error message received from DLC Service";
  return DlcError::kUnknown;
}

void OnInstallDlcComplete(const std::string& dlc_metrics_label,
                          base::OnceCallback<void(base::FilePath)> on_done,
                          const DlcserviceClient::InstallResult& result) {
  CHECK(on_done);
  VLOG(1) << "Finished installing " << kTimeOfDayDlcId << " with error code "
          << result.error;
  base::UmaHistogramEnumeration(
      base::StringPrintf("Ash.AmbientMode.VideoDlcInstall.%s.Error",
                         dlc_metrics_label.c_str()),
      ConvertToDlcErrorEnum(result.error));
  base::FilePath install_dir;
  if (result.error == dlcservice::kErrorNone) {
    install_dir = base::FilePath(result.root_path);
  } else {
    LOG(ERROR) << "Failed to install " << kTimeOfDayDlcId << " with error "
               << result.error;
  }
  std::move(on_done).Run(install_dir);
}

void BuildAmbientVideoHtmlPath(base::OnceCallback<void(base::FilePath)> on_done,
                               base::FilePath root_dir) {
  CHECK(on_done);
  base::FilePath full_path;
  // `root_dir` can be empty if `InstallTimeOfDayDlc()` fails.
  if (!root_dir.empty()) {
    full_path = root_dir.Append(kTimeOfDayVideoHtmlSubPath);
  }
  std::move(on_done).Run(std::move(full_path));
}

// Installs the TimeOfDay DLC package containing assets for the
// Time Of Day screen saver. DLC will eventually replace the Time Of Day assets
// currently stored in rootfs. Returns the root directory where the assets are
// located. Returns an empty `base::FilePath` if the install fails.
//
// This is a successful no-op if the DLC is already installed.
void InstallTimeOfDayDlc(std::string dlc_metrics_label,
                         base::OnceCallback<void(base::FilePath)> on_done) {
  DlcserviceClient* client = DlcserviceClient::Get();
  CHECK(client);
  dlcservice::InstallRequest install_request;
  install_request.set_id(kTimeOfDayDlcId);
  VLOG(1) << "Installing " << kTimeOfDayDlcId;
  client->Install(
      install_request,
      base::BindOnce(&OnInstallDlcComplete, std::move(dlc_metrics_label),
                     std::move(on_done)),
      /*ProgressCallback=*/base::DoNothing());
}

}  // namespace

void GetAmbientVideoHtmlPath(std::string dlc_metrics_label,
                             base::OnceCallback<void(base::FilePath)> on_done) {
  InstallTimeOfDayDlc(
      std::move(dlc_metrics_label),
      base::BindOnce(&BuildAmbientVideoHtmlPath, std::move(on_done)));
}

void InstallAmbientVideoDlcInBackground() {
  GetAmbientVideoHtmlPath(ambient::kAmbientVideoDlcBackgroundLabel,
                          base::DoNothing());
}

const base::FilePath::CharType kTimeOfDayCloudsVideo[] =
    FILE_PATH_LITERAL("clouds.webm");
const base::FilePath::CharType kTimeOfDayNewMexicoVideo[] =
    FILE_PATH_LITERAL("new_mexico.webm");
const base::FilePath::CharType kTimeOfDayVideoHtmlSubPath[] =
    FILE_PATH_LITERAL("personalization/time_of_day/src/ambient_video.html");

}  // namespace ash