chromium/chrome/browser/ash/dbus/component_updater_service_provider.cc

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/dbus/component_updater_service_provider.h"

#include <utility>

#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "chrome/browser/component_updater/cros_component_installer_chromeos.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "dbus/bus.h"
#include "dbus/message.h"
#include "third_party/cros_system_api/dbus/service_constants.h"

namespace ash {

namespace {

const char kErrorInvalidArgs[] = "org.freedesktop.DBus.Error.InvalidArgs";
const char kErrorInternalError[] = "org.freedesktop.DBus.Error.InternalError";

std::string ErrorToString(component_updater::ComponentManagerAsh::Error error) {
  switch (error) {
    case component_updater::ComponentManagerAsh::Error::NONE:
      return "NONE";
    case component_updater::ComponentManagerAsh::Error::UNKNOWN_COMPONENT:
      return "UNKNOWN_COMPONENT";
    case component_updater::ComponentManagerAsh::Error::INSTALL_FAILURE:
      return "INSTALL_FAILURE";
    case component_updater::ComponentManagerAsh::Error::MOUNT_FAILURE:
      return "MOUNT_FAILURE";
    case component_updater::ComponentManagerAsh::Error::
        COMPATIBILITY_CHECK_FAILED:
      return "COMPATIBILITY_CHECK_FAILED";
    case component_updater::ComponentManagerAsh::Error::NOT_FOUND:
      return "NOT_FOUND";
    case component_updater::ComponentManagerAsh::Error::UPDATE_IN_PROGRESS:
      return "UPDATE_IN_PROGRESS";
  }
  return "Unknown error code";
}

}  // namespace

ComponentUpdaterServiceProvider::ComponentUpdaterServiceProvider(
    component_updater::ComponentManagerAsh* cros_component_manager) {
  DCHECK(cros_component_manager);

  cros_component_manager_ = cros_component_manager;
  cros_component_manager_->SetDelegate(this);
}

ComponentUpdaterServiceProvider::~ComponentUpdaterServiceProvider() {
  cros_component_manager_->SetDelegate(nullptr);
}

void ComponentUpdaterServiceProvider::Start(
    scoped_refptr<dbus::ExportedObject> exported_object) {
  exported_object->ExportMethod(
      chromeos::kComponentUpdaterServiceInterface,
      chromeos::kComponentUpdaterServiceLoadComponentMethod,
      base::BindRepeating(&ComponentUpdaterServiceProvider::LoadComponent,
                          weak_ptr_factory_.GetWeakPtr()),
      base::BindOnce(&ComponentUpdaterServiceProvider::OnExported,
                     weak_ptr_factory_.GetWeakPtr()));

  exported_object->ExportMethod(
      chromeos::kComponentUpdaterServiceInterface,
      chromeos::kComponentUpdaterServiceUnloadComponentMethod,
      base::BindRepeating(&ComponentUpdaterServiceProvider::UnloadComponent,
                          weak_ptr_factory_.GetWeakPtr()),
      base::BindOnce(&ComponentUpdaterServiceProvider::OnExported,
                     weak_ptr_factory_.GetWeakPtr()));

  exported_object_ = exported_object;
}

void ComponentUpdaterServiceProvider::EmitInstalledSignal(
    const std::string& component) {
  content::GetUIThreadTaskRunner({})->PostTask(
      FROM_HERE,
      base::BindOnce(
          &ComponentUpdaterServiceProvider::EmitInstalledSignalInternal,
          weak_ptr_factory_.GetWeakPtr(), component));
}

void ComponentUpdaterServiceProvider::OnExported(
    const std::string& interface_name,
    const std::string& method_name,
    bool success) {
  if (!success) {
    LOG(ERROR) << "Failed to export " << interface_name << "." << method_name;
  }
}

void ComponentUpdaterServiceProvider::LoadComponent(
    dbus::MethodCall* method_call,
    dbus::ExportedObject::ResponseSender response_sender) {
  dbus::MessageReader reader(method_call);
  std::string component_name;
  // |mount| is an optional parameter, and by default is true.
  bool mount = true;
  if (reader.PopString(&component_name)) {
    // dbus::MessageReader::PopBool sets its out-param to false on failure.
    // Resets |mount| to its default value on failure.
    if (!reader.PopBool(&mount))
      mount = true;
    cros_component_manager_->Load(
        component_name,
        mount ? component_updater::ComponentManagerAsh::MountPolicy::kMount
              : component_updater::ComponentManagerAsh::MountPolicy::kDontMount,
        component_updater::ComponentManagerAsh::UpdatePolicy::kDontForce,
        base::BindOnce(&ComponentUpdaterServiceProvider::OnLoadComponent,
                       weak_ptr_factory_.GetWeakPtr(), method_call,
                       std::move(response_sender)));
  } else {
    std::unique_ptr<dbus::ErrorResponse> error_response =
        dbus::ErrorResponse::FromMethodCall(
            method_call, kErrorInvalidArgs,
            "Need a string and a boolean parameter.");
    std::move(response_sender).Run(std::move(error_response));
  }
}

void ComponentUpdaterServiceProvider::OnLoadComponent(
    dbus::MethodCall* method_call,
    dbus::ExportedObject::ResponseSender response_sender,
    component_updater::ComponentManagerAsh::Error error,
    const base::FilePath& result) {
  if (error != component_updater::ComponentManagerAsh::Error::NONE) {
    LOG(ERROR) << "Component updater Load API error: " << ErrorToString(error);
  }

  std::unique_ptr<dbus::Response> response =
      dbus::Response::FromMethodCall(method_call);
  dbus::MessageWriter writer(response.get());
  writer.AppendString(result.value());
  std::move(response_sender).Run(std::move(response));
}

void ComponentUpdaterServiceProvider::UnloadComponent(
    dbus::MethodCall* method_call,
    dbus::ExportedObject::ResponseSender response_sender) {
  dbus::MessageReader reader(method_call);
  std::string component_name;
  if (reader.PopString(&component_name)) {
    if (cros_component_manager_->Unload(component_name)) {
      std::move(response_sender)
          .Run(dbus::Response::FromMethodCall(method_call));
    } else {
      std::move(response_sender)
          .Run(dbus::ErrorResponse::FromMethodCall(
              method_call, kErrorInternalError, "Failed to unload component"));
    }
  } else {
    std::move(response_sender)
        .Run(dbus::ErrorResponse::FromMethodCall(
            method_call, kErrorInvalidArgs,
            "Missing component name string argument."));
  }
}

void ComponentUpdaterServiceProvider::EmitInstalledSignalInternal(
    const std::string& component) {
  DCHECK(exported_object_);

  dbus::Signal signal(
      chromeos::kComponentUpdaterServiceInterface,
      chromeos::kComponentUpdaterServiceComponentInstalledSignal);

  dbus::MessageWriter writer(&signal);
  writer.AppendString(component);
  exported_object_->SendSignal(&signal);
}

}  // namespace ash