mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 11:36:00 +00:00
power.stats: Add more informative default implementation
The original default implementation for power.stats HAL did not have a
good example implementation of the power statistics related APIs. Adding
a new default implementation that gives a more informative example
Bug: 122267057
Test: run vts -m VtsHalPowerStatsV1_0Target
Test: adb shell "lshal debug android.hardware.power.stats@1.0::IPowerStats/default"
Observed the following output
========== PowerStats HAL 1.0 state residencies ==========
Entity State Total time Total entries Last entry timestamp
DefaultEntity Active 1 ms 2 3 ms
DefaultEntity Sleep 4 ms 5 6 ms
========== End of PowerStats HAL 1.0 state residencies ==========
Change-Id: Ida0951c267f609b16bb6406da150ed2e504ced9a
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
|
||||
cc_library_shared {
|
||||
cc_binary {
|
||||
name: "android.hardware.power.stats@1.0-service",
|
||||
relative_install_path: "hw",
|
||||
init_rc: ["android.hardware.power.stats@1.0-service.rc"],
|
||||
|
||||
3
power/stats/1.0/default/OWNERS
Normal file
3
power/stats/1.0/default/OWNERS
Normal file
@@ -0,0 +1,3 @@
|
||||
krossmo@google.com
|
||||
bsschwar@google.com
|
||||
tstrudel@google.com
|
||||
@@ -287,25 +287,218 @@ Return<void> PowerStats::streamEnergyData(uint32_t timeMs, uint32_t samplingRate
|
||||
return Void();
|
||||
}
|
||||
|
||||
uint32_t PowerStats::addPowerEntity(const std::string& name, PowerEntityType type) {
|
||||
uint32_t id = mPowerEntityInfos.size();
|
||||
mPowerEntityInfos.push_back({id, name, type});
|
||||
return id;
|
||||
}
|
||||
|
||||
void PowerStats::addStateResidencyDataProvider(std::shared_ptr<IStateResidencyDataProvider> p) {
|
||||
std::vector<PowerEntityStateSpace> stateSpaces = p->getStateSpaces();
|
||||
for (auto stateSpace : stateSpaces) {
|
||||
mPowerEntityStateSpaces.emplace(stateSpace.powerEntityId, stateSpace);
|
||||
mStateResidencyDataProviders.emplace(stateSpace.powerEntityId, p);
|
||||
}
|
||||
}
|
||||
|
||||
Return<void> PowerStats::getPowerEntityInfo(getPowerEntityInfo_cb _hidl_cb) {
|
||||
hidl_vec<PowerEntityInfo> eInfo;
|
||||
_hidl_cb(eInfo, Status::NOT_SUPPORTED);
|
||||
// If not configured, return NOT_SUPPORTED
|
||||
if (mPowerEntityInfos.empty()) {
|
||||
_hidl_cb({}, Status::NOT_SUPPORTED);
|
||||
return Void();
|
||||
}
|
||||
|
||||
_hidl_cb(mPowerEntityInfos, Status::SUCCESS);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> PowerStats::getPowerEntityStateInfo(const hidl_vec<uint32_t>& powerEntityIds,
|
||||
getPowerEntityStateInfo_cb _hidl_cb) {
|
||||
(void)powerEntityIds;
|
||||
hidl_vec<PowerEntityStateSpace> powerEntityStateSpaces;
|
||||
_hidl_cb(powerEntityStateSpaces, Status::NOT_SUPPORTED);
|
||||
// If not configured, return NOT_SUPPORTED
|
||||
if (mPowerEntityStateSpaces.empty()) {
|
||||
_hidl_cb({}, Status::NOT_SUPPORTED);
|
||||
return Void();
|
||||
}
|
||||
|
||||
std::vector<PowerEntityStateSpace> stateSpaces;
|
||||
|
||||
// If powerEntityIds is empty then return state space info for all entities
|
||||
if (powerEntityIds.size() == 0) {
|
||||
stateSpaces.reserve(mPowerEntityStateSpaces.size());
|
||||
for (auto i : mPowerEntityStateSpaces) {
|
||||
stateSpaces.emplace_back(i.second);
|
||||
}
|
||||
_hidl_cb(stateSpaces, Status::SUCCESS);
|
||||
return Void();
|
||||
}
|
||||
|
||||
// Return state space information only for valid ids
|
||||
auto ret = Status::SUCCESS;
|
||||
stateSpaces.reserve(powerEntityIds.size());
|
||||
for (const uint32_t id : powerEntityIds) {
|
||||
auto stateSpace = mPowerEntityStateSpaces.find(id);
|
||||
if (stateSpace != mPowerEntityStateSpaces.end()) {
|
||||
stateSpaces.emplace_back(stateSpace->second);
|
||||
} else {
|
||||
ret = Status::INVALID_INPUT;
|
||||
}
|
||||
}
|
||||
|
||||
_hidl_cb(stateSpaces, ret);
|
||||
return Void();
|
||||
}
|
||||
|
||||
Return<void> PowerStats::getPowerEntityStateResidencyData(
|
||||
const hidl_vec<uint32_t>& powerEntityIds, getPowerEntityStateResidencyData_cb _hidl_cb) {
|
||||
(void)powerEntityIds;
|
||||
const hidl_vec<uint32_t>& powerEntityIds, getPowerEntityStateResidencyData_cb _hidl_cb) {
|
||||
// If not configured, return NOT_SUPPORTED
|
||||
if (mStateResidencyDataProviders.empty() || mPowerEntityStateSpaces.empty()) {
|
||||
_hidl_cb({}, Status::NOT_SUPPORTED);
|
||||
return Void();
|
||||
}
|
||||
|
||||
// If powerEntityIds is empty then return data for all supported entities
|
||||
if (powerEntityIds.size() == 0) {
|
||||
std::vector<uint32_t> ids;
|
||||
for (auto stateSpace : mPowerEntityStateSpaces) {
|
||||
ids.emplace_back(stateSpace.first);
|
||||
}
|
||||
return getPowerEntityStateResidencyData(ids, _hidl_cb);
|
||||
}
|
||||
|
||||
std::unordered_map<uint32_t, PowerEntityStateResidencyResult> stateResidencies;
|
||||
std::vector<PowerEntityStateResidencyResult> results;
|
||||
results.reserve(powerEntityIds.size());
|
||||
|
||||
// return results for only the given powerEntityIds
|
||||
bool invalidInput = false;
|
||||
bool filesystemError = false;
|
||||
for (auto id : powerEntityIds) {
|
||||
auto dataProvider = mStateResidencyDataProviders.find(id);
|
||||
// skip if the given powerEntityId does not have an associated StateResidencyDataProvider
|
||||
if (dataProvider == mStateResidencyDataProviders.end()) {
|
||||
invalidInput = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// get the results if we have not already done so.
|
||||
if (stateResidencies.find(id) == stateResidencies.end()) {
|
||||
if (!dataProvider->second->getResults(stateResidencies)) {
|
||||
filesystemError = true;
|
||||
}
|
||||
}
|
||||
|
||||
// append results
|
||||
auto stateResidency = stateResidencies.find(id);
|
||||
if (stateResidency != stateResidencies.end()) {
|
||||
results.emplace_back(stateResidency->second);
|
||||
}
|
||||
}
|
||||
|
||||
auto ret = Status::SUCCESS;
|
||||
if (filesystemError) {
|
||||
ret = Status::FILESYSTEM_ERROR;
|
||||
} else if (invalidInput) {
|
||||
ret = Status::INVALID_INPUT;
|
||||
}
|
||||
|
||||
_hidl_cb(results, ret);
|
||||
return Void();
|
||||
}
|
||||
|
||||
bool DumpResidencyDataToFd(const hidl_vec<PowerEntityInfo>& infos,
|
||||
const hidl_vec<PowerEntityStateSpace>& stateSpaces,
|
||||
const hidl_vec<PowerEntityStateResidencyResult>& results, int fd) {
|
||||
// construct lookup table of powerEntityId to name
|
||||
std::unordered_map<uint32_t, std::string> entityNames;
|
||||
for (auto info : infos) {
|
||||
entityNames.emplace(info.powerEntityId, info.powerEntityName);
|
||||
}
|
||||
|
||||
// construct lookup table of powerEntityId, powerEntityStateId to state name
|
||||
std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> stateNames;
|
||||
for (auto stateSpace : stateSpaces) {
|
||||
stateNames.emplace(stateSpace.powerEntityId, std::unordered_map<uint32_t, std::string>());
|
||||
for (auto state : stateSpace.states) {
|
||||
stateNames.at(stateSpace.powerEntityId)
|
||||
.emplace(state.powerEntityStateId, state.powerEntityStateName);
|
||||
}
|
||||
}
|
||||
|
||||
std::ostringstream dumpStats;
|
||||
dumpStats << "\n========== PowerStats HAL 1.0 state residencies ==========\n";
|
||||
|
||||
const char* headerFormat = " %14s %14s %16s %15s %16s\n";
|
||||
const char* dataFormat =
|
||||
" %14s %14s %13" PRIu64 " ms %15" PRIu64 " %13" PRIu64 " ms\n";
|
||||
dumpStats << android::base::StringPrintf(headerFormat, "Entity", "State", "Total time",
|
||||
"Total entries", "Last entry timestamp");
|
||||
|
||||
for (auto result : results) {
|
||||
for (auto stateResidency : result.stateResidencyData) {
|
||||
dumpStats << android::base::StringPrintf(
|
||||
dataFormat, entityNames.at(result.powerEntityId).c_str(),
|
||||
stateNames.at(result.powerEntityId)
|
||||
.at(stateResidency.powerEntityStateId)
|
||||
.c_str(),
|
||||
stateResidency.totalTimeInStateMs, stateResidency.totalStateEntryCount,
|
||||
stateResidency.lastEntryTimestampMs);
|
||||
}
|
||||
}
|
||||
|
||||
dumpStats << "========== End of PowerStats HAL 1.0 state residencies ==========\n";
|
||||
|
||||
return android::base::WriteStringToFd(dumpStats.str(), fd);
|
||||
}
|
||||
|
||||
Return<void> PowerStats::debug(const hidl_handle& handle, const hidl_vec<hidl_string>&) {
|
||||
if (handle == nullptr || handle->numFds < 1) {
|
||||
return Void();
|
||||
}
|
||||
|
||||
int fd = handle->data[0];
|
||||
Status status;
|
||||
hidl_vec<PowerEntityInfo> infos;
|
||||
|
||||
// Get power entity information
|
||||
getPowerEntityInfo([&status, &infos](auto rInfos, auto rStatus) {
|
||||
status = rStatus;
|
||||
infos = rInfos;
|
||||
});
|
||||
if (status != Status::SUCCESS) {
|
||||
LOG(ERROR) << "Error getting power entity info";
|
||||
return Void();
|
||||
}
|
||||
|
||||
// Get power entity state information
|
||||
hidl_vec<PowerEntityStateSpace> stateSpaces;
|
||||
getPowerEntityStateInfo({}, [&status, &stateSpaces](auto rStateSpaces, auto rStatus) {
|
||||
status = rStatus;
|
||||
stateSpaces = rStateSpaces;
|
||||
});
|
||||
if (status != Status::SUCCESS) {
|
||||
LOG(ERROR) << "Error getting state info";
|
||||
return Void();
|
||||
}
|
||||
|
||||
// Get power entity state residency data
|
||||
hidl_vec<PowerEntityStateResidencyResult> results;
|
||||
_hidl_cb(results, Status::NOT_SUPPORTED);
|
||||
getPowerEntityStateResidencyData({}, [&status, &results](auto rResults, auto rStatus) {
|
||||
status = rStatus;
|
||||
results = rResults;
|
||||
});
|
||||
|
||||
// This implementation of getPowerEntityStateResidencyData supports the
|
||||
// return of partial results if status == FILESYSTEM_ERROR.
|
||||
if (status != Status::SUCCESS) {
|
||||
LOG(ERROR) << "Error getting residency data -- Some results missing";
|
||||
}
|
||||
|
||||
if (!DumpResidencyDataToFd(infos, stateSpaces, results, fd)) {
|
||||
PLOG(ERROR) << "Failed to dump residency data to fd";
|
||||
}
|
||||
|
||||
fsync(fd);
|
||||
|
||||
return Void();
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <fmq/MessageQueue.h>
|
||||
#include <hidl/MQDescriptor.h>
|
||||
#include <hidl/Status.h>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
@@ -60,8 +61,19 @@ struct OnDeviceMmt {
|
||||
std::unique_ptr<MessageQueueSync> fmqSynchronized;
|
||||
};
|
||||
|
||||
class IStateResidencyDataProvider {
|
||||
public:
|
||||
virtual ~IStateResidencyDataProvider() = default;
|
||||
virtual bool getResults(
|
||||
std::unordered_map<uint32_t, PowerEntityStateResidencyResult>& results) = 0;
|
||||
virtual std::vector<PowerEntityStateSpace> getStateSpaces() = 0;
|
||||
};
|
||||
|
||||
struct PowerStats : public IPowerStats {
|
||||
public:
|
||||
PowerStats();
|
||||
uint32_t addPowerEntity(const std::string& name, PowerEntityType type);
|
||||
void addStateResidencyDataProvider(std::shared_ptr<IStateResidencyDataProvider> p);
|
||||
// Methods from ::android::hardware::power::stats::V1_0::IPowerStats follow.
|
||||
Return<void> getRailInfo(getRailInfo_cb _hidl_cb) override;
|
||||
Return<void> getEnergyData(const hidl_vec<uint32_t>& railIndices,
|
||||
@@ -75,12 +87,19 @@ struct PowerStats : public IPowerStats {
|
||||
const hidl_vec<uint32_t>& powerEntityIds,
|
||||
getPowerEntityStateResidencyData_cb _hidl_cb) override;
|
||||
|
||||
// Methods from ::android::hidl::base::V1_0::IBase follow.
|
||||
Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args) override;
|
||||
|
||||
private:
|
||||
OnDeviceMmt mPm;
|
||||
void findIioPowerMonitorNodes();
|
||||
size_t parsePowerRails();
|
||||
int parseIioEnergyNode(std::string devName);
|
||||
Status parseIioEnergyNodes();
|
||||
std::vector<PowerEntityInfo> mPowerEntityInfos;
|
||||
std::unordered_map<uint32_t, PowerEntityStateSpace> mPowerEntityStateSpaces;
|
||||
std::unordered_map<uint32_t, std::shared_ptr<IStateResidencyDataProvider>>
|
||||
mStateResidencyDataProviders;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
|
||||
@@ -31,17 +31,69 @@ using android::hardware::joinRpcThreadpool;
|
||||
|
||||
// Generated HIDL files
|
||||
using android::hardware::power::stats::V1_0::IPowerStats;
|
||||
using android::hardware::power::stats::V1_0::PowerEntityStateResidencyResult;
|
||||
using android::hardware::power::stats::V1_0::PowerEntityStateSpace;
|
||||
using android::hardware::power::stats::V1_0::PowerEntityType;
|
||||
using android::hardware::power::stats::V1_0::implementation::IStateResidencyDataProvider;
|
||||
using android::hardware::power::stats::V1_0::implementation::PowerStats;
|
||||
|
||||
class DefaultStateResidencyDataProvider : public IStateResidencyDataProvider {
|
||||
public:
|
||||
DefaultStateResidencyDataProvider(uint32_t id)
|
||||
: mPowerEntityId(id), mActiveStateId(0), mSleepStateId(1) {}
|
||||
~DefaultStateResidencyDataProvider() = default;
|
||||
|
||||
bool getResults(std::unordered_map<uint32_t, PowerEntityStateResidencyResult>& results) {
|
||||
PowerEntityStateResidencyResult result = { .powerEntityId = mPowerEntityId };
|
||||
result.stateResidencyData.resize(2);
|
||||
|
||||
// Using fake numbers here for display only. A real implementation would
|
||||
// use actual tracked stats.
|
||||
result.stateResidencyData[0] = {
|
||||
.powerEntityStateId = mActiveStateId,
|
||||
.totalTimeInStateMs = 1,
|
||||
.totalStateEntryCount = 2,
|
||||
.lastEntryTimestampMs = 3
|
||||
};
|
||||
result.stateResidencyData[1] = {
|
||||
.powerEntityStateId = mSleepStateId,
|
||||
.totalTimeInStateMs = 4,
|
||||
.totalStateEntryCount = 5,
|
||||
.lastEntryTimestampMs = 6,
|
||||
};
|
||||
results.emplace(mPowerEntityId, result);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<PowerEntityStateSpace> getStateSpaces() {
|
||||
return {{
|
||||
.powerEntityId = mPowerEntityId,
|
||||
.states = {
|
||||
{.powerEntityStateId = mActiveStateId, .powerEntityStateName = "Active"},
|
||||
{.powerEntityStateId = mSleepStateId, .powerEntityStateName = "Sleep"}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
private:
|
||||
const uint32_t mPowerEntityId;
|
||||
const uint32_t mActiveStateId;
|
||||
const uint32_t mSleepStateId;
|
||||
};
|
||||
|
||||
int main(int /* argc */, char** /* argv */) {
|
||||
ALOGI("power.stats service 1.0 is starting.");
|
||||
|
||||
android::sp<IPowerStats> service = new PowerStats();
|
||||
PowerStats* service = new PowerStats();
|
||||
if (service == nullptr) {
|
||||
ALOGE("Can not create an instance of power.stats HAL Iface, exiting.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32_t defaultId = service->addPowerEntity("DefaultEntity", PowerEntityType::SUBSYSTEM);
|
||||
auto defaultSdp = std::make_shared<DefaultStateResidencyDataProvider>(defaultId);
|
||||
service->addStateResidencyDataProvider(std::move(defaultSdp));
|
||||
|
||||
configureRpcThreadpool(1, true /*callerWillJoin*/);
|
||||
|
||||
status_t status = service->registerAsService();
|
||||
|
||||
Reference in New Issue
Block a user