chromium/components/performance_manager/power/battery_level_provider_chromeos.cc

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/performance_manager/power/battery_level_provider_chromeos.h"

#include <optional>

#include "chromeos/dbus/power/power_manager_client.h"
#include "chromeos/dbus/power_manager/power_supply_properties.pb.h"

namespace performance_manager::power {

const double kmAhPerAh = 1000.0;

BatteryLevelProviderChromeOS::BatteryLevelProviderChromeOS(
    chromeos::PowerManagerClient* power_manager_client)
    : power_manager_client_(power_manager_client) {
  DCHECK(power_manager_client_);
}

BatteryLevelProviderChromeOS::~BatteryLevelProviderChromeOS() = default;

// static
std::unique_ptr<base::BatteryLevelProvider>
BatteryLevelProviderChromeOS::Create() {
  return std::make_unique<
      performance_manager::power::BatteryLevelProviderChromeOS>(
      chromeos::PowerManagerClient::Get());
}

void BatteryLevelProviderChromeOS::GetBatteryState(
    base::OnceCallback<
        void(const std::optional<base::BatteryLevelProvider::BatteryState>&)>
        callback) {
  DCHECK(power_manager_client_);

  const std::optional<power_manager::PowerSupplyProperties>& power_status =
      power_manager_client_->GetLastStatus();
  if (!power_status) {
    std::move(callback).Run(std::nullopt);
    return;
  }

  base::BatteryLevelProvider::BatteryState state;

  if (power_status->battery_state() ==
      power_manager::PowerSupplyProperties::NOT_PRESENT) {
    state = {
        .battery_count = 0,
        .is_external_power_connected = true,
        .current_capacity = std::nullopt,
        .full_charged_capacity = std::nullopt,
        .charge_unit = std::nullopt,
        // TODO(anthonyvd): This should be the time that the state was last
        // sampled from the system, but PowerManagerClient doesn't yet provide
        // this information.
        .capture_time = base::TimeTicks::Now(),
    };
  } else {
    bool has_charge_in_ah = power_status->has_battery_charge() &&
                            power_status->has_battery_charge_full();
    bool has_charge_in_percent = power_status->has_battery_percent();

    std::optional<uint64_t> charge;
    std::optional<uint64_t> charge_full;
    std::optional<base::BatteryLevelProvider::BatteryLevelUnit> charge_unit =
        std::nullopt;

    // There are 3 ways the power manager could report the battery capacity,
    // affecting how the battery state will be constructed. In order of
    // preference:
    //
    // 1- Power Manager reports the charge in Ah, and reports voltage. We report
    // the capacity in mWh.
    //
    // 2- Power Manager reports the charge in Ah, and does not report voltage.
    // We report the capacity in mAh.
    //
    // 3- Power Manager reports the charge as a percentage. We report the charge
    // as relative (percentage).
    if (has_charge_in_ah) {
      if (power_status->has_battery_voltage()) {
        charge = power_status->battery_charge() * kmAhPerAh *
                 power_status->battery_voltage();
        charge_full = power_status->battery_charge_full() * kmAhPerAh *
                      power_status->battery_voltage();
        charge_unit = base::BatteryLevelProvider::BatteryLevelUnit::kMWh;
      } else {
        charge = power_status->battery_charge() * kmAhPerAh;
        charge_full = power_status->battery_charge_full() * kmAhPerAh;
        charge_unit = base::BatteryLevelProvider::BatteryLevelUnit::kMAh;
      }
    } else if (has_charge_in_percent) {
      charge = power_status->battery_percent();
      charge_full = 100;
      charge_unit = base::BatteryLevelProvider::BatteryLevelUnit::kRelative;
    }

    state = {
        .battery_count = 1,
        .is_external_power_connected =
            power_status->external_power() !=
            power_manager::PowerSupplyProperties::DISCONNECTED,
        .current_capacity = charge,
        .full_charged_capacity = charge_full,
        .charge_unit = charge_unit,
        // TODO(anthonyvd): This should be the time that the state was last
        // sampled from the system, but PowerManagerClient doesn't yet provide
        // this information.
        .capture_time = base::TimeTicks::Now(),
    };
  }

  std::move(callback).Run(state);
}

}  // namespace performance_manager::power