blob: 36072571c7cf7aa85eafbdced14f842cf2b2dbe5 [file] [log] [blame]
// Copyright 2020 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 "diagnostics/cros_healthd/routines/ac_power/ac_power.h"
#include <base/files/file_enumerator.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/strings/string_util.h>
namespace diagnostics {
namespace mojo_ipc = ::chromeos::cros_healthd::mojom;
namespace {
// Path to the power_supply directory. All subdirectories will be searched to
// try and find the path to a connected AC adapter.
constexpr char kPowerSupplyDirectoryPath[] = "sys/class/power_supply";
// Names of the two files read by the AC power routine.
constexpr char kOnlineFileName[] = "online";
constexpr char kTypeFileName[] = "type";
// POD struct which holds the whitespace-trimmed contents of the online and type
// files for the power supply under test.
struct PowerSupplyFileContents {
std::string online; // Whitespace-trimmed contents of the online file.
std::string type; // Whitespace-trimmed contents of the type file.
};
} // namespace
const char kAcPowerRoutineSucceededMessage[] = "AC Power routine passed.";
const char kAcPowerRoutineFailedNotOnlineMessage[] =
"Expected online power supply, found offline power supply.";
const char kAcPowerRoutineFailedNotOfflineMessage[] =
"Expected offline power supply, found online power supply.";
const char kAcPowerRoutineFailedMismatchedPowerTypesMessage[] =
"Read power type different from expected power type.";
const char kAcPowerRoutineNoValidPowerSupplyMessage[] =
"No valid AC power supply found.";
const char kAcPowerRoutineCancelledMessage[] = "AC Power routine cancelled.";
// We want a value here that is greater than zero to show that the routine has
// started. But it hasn't really done any work, so the value shouldn't be too
// high.
const uint32_t kAcPowerRoutineWaitingProgressPercent = 33;
AcPowerRoutine::AcPowerRoutine(
chromeos::cros_healthd::mojom::AcPowerStatusEnum expected_status,
const base::Optional<std::string>& expected_power_type,
const base::FilePath& root_dir)
: status_(mojo_ipc::DiagnosticRoutineStatusEnum::kReady),
expected_power_status_(expected_status),
expected_power_type_(expected_power_type),
root_dir_(root_dir) {}
AcPowerRoutine::~AcPowerRoutine() = default;
void AcPowerRoutine::Start() {
DCHECK_EQ(status_, mojo_ipc::DiagnosticRoutineStatusEnum::kReady);
// Transition to waiting so the user can plug or unplug the AC adapter as
// necessary.
status_ = mojo_ipc::DiagnosticRoutineStatusEnum::kWaiting;
CalculateProgressPercent();
}
void AcPowerRoutine::Resume() {
DCHECK_EQ(status_, mojo_ipc::DiagnosticRoutineStatusEnum::kWaiting);
status_ = RunAcPowerRoutine();
if (status_ != mojo_ipc::DiagnosticRoutineStatusEnum::kPassed)
LOG(ERROR) << "Routine failed: " << status_message_;
}
void AcPowerRoutine::Cancel() {
// Only cancel the routine if it's in the waiting state. Otherwise, it either
// hasn't begun or has already finished.
if (status_ == mojo_ipc::DiagnosticRoutineStatusEnum::kWaiting) {
status_ = mojo_ipc::DiagnosticRoutineStatusEnum::kCancelled;
status_message_ = kAcPowerRoutineCancelledMessage;
}
}
void AcPowerRoutine::PopulateStatusUpdate(mojo_ipc::RoutineUpdate* response,
bool include_output) {
if (status_ == mojo_ipc::DiagnosticRoutineStatusEnum::kWaiting) {
mojo_ipc::InteractiveRoutineUpdate interactive_update;
interactive_update.user_message =
(expected_power_status_ == mojo_ipc::AcPowerStatusEnum::kConnected)
? mojo_ipc::DiagnosticRoutineUserMessageEnum::kPlugInACPower
: mojo_ipc::DiagnosticRoutineUserMessageEnum::kUnplugACPower;
response->routine_update_union->set_interactive_update(
interactive_update.Clone());
} else {
mojo_ipc::NonInteractiveRoutineUpdate noninteractive_update;
noninteractive_update.status = status_;
noninteractive_update.status_message = status_message_;
response->routine_update_union->set_noninteractive_update(
noninteractive_update.Clone());
}
CalculateProgressPercent();
response->progress_percent = progress_percent_;
}
mojo_ipc::DiagnosticRoutineStatusEnum AcPowerRoutine::GetStatus() {
return status_;
}
void AcPowerRoutine::CalculateProgressPercent() {
// If the routine has been started and is waiting, assign a reasonable
// progress percentage that signifies the routine has been started.
if (status_ == mojo_ipc::DiagnosticRoutineStatusEnum::kWaiting) {
progress_percent_ = kAcPowerRoutineWaitingProgressPercent;
} else if (status_ == mojo_ipc::DiagnosticRoutineStatusEnum::kPassed ||
status_ == mojo_ipc::DiagnosticRoutineStatusEnum::kFailed) {
// The routine has finished, so report 100.
progress_percent_ = 100;
}
}
mojo_ipc::DiagnosticRoutineStatusEnum AcPowerRoutine::RunAcPowerRoutine() {
base::FileEnumerator dir_enumerator(
root_dir_.AppendASCII(kPowerSupplyDirectoryPath),
false /* is_recursive */,
base::FileEnumerator::SHOW_SYM_LINKS | base::FileEnumerator::FILES |
base::FileEnumerator::DIRECTORIES);
PowerSupplyFileContents contents;
bool valid_path_found = false;
for (base::FilePath path = dir_enumerator.Next(); !path.empty();
path = dir_enumerator.Next()) {
// Skip all power supplies of unknown type.
std::string type;
if (!base::ReadFileToString(path.AppendASCII(kTypeFileName), &type)) {
continue;
}
// Skip all batteries.
base::TrimWhitespaceASCII(type, base::TRIM_ALL, &type);
if (type == "Battery")
continue;
// Skip all power supplies which don't populate the online file.
std::string online;
if (!base::ReadFileToString(path.AppendASCII(kOnlineFileName), &online))
continue;
// If we found an online power supply, then that's the power supply we wish
// to test.
base::TrimWhitespaceASCII(online, base::TRIM_ALL, &online);
if (online == "1") {
valid_path_found = true;
contents.online = online;
contents.type = type;
break;
}
// If we have an offline power supply, but haven't found any online power
// supplies, then we have a candidate for power supply to test.
if (!valid_path_found) {
valid_path_found = true;
contents.online = online;
contents.type = type;
}
}
if (!valid_path_found) {
status_message_ = kAcPowerRoutineNoValidPowerSupplyMessage;
return mojo_ipc::DiagnosticRoutineStatusEnum::kError;
}
// Test the contents of the path's online file against the input value.
if (expected_power_status_ == mojo_ipc::AcPowerStatusEnum::kConnected &&
contents.online != "1") {
status_message_ = kAcPowerRoutineFailedNotOnlineMessage;
return mojo_ipc::DiagnosticRoutineStatusEnum::kFailed;
} else if (expected_power_status_ ==
mojo_ipc::AcPowerStatusEnum::kDisconnected &&
contents.online != "0") {
status_message_ = kAcPowerRoutineFailedNotOfflineMessage;
return mojo_ipc::DiagnosticRoutineStatusEnum::kFailed;
}
// Test the contents of the path's type file against the input value. This is
// an optional test, and won't be performed if |expected_power_type_| wasn't
// specified.
if (expected_power_type_.has_value() &&
expected_power_type_.value() != contents.type) {
status_message_ = kAcPowerRoutineFailedMismatchedPowerTypesMessage;
return mojo_ipc::DiagnosticRoutineStatusEnum::kFailed;
}
status_message_ = kAcPowerRoutineSucceededMessage;
return mojo_ipc::DiagnosticRoutineStatusEnum::kPassed;
}
} // namespace diagnostics