From 9c944a129da4b51aeffcd68dcaa20cf5d9234e4b Mon Sep 17 00:00:00 2001 From: Benjamin Schwartz Date: Mon, 1 Mar 2021 13:29:11 -0800 Subject: [PATCH] power/stats: Add default implementation Bug: 181592995 Test: atest VtsHalPowerStatsTargetTest Change-Id: I87ea4974f857ebd126b80a45c7d7403307bceb03 --- power/stats/aidl/default/FakeEnergyConsumer.h | 83 +++++++++++ power/stats/aidl/default/FakeEnergyMeter.h | 109 ++++++++++++++ .../default/FakeStateResidencyDataProvider.h | 87 +++++++++++ power/stats/aidl/default/PowerStats.cpp | 137 ++++++++++++++++-- power/stats/aidl/default/PowerStats.h | 41 ++++++ power/stats/aidl/default/main.cpp | 45 ++++++ 6 files changed, 489 insertions(+), 13 deletions(-) create mode 100644 power/stats/aidl/default/FakeEnergyConsumer.h create mode 100644 power/stats/aidl/default/FakeEnergyMeter.h create mode 100644 power/stats/aidl/default/FakeStateResidencyDataProvider.h diff --git a/power/stats/aidl/default/FakeEnergyConsumer.h b/power/stats/aidl/default/FakeEnergyConsumer.h new file mode 100644 index 0000000000..f41aa6ea86 --- /dev/null +++ b/power/stats/aidl/default/FakeEnergyConsumer.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2021 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. + */ + +#pragma once + +#include + +#include + +#include +#include + +namespace aidl { +namespace android { +namespace hardware { +namespace power { +namespace stats { + +class FakeEnergyConsumer : public PowerStats::IEnergyConsumer { + public: + FakeEnergyConsumer(EnergyConsumerType type, std::string name) : mType(type), mName(name) { + mResult.timestampMs = 0; + mResult.energyUWs = 0; + mResult.attribution = {}; + } + + ~FakeEnergyConsumer() = default; + + std::string getName() override { return mName; } + + EnergyConsumerType getType() override { return mType; } + + std::optional getEnergyConsumed() override { + mFakeEnergyConsumerResult.update(&mResult); + return mResult; + } + + private: + class FakeEnergyConsumerResult { + public: + FakeEnergyConsumerResult() : mDistribution(1, 100) {} + void update(EnergyConsumerResult* result) { + // generates number in the range 1..100 + auto randNum = std::bind(mDistribution, mGenerator); + + // Get current time since boot in milliseconds + uint64_t now = std::chrono::time_point_cast( + ::android::base::boot_clock::now()) + .time_since_epoch() + .count(); + result->timestampMs = now; + result->energyUWs += randNum() * 100; + } + + private: + std::default_random_engine mGenerator; + std::uniform_int_distribution mDistribution; + }; + + EnergyConsumerType mType; + std::string mName; + FakeEnergyConsumerResult mFakeEnergyConsumerResult; + EnergyConsumerResult mResult; +}; + +} // namespace stats +} // namespace power +} // namespace hardware +} // namespace android +} // namespace aidl \ No newline at end of file diff --git a/power/stats/aidl/default/FakeEnergyMeter.h b/power/stats/aidl/default/FakeEnergyMeter.h new file mode 100644 index 0000000000..f0d4ee7dfc --- /dev/null +++ b/power/stats/aidl/default/FakeEnergyMeter.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2021 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. + */ + +#pragma once + +#include + +#include + +#include +#include + +namespace aidl { +namespace android { +namespace hardware { +namespace power { +namespace stats { + +class FakeEnergyMeter : public PowerStats::IEnergyMeter { + public: + FakeEnergyMeter(std::vector> channelNames) { + int32_t channelId = 0; + for (const auto& [name, subsystem] : channelNames) { + Channel c; + c.id = channelId++; + c.name = name; + c.subsystem = subsystem; + + EnergyMeasurement m; + m.id = c.id; + m.timestampMs = 0; + m.durationMs = 0; + m.energyUWs = 0; + + mChannels.push_back(c); + mEnergyMeasurements.push_back(m); + } + } + ~FakeEnergyMeter() = default; + ndk::ScopedAStatus readEnergyMeter(const std::vector& in_channelIds, + std::vector* _aidl_return) override { + for (auto& measurement : mEnergyMeasurements) { + mFakeEnergyMeasurement.update(&measurement); + } + + if (in_channelIds.empty()) { + *_aidl_return = mEnergyMeasurements; + } else { + for (int32_t id : in_channelIds) { + if (id >= 0 && id < mEnergyMeasurements.size()) { + _aidl_return->push_back(mEnergyMeasurements[id]); + } + } + } + + return ndk::ScopedAStatus::ok(); + } + + ndk::ScopedAStatus getEnergyMeterInfo(std::vector* _aidl_return) override { + *_aidl_return = mChannels; + return ndk::ScopedAStatus::ok(); + } + + private: + class FakeEnergyMeasurement { + public: + FakeEnergyMeasurement() : mDistribution(1, 100) {} + void update(EnergyMeasurement* measurement) { + // generates number in the range 1..100 + auto randNum = std::bind(mDistribution, mGenerator); + + // Get current time since boot in milliseconds + uint64_t now = std::chrono::time_point_cast( + ::android::base::boot_clock::now()) + .time_since_epoch() + .count(); + measurement->timestampMs = now; + measurement->durationMs = now; + measurement->energyUWs += randNum() * 100; + } + + private: + std::default_random_engine mGenerator; + std::uniform_int_distribution mDistribution; + }; + + std::vector mChannels; + FakeEnergyMeasurement mFakeEnergyMeasurement; + std::vector mEnergyMeasurements; +}; + +} // namespace stats +} // namespace power +} // namespace hardware +} // namespace android +} // namespace aidl \ No newline at end of file diff --git a/power/stats/aidl/default/FakeStateResidencyDataProvider.h b/power/stats/aidl/default/FakeStateResidencyDataProvider.h new file mode 100644 index 0000000000..2eeab61dbc --- /dev/null +++ b/power/stats/aidl/default/FakeStateResidencyDataProvider.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2021 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. + */ + +#pragma once + +#include + +#include + +namespace aidl { +namespace android { +namespace hardware { +namespace power { +namespace stats { + +class FakeStateResidencyDataProvider : public PowerStats::IStateResidencyDataProvider { + public: + FakeStateResidencyDataProvider(const std::string& name, std::vector states) + : mName(name), mStates(states) { + for (const auto& state : mStates) { + StateResidency r; + r.id = state.id; + r.totalTimeInStateMs = 0; + r.totalStateEntryCount = 0; + r.lastEntryTimestampMs = 0; + mResidencies.push_back(r); + } + } + ~FakeStateResidencyDataProvider() = default; + + // Methods from PowerStats::IStateResidencyDataProvider + bool getStateResidencies( + std::unordered_map>* residencies) override { + for (auto& residency : mResidencies) { + mFakeStateResidency.update(&residency); + } + + residencies->emplace(mName, mResidencies); + return true; + } + + std::unordered_map> getInfo() override { + return {{mName, mStates}}; + } + + private: + class FakeStateResidency { + public: + FakeStateResidency() : mDistribution(1, 100) {} + void update(StateResidency* residency) { + // generates number in the range 1..100 + auto randNum = std::bind(mDistribution, mGenerator); + + residency->totalTimeInStateMs += randNum() * 100; + residency->totalStateEntryCount += randNum(); + residency->lastEntryTimestampMs += randNum() * 100; + } + + private: + std::default_random_engine mGenerator; + std::uniform_int_distribution mDistribution; + }; + + const std::string mName; + const std::vector mStates; + FakeStateResidency mFakeStateResidency; + std::vector mResidencies; +}; + +} // namespace stats +} // namespace power +} // namespace hardware +} // namespace android +} // namespace aidl \ No newline at end of file diff --git a/power/stats/aidl/default/PowerStats.cpp b/power/stats/aidl/default/PowerStats.cpp index 0ffbd083c6..13735026c5 100644 --- a/power/stats/aidl/default/PowerStats.cpp +++ b/power/stats/aidl/default/PowerStats.cpp @@ -18,46 +18,157 @@ #include +#include + namespace aidl { namespace android { namespace hardware { namespace power { namespace stats { +void PowerStats::addStateResidencyDataProvider(std::unique_ptr p) { + if (!p) { + return; + } + + int32_t id = mPowerEntityInfos.size(); + + for (const auto& [entityName, states] : p->getInfo()) { + PowerEntity i = { + .id = id++, + .name = entityName, + .states = states, + }; + mPowerEntityInfos.emplace_back(i); + mStateResidencyDataProviders.emplace_back(std::move(p)); + } +} + +void PowerStats::addEnergyConsumer(std::unique_ptr p) { + if (!p) { + return; + } + + EnergyConsumerType type = p->getType(); + std::string name = p->getName(); + int32_t count = count_if(mEnergyConsumerInfos.begin(), mEnergyConsumerInfos.end(), + [&type](const EnergyConsumer& c) { return type == c.type; }); + int32_t id = mEnergyConsumers.size(); + mEnergyConsumerInfos.emplace_back( + EnergyConsumer{.id = id, .ordinal = count, .type = type, .name = name}); + mEnergyConsumers.emplace_back(std::move(p)); +} + +void PowerStats::setEnergyMeter(std::unique_ptr p) { + mEnergyMeter = std::move(p); +} + ndk::ScopedAStatus PowerStats::getPowerEntityInfo(std::vector* _aidl_return) { - (void)_aidl_return; + *_aidl_return = mPowerEntityInfos; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus PowerStats::getStateResidency(const std::vector& in_powerEntityIds, std::vector* _aidl_return) { - (void)in_powerEntityIds; - (void)_aidl_return; - return ndk::ScopedAStatus::ok(); + if (mPowerEntityInfos.empty()) { + return ndk::ScopedAStatus::ok(); + } + + // If in_powerEntityIds is empty then return data for all supported entities + if (in_powerEntityIds.empty()) { + std::vector v(mPowerEntityInfos.size()); + std::iota(std::begin(v), std::end(v), 0); + return getStateResidency(v, _aidl_return); + } + + binder_status_t err = STATUS_OK; + + std::unordered_map> stateResidencies; + + for (const int32_t id : in_powerEntityIds) { + // skip any invalid ids + if (id < 0 || id >= mPowerEntityInfos.size()) { + continue; + } + + // Check to see if we already have data for the given id + std::string powerEntityName = mPowerEntityInfos[id].name; + if (stateResidencies.find(powerEntityName) == stateResidencies.end()) { + mStateResidencyDataProviders[id]->getStateResidencies(&stateResidencies); + } + + // Append results if we have them + auto stateResidency = stateResidencies.find(powerEntityName); + if (stateResidency != stateResidencies.end()) { + StateResidencyResult res = { + .id = id, + .stateResidencyData = stateResidency->second, + }; + _aidl_return->emplace_back(res); + } else { + // Failed to retrieve results for the given id. + err = STATUS_FAILED_TRANSACTION; + } + } + + return ndk::ScopedAStatus::fromStatus(err); } ndk::ScopedAStatus PowerStats::getEnergyConsumerInfo(std::vector* _aidl_return) { - (void)_aidl_return; + *_aidl_return = mEnergyConsumerInfos; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus PowerStats::getEnergyConsumed(const std::vector& in_energyConsumerIds, std::vector* _aidl_return) { - (void)in_energyConsumerIds; - (void)_aidl_return; - return ndk::ScopedAStatus::ok(); + if (mEnergyConsumers.empty()) { + return ndk::ScopedAStatus::ok(); + } + + // If in_powerEntityIds is empty then return data for all supported energy consumers + if (in_energyConsumerIds.empty()) { + std::vector v(mEnergyConsumerInfos.size()); + std::iota(std::begin(v), std::end(v), 0); + return getEnergyConsumed(v, _aidl_return); + } + + binder_status_t err = STATUS_OK; + + for (const auto id : in_energyConsumerIds) { + // skip any invalid ids + if (id < 0 || id >= mEnergyConsumers.size()) { + continue; + } + + auto optionalResult = mEnergyConsumers[id]->getEnergyConsumed(); + if (optionalResult) { + EnergyConsumerResult result = optionalResult.value(); + result.id = id; + _aidl_return->emplace_back(result); + } else { + // Failed to retrieve results for the given id. + err = STATUS_FAILED_TRANSACTION; + } + } + + return ndk::ScopedAStatus::fromStatus(err); } ndk::ScopedAStatus PowerStats::getEnergyMeterInfo(std::vector* _aidl_return) { - (void)_aidl_return; - return ndk::ScopedAStatus::ok(); + if (!mEnergyMeter) { + return ndk::ScopedAStatus::ok(); + } + + return mEnergyMeter->getEnergyMeterInfo(_aidl_return); } ndk::ScopedAStatus PowerStats::readEnergyMeter(const std::vector& in_channelIds, std::vector* _aidl_return) { - (void)in_channelIds; - (void)_aidl_return; - return ndk::ScopedAStatus::ok(); + if (!mEnergyMeter) { + return ndk::ScopedAStatus::ok(); + } + + return mEnergyMeter->readEnergyMeter(in_channelIds, _aidl_return); } } // namespace stats diff --git a/power/stats/aidl/default/PowerStats.h b/power/stats/aidl/default/PowerStats.h index cb98e553f3..f4c5e69569 100644 --- a/power/stats/aidl/default/PowerStats.h +++ b/power/stats/aidl/default/PowerStats.h @@ -18,6 +18,8 @@ #include +#include + namespace aidl { namespace android { namespace hardware { @@ -26,7 +28,37 @@ namespace stats { class PowerStats : public BnPowerStats { public: + class IStateResidencyDataProvider { + public: + virtual ~IStateResidencyDataProvider() = default; + virtual bool getStateResidencies( + std::unordered_map>* residencies) = 0; + virtual std::unordered_map> getInfo() = 0; + }; + + class IEnergyConsumer { + public: + virtual ~IEnergyConsumer() = default; + virtual std::string getName() = 0; + virtual EnergyConsumerType getType() = 0; + virtual std::optional getEnergyConsumed() = 0; + }; + + class IEnergyMeter { + public: + virtual ~IEnergyMeter() = default; + virtual ndk::ScopedAStatus readEnergyMeter( + const std::vector& in_channelIds, + std::vector* _aidl_return) = 0; + virtual ndk::ScopedAStatus getEnergyMeterInfo(std::vector* _aidl_return) = 0; + }; + PowerStats() = default; + + void addStateResidencyDataProvider(std::unique_ptr p); + void addEnergyConsumer(std::unique_ptr p); + void setEnergyMeter(std::unique_ptr p); + // Methods from aidl::android::hardware::power::stats::IPowerStats ndk::ScopedAStatus getPowerEntityInfo(std::vector* _aidl_return) override; ndk::ScopedAStatus getStateResidency(const std::vector& in_powerEntityIds, @@ -37,6 +69,15 @@ class PowerStats : public BnPowerStats { ndk::ScopedAStatus getEnergyMeterInfo(std::vector* _aidl_return) override; ndk::ScopedAStatus readEnergyMeter(const std::vector& in_channelIds, std::vector* _aidl_return) override; + + private: + std::vector> mStateResidencyDataProviders; + std::vector mPowerEntityInfos; + + std::vector> mEnergyConsumers; + std::vector mEnergyConsumerInfos; + + std::unique_ptr mEnergyMeter; }; } // namespace stats diff --git a/power/stats/aidl/default/main.cpp b/power/stats/aidl/default/main.cpp index 0469b4c21d..2fe3d2e103 100644 --- a/power/stats/aidl/default/main.cpp +++ b/power/stats/aidl/default/main.cpp @@ -16,16 +16,61 @@ #include "PowerStats.h" +#include "FakeEnergyConsumer.h" +#include "FakeEnergyMeter.h" +#include "FakeStateResidencyDataProvider.h" + #include #include #include +using aidl::android::hardware::power::stats::EnergyConsumerType; +using aidl::android::hardware::power::stats::FakeEnergyConsumer; +using aidl::android::hardware::power::stats::FakeEnergyMeter; +using aidl::android::hardware::power::stats::FakeStateResidencyDataProvider; using aidl::android::hardware::power::stats::PowerStats; +using aidl::android::hardware::power::stats::State; + +void setFakeEnergyMeter(std::shared_ptr p) { + p->setEnergyMeter( + std::make_unique(std::vector>{ + {"Rail1", "Display"}, + {"Rail2", "CPU"}, + {"Rail3", "Modem"}, + })); +} + +void addFakeStateResidencyDataProvider1(std::shared_ptr p) { + p->addStateResidencyDataProvider(std::make_unique( + "CPU", std::vector{{0, "Idle"}, {1, "Active"}})); +} + +void addFakeStateResidencyDataProvider2(std::shared_ptr p) { + p->addStateResidencyDataProvider(std::make_unique( + "Display", std::vector{{0, "Off"}, {1, "On"}})); +} + +void addFakeEnergyConsumer1(std::shared_ptr p) { + p->addEnergyConsumer(std::make_unique(EnergyConsumerType::OTHER, "GPU")); +} + +void addFakeEnergyConsumer2(std::shared_ptr p) { + p->addEnergyConsumer( + std::make_unique(EnergyConsumerType::MOBILE_RADIO, "MODEM")); +} int main() { ABinderProcess_setThreadPoolMaxThreadCount(0); std::shared_ptr p = ndk::SharedRefBase::make(); + setFakeEnergyMeter(p); + + addFakeStateResidencyDataProvider1(p); + addFakeStateResidencyDataProvider2(p); + + addFakeEnergyConsumer1(p); + addFakeEnergyConsumer2(p); + const std::string instance = std::string() + PowerStats::descriptor + "/default"; binder_status_t status = AServiceManager_addService(p->asBinder().get(), instance.c_str()); CHECK(status == STATUS_OK);