blob: 9885b745079043d1bf1c83d886cea6e3ae9835ac [file] [log] [blame] [edit]
// Copyright 2021 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "rmad/daemon/dbus_service.h"
#include <sysexits.h>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/functional/bind.h>
#include <base/logging.h>
#include <base/process/launch.h>
#include <base/task/single_thread_task_runner.h>
#include <dbus/bus.h>
#include <dbus/object_path.h>
#include <dbus/rmad/dbus-constants.h>
#include <mojo/core/embedder/scoped_ipc_support.h>
#include <mojo/public/cpp/bindings/remote.h>
#include <mojo/public/cpp/platform/platform_channel_endpoint.h>
#include <mojo/public/cpp/system/invitation.h>
#include "rmad/constants.h"
#include "rmad/daemon/daemon_callback.h"
#include "rmad/system/tpm_manager_client_impl.h"
#include "rmad/utils/cros_config_utils_impl.h"
#include "rmad/utils/crossystem_utils_impl.h"
#include "rmad/utils/dbus_utils.h"
#include "rmad/utils/futility_utils.h"
namespace {
// We don't need a minimal Mojo version. Set it to 0.
constexpr uint32_t kMojoVersion = 0;
std::optional<rmad::DiagnosticsAppInfo> ConvertFromMojomDiagnosticsAppInfo(
chromeos::rmad::mojom::DiagnosticsAppInfoPtr ptr) {
if (ptr) {
return rmad::DiagnosticsAppInfo{.swbn_path = ptr->swbn_path,
.crx_path = ptr->crx_path};
}
return std::nullopt;
}
std::optional<rmad::FlashInfo> ConvertFromMojomFlashInfo(
chromeos::rmad::mojom::FlashInfoPtr ptr) {
if (ptr) {
return rmad::FlashInfo{.flash_name = ptr->flash_name,
.wpsr_start = ptr->wpsr_start,
.wpsr_length = ptr->wpsr_length};
}
return std::nullopt;
}
} // namespace
namespace rmad {
using brillo::dbus_utils::AsyncEventSequencer;
using brillo::dbus_utils::DBusObject;
DBusService::DBusService(mojo::PlatformChannelEndpoint endpoint,
RmadInterface* rmad_interface)
: brillo::DBusServiceDaemon(kRmadServiceName),
rmad_interface_(rmad_interface),
state_file_path_(base::FilePath(kDefaultUnencryptedRmaDirPath)
.Append(kJsonStoreFilePath)),
test_dir_path_(
base::FilePath(kDefaultWorkingDirPath).Append(kTestDirPath)),
is_external_utils_initialized_(false),
is_interface_set_up_(false) {
// Establish connection to the executor process.
ipc_support_ = std::make_unique<mojo::core::ScopedIPCSupport>(
base::SingleThreadTaskRunner::GetCurrentDefault(),
mojo::core::ScopedIPCSupport::ShutdownPolicy::CLEAN);
// Send invitation to the executor process.
mojo::OutgoingInvitation invitation;
mojo::ScopedMessagePipeHandle pipe =
invitation.AttachMessagePipe(kRmadInternalMojoPipeName);
mojo::OutgoingInvitation::Send(std::move(invitation),
base::kNullProcessHandle, std::move(endpoint));
executor_.Bind(mojo::PendingRemote<chromeos::rmad::mojom::Executor>(
std::move(pipe), kMojoVersion));
// Quit the daemon when the communication disconnects.
executor_.set_disconnect_handler(base::BindOnce(
&DBusService::OnExecutorDisconnected, weak_ptr_factory_.GetWeakPtr()));
}
DBusService::DBusService(const scoped_refptr<dbus::Bus>& bus,
RmadInterface* rmad_interface,
const base::FilePath& state_file_path,
const base::FilePath& test_dir_path,
std::unique_ptr<TpmManagerClient> tpm_manager_client,
std::unique_ptr<CrosConfigUtils> cros_config_utils,
std::unique_ptr<CrosSystemUtils> crossystem_utils)
: brillo::DBusServiceDaemon(kRmadServiceName),
rmad_interface_(rmad_interface),
state_file_path_(state_file_path),
test_dir_path_(test_dir_path),
tpm_manager_client_(std::move(tpm_manager_client)),
cros_config_utils_(std::move(cros_config_utils)),
crossystem_utils_(std::move(crossystem_utils)),
is_external_utils_initialized_(true),
is_interface_set_up_(false) {
dbus_object_ = std::make_unique<DBusObject>(
nullptr, bus, dbus::ObjectPath(kRmadServicePath));
}
int DBusService::OnEventLoopStarted() {
const int exit_code = DBusServiceDaemon::OnEventLoopStarted();
if (exit_code != EX_OK) {
return exit_code;
}
if (!is_external_utils_initialized_) {
tpm_manager_client_ =
std::make_unique<TpmManagerClientImpl>(GetSystemBus());
cros_config_utils_ = std::make_unique<CrosConfigUtilsImpl>();
crossystem_utils_ = std::make_unique<CrosSystemUtilsImpl>();
is_external_utils_initialized_ = true;
}
is_rma_required_ = CheckRmaCriteria();
return EX_OK;
}
void DBusService::RegisterDBusObjectsAsync(AsyncEventSequencer* sequencer) {
if (!dbus_object_.get()) {
CHECK(bus_.get());
dbus_object_ = std::make_unique<DBusObject>(
nullptr, bus_, dbus::ObjectPath(kRmadServicePath));
}
brillo::dbus_utils::DBusInterface* dbus_interface =
dbus_object_->AddOrGetInterface(kRmadInterfaceName);
dbus_interface->AddMethodHandler(
kIsRmaRequiredMethod,
base::BindRepeating(&DBusService::HandleIsRmaRequiredMethod,
weak_ptr_factory_.GetWeakPtr()));
dbus_interface->AddMethodHandler(
kGetCurrentStateMethod,
base::BindRepeating(&DBusService::DelegateToInterface<GetStateReply>,
weak_ptr_factory_.GetWeakPtr(),
&RmadInterface::GetCurrentState));
dbus_interface->AddMethodHandler(
kTransitionNextStateMethod,
base::BindRepeating(
&DBusService::DelegateToInterface<GetStateReply,
const TransitionNextStateRequest&>,
weak_ptr_factory_.GetWeakPtr(), &RmadInterface::TransitionNextState));
dbus_interface->AddMethodHandler(
kTransitionPreviousStateMethod,
base::BindRepeating(&DBusService::DelegateToInterface<GetStateReply>,
weak_ptr_factory_.GetWeakPtr(),
&RmadInterface::TransitionPreviousState));
dbus_interface->AddMethodHandler(
kAbortRmaMethod,
base::BindRepeating(&DBusService::DelegateToInterface<AbortRmaReply>,
weak_ptr_factory_.GetWeakPtr(),
&RmadInterface::AbortRma));
dbus_interface->AddMethodHandler(
kGetLogMethod,
base::BindRepeating(&DBusService::DelegateToInterface<GetLogReply>,
weak_ptr_factory_.GetWeakPtr(),
&RmadInterface::GetLog));
dbus_interface->AddMethodHandler(
kSaveLogMethod,
base::BindRepeating(
&DBusService::DelegateToInterface<SaveLogReply, const std::string&>,
weak_ptr_factory_.GetWeakPtr(), &RmadInterface::SaveLog));
dbus_interface->AddMethodHandler(
kRecordBrowserActionMetricMethod,
base::BindRepeating(&DBusService::DelegateToInterface<
RecordBrowserActionMetricReply,
const RecordBrowserActionMetricRequest&>,
weak_ptr_factory_.GetWeakPtr(),
&RmadInterface::RecordBrowserActionMetric));
dbus_interface->AddMethodHandler(
kExtractExternalDiagnosticsAppMethod,
base::BindRepeating(
&DBusService::DelegateToInterface<ExtractExternalDiagnosticsAppReply>,
weak_ptr_factory_.GetWeakPtr(),
&RmadInterface::ExtractExternalDiagnosticsApp));
dbus_interface->AddMethodHandler(
kInstallExtractedDiagnosticsAppMethod,
base::BindRepeating(&DBusService::DelegateToInterface<
InstallExtractedDiagnosticsAppReply>,
weak_ptr_factory_.GetWeakPtr(),
&RmadInterface::InstallExtractedDiagnosticsApp));
dbus_interface->AddMethodHandler(
kGetInstalledDiagnosticsAppMethod,
base::BindRepeating(
&DBusService::DelegateToInterface<GetInstalledDiagnosticsAppReply>,
weak_ptr_factory_.GetWeakPtr(),
&RmadInterface::GetInstalledDiagnosticsApp));
error_signal_ = dbus_interface->RegisterSignal<int>(kErrorSignal);
hardware_verification_signal_ =
dbus_interface->RegisterSignal<std::tuple<bool, std::string>>(
kHardwareVerificationResultSignal);
update_ro_firmware_status_signal_ =
dbus_interface->RegisterSignal<int>(kUpdateRoFirmwareStatusSignal);
calibration_overall_signal_ =
dbus_interface->RegisterSignal<int>(kCalibrationOverallSignal);
calibration_component_signal_ =
dbus_interface->RegisterSignal<std::tuple<int, int, double>>(
kCalibrationProgressSignal);
provision_signal_ =
dbus_interface->RegisterSignal<std::tuple<int, double, int>>(
kProvisioningProgressSignal);
finalize_signal_ =
dbus_interface->RegisterSignal<std::tuple<int, double, int>>(
kFinalizeProgressSignal);
hwwp_signal_ =
dbus_interface->RegisterSignal<bool>(kHardwareWriteProtectionStateSignal);
power_cable_signal_ =
dbus_interface->RegisterSignal<bool>(kPowerCableStateSignal);
external_disk_signal_ =
dbus_interface->RegisterSignal<bool>(kExternalDiskDetectedSignal);
dbus_object_->RegisterAsync(
sequencer->GetHandler("Failed to register D-Bus objects.", true));
}
bool DBusService::IsRMAAllowed() const {
// Always allow Shimless RMA if test directory exist for development.
int cros_debug;
if (crossystem_utils_->GetCrosDebug(&cros_debug) && cros_debug == 1 &&
base::PathExists(test_dir_path_)) {
return true;
}
// Only allow Shimless RMA in normal mode.
if (std::string mainfw_type;
!crossystem_utils_->GetMainFwType(&mainfw_type) ||
mainfw_type != "normal") {
return false;
}
// Only allow Shimless RMA if it's enabled in cros_config.
if (RmadConfig config;
!cros_config_utils_->GetRmadConfig(&config) || !config.enabled) {
return false;
}
return true;
}
bool DBusService::CheckRmaCriteria() const {
if (!IsRMAAllowed()) {
return false;
}
// Shimless RMA is allowed. Trigger it when either condition is satisfied:
// - Shimless RMA state file exists: Shimless RMA is triggered before and not
// completed yet.
// - RO verification triggered: Shimless RMA is manually triggered at boot.
if (base::PathExists(state_file_path_)) {
return true;
}
CHECK(is_external_utils_initialized_);
if (RoVerificationStatus status;
tpm_manager_client_->GetRoVerificationStatus(&status) &&
(status == RMAD_RO_VERIFICATION_PASS ||
status == RMAD_RO_VERIFICATION_UNSUPPORTED_TRIGGERED)) {
// Initialize the state file so we can reliably boot into RMA even if Chrome
// accidentally reboots the device before calling |GetCurrentState| API.
base::WriteFile(state_file_path_, "{}");
return true;
}
return false;
}
bool DBusService::SetUpInterface() {
CHECK(rmad_interface_);
if (!is_interface_set_up_) {
if (!rmad_interface_->SetUp(CreateDaemonCallback())) {
return false;
}
is_interface_set_up_ = true;
rmad_interface_->TryTransitionNextStateFromCurrentState();
}
return true;
}
scoped_refptr<DaemonCallback> DBusService::CreateDaemonCallback() const {
auto daemon_callback = base::MakeRefCounted<DaemonCallback>();
daemon_callback->SetHardwareVerificationSignalCallback(
base::BindRepeating(&DBusService::SendHardwareVerificationResultSignal,
weak_ptr_factory_.GetMutableWeakPtr()));
daemon_callback->SetUpdateRoFirmwareSignalCallback(
base::BindRepeating(&DBusService::SendUpdateRoFirmwareStatusSignal,
weak_ptr_factory_.GetMutableWeakPtr()));
daemon_callback->SetCalibrationOverallSignalCallback(
base::BindRepeating(&DBusService::SendCalibrationOverallSignal,
weak_ptr_factory_.GetMutableWeakPtr()));
daemon_callback->SetCalibrationComponentSignalCallback(
base::BindRepeating(&DBusService::SendCalibrationProgressSignal,
weak_ptr_factory_.GetMutableWeakPtr()));
daemon_callback->SetProvisionSignalCallback(
base::BindRepeating(&DBusService::SendProvisionProgressSignal,
weak_ptr_factory_.GetMutableWeakPtr()));
daemon_callback->SetFinalizeSignalCallback(
base::BindRepeating(&DBusService::SendFinalizeProgressSignal,
weak_ptr_factory_.GetMutableWeakPtr()));
daemon_callback->SetWriteProtectSignalCallback(
base::BindRepeating(&DBusService::SendHardwareWriteProtectionStateSignal,
weak_ptr_factory_.GetMutableWeakPtr()));
daemon_callback->SetPowerCableSignalCallback(
base::BindRepeating(&DBusService::SendPowerCableStateSignal,
weak_ptr_factory_.GetMutableWeakPtr()));
daemon_callback->SetExternalDiskSignalCallback(
base::BindRepeating(&DBusService::SendExternalDiskSignal,
weak_ptr_factory_.GetMutableWeakPtr()));
daemon_callback->SetExecuteMountAndWriteLogCallback(
base::BindRepeating(&DBusService::ExecuteMountAndWriteLog,
weak_ptr_factory_.GetMutableWeakPtr()));
daemon_callback->SetExecuteMountAndCopyFirmwareUpdaterCallback(
base::BindRepeating(&DBusService::ExecuteMountAndCopyFirmwareUpdater,
weak_ptr_factory_.GetMutableWeakPtr()));
daemon_callback->SetExecuteMountAndCopyDiagnosticsAppCallback(
base::BindRepeating(&DBusService::ExecuteMountAndCopyDiagnosticsApp,
weak_ptr_factory_.GetMutableWeakPtr()));
daemon_callback->SetExecuteRebootEcCallback(base::BindRepeating(
&DBusService::ExecuteRebootEc, weak_ptr_factory_.GetMutableWeakPtr()));
daemon_callback->SetExecuteRequestRmaPowerwashCallback(
base::BindRepeating(&DBusService::ExecuteRequestRmaPowerwash,
weak_ptr_factory_.GetMutableWeakPtr()));
daemon_callback->SetExecuteRequestBatteryCutoffCallback(
base::BindRepeating(&DBusService::ExecuteRequestBatteryCutoff,
weak_ptr_factory_.GetMutableWeakPtr()));
daemon_callback->SetExecuteGetFlashInfoCallback(
base::BindRepeating(&DBusService::ExecuteGetFlashInfo,
weak_ptr_factory_.GetMutableWeakPtr()));
return daemon_callback;
}
void DBusService::HandleIsRmaRequiredMethod(
DBusMethodResponsePtr<bool> response) {
// Quit the daemon if we are not in RMA.
bool quit_daemon = !is_rma_required_;
SendReply(std::move(response), is_rma_required_, quit_daemon);
}
void DBusService::SendErrorSignal(RmadErrorCode error) {
auto signal = error_signal_.lock();
if (signal) {
signal->Send(static_cast<int>(error));
}
}
void DBusService::SendHardwareVerificationResultSignal(
const HardwareVerificationResult& result) {
auto signal = hardware_verification_signal_.lock();
if (signal) {
signal->Send(std::tuple(result.is_compliant(), result.error_str()));
}
}
void DBusService::SendUpdateRoFirmwareStatusSignal(
UpdateRoFirmwareStatus status) {
auto signal = update_ro_firmware_status_signal_.lock();
if (signal) {
signal->Send(static_cast<int>(status));
}
}
void DBusService::SendCalibrationOverallSignal(
CalibrationOverallStatus status) {
auto signal = calibration_overall_signal_.lock();
if (signal) {
signal->Send(static_cast<int>(status));
}
}
void DBusService::SendCalibrationProgressSignal(
CalibrationComponentStatus status) {
auto signal = calibration_component_signal_.lock();
if (signal) {
signal->Send(std::tuple(static_cast<int>(status.component()),
static_cast<int>(status.status()),
status.progress()));
}
}
void DBusService::SendProvisionProgressSignal(const ProvisionStatus& status) {
auto signal = provision_signal_.lock();
if (signal) {
signal->Send(std::tuple(static_cast<int>(status.status()),
status.progress(),
static_cast<int>(status.error())));
}
}
void DBusService::SendFinalizeProgressSignal(const FinalizeStatus& status) {
auto signal = finalize_signal_.lock();
if (signal) {
signal->Send(std::tuple(static_cast<int>(status.status()),
status.progress(),
static_cast<int>(status.error())));
}
}
void DBusService::SendHardwareWriteProtectionStateSignal(bool enabled) {
auto signal = hwwp_signal_.lock();
if (signal) {
signal->Send(enabled);
}
}
void DBusService::SendPowerCableStateSignal(bool plugged_in) {
auto signal = power_cable_signal_.lock();
if (signal) {
signal->Send(plugged_in);
}
}
void DBusService::SendExternalDiskSignal(bool detected) {
auto signal = external_disk_signal_.lock();
if (signal) {
signal->Send(detected);
}
}
void DBusService::ExecuteMountAndWriteLog(
uint8_t device_id,
const std::string& text_log,
const std::string& json_log,
const std::string& system_log,
const std::string& diagnostics_log,
base::OnceCallback<void(const std::optional<std::string>&)> callback) {
executor_->MountAndWriteLog(device_id, text_log, json_log, system_log,
diagnostics_log, std::move(callback));
}
void DBusService::ExecuteMountAndCopyFirmwareUpdater(
uint8_t device_id, base::OnceCallback<void(bool)> callback) {
executor_->MountAndCopyFirmwareUpdater(device_id, std::move(callback));
}
void DBusService::ExecuteMountAndCopyDiagnosticsApp(
uint8_t device_id,
base::OnceCallback<void(const std::optional<DiagnosticsAppInfo>&)>
callback) {
executor_->MountAndCopyDiagnosticsApp(
device_id, base::BindOnce(&ConvertFromMojomDiagnosticsAppInfo)
.Then(std::move(callback)));
}
void DBusService::ExecuteRebootEc(base::OnceCallback<void(bool)> callback) {
executor_->RebootEc(std::move(callback));
}
void DBusService::ExecuteRequestRmaPowerwash(
base::OnceCallback<void(bool)> callback) {
executor_->RequestRmaPowerwash(std::move(callback));
}
void DBusService::ExecuteRequestBatteryCutoff(
base::OnceCallback<void(bool)> callback) {
executor_->RequestBatteryCutoff(std::move(callback));
}
void DBusService::ExecuteGetFlashInfo(
base::OnceCallback<void(const std::optional<FlashInfo>&)> callback) {
executor_->GetFlashInfo(
base::BindOnce(&ConvertFromMojomFlashInfo).Then(std::move(callback)));
}
void DBusService::OnExecutorDisconnected() {
VLOG(1) << "Executor disconnected";
PostQuitTask();
}
void DBusService::PostQuitTask() {
if (bus_) {
VLOG(1) << "Stopping DBus service";
bus_->GetOriginTaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(&Daemon::Quit, weak_ptr_factory_.GetWeakPtr()));
}
}
} // namespace rmad