| // 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 |