power/stats: Add default implementation

Bug: 181592995
Test: atest VtsHalPowerStatsTargetTest
Change-Id: I87ea4974f857ebd126b80a45c7d7403307bceb03
This commit is contained in:
Benjamin Schwartz
2021-03-01 13:29:11 -08:00
parent c7d67a734a
commit 9c944a129d
6 changed files with 489 additions and 13 deletions

View File

@@ -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 <PowerStats.h>
#include <android-base/chrono_utils.h>
#include <chrono>
#include <random>
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<EnergyConsumerResult> 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<std::chrono::milliseconds>(
::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<int> mDistribution;
};
EnergyConsumerType mType;
std::string mName;
FakeEnergyConsumerResult mFakeEnergyConsumerResult;
EnergyConsumerResult mResult;
};
} // namespace stats
} // namespace power
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -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 <PowerStats.h>
#include <android-base/chrono_utils.h>
#include <chrono>
#include <random>
namespace aidl {
namespace android {
namespace hardware {
namespace power {
namespace stats {
class FakeEnergyMeter : public PowerStats::IEnergyMeter {
public:
FakeEnergyMeter(std::vector<std::pair<std::string, std::string>> 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<int32_t>& in_channelIds,
std::vector<EnergyMeasurement>* _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<Channel>* _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<std::chrono::milliseconds>(
::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<int> mDistribution;
};
std::vector<Channel> mChannels;
FakeEnergyMeasurement mFakeEnergyMeasurement;
std::vector<EnergyMeasurement> mEnergyMeasurements;
};
} // namespace stats
} // namespace power
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -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 <PowerStats.h>
#include <random>
namespace aidl {
namespace android {
namespace hardware {
namespace power {
namespace stats {
class FakeStateResidencyDataProvider : public PowerStats::IStateResidencyDataProvider {
public:
FakeStateResidencyDataProvider(const std::string& name, std::vector<State> 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<std::string, std::vector<StateResidency>>* residencies) override {
for (auto& residency : mResidencies) {
mFakeStateResidency.update(&residency);
}
residencies->emplace(mName, mResidencies);
return true;
}
std::unordered_map<std::string, std::vector<State>> 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<int> mDistribution;
};
const std::string mName;
const std::vector<State> mStates;
FakeStateResidency mFakeStateResidency;
std::vector<StateResidency> mResidencies;
};
} // namespace stats
} // namespace power
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -18,46 +18,157 @@
#include <android-base/logging.h>
#include <numeric>
namespace aidl {
namespace android {
namespace hardware {
namespace power {
namespace stats {
void PowerStats::addStateResidencyDataProvider(std::unique_ptr<IStateResidencyDataProvider> 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<IEnergyConsumer> 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<IEnergyMeter> p) {
mEnergyMeter = std::move(p);
}
ndk::ScopedAStatus PowerStats::getPowerEntityInfo(std::vector<PowerEntity>* _aidl_return) {
(void)_aidl_return;
*_aidl_return = mPowerEntityInfos;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus PowerStats::getStateResidency(const std::vector<int32_t>& in_powerEntityIds,
std::vector<StateResidencyResult>* _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<int32_t> 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<std::string, std::vector<StateResidency>> 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<EnergyConsumer>* _aidl_return) {
(void)_aidl_return;
*_aidl_return = mEnergyConsumerInfos;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus PowerStats::getEnergyConsumed(const std::vector<int32_t>& in_energyConsumerIds,
std::vector<EnergyConsumerResult>* _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<int32_t> 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<Channel>* _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<int32_t>& in_channelIds,
std::vector<EnergyMeasurement>* _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

View File

@@ -18,6 +18,8 @@
#include <aidl/android/hardware/power/stats/BnPowerStats.h>
#include <unordered_map>
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<std::string, std::vector<StateResidency>>* residencies) = 0;
virtual std::unordered_map<std::string, std::vector<State>> getInfo() = 0;
};
class IEnergyConsumer {
public:
virtual ~IEnergyConsumer() = default;
virtual std::string getName() = 0;
virtual EnergyConsumerType getType() = 0;
virtual std::optional<EnergyConsumerResult> getEnergyConsumed() = 0;
};
class IEnergyMeter {
public:
virtual ~IEnergyMeter() = default;
virtual ndk::ScopedAStatus readEnergyMeter(
const std::vector<int32_t>& in_channelIds,
std::vector<EnergyMeasurement>* _aidl_return) = 0;
virtual ndk::ScopedAStatus getEnergyMeterInfo(std::vector<Channel>* _aidl_return) = 0;
};
PowerStats() = default;
void addStateResidencyDataProvider(std::unique_ptr<IStateResidencyDataProvider> p);
void addEnergyConsumer(std::unique_ptr<IEnergyConsumer> p);
void setEnergyMeter(std::unique_ptr<IEnergyMeter> p);
// Methods from aidl::android::hardware::power::stats::IPowerStats
ndk::ScopedAStatus getPowerEntityInfo(std::vector<PowerEntity>* _aidl_return) override;
ndk::ScopedAStatus getStateResidency(const std::vector<int32_t>& in_powerEntityIds,
@@ -37,6 +69,15 @@ class PowerStats : public BnPowerStats {
ndk::ScopedAStatus getEnergyMeterInfo(std::vector<Channel>* _aidl_return) override;
ndk::ScopedAStatus readEnergyMeter(const std::vector<int32_t>& in_channelIds,
std::vector<EnergyMeasurement>* _aidl_return) override;
private:
std::vector<std::unique_ptr<IStateResidencyDataProvider>> mStateResidencyDataProviders;
std::vector<PowerEntity> mPowerEntityInfos;
std::vector<std::unique_ptr<IEnergyConsumer>> mEnergyConsumers;
std::vector<EnergyConsumer> mEnergyConsumerInfos;
std::unique_ptr<IEnergyMeter> mEnergyMeter;
};
} // namespace stats

View File

@@ -16,16 +16,61 @@
#include "PowerStats.h"
#include "FakeEnergyConsumer.h"
#include "FakeEnergyMeter.h"
#include "FakeStateResidencyDataProvider.h"
#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
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<PowerStats> p) {
p->setEnergyMeter(
std::make_unique<FakeEnergyMeter>(std::vector<std::pair<std::string, std::string>>{
{"Rail1", "Display"},
{"Rail2", "CPU"},
{"Rail3", "Modem"},
}));
}
void addFakeStateResidencyDataProvider1(std::shared_ptr<PowerStats> p) {
p->addStateResidencyDataProvider(std::make_unique<FakeStateResidencyDataProvider>(
"CPU", std::vector<State>{{0, "Idle"}, {1, "Active"}}));
}
void addFakeStateResidencyDataProvider2(std::shared_ptr<PowerStats> p) {
p->addStateResidencyDataProvider(std::make_unique<FakeStateResidencyDataProvider>(
"Display", std::vector<State>{{0, "Off"}, {1, "On"}}));
}
void addFakeEnergyConsumer1(std::shared_ptr<PowerStats> p) {
p->addEnergyConsumer(std::make_unique<FakeEnergyConsumer>(EnergyConsumerType::OTHER, "GPU"));
}
void addFakeEnergyConsumer2(std::shared_ptr<PowerStats> p) {
p->addEnergyConsumer(
std::make_unique<FakeEnergyConsumer>(EnergyConsumerType::MOBILE_RADIO, "MODEM"));
}
int main() {
ABinderProcess_setThreadPoolMaxThreadCount(0);
std::shared_ptr<PowerStats> p = ndk::SharedRefBase::make<PowerStats>();
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);