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
diff --git a/power/stats/1.0/default/Android.bp b/power/stats/1.0/default/Android.bp
index 04270c1..b57466d 100644
--- a/power/stats/1.0/default/Android.bp
+++ b/power/stats/1.0/default/Android.bp
@@ -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"],
diff --git a/power/stats/1.0/default/OWNERS b/power/stats/1.0/default/OWNERS
new file mode 100644
index 0000000..2d95a97
--- /dev/null
+++ b/power/stats/1.0/default/OWNERS
@@ -0,0 +1,3 @@
+krossmo@google.com
+bsschwar@google.com
+tstrudel@google.com
diff --git a/power/stats/1.0/default/PowerStats.cpp b/power/stats/1.0/default/PowerStats.cpp
index 810c575..350aa62 100644
--- a/power/stats/1.0/default/PowerStats.cpp
+++ b/power/stats/1.0/default/PowerStats.cpp
@@ -287,25 +287,218 @@
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();
}
diff --git a/power/stats/1.0/default/PowerStats.h b/power/stats/1.0/default/PowerStats.h
index fb2c6a8..d67fb35 100644
--- a/power/stats/1.0/default/PowerStats.h
+++ b/power/stats/1.0/default/PowerStats.h
@@ -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 @@
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 @@
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
diff --git a/power/stats/1.0/default/service.cpp b/power/stats/1.0/default/service.cpp
index 80649f5..a516536 100644
--- a/power/stats/1.0/default/service.cpp
+++ b/power/stats/1.0/default/service.cpp
@@ -31,17 +31,69 @@
// 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();