| // Copyright 2021 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "rmad/rmad_interface_impl.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/check.h> |
| #include <base/files/file_path.h> |
| #include <base/files/file_util.h> |
| #include <base/logging.h> |
| #include <base/memory/scoped_refptr.h> |
| #include <base/time/time.h> |
| #include <base/values.h> |
| |
| #include "rmad/constants.h" |
| #include "rmad/metrics/metrics_utils_impl.h" |
| #include "rmad/proto_bindings/rmad.pb.h" |
| #include "rmad/system/fake_runtime_probe_client.h" |
| #include "rmad/system/fake_shill_client.h" |
| #include "rmad/system/fake_tpm_manager_client.h" |
| #include "rmad/system/power_manager_client_impl.h" |
| #include "rmad/system/runtime_probe_client_impl.h" |
| #include "rmad/system/shill_client_impl.h" |
| #include "rmad/system/tpm_manager_client_impl.h" |
| #include "rmad/utils/cmd_utils_impl.h" |
| #include "rmad/utils/dbus_utils.h" |
| #include "rmad/utils/fake_cmd_utils.h" |
| |
| namespace rmad { |
| |
| namespace { |
| |
| const RmadState::StateCase kInitialStateCase = RmadState::kWelcome; |
| |
| const char kCroslogCmd[] = "/usr/sbin/croslog"; |
| |
| const char kInitctlCmd[] = "/sbin/initctl"; |
| const std::vector<std::string> kWaitServices = {"system-services"}; |
| const int kWaitServicesPollInterval = 1; // 1 second. |
| const int kWaitServicesRetries = 10; |
| |
| } // namespace |
| |
| RmadInterfaceImpl::RmadInterfaceImpl() |
| : RmadInterface(), |
| external_utils_initialized_(false), |
| current_state_case_(RmadState::STATE_NOT_SET), |
| test_mode_(false) {} |
| |
| RmadInterfaceImpl::RmadInterfaceImpl( |
| scoped_refptr<JsonStore> json_store, |
| std::unique_ptr<StateHandlerManager> state_handler_manager, |
| std::unique_ptr<RuntimeProbeClient> runtime_probe_client, |
| std::unique_ptr<ShillClient> shill_client, |
| std::unique_ptr<TpmManagerClient> tpm_manager_client, |
| std::unique_ptr<PowerManagerClient> power_manager_client, |
| std::unique_ptr<CmdUtils> cmd_utils, |
| std::unique_ptr<MetricsUtils> metrics_utils) |
| : RmadInterface(), |
| json_store_(json_store), |
| state_handler_manager_(std::move(state_handler_manager)), |
| runtime_probe_client_(std::move(runtime_probe_client)), |
| shill_client_(std::move(shill_client)), |
| tpm_manager_client_(std::move(tpm_manager_client)), |
| power_manager_client_(std::move(power_manager_client)), |
| cmd_utils_(std::move(cmd_utils)), |
| metrics_utils_(std::move(metrics_utils)), |
| external_utils_initialized_(true), |
| current_state_case_(RmadState::STATE_NOT_SET), |
| test_mode_(false) {} |
| |
| bool RmadInterfaceImpl::StoreStateHistory() { |
| std::vector<int> state_history; |
| for (auto s : state_history_) { |
| state_history.push_back(RmadState::StateCase(s)); |
| } |
| return json_store_->SetValue(kStateHistory, state_history); |
| } |
| |
| void RmadInterfaceImpl::InitializeExternalUtils() { |
| json_store_ = base::MakeRefCounted<JsonStore>( |
| base::FilePath(kDefaultJsonStoreFilePath)); |
| state_handler_manager_ = std::make_unique<StateHandlerManager>(json_store_); |
| if (test_mode_) { |
| state_handler_manager_->RegisterFakeStateHandlers(); |
| const base::FilePath test_dir_path = |
| base::FilePath(kDefaultWorkingDirPath).AppendASCII(kTestDirPath); |
| runtime_probe_client_ = std::make_unique<fake::FakeRuntimeProbeClient>(); |
| shill_client_ = std::make_unique<fake::FakeShillClient>(); |
| tpm_manager_client_ = |
| std::make_unique<fake::FakeTpmManagerClient>(test_dir_path); |
| // Still use the real power_manager. |
| power_manager_client_ = |
| std::make_unique<PowerManagerClientImpl>(GetSystemBus()); |
| cmd_utils_ = std::make_unique<fake::FakeCmdUtils>(); |
| } else { |
| state_handler_manager_->RegisterStateHandlers(); |
| runtime_probe_client_ = |
| std::make_unique<RuntimeProbeClientImpl>(GetSystemBus()); |
| shill_client_ = std::make_unique<ShillClientImpl>(GetSystemBus()); |
| tpm_manager_client_ = |
| std::make_unique<TpmManagerClientImpl>(GetSystemBus()); |
| power_manager_client_ = |
| std::make_unique<PowerManagerClientImpl>(GetSystemBus()); |
| cmd_utils_ = std::make_unique<CmdUtilsImpl>(); |
| } |
| } |
| |
| bool RmadInterfaceImpl::WaitForServices() { |
| if (test_mode_) { |
| return true; |
| } |
| CHECK(external_utils_initialized_); |
| std::string output; |
| for (int i = 0; i < kWaitServicesRetries; ++i) { |
| LOG(INFO) << "Checking services"; |
| bool all_running = true; |
| for (const std::string& service : kWaitServices) { |
| cmd_utils_->GetOutput({kInitctlCmd, "status", service}, &output); |
| if (output.find("running") == std::string::npos) { |
| all_running = false; |
| break; |
| } |
| } |
| if (all_running) { |
| return true; |
| } |
| sleep(kWaitServicesPollInterval); |
| } |
| return false; |
| } |
| |
| bool RmadInterfaceImpl::SetUp() { |
| // Initialize external utilities if needed. |
| if (!external_utils_initialized_) { |
| InitializeExternalUtils(); |
| external_utils_initialized_ = true; |
| metrics_utils_ = std::make_unique<MetricsUtilsImpl>(); |
| } |
| // Wait for system services to be ready. |
| if (!WaitForServices()) { |
| return false; |
| } |
| // Initialize |current state_|, |state_history_|, and |can_abort_| flag. |
| current_state_case_ = RmadState::STATE_NOT_SET; |
| state_history_.clear(); |
| can_abort_ = true; |
| // Something's wrong with the state file. Try to clear it. |
| if (json_store_->ReadOnly()) { |
| LOG(WARNING) << "Corrupted RMA state file. Trying to fix it"; |
| if (!json_store_->Clear() || !json_store_->InitFromFile()) { |
| LOG(ERROR) << "Failed to fix RMA state file"; |
| return false; |
| } |
| } |
| DCHECK(!json_store_->ReadOnly()); |
| if (json_store_->GetReadError() != JsonStore::READ_ERROR_NO_SUCH_FILE) { |
| if (std::vector<int> state_history; |
| json_store_->GetReadError() == JsonStore::READ_ERROR_NONE && |
| json_store_->GetValue(kStateHistory, &state_history) && |
| state_history.size()) { |
| for (int state : state_history) { |
| // Reject any state that does not have a handler. |
| if (RmadState::StateCase s = RmadState::StateCase(state); |
| auto handler = state_handler_manager_->GetStateHandler(s)) { |
| state_history_.push_back(s); |
| can_abort_ &= handler->IsRepeatable(); |
| } else { |
| // TODO(chenghan): Return to welcome screen with an error implying |
| // an unsupported state. |
| LOG(ERROR) << "Missing handler for state " << state << "."; |
| } |
| } |
| } |
| if (state_history_.size() > 0) { |
| current_state_case_ = state_history_.back(); |
| } else { |
| LOG(WARNING) << "Could not read state history from json store, reset to " |
| "initial state."; |
| current_state_case_ = kInitialStateCase; |
| state_history_.push_back(current_state_case_); |
| if (!StoreStateHistory()) { |
| LOG(ERROR) << "Could not store initial state"; |
| // TODO(chenghan): Send a signal to Chrome that the json store failed so |
| // a message can be displayed. |
| return false; |
| } |
| } |
| } else if (RoVerificationStatus status; |
| tpm_manager_client_->GetRoVerificationStatus(&status) && |
| (status == RoVerificationStatus::PASS || |
| status == RoVerificationStatus::UNSUPPORTED_TRIGGERED)) { |
| VLOG(1) << "RO verification triggered"; |
| current_state_case_ = kInitialStateCase; |
| state_history_.push_back(current_state_case_); |
| if (!StoreStateHistory()) { |
| LOG(ERROR) << "Could not store initial state"; |
| // TODO(chenghan): Send a signal to Chrome that the json store failed so |
| // a message can be displayed. |
| return false; |
| } |
| |
| if (!json_store_->SetValue(kRoFirmwareVerified, |
| status == RoVerificationStatus::PASS)) { |
| LOG(ERROR) << "Could not store RO firmware verification status"; |
| } |
| } |
| |
| double current_timestamp = base::Time::Now().ToDoubleT(); |
| if (!json_store_->SetValue(kSetupTimestamp, current_timestamp)) { |
| LOG(ERROR) << "Could not store setup time"; |
| return false; |
| } |
| if (double first_setup_time; |
| !json_store_->GetValue(kFirstSetupTimestamp, &first_setup_time) && |
| !json_store_->SetValue(kFirstSetupTimestamp, current_timestamp)) { |
| LOG(ERROR) << "Could not store first setup time"; |
| return false; |
| } |
| |
| // If we are in the RMA process: |
| // 1. Disable cellular to prevent accidentally using it. |
| // 2. Start monitoring test files if we are running in test mode. |
| // TODO(chenghan): Disable cellular in a separate thread to shorten the |
| // response time. |
| if (current_state_case_ != RmadState::STATE_NOT_SET) { |
| if (ComponentsWithIdentifier components; |
| runtime_probe_client_->ProbeCategories({RMAD_COMPONENT_CELLULAR}, |
| &components) && |
| components.size() > 0) { |
| LOG(INFO) << "Disabling cellular network"; |
| DCHECK(shill_client_->DisableCellular()); |
| } |
| if (test_mode_) { |
| ClearTestRequests(); |
| test_mode_monitor_timer_.Start(FROM_HERE, kTestModeMonitorInterval, this, |
| &RmadInterfaceImpl::MonitorTestRequests); |
| } |
| } |
| |
| return true; |
| } |
| |
| RmadErrorCode RmadInterfaceImpl::GetInitializedStateHandler( |
| RmadState::StateCase state_case, |
| scoped_refptr<BaseStateHandler>* state_handler) const { |
| auto handler = state_handler_manager_->GetStateHandler(state_case); |
| if (!handler) { |
| LOG(INFO) << "No registered state handler for state " << state_case; |
| return RMAD_ERROR_STATE_HANDLER_MISSING; |
| } |
| if (RmadErrorCode init_error = handler->InitializeState(); |
| init_error != RMAD_ERROR_OK) { |
| LOG(INFO) << "Failed to initialize current state " << state_case; |
| return init_error; |
| } |
| *state_handler = handler; |
| return RMAD_ERROR_OK; |
| } |
| |
| void RmadInterfaceImpl::RegisterSignalSender( |
| RmadState::StateCase state_case, |
| std::unique_ptr<base::RepeatingCallback<bool(bool)>> callback) { |
| auto state_handler = state_handler_manager_->GetStateHandler(state_case); |
| if (state_handler) { |
| state_handler->RegisterSignalSender(std::move(callback)); |
| } |
| } |
| |
| void RmadInterfaceImpl::RegisterSignalSender( |
| RmadState::StateCase state_case, |
| std::unique_ptr<HardwareVerificationResultSignalCallback> callback) { |
| auto state_handler = state_handler_manager_->GetStateHandler(state_case); |
| if (state_handler) { |
| state_handler->RegisterSignalSender(std::move(callback)); |
| } |
| } |
| |
| void RmadInterfaceImpl::RegisterSignalSender( |
| RmadState::StateCase state_case, |
| std::unique_ptr<UpdateRoFirmwareStatusSignalCallback> callback) { |
| auto state_handler = state_handler_manager_->GetStateHandler(state_case); |
| if (state_handler) { |
| state_handler->RegisterSignalSender(std::move(callback)); |
| } |
| } |
| |
| void RmadInterfaceImpl::RegisterSignalSender( |
| RmadState::StateCase state_case, |
| std::unique_ptr<CalibrationOverallSignalCallback> callback) { |
| auto state_handler = state_handler_manager_->GetStateHandler(state_case); |
| if (state_handler) { |
| state_handler->RegisterSignalSender(std::move(callback)); |
| } |
| } |
| |
| void RmadInterfaceImpl::RegisterSignalSender( |
| RmadState::StateCase state_case, |
| std::unique_ptr<CalibrationComponentSignalCallback> callback) { |
| auto state_handler = state_handler_manager_->GetStateHandler(state_case); |
| if (state_handler) { |
| state_handler->RegisterSignalSender(std::move(callback)); |
| } |
| } |
| |
| void RmadInterfaceImpl::RegisterSignalSender( |
| RmadState::StateCase state_case, |
| std::unique_ptr<ProvisionSignalCallback> callback) { |
| auto state_handler = state_handler_manager_->GetStateHandler(state_case); |
| if (state_handler) { |
| state_handler->RegisterSignalSender(std::move(callback)); |
| } |
| } |
| |
| void RmadInterfaceImpl::RegisterSignalSender( |
| RmadState::StateCase state_case, |
| std::unique_ptr<FinalizeSignalCallback> callback) { |
| auto state_handler = state_handler_manager_->GetStateHandler(state_case); |
| if (state_handler) { |
| state_handler->RegisterSignalSender(std::move(callback)); |
| } |
| } |
| |
| void RmadInterfaceImpl::TryTransitionNextStateFromCurrentState() { |
| LOG(INFO) << "Trying a state transition using current state"; |
| TransitionNextStateInternal(TransitionNextStateRequest(), true); |
| } |
| |
| void RmadInterfaceImpl::GetCurrentState(GetStateCallback callback) { |
| GetStateReply reply = GetCurrentStateInternal(); |
| std::move(callback).Run(reply); |
| } |
| |
| GetStateReply RmadInterfaceImpl::GetCurrentStateInternal() { |
| GetStateReply reply; |
| scoped_refptr<BaseStateHandler> state_handler; |
| |
| if (current_state_case_ == RmadState::STATE_NOT_SET) { |
| reply.set_error(RMAD_ERROR_RMA_NOT_REQUIRED); |
| } else if (RmadErrorCode error = GetInitializedStateHandler( |
| current_state_case_, &state_handler); |
| error != RMAD_ERROR_OK) { |
| reply.set_error(error); |
| } else { |
| LOG(INFO) << "Get current state succeeded: " << current_state_case_; |
| reply.set_error(RMAD_ERROR_OK); |
| reply.set_allocated_state(new RmadState(state_handler->GetState(true))); |
| reply.set_can_go_back(CanGoBack()); |
| reply.set_can_abort(CanAbort()); |
| } |
| |
| return reply; |
| } |
| |
| void RmadInterfaceImpl::TransitionNextState( |
| const TransitionNextStateRequest& request, GetStateCallback callback) { |
| GetStateReply reply = TransitionNextStateInternal(request, false); |
| std::move(callback).Run(reply); |
| } |
| |
| GetStateReply RmadInterfaceImpl::TransitionNextStateInternal( |
| const TransitionNextStateRequest& request, bool try_at_boot) { |
| GetStateReply reply; |
| if (current_state_case_ == RmadState::STATE_NOT_SET) { |
| reply.set_error(RMAD_ERROR_RMA_NOT_REQUIRED); |
| return reply; |
| } |
| |
| scoped_refptr<BaseStateHandler> current_state_handler, next_state_handler; |
| if (RmadErrorCode error = GetInitializedStateHandler(current_state_case_, |
| ¤t_state_handler); |
| error != RMAD_ERROR_OK) { |
| DLOG(FATAL) << "Current state initialization failed"; |
| reply.set_error(error); |
| return reply; |
| } |
| |
| // Initialize the default reply. |
| reply.set_error(RMAD_ERROR_NOT_SET); |
| reply.set_allocated_state(new RmadState(current_state_handler->GetState())); |
| reply.set_can_go_back(CanGoBack()); |
| reply.set_can_abort(CanAbort()); |
| |
| auto [next_state_case_error, next_state_case] = |
| try_at_boot ? current_state_handler->TryGetNextStateCaseAtBoot() |
| : current_state_handler->GetNextStateCase(request.state()); |
| if (next_state_case_error != RMAD_ERROR_OK) { |
| LOG(INFO) << "Transitioning to next state rejected by state " |
| << current_state_case_; |
| CHECK(next_state_case == current_state_case_) |
| << "State transition should not happen with errors."; |
| reply.set_error(next_state_case_error); |
| return reply; |
| } |
| |
| CHECK(next_state_case != current_state_case_) |
| << "Staying at the same state without errors."; |
| |
| if (RmadErrorCode error = |
| GetInitializedStateHandler(next_state_case, &next_state_handler); |
| error != RMAD_ERROR_OK) { |
| reply.set_error(error); |
| return reply; |
| } |
| |
| // Transition to next state. |
| LOG(INFO) << "Transition to next state succeeded: from " |
| << current_state_case_ << " to " << next_state_case; |
| current_state_handler->CleanUpState(); |
| // Append next state to stack. |
| state_history_.push_back(next_state_case); |
| if (!StoreStateHistory()) { |
| // TODO(chenghan): Add error replies when failed to write |json_store_|. |
| LOG(ERROR) << "Could not store history"; |
| } |
| // Update state. |
| current_state_case_ = next_state_case; |
| // This is a one-way transition. |can_abort| cannot go from false to |
| // true, unless we restart the whole RMA process. |
| can_abort_ &= next_state_handler->IsRepeatable(); |
| |
| // If the new state needs the daemon to quit and restart, request to quit the |
| // daemon here. |
| if (std::find(kQuitDaemonStates.begin(), kQuitDaemonStates.end(), |
| current_state_case_) != kQuitDaemonStates.end()) { |
| CHECK(request_quit_daemon_callback_); |
| request_quit_daemon_callback_->Run(); |
| } |
| |
| reply.set_error(RMAD_ERROR_OK); |
| reply.set_allocated_state(new RmadState(next_state_handler->GetState(true))); |
| reply.set_can_go_back(CanGoBack()); |
| reply.set_can_abort(CanAbort()); |
| return reply; |
| } |
| |
| void RmadInterfaceImpl::TransitionPreviousState(GetStateCallback callback) { |
| GetStateReply reply; |
| if (current_state_case_ == RmadState::STATE_NOT_SET) { |
| reply.set_error(RMAD_ERROR_RMA_NOT_REQUIRED); |
| std::move(callback).Run(reply); |
| return; |
| } |
| |
| scoped_refptr<BaseStateHandler> current_state_handler, prev_state_handler; |
| if (RmadErrorCode error = GetInitializedStateHandler(current_state_case_, |
| ¤t_state_handler); |
| error != RMAD_ERROR_OK) { |
| DLOG(FATAL) << "Current state initialization failed"; |
| reply.set_error(error); |
| std::move(callback).Run(reply); |
| return; |
| } |
| |
| // Initialize the default reply. |
| reply.set_error(RMAD_ERROR_NOT_SET); |
| reply.set_allocated_state(new RmadState(current_state_handler->GetState())); |
| reply.set_can_go_back(CanGoBack()); |
| reply.set_can_abort(CanAbort()); |
| |
| if (!CanGoBack()) { |
| LOG(INFO) << "Cannot go back to previous state"; |
| reply.set_error(RMAD_ERROR_TRANSITION_FAILED); |
| std::move(callback).Run(reply); |
| return; |
| } |
| |
| RmadState::StateCase prev_state_case = *std::prev(state_history_.end(), 2); |
| if (RmadErrorCode error = |
| GetInitializedStateHandler(prev_state_case, &prev_state_handler); |
| error != RMAD_ERROR_OK) { |
| reply.set_error(error); |
| std::move(callback).Run(reply); |
| return; |
| } |
| |
| // Transition to previous state. |
| LOG(INFO) << "Transition to previous state succeeded: from " |
| << current_state_case_ << " to " << prev_state_case; |
| current_state_handler->CleanUpState(); |
| // Remove current state from stack. |
| state_history_.pop_back(); |
| if (!StoreStateHistory()) { |
| LOG(ERROR) << "Could not store history"; |
| } |
| // Update state. |
| current_state_case_ = prev_state_case; |
| |
| reply.set_error(RMAD_ERROR_OK); |
| reply.set_allocated_state(new RmadState(prev_state_handler->GetState(true))); |
| reply.set_can_go_back(CanGoBack()); |
| reply.set_can_abort(CanAbort()); |
| std::move(callback).Run(reply); |
| } |
| |
| void RmadInterfaceImpl::AbortRma(AbortRmaCallback callback) { |
| AbortRmaReply reply; |
| if (current_state_case_ == RmadState::STATE_NOT_SET) { |
| reply.set_error(RMAD_ERROR_RMA_NOT_REQUIRED); |
| } else if (can_abort_) { |
| VLOG(1) << "AbortRma: Abort allowed."; |
| if (!metrics_utils_->Record(json_store_, false)) { |
| LOG(ERROR) << "AbortRma: Failed to generate and record metrics."; |
| } |
| if (json_store_->ClearAndDeleteFile()) { |
| current_state_case_ = RmadState::STATE_NOT_SET; |
| reply.set_error(RMAD_ERROR_RMA_NOT_REQUIRED); |
| } else { |
| LOG(ERROR) << "AbortRma: Failed to clear RMA state file"; |
| reply.set_error(RMAD_ERROR_ABORT_FAILED); |
| } |
| } else { |
| VLOG(1) << "AbortRma: Failed to abort."; |
| reply.set_error(RMAD_ERROR_ABORT_FAILED); |
| } |
| |
| std::move(callback).Run(reply); |
| } |
| |
| void RmadInterfaceImpl::GetLog(GetLogCallback callback) { |
| GetLogReply reply; |
| std::string log_string; |
| if (cmd_utils_->GetOutput({kCroslogCmd, "--identifier=rmad"}, &log_string)) { |
| reply.set_log(log_string); |
| } else { |
| LOG(ERROR) << "Failed to generate logs"; |
| reply.set_error(RMAD_ERROR_CANNOT_GET_LOG); |
| } |
| std::move(callback).Run(reply); |
| } |
| |
| void RmadInterfaceImpl::SaveLog(const std::string& diagnostics_log_path, |
| SaveLogCallback callback) { |
| SaveLogReply reply; |
| reply.set_error(RMAD_ERROR_OK); |
| reply.set_save_path("fake_path"); |
| std::move(callback).Run(reply); |
| } |
| |
| bool RmadInterfaceImpl::CanGoBack() const { |
| if (state_history_.size() > 1) { |
| const auto current_state_handler = |
| state_handler_manager_->GetStateHandler(state_history_.back()); |
| const auto prev_state_handler = state_handler_manager_->GetStateHandler( |
| *std::prev(state_history_.end(), 2)); |
| CHECK(current_state_handler); |
| CHECK(prev_state_handler); |
| return (current_state_handler->IsRepeatable() && |
| prev_state_handler->IsRepeatable()); |
| } |
| return false; |
| } |
| |
| void RmadInterfaceImpl::ClearTestRequests() { |
| // Check if powerwash or cutoff is requested in test mode. The files are |
| // created in the test directory so they are not picked up by the init script |
| // rmad.conf. |
| const base::FilePath test_dir_path = |
| base::FilePath(kDefaultWorkingDirPath).AppendASCII(kTestDirPath); |
| // Check if powerwash is requested. |
| const base::FilePath powerwash_request_file_path = |
| test_dir_path.AppendASCII(kPowerwashRequestFilePath); |
| if (base::PathExists(powerwash_request_file_path)) { |
| base::DeleteFile(powerwash_request_file_path); |
| LOG(INFO) << "Powerwash requested and ignored"; |
| } |
| // Check if cutoff is requested. |
| const base::FilePath cutoff_request_file_path = |
| test_dir_path.AppendASCII(kCutoffRequestFilePath); |
| if (base::PathExists(cutoff_request_file_path)) { |
| base::DeleteFile(cutoff_request_file_path); |
| LOG(INFO) << "Cutoff requested and ignored"; |
| } |
| } |
| |
| void RmadInterfaceImpl::MonitorTestRequests() { |
| const base::FilePath test_dir_path = |
| base::FilePath(kDefaultWorkingDirPath).AppendASCII(kTestDirPath); |
| // Check if reboot is requested. |
| const base::FilePath reboot_request_file_path = |
| test_dir_path.AppendASCII(fake::kRebootRequestFilePath); |
| if (base::PathExists(reboot_request_file_path)) { |
| base::DeleteFile(reboot_request_file_path); |
| power_manager_client_->Restart(); |
| } |
| // Check if shutdown is requested. |
| const base::FilePath shutdown_request_file_path = |
| test_dir_path.AppendASCII(fake::kShutdownRequestFilePath); |
| if (base::PathExists(shutdown_request_file_path)) { |
| base::DeleteFile(shutdown_request_file_path); |
| power_manager_client_->Shutdown(); |
| } |
| } |
| |
| } // namespace rmad |