chromium/chrome/browser/ash/policy/handlers/device_dlc_predownload_list_policy_handler.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 "device_dlc_predownload_list_policy_handler.h"

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

#include "base/containers/contains.h"
#include "base/containers/fixed_flat_map.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "chromeos/ash/components/settings/cros_settings.h"

namespace policy {

namespace {

void OnInstallDlcComplete(
    const base::Value& dlc_id,
    const ash::DlcserviceClient::InstallResult& install_result) {
  if (install_result.error != dlcservice::kErrorNone) {
    LOG(ERROR) << "Failed to install DLC (" << dlc_id
               << "): " << install_result.error;
  }
}

}  // namespace

class OnInstallDlcHandlerImpl
    : public DeviceDlcPredownloadListPolicyHandler::OnInstallDlcHandler {
 public:
  OnInstallDlcHandlerImpl() = default;
  ~OnInstallDlcHandlerImpl() override = default;

  void OnInstallDlcComplete(
      const base::Value& dlc_id,
      const ash::DlcserviceClient::InstallResult& install_result) override {
    if (install_result.error != dlcservice::kErrorNone) {
      LOG(ERROR) << "Failed to install DLC (" << dlc_id
                 << "): " << install_result.error;
    }
  }
};

DeviceDlcPredownloadListPolicyHandler::
    ~DeviceDlcPredownloadListPolicyHandler() = default;

// static
base::Value::List
DeviceDlcPredownloadListPolicyHandler::DecodeDeviceDlcPredownloadListPolicy(
    const google::protobuf::RepeatedPtrField<std::string>& raw_policy_value,
    std::string& out_warning) {
  std::vector<std::string> unknown_dlcs;
  constexpr auto policy_value_to_dlc_id =
      base::MakeFixedFlatMap<std::string_view, std::string_view>(
          {{"scanner_drivers", "sane-backends-pfu"}});

  base::Value::List dlcs_to_predownload =
      base::Value::List::with_capacity(raw_policy_value.size());
  for (const auto& dlc_to_predownload : raw_policy_value) {
    if (!policy_value_to_dlc_id.contains(dlc_to_predownload)) {
      unknown_dlcs.push_back(dlc_to_predownload);
      continue;
    }
    std::string_view dlc_id = policy_value_to_dlc_id.at(dlc_to_predownload);
    if (!base::Contains(dlcs_to_predownload, dlc_id)) {
      // Silently ignore duplicate values.
      dlcs_to_predownload.Append(dlc_id);
    }
  }

  if (unknown_dlcs.empty()) {
    out_warning.clear();
  } else {
    out_warning =
        base::StrCat({"Unknown DLCs: ", base::JoinString(unknown_dlcs, ", ")});
  }

  return dlcs_to_predownload;
}

// static
std::unique_ptr<DeviceDlcPredownloadListPolicyHandler>
DeviceDlcPredownloadListPolicyHandler::Create() {
  return base::WrapUnique(new DeviceDlcPredownloadListPolicyHandler());
}

DeviceDlcPredownloadListPolicyHandler::DeviceDlcPredownloadListPolicyHandler()
    : cros_settings_(ash::CrosSettings::Get()) {
  dlc_predownloader_subscription_ = cros_settings_->AddSettingsObserver(
      ash::kDeviceDlcPredownloadList,
      base::BindRepeating(
          &DeviceDlcPredownloadListPolicyHandler::TriggerPredownloadDlcs,
          base::Unretained(this)));
  // Trigger predownload in case `CrosSettings` are already fetched to the
  // device.
  this->TriggerPredownloadDlcs();
}

void DeviceDlcPredownloadListPolicyHandler::TriggerPredownloadDlcs() {
  const base::Value::List* dlcs_to_predownload = nullptr;
  cros_settings_->GetList(ash::kDeviceDlcPredownloadList, &dlcs_to_predownload);

  if (!dlcs_to_predownload) {
    return;
  }

  for (const base::Value& dlc_id : *dlcs_to_predownload) {
    dlcservice::InstallRequest install_request;
    install_request.set_id(dlc_id.GetString());
    ash::DlcserviceClient::Get()->Install(
        install_request, base::BindOnce(OnInstallDlcComplete, dlc_id.Clone()),
        /*progress_callback=*/base::DoNothing());
  }
}

}  // namespace policy