diff --git a/power/stats/1.0/default/Android.bp b/power/stats/1.0/default/Android.bp index 04270c14fe..b57466d18f 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 0000000000..2d95a97535 --- /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 810c575a87..350aa623ea 100644 --- a/power/stats/1.0/default/PowerStats.cpp +++ b/power/stats/1.0/default/PowerStats.cpp @@ -287,25 +287,218 @@ Return 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 p) { + std::vector stateSpaces = p->getStateSpaces(); + for (auto stateSpace : stateSpaces) { + mPowerEntityStateSpaces.emplace(stateSpace.powerEntityId, stateSpace); + mStateResidencyDataProviders.emplace(stateSpace.powerEntityId, p); + } +} + Return PowerStats::getPowerEntityInfo(getPowerEntityInfo_cb _hidl_cb) { - hidl_vec 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 PowerStats::getPowerEntityStateInfo(const hidl_vec& powerEntityIds, getPowerEntityStateInfo_cb _hidl_cb) { - (void)powerEntityIds; - hidl_vec 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 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 PowerStats::getPowerEntityStateResidencyData( - const hidl_vec& powerEntityIds, getPowerEntityStateResidencyData_cb _hidl_cb) { - (void)powerEntityIds; + const hidl_vec& 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 ids; + for (auto stateSpace : mPowerEntityStateSpaces) { + ids.emplace_back(stateSpace.first); + } + return getPowerEntityStateResidencyData(ids, _hidl_cb); + } + + std::unordered_map stateResidencies; + std::vector 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& infos, + const hidl_vec& stateSpaces, + const hidl_vec& results, int fd) { + // construct lookup table of powerEntityId to name + std::unordered_map entityNames; + for (auto info : infos) { + entityNames.emplace(info.powerEntityId, info.powerEntityName); + } + + // construct lookup table of powerEntityId, powerEntityStateId to state name + std::unordered_map> stateNames; + for (auto stateSpace : stateSpaces) { + stateNames.emplace(stateSpace.powerEntityId, std::unordered_map()); + 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 PowerStats::debug(const hidl_handle& handle, const hidl_vec&) { + if (handle == nullptr || handle->numFds < 1) { + return Void(); + } + + int fd = handle->data[0]; + Status status; + hidl_vec 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 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 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 fb2c6a8ce5..d67fb3508f 100644 --- a/power/stats/1.0/default/PowerStats.h +++ b/power/stats/1.0/default/PowerStats.h @@ -21,6 +21,7 @@ #include #include #include +#include namespace android { namespace hardware { @@ -60,8 +61,19 @@ struct OnDeviceMmt { std::unique_ptr fmqSynchronized; }; +class IStateResidencyDataProvider { + public: + virtual ~IStateResidencyDataProvider() = default; + virtual bool getResults( + std::unordered_map& results) = 0; + virtual std::vector getStateSpaces() = 0; +}; + struct PowerStats : public IPowerStats { + public: PowerStats(); + uint32_t addPowerEntity(const std::string& name, PowerEntityType type); + void addStateResidencyDataProvider(std::shared_ptr p); // Methods from ::android::hardware::power::stats::V1_0::IPowerStats follow. Return getRailInfo(getRailInfo_cb _hidl_cb) override; Return getEnergyData(const hidl_vec& railIndices, @@ -75,12 +87,19 @@ struct PowerStats : public IPowerStats { const hidl_vec& powerEntityIds, getPowerEntityStateResidencyData_cb _hidl_cb) override; + // Methods from ::android::hidl::base::V1_0::IBase follow. + Return debug(const hidl_handle& fd, const hidl_vec& args) override; + private: OnDeviceMmt mPm; void findIioPowerMonitorNodes(); size_t parsePowerRails(); int parseIioEnergyNode(std::string devName); Status parseIioEnergyNodes(); + std::vector mPowerEntityInfos; + std::unordered_map mPowerEntityStateSpaces; + std::unordered_map> + mStateResidencyDataProviders; }; } // namespace implementation diff --git a/power/stats/1.0/default/service.cpp b/power/stats/1.0/default/service.cpp index 80649f5c5e..a516536d15 100644 --- a/power/stats/1.0/default/service.cpp +++ b/power/stats/1.0/default/service.cpp @@ -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& 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 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 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(defaultId); + service->addStateResidencyDataProvider(std::move(defaultSdp)); + configureRpcThreadpool(1, true /*callerWillJoin*/); status_t status = service->registerAsService();