blob: 7996f37c3a5d13eed2faae3bcb17a542063d4d6d [file] [log] [blame]
// Copyright 2019 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/fetchers/battery_fetcher.h"
#include <algorithm>
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <base/bind.h>
#include <base/process/launch.h>
#include <base/files/file_util.h>
#include <base/optional.h>
#include <base/strings/stringprintf.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <base/values.h>
#include <re2/re2.h>
#include "debugd/dbus-proxies.h"
#include "diagnostics/cros_healthd/utils/error_utils.h"
#include "power_manager/proto_bindings/power_supply_properties.pb.h"
namespace diagnostics {
namespace {
namespace mojo_ipc = ::chromeos::cros_healthd::mojom;
// The name of the Smart Battery manufacture date metric.
constexpr char kManufactureDateSmart[] = "manufacture_date_smart";
// The name of the Smart Battery temperature metric.
constexpr char kTemperatureSmart[] = "temperature_smart";
// The maximum amount of time to wait for a debugd response.
constexpr int kDebugdDBusTimeout = 10 * 1000;
// Converts a Smart Battery manufacture date from the ((year - 1980) * 512 +
// month * 32 + day) format to yyyy-mm-dd format.
std::string ConvertSmartBatteryManufactureDate(uint32_t manufacture_date) {
int remainder = manufacture_date;
int day = remainder % 32;
remainder /= 32;
int month = remainder % 16;
remainder /= 16;
int year = remainder + 1980;
return base::StringPrintf("%04d-%02d-%02d", year, month, day);
}
} // namespace
BatteryFetcher::BatteryFetcher(Context* context) : context_(context) {
DCHECK(context_);
}
BatteryFetcher::~BatteryFetcher() = default;
mojo_ipc::BatteryResultPtr BatteryFetcher::FetchBatteryInfo() {
if (!context_->system_config()->HasBattery())
return mojo_ipc::BatteryResult::NewBatteryInfo(mojo_ipc::BatteryInfoPtr());
mojo_ipc::BatteryInfo info;
auto power_supply_proto =
context_->powerd_adapter()->GetPowerSupplyProperties();
if (!power_supply_proto) {
return mojo_ipc::BatteryResult::NewError(CreateAndLogProbeError(
mojo_ipc::ErrorType::kSystemUtilityError,
"Failed to obtain power supply properties from powerd"));
}
auto error =
PopulateBatteryInfoFromPowerdResponse(power_supply_proto.value(), &info);
if (error.has_value()) {
return mojo_ipc::BatteryResult::NewError(std::move(error.value()));
}
if (context_->system_config()->HasSmartBattery()) {
error = PopulateSmartBatteryInfo(&info);
if (error.has_value()) {
return mojo_ipc::BatteryResult::NewError(std::move(error.value()));
}
}
return mojo_ipc::BatteryResult::NewBatteryInfo(info.Clone());
}
base::Optional<mojo_ipc::ProbeErrorPtr>
BatteryFetcher::PopulateBatteryInfoFromPowerdResponse(
const power_manager::PowerSupplyProperties& power_supply_proto,
mojo_ipc::BatteryInfo* info) {
DCHECK(info);
if (!power_supply_proto.has_battery_state() ||
power_supply_proto.battery_state() ==
power_manager::PowerSupplyProperties_BatteryState_NOT_PRESENT) {
return CreateAndLogProbeError(
mojo_ipc::ErrorType::kSystemUtilityError,
"PowerSupplyProperties protobuf indicates battery is not present");
}
info->cycle_count = power_supply_proto.battery_cycle_count();
info->vendor = power_supply_proto.battery_vendor();
info->voltage_now = power_supply_proto.battery_voltage();
info->charge_full = power_supply_proto.battery_charge_full();
info->charge_full_design = power_supply_proto.battery_charge_full_design();
info->serial_number = power_supply_proto.battery_serial_number();
info->voltage_min_design = power_supply_proto.battery_voltage_min_design();
info->model_name = power_supply_proto.battery_model_name();
info->charge_now = power_supply_proto.battery_charge();
info->current_now = power_supply_proto.battery_current();
info->technology = power_supply_proto.battery_technology();
info->status = power_supply_proto.battery_status();
return base::nullopt;
}
base::Optional<mojo_ipc::ProbeErrorPtr>
BatteryFetcher::PopulateSmartBatteryInfo(mojo_ipc::BatteryInfo* info) {
uint32_t manufacture_date;
auto convert_hex_string_to_uint32 =
base::BindOnce([](const base::StringPiece& input, uint32_t* output) {
return base::HexStringToUInt(input, output);
});
auto error = GetSmartBatteryMetric(kManufactureDateSmart,
std::move(convert_hex_string_to_uint32),
&manufacture_date);
if (error.has_value()) {
return error;
}
info->manufacture_date = ConvertSmartBatteryManufactureDate(manufacture_date);
uint64_t temperature;
auto convert_hex_string_to_uint64 =
base::BindOnce([](const base::StringPiece& input, uint64_t* output) {
return base::HexStringToUInt64(input, output);
});
error = GetSmartBatteryMetric(
kTemperatureSmart, std::move(convert_hex_string_to_uint64), &temperature);
if (error.has_value()) {
return error;
}
info->temperature = mojo_ipc::NullableUint64::New(temperature);
return base::nullopt;
}
template <typename T>
base::Optional<mojo_ipc::ProbeErrorPtr> BatteryFetcher::GetSmartBatteryMetric(
const std::string& metric_name,
base::OnceCallback<bool(const base::StringPiece& input, T* output)>
convert_string_to_num,
T* metric_value) {
brillo::ErrorPtr error;
std::string debugd_result;
if (!context_->debugd_proxy()->CollectSmartBatteryMetric(
metric_name, &debugd_result, &error, kDebugdDBusTimeout)) {
return CreateAndLogProbeError(mojo_ipc::ErrorType::kSystemUtilityError,
"Failed retrieving " + metric_name +
" from debugd: " + error->GetCode() +
" " + error->GetMessage());
}
// Parse the output from debugd to obtain the battery metric.
constexpr auto kRegexPattern =
R"(^Read from I2C port [\d]+ at .* offset .* = (.+)$)";
std::string reg_value;
if (!RE2::PartialMatch(base::CollapseWhitespaceASCII(debugd_result, true),
kRegexPattern, &reg_value)) {
return CreateAndLogProbeError(
mojo_ipc::ErrorType::kParseError,
"Failed to match debugd output to regex: " + debugd_result);
}
if (!std::move(convert_string_to_num).Run(reg_value, metric_value)) {
return CreateAndLogProbeError(
mojo_ipc::ErrorType::kParseError,
"Unable to run convert string to num callback");
}
return base::nullopt;
}
} // namespace diagnostics