1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "libpixelpowerstats"
18
19 #include <memory>
20 #include <string>
21 #include <unordered_map>
22 #include <utility>
23 #include <vector>
24
25 #include <pixelpowerstats/PowerStats.h>
26
27 #include <android-base/file.h>
28 #include <android-base/logging.h>
29 #include <android-base/stringprintf.h>
30
31 #include <inttypes.h>
32 #include <time.h>
33
34 namespace android {
35 namespace hardware {
36 namespace power {
37 namespace stats {
38 namespace V1_0 {
39 namespace implementation {
40
setRailDataProvider(std::unique_ptr<IRailDataProvider> dataProvider)41 void PowerStats::setRailDataProvider(std::unique_ptr<IRailDataProvider> dataProvider) {
42 mRailDataProvider = std::move(dataProvider);
43 }
44
getRailInfo(getRailInfo_cb _hidl_cb)45 Return<void> PowerStats::getRailInfo(getRailInfo_cb _hidl_cb) {
46 if (!mRailDataProvider) {
47 _hidl_cb({}, Status::NOT_SUPPORTED);
48 return Void();
49 }
50
51 return mRailDataProvider->getRailInfo(_hidl_cb);
52 }
53
getEnergyData(const hidl_vec<uint32_t> & railIndices,getEnergyData_cb _hidl_cb)54 Return<void> PowerStats::getEnergyData(const hidl_vec<uint32_t> &railIndices,
55 getEnergyData_cb _hidl_cb) {
56 if (!mRailDataProvider) {
57 _hidl_cb({}, Status::NOT_SUPPORTED);
58 return Void();
59 }
60
61 return mRailDataProvider->getEnergyData(railIndices, _hidl_cb);
62 }
63
streamEnergyData(uint32_t timeMs,uint32_t samplingRate,streamEnergyData_cb _hidl_cb)64 Return<void> PowerStats::streamEnergyData(uint32_t timeMs, uint32_t samplingRate,
65 streamEnergyData_cb _hidl_cb) {
66 if (!mRailDataProvider) {
67 _hidl_cb({}, 0, 0, Status::NOT_SUPPORTED);
68 return Void();
69 }
70
71 return mRailDataProvider->streamEnergyData(timeMs, samplingRate, _hidl_cb);
72 }
73
addPowerEntity(const std::string & name,PowerEntityType type)74 uint32_t PowerStats::addPowerEntity(const std::string &name, PowerEntityType type) {
75 uint32_t id = mPowerEntityInfos.size();
76 mPowerEntityInfos.push_back({id, name, type});
77 return id;
78 }
79
addStateResidencyDataProvider(sp<IStateResidencyDataProvider> p)80 void PowerStats::addStateResidencyDataProvider(sp<IStateResidencyDataProvider> p) {
81 std::vector<PowerEntityStateSpace> stateSpaces = p->getStateSpaces();
82 for (auto stateSpace : stateSpaces) {
83 mPowerEntityStateSpaces.emplace(stateSpace.powerEntityId, stateSpace);
84 mStateResidencyDataProviders.emplace(stateSpace.powerEntityId, p);
85 }
86 }
87
getPowerEntityInfo(getPowerEntityInfo_cb _hidl_cb)88 Return<void> PowerStats::getPowerEntityInfo(getPowerEntityInfo_cb _hidl_cb) {
89 // If not configured, return NOT_SUPPORTED
90 if (mPowerEntityInfos.empty()) {
91 _hidl_cb({}, Status::NOT_SUPPORTED);
92 return Void();
93 }
94
95 _hidl_cb(mPowerEntityInfos, Status::SUCCESS);
96 return Void();
97 }
98
getPowerEntityStateInfo(const hidl_vec<uint32_t> & powerEntityIds,getPowerEntityStateInfo_cb _hidl_cb)99 Return<void> PowerStats::getPowerEntityStateInfo(const hidl_vec<uint32_t> &powerEntityIds,
100 getPowerEntityStateInfo_cb _hidl_cb) {
101 // If not configured, return NOT_SUPPORTED
102 if (mPowerEntityStateSpaces.empty()) {
103 _hidl_cb({}, Status::NOT_SUPPORTED);
104 return Void();
105 }
106
107 std::vector<PowerEntityStateSpace> stateSpaces;
108
109 // If powerEntityIds is empty then return state space info for all entities
110 if (powerEntityIds.size() == 0) {
111 stateSpaces.reserve(mPowerEntityStateSpaces.size());
112 for (auto i : mPowerEntityStateSpaces) {
113 stateSpaces.emplace_back(i.second);
114 }
115 _hidl_cb(stateSpaces, Status::SUCCESS);
116 return Void();
117 }
118
119 // Return state space information only for valid ids
120 auto ret = Status::SUCCESS;
121 stateSpaces.reserve(powerEntityIds.size());
122 for (const uint32_t id : powerEntityIds) {
123 auto stateSpace = mPowerEntityStateSpaces.find(id);
124 if (stateSpace != mPowerEntityStateSpaces.end()) {
125 stateSpaces.emplace_back(stateSpace->second);
126 } else {
127 ret = Status::INVALID_INPUT;
128 }
129 }
130
131 _hidl_cb(stateSpaces, ret);
132 return Void();
133 }
134
getPowerEntityStateResidencyData(const hidl_vec<uint32_t> & powerEntityIds,getPowerEntityStateResidencyData_cb _hidl_cb)135 Return<void> PowerStats::getPowerEntityStateResidencyData(
136 const hidl_vec<uint32_t> &powerEntityIds, getPowerEntityStateResidencyData_cb _hidl_cb) {
137 // If not configured, return NOT_SUPPORTED
138 if (mStateResidencyDataProviders.empty() || mPowerEntityStateSpaces.empty()) {
139 _hidl_cb({}, Status::NOT_SUPPORTED);
140 return Void();
141 }
142
143 // If powerEntityIds is empty then return data for all supported entities
144 if (powerEntityIds.size() == 0) {
145 std::vector<uint32_t> ids;
146 for (auto stateSpace : mPowerEntityStateSpaces) {
147 ids.emplace_back(stateSpace.first);
148 }
149 return getPowerEntityStateResidencyData(ids, _hidl_cb);
150 }
151
152 std::unordered_map<uint32_t, PowerEntityStateResidencyResult> stateResidencies;
153 std::vector<PowerEntityStateResidencyResult> results;
154 results.reserve(powerEntityIds.size());
155
156 // return results for only the given powerEntityIds
157 bool invalidInput = false;
158 bool filesystemError = false;
159 for (auto id : powerEntityIds) {
160 auto dataProvider = mStateResidencyDataProviders.find(id);
161 // skip if the given powerEntityId does not have an associated StateResidencyDataProvider
162 if (dataProvider == mStateResidencyDataProviders.end()) {
163 invalidInput = true;
164 continue;
165 }
166
167 // get the results if we have not already done so.
168 if (stateResidencies.find(id) == stateResidencies.end()) {
169 if (!dataProvider->second->getResults(stateResidencies)) {
170 filesystemError = true;
171 }
172 }
173
174 // append results
175 auto stateResidency = stateResidencies.find(id);
176 if (stateResidency != stateResidencies.end()) {
177 results.emplace_back(stateResidency->second);
178 }
179 }
180
181 auto ret = Status::SUCCESS;
182 if (filesystemError) {
183 ret = Status::FILESYSTEM_ERROR;
184 } else if (invalidInput) {
185 ret = Status::INVALID_INPUT;
186 }
187
188 _hidl_cb(results, ret);
189 return Void();
190 }
191
192 //
193 // Debugging utilities to support printing data via debug()
194 //
195
getTimeElapsedMs(const struct timespec & now,const struct timespec & then)196 static uint64_t getTimeElapsedMs(const struct timespec &now, const struct timespec &then) {
197 uint64_t thenMs = then.tv_sec * 1000 + (then.tv_nsec / 1000000);
198 uint64_t nowMs = now.tv_sec * 1000 + (now.tv_nsec / 1000000);
199 return (nowMs - thenMs);
200 }
201
202 static const char RESIDENCY_HEADER[] =
203 "\n============= PowerStats HAL 1.0 state residencies ==============\n";
204 static const char RESIDENCY_FOOTER[] =
205 "========== End of PowerStats HAL 1.0 state residencies ==========\n";
206
DumpResidencyDataToFd(const std::unordered_map<uint32_t,std::string> & entityNames,const std::unordered_map<uint32_t,std::unordered_map<uint32_t,std::string>> stateNames,const hidl_vec<PowerEntityStateResidencyResult> & results,int fd)207 static bool DumpResidencyDataToFd(
208 const std::unordered_map<uint32_t, std::string> &entityNames,
209 const std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> stateNames,
210 const hidl_vec<PowerEntityStateResidencyResult> &results, int fd) {
211 std::ostringstream dumpStats;
212 const char *headerFormat = " %14s %14s %16s %15s %17s\n";
213 const char *dataFormat =
214 " %14s %14s %13" PRIu64 " ms %15" PRIu64 " %14" PRIu64 " ms\n";
215
216 dumpStats << RESIDENCY_HEADER;
217 dumpStats << android::base::StringPrintf(headerFormat, "Entity", "State", "Total time",
218 "Total entries", "Last entry tstamp");
219
220 for (auto result : results) {
221 for (auto stateResidency : result.stateResidencyData) {
222 dumpStats << android::base::StringPrintf(
223 dataFormat, entityNames.at(result.powerEntityId).c_str(),
224 stateNames.at(result.powerEntityId)
225 .at(stateResidency.powerEntityStateId).c_str(),
226 stateResidency.totalTimeInStateMs, stateResidency.totalStateEntryCount,
227 stateResidency.lastEntryTimestampMs);
228 }
229 }
230
231 dumpStats << RESIDENCY_FOOTER;
232
233 return android::base::WriteStringToFd(dumpStats.str(), fd);
234 }
235
DumpResidencyDataDiffToFd(const std::unordered_map<uint32_t,std::string> & entityNames,const std::unordered_map<uint32_t,std::unordered_map<uint32_t,std::string>> stateNames,uint64_t elapsedTimeMs,const hidl_vec<PowerEntityStateResidencyResult> & prevResults,const hidl_vec<PowerEntityStateResidencyResult> & results,int fd)236 static bool DumpResidencyDataDiffToFd(
237 const std::unordered_map<uint32_t, std::string> &entityNames,
238 const std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> stateNames,
239 uint64_t elapsedTimeMs, const hidl_vec<PowerEntityStateResidencyResult> &prevResults,
240 const hidl_vec<PowerEntityStateResidencyResult> &results, int fd) {
241 std::ostringstream dumpStats;
242 const char *headerFormat = " %14s %14s %16s (%14s) %15s (%16s) %17s (%14s)\n";
243 const char *dataFormatWithDelta =
244 " %14s %14s %13" PRIu64 " ms (%14" PRId64 ") %15" PRIu64 " (%16" PRId64 ")"
245 " %14" PRIu64 " ms (%14" PRId64 ")\n";
246 const char *dataFormatWithoutDelta =
247 " %14s %14s %13" PRIu64 " ms ( none) %15" PRIu64 " ( none)"
248 " %14" PRIu64 " ms ( none)\n";
249
250 dumpStats << RESIDENCY_HEADER;
251 dumpStats << "Elapsed time: "
252 << (elapsedTimeMs == 0 ? "unknown" : std::to_string(elapsedTimeMs)) << " ms\n";
253
254 dumpStats << android::base::StringPrintf(headerFormat, "Entity", "State", "Total time",
255 "Delta ", "Total entries", "Delta ",
256 "Last entry tstamp", "Delta ");
257
258 // Process prevResults into a 2-tier lookup table for easy reference
259 std::unordered_map<uint32_t, std::unordered_map<uint32_t, PowerEntityStateResidencyData>>
260 prevResultsMap;
261 for (auto prevResult : prevResults) {
262 prevResultsMap.emplace(prevResult.powerEntityId,
263 std::unordered_map<uint32_t, PowerEntityStateResidencyData>());
264 for (auto stateResidency : prevResult.stateResidencyData) {
265 prevResultsMap.at(prevResult.powerEntityId)
266 .emplace(stateResidency.powerEntityStateId, stateResidency);
267 }
268 }
269
270 // Iterate over the new result data (one "result" per entity)
271 for (auto result : results) {
272 uint32_t entityId = result.powerEntityId;
273 const char *entityName = entityNames.at(entityId).c_str();
274
275 // Look up previous result data for the same entity
276 auto prevEntityResultIt = prevResultsMap.find(entityId);
277
278 // Iterate over individual states within the current entity's new result
279 for (auto stateResidency : result.stateResidencyData) {
280 uint32_t stateId = stateResidency.powerEntityStateId;
281 const char *stateName = stateNames.at(entityId).at(stateId).c_str();
282
283 // If a previous result was found for the same entity, see if that
284 // result also contains data for the current state
285 bool prevValueFound = false;
286 if (prevEntityResultIt != prevResultsMap.end()) {
287 auto prevStateResidencyIt = prevEntityResultIt->second.find(stateId);
288 // If a previous result was found for the current entity and state, calculate the
289 // deltas and display them along with new result
290 if (prevStateResidencyIt != prevEntityResultIt->second.end()) {
291 int64_t deltaTotalTime = stateResidency.totalTimeInStateMs -
292 prevStateResidencyIt->second.totalTimeInStateMs;
293 int64_t deltaTotalCount = stateResidency.totalStateEntryCount -
294 prevStateResidencyIt->second.totalStateEntryCount;
295 int64_t deltaTimestamp = stateResidency.lastEntryTimestampMs -
296 prevStateResidencyIt->second.lastEntryTimestampMs;
297
298 dumpStats << android::base::StringPrintf(
299 dataFormatWithDelta, entityName, stateName,
300 stateResidency.totalTimeInStateMs, deltaTotalTime,
301 stateResidency.totalStateEntryCount, deltaTotalCount,
302 stateResidency.lastEntryTimestampMs, deltaTimestamp);
303 prevValueFound = true;
304 }
305 }
306
307 // If no previous result was found for the current entity and state, display the new
308 // result without deltas
309 if (!prevValueFound) {
310 dumpStats << android::base::StringPrintf(
311 dataFormatWithoutDelta, entityName, stateName,
312 stateResidency.totalTimeInStateMs, stateResidency.totalStateEntryCount,
313 stateResidency.lastEntryTimestampMs);
314 }
315 }
316 }
317
318 dumpStats << RESIDENCY_FOOTER;
319
320 return android::base::WriteStringToFd(dumpStats.str(), fd);
321 }
322
debugStateResidency(const std::unordered_map<uint32_t,std::string> & entityNames,int fd,bool delta)323 void PowerStats::debugStateResidency(const std::unordered_map<uint32_t, std::string> &entityNames,
324 int fd, bool delta) {
325 static struct timespec prevDataTime;
326 static bool prevDataTimeValid = false;
327 struct timespec dataTime;
328 bool dataTimeValid;
329
330 // Get power entity state space information
331 Status status;
332 hidl_vec<PowerEntityStateSpace> stateSpaces;
333 getPowerEntityStateInfo({}, [&status, &stateSpaces](auto rStateSpaces, auto rStatus) {
334 status = rStatus;
335 stateSpaces = rStateSpaces;
336 });
337 if (status != Status::SUCCESS) {
338 LOG(ERROR) << "Error getting state info";
339 return;
340 }
341
342 // Construct lookup table of powerEntityId, powerEntityStateId to state name
343 std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> stateNames;
344 for (auto stateSpace : stateSpaces) {
345 stateNames.emplace(stateSpace.powerEntityId, std::unordered_map<uint32_t, std::string>());
346 auto &entityStateNames = stateNames.at(stateSpace.powerEntityId);
347 for (auto state : stateSpace.states) {
348 entityStateNames.emplace(state.powerEntityStateId, state.powerEntityStateName);
349 }
350 }
351
352 // Get power entity state residency data
353 hidl_vec<PowerEntityStateResidencyResult> results;
354 getPowerEntityStateResidencyData(
355 {}, [&status, &results, &dataTime, &dataTimeValid](auto rResults, auto rStatus) {
356 status = rStatus;
357 results = rResults;
358 dataTimeValid = (clock_gettime(CLOCK_BOOTTIME, &dataTime) == 0);
359 });
360
361 // This implementation of getPowerEntityStateResidencyData supports the
362 // return of partial results if status == FILESYSTEM_ERROR.
363 if (status != Status::SUCCESS) {
364 LOG(ERROR) << "Error getting residency data -- Some results missing";
365 }
366
367 if (!delta) {
368 // If no delta argument was supplied, just dump the latest data
369 if (!DumpResidencyDataToFd(entityNames, stateNames, results, fd)) {
370 PLOG(ERROR) << "Failed to dump residency data to fd";
371 }
372 } else {
373 // If the delta argument was supplied, calculate the elapsed time since the previous
374 // result and then dump the latest data along with elapsed time and deltas
375 static hidl_vec<PowerEntityStateResidencyResult> prevResults;
376 uint64_t elapsedTimeMs = 0;
377 if (dataTimeValid && prevDataTimeValid) {
378 elapsedTimeMs = getTimeElapsedMs(dataTime, prevDataTime);
379 }
380
381 if (!DumpResidencyDataDiffToFd(entityNames, stateNames, elapsedTimeMs, prevResults,
382 results, fd)) {
383 PLOG(ERROR) << "Failed to dump residency data delta to fd";
384 }
385
386 prevResults = results;
387 prevDataTime = dataTime;
388 prevDataTimeValid = dataTimeValid;
389 }
390 }
391
392 static const char ENERGYDATA_HEADER[] =
393 "\n============= PowerStats HAL 1.0 rail energy data ==============\n";
394 static const char ENERGYDATA_FOOTER[] =
395 "========== End of PowerStats HAL 1.0 rail energy data ==========\n";
396
DumpEnergyDataToFd(const std::unordered_map<uint32_t,std::pair<std::string,std::string>> & railNames,const hidl_vec<EnergyData> & energyData,int fd)397 static bool DumpEnergyDataToFd(
398 const std::unordered_map<uint32_t, std::pair<std::string, std::string>> &railNames,
399 const hidl_vec<EnergyData> &energyData, int fd) {
400 std::ostringstream dumpStats;
401 const char *headerFormat = " %14s %18s %18s\n";
402 const char *dataFormat = " %14s %18s %14.2f mWs\n";
403
404 dumpStats << ENERGYDATA_HEADER;
405 dumpStats << android::base::StringPrintf(headerFormat, "Subsys", "Rail", "Cumulative Energy");
406
407 for (auto data : energyData) {
408 dumpStats << android::base::StringPrintf(dataFormat, railNames.at(data.index).first.c_str(),
409 railNames.at(data.index).second.c_str(),
410 static_cast<float>(data.energy) / 1000.0);
411 }
412
413 dumpStats << ENERGYDATA_FOOTER;
414
415 return android::base::WriteStringToFd(dumpStats.str(), fd);
416 }
417
DumpEnergyDataDiffToFd(const std::unordered_map<uint32_t,std::pair<std::string,std::string>> & railNames,uint64_t elapsedTimeMs,const hidl_vec<EnergyData> & prevEnergyData,const hidl_vec<EnergyData> & energyData,int fd)418 static bool DumpEnergyDataDiffToFd(
419 const std::unordered_map<uint32_t, std::pair<std::string, std::string>> &railNames,
420 uint64_t elapsedTimeMs, const hidl_vec<EnergyData> &prevEnergyData,
421 const hidl_vec<EnergyData> &energyData, int fd) {
422 std::ostringstream dumpStats;
423 const char *headerFormat = " %14s %18s %18s (%14s)\n";
424 const char *dataFormatWithDelta = " %14s %18s %14.2f mWs (%14.2f)\n";
425 const char *dataFormatWithoutDelta = " %14s %18s %14.2f mWs ( none)\n";
426
427 dumpStats << ENERGYDATA_HEADER;
428 dumpStats << "Elapsed time: "
429 << (elapsedTimeMs == 0 ? "unknown" : std::to_string(elapsedTimeMs)) << " ms\n";
430
431 dumpStats << android::base::StringPrintf(headerFormat, "Subsys", "Rail", "Cumulative Energy",
432 "Delta ");
433
434 std::unordered_map<uint32_t, uint64_t> prevEnergyDataMap;
435 for (auto data : prevEnergyData) {
436 prevEnergyDataMap.emplace(data.index, data.energy);
437 }
438
439 for (auto data : energyData) {
440 const char *subsysName = railNames.at(data.index).first.c_str();
441 const char *railName = railNames.at(data.index).second.c_str();
442
443 auto prevEnergyDataIt = prevEnergyDataMap.find(data.index);
444
445 if (prevEnergyDataIt != prevEnergyDataMap.end()) {
446 int64_t deltaEnergy = data.energy - prevEnergyDataIt->second;
447
448 dumpStats << android::base::StringPrintf(dataFormatWithDelta, subsysName, railName,
449 static_cast<float>(data.energy) / 1000.0,
450 static_cast<float>(deltaEnergy) / 1000.0);
451 } else {
452 dumpStats << android::base::StringPrintf(dataFormatWithoutDelta, subsysName, railName,
453 static_cast<float>(data.energy) / 1000.0);
454 }
455 }
456
457 dumpStats << ENERGYDATA_FOOTER;
458
459 return android::base::WriteStringToFd(dumpStats.str(), fd);
460 }
461
debugEnergyData(int fd,bool delta)462 void PowerStats::debugEnergyData(int fd, bool delta) {
463 static struct timespec prevDataTime;
464 static bool prevDataTimeValid = false;
465 struct timespec dataTime;
466 bool dataTimeValid = false;
467
468 std::unordered_map<uint32_t, std::pair<std::string, std::string>> railNames;
469 getRailInfo([&railNames](auto infos, auto /* status */) {
470 // Don't care about the status. infos will be nonempty if rail energy is supported.
471 for (auto info : infos) {
472 railNames.emplace(info.index, std::make_pair(info.subsysName, info.railName));
473 }
474 });
475 if (railNames.empty()) {
476 return;
477 }
478
479 Status status;
480 hidl_vec<EnergyData> energyData;
481 getEnergyData(
482 {}, [&status, &energyData, &dataTime, &dataTimeValid](auto rEnergyData, auto rStatus) {
483 status = rStatus;
484 energyData = rEnergyData;
485 dataTimeValid = (clock_gettime(CLOCK_BOOTTIME, &dataTime) == 0);
486 });
487
488 // getEnergyData returns no results if status != SUCCESS.
489 if (status != Status::SUCCESS) {
490 LOG(ERROR) << "Error getting rail data";
491 return;
492 }
493
494 if (!delta) {
495 if (!DumpEnergyDataToFd(railNames, energyData, fd)) {
496 PLOG(ERROR) << "Failed to dump energy data to fd";
497 }
498 } else {
499 // If the delta argument was supplied, calculate the elapsed time since the previous
500 // result and then dump the latest data along with elapsed time and deltas
501 static hidl_vec<EnergyData> prevEnergyData;
502 uint64_t elapsedTimeMs = 0;
503 if (dataTimeValid && prevDataTimeValid) {
504 elapsedTimeMs = getTimeElapsedMs(dataTime, prevDataTime);
505 }
506
507 if (!DumpEnergyDataDiffToFd(railNames, elapsedTimeMs, prevEnergyData, energyData, fd)) {
508 PLOG(ERROR) << "Failed to dump energy data delta to fd";
509 }
510
511 prevEnergyData = energyData;
512 prevDataTime = dataTime;
513 prevDataTimeValid = dataTimeValid;
514 }
515 }
516
debug(const hidl_handle & handle,const hidl_vec<hidl_string> & args)517 Return<void> PowerStats::debug(const hidl_handle &handle, const hidl_vec<hidl_string> &args) {
518 if (handle == nullptr || handle->numFds < 1) {
519 return Void();
520 }
521
522 int fd = handle->data[0];
523 bool delta = (args.size() == 1) && (args[0] == "delta");
524
525 // Get power entity information, which is common across all supported data categories
526 Status status;
527 hidl_vec<PowerEntityInfo> stateInfos;
528 getPowerEntityInfo([&status, &stateInfos](auto rInfos, auto rStatus) {
529 status = rStatus;
530 stateInfos = rInfos;
531 });
532 if (status != Status::SUCCESS) {
533 LOG(ERROR) << "Error getting power entity info";
534 return Void();
535 }
536
537 // Construct lookup table of powerEntityId to name
538 std::unordered_map<uint32_t, std::string> entityNames;
539 for (auto info : stateInfos) {
540 entityNames.emplace(info.powerEntityId, info.powerEntityName);
541 }
542
543 // Generate debug output for supported data categories
544 debugStateResidency(entityNames, fd, delta);
545
546 // Generate debug output for energy data
547 debugEnergyData(fd, delta);
548
549 fsync(fd);
550 return Void();
551 }
552
553 } // namespace implementation
554 } // namespace V1_0
555 } // namespace stats
556 } // namespace power
557 } // namespace hardware
558 } // namespace android
559