Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 44acd221 authored by Andy Hung's avatar Andy Hung
Browse files

psh_utils: Add HealthStats collection

Flag: com.android.media.audioserver.power_stats
Test: atest powerstats_collector_tests
Bug: 350114693
Change-Id: I92e0e68bbeef548b314c92e382d4057218838b14
parent 762f3210
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@ package {

// libraries that are included whole_static for test apps
ndk_libs = [
    "android.hardware.health-V3-ndk",
    "android.hardware.power.stats-V1-ndk",
]

@@ -18,6 +19,8 @@ cc_library {
    local_include_dirs: ["include"],
    export_include_dirs: ["include"],
    srcs: [
        "HealthStats.cpp",
        "HealthStatsProvider.cpp",
        "PowerStats.cpp",
        "PowerStatsCollector.cpp",
        "PowerStatsProvider.cpp",
+79 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <android-base/logging.h>
#include <psh_utils/HealthStats.h>

namespace android::media::psh_utils {

template <typename T>
const T& choose_voltage(const T& a, const T& b) {
   return std::max(a, b);  // we use max here, could use avg.
}

std::string HealthStats::toString() const {
    std::string result;
    const float batteryVoltage = batteryVoltageMillivolts * 1e-3f;  // Volts
    const float charge = batteryChargeCounterUah * (3600 * 1e-6);  // Joules = Amp-Second
    result.append(" battery_voltage: ")
            .append(std::to_string(batteryVoltage))
            .append(" charge: ")
            .append(std::to_string(charge));
    return result;
}

std::string HealthStats::normalizedEnergy(double timeSec) const {
    std::string result;
    const float batteryVoltage = batteryVoltageMillivolts * 1e-3f;   // Volts
    const float charge = -batteryChargeCounterUah * (3600 * 1e-6f);  // Joules = Amp-Second
    const float watts = charge * batteryVoltage / timeSec;
    result.append(" battery_voltage: ")
            .append(std::to_string(batteryVoltage))
            .append(" J: ")
            .append(std::to_string(charge))
            .append(" W: ")
            .append(std::to_string(watts));
    return result;
}

HealthStats HealthStats::operator+=(const HealthStats& other) {
    batteryVoltageMillivolts = choose_voltage(
            batteryVoltageMillivolts, other.batteryVoltageMillivolts);
    batteryFullChargeUah = std::max(batteryFullChargeUah, other.batteryFullChargeUah);
    batteryChargeCounterUah += other.batteryChargeCounterUah;
    return *this;
}

HealthStats HealthStats::operator-=(const HealthStats& other) {
    batteryVoltageMillivolts = choose_voltage(
            batteryVoltageMillivolts, other.batteryVoltageMillivolts);
    batteryFullChargeUah = std::max(batteryFullChargeUah, other.batteryFullChargeUah);
    batteryChargeCounterUah -= other.batteryChargeCounterUah;
    return *this;
}

HealthStats HealthStats::operator+(const HealthStats& other) const {
    HealthStats result = *this;
    result += other;
    return result;
}

HealthStats HealthStats::operator-(const HealthStats& other) const {
    HealthStats result = *this;
    result -= other;
    return result;
}

} // namespace android::media::psh_utils
+63 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "PowerStatsProvider.h"
#include <aidl/android/hardware/health/IHealth.h>
#include <android-base/logging.h>
#include <android/binder_manager.h>

using ::aidl::android::hardware::health::HealthInfo;
using ::aidl::android::hardware::health::IHealth;

namespace android::media::psh_utils {

static auto getHealthService() {
    [[clang::no_destroy]] static constinit std::mutex m;
    [[clang::no_destroy]] static constinit
            std::shared_ptr<IHealth> healthService;

    std::lock_guard l(m);
    if (healthService) {
        return healthService;
    }
    const auto serviceName =
            std::string(IHealth::descriptor).append("/default");
    healthService = IHealth::fromBinder(
            ::ndk::SpAIBinder(AServiceManager_checkService(serviceName.c_str())));
    return healthService;
}

status_t HealthStatsDataProvider::fill(PowerStats* stat) const {
    if (stat == nullptr) return BAD_VALUE;
    HealthStats& stats = stat->health_stats;
    auto healthService = getHealthService();
    if (healthService == nullptr) {
        LOG(ERROR) << "unable to get health AIDL service";
        return NO_INIT;
    }
    HealthInfo healthInfo;
    if (!healthService->getHealthInfo(&healthInfo).isOk()) {
        LOG(ERROR) << __func__ << ": unable to get health info";
        return INVALID_OPERATION;
    }

    stats.batteryVoltageMillivolts = healthInfo.batteryVoltageMillivolts;
    stats.batteryFullChargeUah = healthInfo.batteryFullChargeUah;
    stats.batteryChargeCounterUah = healthInfo.batteryChargeCounterUah;
    return NO_ERROR;
}

} // namespace android::media::psh_utils
+5 −0
Original line number Diff line number Diff line
@@ -163,6 +163,7 @@ PowerStats::RailEnergy PowerStats::RailEnergy::operator-(const RailEnergy& other
std::string PowerStats::toString() const {
    std::string result;
    result.append(metadata.toString()).append("\n");
    result.append(health_stats.toString()).append("\n");
    for (const auto &residency: power_entity_state_residency) {
        result.append(residency.toString()).append("\n");
    }
@@ -182,6 +183,8 @@ std::string PowerStats::normalizedEnergy() const {
            .append(" duration_monotonic: ")
            .append(std::to_string(metadata.duration_monotonic_ms * 1e-3f))
            .append("\n");
    result.append(health_stats.normalizedEnergy(metadata.duration_ms * 1e-3f)).append("\n");

    // energy_uws is converted to ave W using recip time in us.
    const float recipTime = 1e-3 / metadata.duration_ms;
    int64_t total_energy = 0;
@@ -221,6 +224,7 @@ std::tuple<float, float, float> PowerStats::energyFrom(const std::string& railMa

PowerStats PowerStats::operator+=(const PowerStats& other) {
    metadata += other.metadata;
    health_stats += other.health_stats;
    if (power_entity_state_residency.empty()) {
        power_entity_state_residency = other.power_entity_state_residency;
    } else {
@@ -240,6 +244,7 @@ PowerStats PowerStats::operator+=(const PowerStats& other) {

PowerStats PowerStats::operator-=(const PowerStats& other) {
    metadata -= other.metadata;
    health_stats -= other.health_stats;
    if (power_entity_state_residency.empty()) {
        power_entity_state_residency = other.power_entity_state_residency;
    } else {
+1 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ namespace android::media::psh_utils {
PowerStatsCollector::PowerStatsCollector() {
    addProvider(std::make_unique<PowerEntityResidencyDataProvider>());
    addProvider(std::make_unique<RailEnergyDataProvider>());
    addProvider(std::make_unique<HealthStatsDataProvider>());
}

/* static */
Loading