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 "android.hardware.power.stats@1.0-service-mock"
18 
19 #include "PowerStats.h"
20 #include <android-base/file.h>
21 #include <android-base/logging.h>
22 #include <android-base/properties.h>
23 #include <android-base/stringprintf.h>
24 #include <android-base/strings.h>
25 #include <inttypes.h>
26 #include <stdlib.h>
27 #include <algorithm>
28 #include <exception>
29 #include <thread>
30 
31 namespace android {
32 namespace hardware {
33 namespace power {
34 namespace stats {
35 namespace V1_0 {
36 namespace implementation {
37 
38 #define MAX_FILE_PATH_LEN 128
39 #define MAX_DEVICE_NAME_LEN 64
40 #define MAX_QUEUE_SIZE 8192
41 
42 constexpr char kIioDirRoot[] = "/sys/bus/iio/devices/";
43 constexpr char kDeviceName[] = "pm_device_name";
44 constexpr char kDeviceType[] = "iio:device";
45 constexpr uint32_t MAX_SAMPLING_RATE = 10;
46 constexpr uint64_t WRITE_TIMEOUT_NS = 1000000000;
47 
findIioPowerMonitorNodes()48 void PowerStats::findIioPowerMonitorNodes() {
49     struct dirent* ent;
50     int fd;
51     char devName[MAX_DEVICE_NAME_LEN];
52     char filePath[MAX_FILE_PATH_LEN];
53     DIR* iioDir = opendir(kIioDirRoot);
54     if (!iioDir) {
55         ALOGE("Error opening directory: %s", kIioDirRoot);
56         return;
57     }
58     while (ent = readdir(iioDir), ent) {
59         if (strcmp(ent->d_name, ".") != 0 && strcmp(ent->d_name, "..") != 0 &&
60             strlen(ent->d_name) > strlen(kDeviceType) &&
61             strncmp(ent->d_name, kDeviceType, strlen(kDeviceType)) == 0) {
62             snprintf(filePath, MAX_FILE_PATH_LEN, "%s/%s", ent->d_name, "name");
63             fd = openat(dirfd(iioDir), filePath, O_RDONLY);
64             if (fd < 0) {
65                 ALOGW("Failed to open directory: %s", filePath);
66                 continue;
67             }
68             if (read(fd, devName, MAX_DEVICE_NAME_LEN) < 0) {
69                 ALOGW("Failed to read device name from file: %s(%d)", filePath, fd);
70                 close(fd);
71                 continue;
72             }
73 
74             if (strncmp(devName, kDeviceName, strlen(kDeviceName)) == 0) {
75                 snprintf(filePath, MAX_FILE_PATH_LEN, "%s/%s", kIioDirRoot, ent->d_name);
76                 mPm.devicePaths.push_back(filePath);
77             }
78             close(fd);
79         }
80     }
81     closedir(iioDir);
82     return;
83 }
84 
parsePowerRails()85 size_t PowerStats::parsePowerRails() {
86     std::string data;
87     std::string railFileName;
88     std::string spsFileName;
89     uint32_t index = 0;
90     unsigned long samplingRate;
91     for (const auto& path : mPm.devicePaths) {
92         railFileName = path + "/enabled_rails";
93         spsFileName = path + "/sampling_rate";
94         if (!android::base::ReadFileToString(spsFileName, &data)) {
95             ALOGW("Error reading file: %s", spsFileName.c_str());
96             continue;
97         }
98         samplingRate = strtoul(data.c_str(), NULL, 10);
99         if (!samplingRate || samplingRate == ULONG_MAX) {
100             ALOGE("Error parsing: %s", spsFileName.c_str());
101             break;
102         }
103         if (!android::base::ReadFileToString(railFileName, &data)) {
104             ALOGW("Error reading file: %s", railFileName.c_str());
105             continue;
106         }
107         std::istringstream railNames(data);
108         std::string line;
109         while (std::getline(railNames, line)) {
110             std::vector<std::string> words = android::base::Split(line, ":");
111             if (words.size() == 2) {
112                 mPm.railsInfo.emplace(
113                         words[0], RailData{.devicePath = path,
114                                            .index = index,
115                                            .subsysName = words[1],
116                                            .samplingRate = static_cast<uint32_t>(samplingRate)});
117                 index++;
118             } else {
119                 ALOGW("Unexpected format in file: %s", railFileName.c_str());
120             }
121         }
122     }
123     return index;
124 }
125 
parseIioEnergyNode(std::string devName)126 int PowerStats::parseIioEnergyNode(std::string devName) {
127     int ret = 0;
128     std::string data;
129     std::string fileName = devName + "/energy_value";
130     if (!android::base::ReadFileToString(fileName, &data)) {
131         ALOGE("Error reading file: %s", fileName.c_str());
132         return -1;
133     }
134 
135     std::istringstream energyData(data);
136     std::string line;
137     uint64_t timestamp = 0;
138     bool timestampRead = false;
139     while (std::getline(energyData, line)) {
140         std::vector<std::string> words = android::base::Split(line, ",");
141         if (timestampRead == false) {
142             if (words.size() == 1) {
143                 timestamp = strtoull(words[0].c_str(), NULL, 10);
144                 if (timestamp == 0 || timestamp == ULLONG_MAX) {
145                     ALOGW("Potentially wrong timestamp: %" PRIu64, timestamp);
146                 }
147                 timestampRead = true;
148             }
149         } else if (words.size() == 2) {
150             std::string railName = words[0];
151             if (mPm.railsInfo.count(railName) != 0) {
152                 size_t index = mPm.railsInfo[railName].index;
153                 mPm.reading[index].index = index;
154                 mPm.reading[index].timestamp = timestamp;
155                 mPm.reading[index].energy = strtoull(words[1].c_str(), NULL, 10);
156                 if (mPm.reading[index].energy == ULLONG_MAX) {
157                     ALOGW("Potentially wrong energy value: %" PRIu64, mPm.reading[index].energy);
158                 }
159             }
160         } else {
161             ALOGW("Unexpected format in file: %s", fileName.c_str());
162             ret = -1;
163             break;
164         }
165     }
166     return ret;
167 }
168 
parseIioEnergyNodes()169 Status PowerStats::parseIioEnergyNodes() {
170     Status ret = Status::SUCCESS;
171     if (mPm.hwEnabled == false) {
172         return Status::NOT_SUPPORTED;
173     }
174 
175     for (const auto& devicePath : mPm.devicePaths) {
176         if (parseIioEnergyNode(devicePath) < 0) {
177             ALOGE("Error in parsing power stats");
178             ret = Status::FILESYSTEM_ERROR;
179             break;
180         }
181     }
182     return ret;
183 }
184 
PowerStats()185 PowerStats::PowerStats() {
186     findIioPowerMonitorNodes();
187     size_t numRails = parsePowerRails();
188     if (mPm.devicePaths.empty() || numRails == 0) {
189         mPm.hwEnabled = false;
190     } else {
191         mPm.hwEnabled = true;
192         mPm.reading.resize(numRails);
193     }
194 }
195 
getRailInfo(getRailInfo_cb _hidl_cb)196 Return<void> PowerStats::getRailInfo(getRailInfo_cb _hidl_cb) {
197     hidl_vec<RailInfo> rInfo;
198     Status ret = Status::SUCCESS;
199     size_t index;
200     std::lock_guard<std::mutex> _lock(mPm.mLock);
201     if (mPm.hwEnabled == false) {
202         _hidl_cb(rInfo, Status::NOT_SUPPORTED);
203         return Void();
204     }
205     rInfo.resize(mPm.railsInfo.size());
206     for (const auto& railData : mPm.railsInfo) {
207         index = railData.second.index;
208         rInfo[index].railName = railData.first;
209         rInfo[index].subsysName = railData.second.subsysName;
210         rInfo[index].index = index;
211         rInfo[index].samplingRate = railData.second.samplingRate;
212     }
213     _hidl_cb(rInfo, ret);
214     return Void();
215 }
216 
getEnergyData(const hidl_vec<uint32_t> & railIndices,getEnergyData_cb _hidl_cb)217 Return<void> PowerStats::getEnergyData(const hidl_vec<uint32_t>& railIndices,
218                                        getEnergyData_cb _hidl_cb) {
219     hidl_vec<EnergyData> eVal;
220     std::lock_guard<std::mutex> _lock(mPm.mLock);
221     Status ret = parseIioEnergyNodes();
222 
223     if (ret != Status::SUCCESS) {
224         ALOGE("Failed to getEnergyData");
225         _hidl_cb(eVal, ret);
226         return Void();
227     }
228 
229     if (railIndices.size() == 0) {
230         eVal.resize(mPm.railsInfo.size());
231         memcpy(&eVal[0], &mPm.reading[0], mPm.reading.size() * sizeof(EnergyData));
232     } else {
233         eVal.resize(railIndices.size());
234         int i = 0;
235         for (const auto& railIndex : railIndices) {
236             if (railIndex >= mPm.reading.size()) {
237                 ret = Status::INVALID_INPUT;
238                 eVal.resize(0);
239                 break;
240             }
241             memcpy(&eVal[i], &mPm.reading[railIndex], sizeof(EnergyData));
242             i++;
243         }
244     }
245     _hidl_cb(eVal, ret);
246     return Void();
247 }
248 
streamEnergyData(uint32_t timeMs,uint32_t samplingRate,streamEnergyData_cb _hidl_cb)249 Return<void> PowerStats::streamEnergyData(uint32_t timeMs, uint32_t samplingRate,
250                                           streamEnergyData_cb _hidl_cb) {
251     std::lock_guard<std::mutex> _lock(mPm.mLock);
252     if (mPm.fmqSynchronized != nullptr) {
253         _hidl_cb(MessageQueueSync::Descriptor(), 0, 0, Status::INSUFFICIENT_RESOURCES);
254         return Void();
255     }
256     uint32_t sps = std::min(samplingRate, MAX_SAMPLING_RATE);
257     uint32_t numSamples = timeMs * sps / 1000;
258     mPm.fmqSynchronized.reset(new (std::nothrow) MessageQueueSync(MAX_QUEUE_SIZE, true));
259     if (mPm.fmqSynchronized == nullptr || mPm.fmqSynchronized->isValid() == false) {
260         mPm.fmqSynchronized = nullptr;
261         _hidl_cb(MessageQueueSync::Descriptor(), 0, 0, Status::INSUFFICIENT_RESOURCES);
262         return Void();
263     }
264     std::thread pollThread = std::thread([this, sps, numSamples]() {
265         uint64_t sleepTimeUs = 1000000 / sps;
266         uint32_t currSamples = 0;
267         while (currSamples < numSamples) {
268             mPm.mLock.lock();
269             if (parseIioEnergyNodes() == Status::SUCCESS) {
270                 mPm.fmqSynchronized->writeBlocking(&mPm.reading[0], mPm.reading.size(),
271                                                    WRITE_TIMEOUT_NS);
272                 mPm.mLock.unlock();
273                 currSamples++;
274                 if (usleep(sleepTimeUs) < 0) {
275                     ALOGW("Sleep interrupted");
276                     break;
277                 }
278             } else {
279                 mPm.mLock.unlock();
280                 break;
281             }
282         }
283         mPm.mLock.lock();
284         mPm.fmqSynchronized = nullptr;
285         mPm.mLock.unlock();
286         return;
287     });
288     pollThread.detach();
289     _hidl_cb(*(mPm.fmqSynchronized)->getDesc(), numSamples, mPm.reading.size(), Status::SUCCESS);
290     return Void();
291 }
292 
addPowerEntity(const std::string & name,PowerEntityType type)293 uint32_t PowerStats::addPowerEntity(const std::string& name, PowerEntityType type) {
294     uint32_t id = mPowerEntityInfos.size();
295     mPowerEntityInfos.push_back({id, name, type});
296     return id;
297 }
298 
addStateResidencyDataProvider(std::shared_ptr<IStateResidencyDataProvider> p)299 void PowerStats::addStateResidencyDataProvider(std::shared_ptr<IStateResidencyDataProvider> p) {
300     std::vector<PowerEntityStateSpace> stateSpaces = p->getStateSpaces();
301     for (auto stateSpace : stateSpaces) {
302         mPowerEntityStateSpaces.emplace(stateSpace.powerEntityId, stateSpace);
303         mStateResidencyDataProviders.emplace(stateSpace.powerEntityId, p);
304     }
305 }
306 
getPowerEntityInfo(getPowerEntityInfo_cb _hidl_cb)307 Return<void> PowerStats::getPowerEntityInfo(getPowerEntityInfo_cb _hidl_cb) {
308     // If not configured, return NOT_SUPPORTED
309     if (mPowerEntityInfos.empty()) {
310         _hidl_cb({}, Status::NOT_SUPPORTED);
311         return Void();
312     }
313 
314     _hidl_cb(mPowerEntityInfos, Status::SUCCESS);
315     return Void();
316 }
317 
getPowerEntityStateInfo(const hidl_vec<uint32_t> & powerEntityIds,getPowerEntityStateInfo_cb _hidl_cb)318 Return<void> PowerStats::getPowerEntityStateInfo(const hidl_vec<uint32_t>& powerEntityIds,
319                                                  getPowerEntityStateInfo_cb _hidl_cb) {
320     // If not configured, return NOT_SUPPORTED
321     if (mPowerEntityStateSpaces.empty()) {
322         _hidl_cb({}, Status::NOT_SUPPORTED);
323         return Void();
324     }
325 
326     std::vector<PowerEntityStateSpace> stateSpaces;
327 
328     // If powerEntityIds is empty then return state space info for all entities
329     if (powerEntityIds.size() == 0) {
330         stateSpaces.reserve(mPowerEntityStateSpaces.size());
331         for (auto i : mPowerEntityStateSpaces) {
332             stateSpaces.emplace_back(i.second);
333         }
334         _hidl_cb(stateSpaces, Status::SUCCESS);
335         return Void();
336     }
337 
338     // Return state space information only for valid ids
339     auto ret = Status::SUCCESS;
340     stateSpaces.reserve(powerEntityIds.size());
341     for (const uint32_t id : powerEntityIds) {
342         auto stateSpace = mPowerEntityStateSpaces.find(id);
343         if (stateSpace != mPowerEntityStateSpaces.end()) {
344             stateSpaces.emplace_back(stateSpace->second);
345         } else {
346             ret = Status::INVALID_INPUT;
347         }
348     }
349 
350     _hidl_cb(stateSpaces, ret);
351     return Void();
352 }
353 
getPowerEntityStateResidencyData(const hidl_vec<uint32_t> & powerEntityIds,getPowerEntityStateResidencyData_cb _hidl_cb)354 Return<void> PowerStats::getPowerEntityStateResidencyData(
355         const hidl_vec<uint32_t>& powerEntityIds, getPowerEntityStateResidencyData_cb _hidl_cb) {
356     // If not configured, return NOT_SUPPORTED
357     if (mStateResidencyDataProviders.empty() || mPowerEntityStateSpaces.empty()) {
358         _hidl_cb({}, Status::NOT_SUPPORTED);
359         return Void();
360     }
361 
362     // If powerEntityIds is empty then return data for all supported entities
363     if (powerEntityIds.size() == 0) {
364         std::vector<uint32_t> ids;
365         for (auto stateSpace : mPowerEntityStateSpaces) {
366             ids.emplace_back(stateSpace.first);
367         }
368         return getPowerEntityStateResidencyData(ids, _hidl_cb);
369     }
370 
371     std::unordered_map<uint32_t, PowerEntityStateResidencyResult> stateResidencies;
372     std::vector<PowerEntityStateResidencyResult> results;
373     results.reserve(powerEntityIds.size());
374 
375     // return results for only the given powerEntityIds
376     bool invalidInput = false;
377     bool filesystemError = false;
378     for (auto id : powerEntityIds) {
379         auto dataProvider = mStateResidencyDataProviders.find(id);
380         // skip if the given powerEntityId does not have an associated StateResidencyDataProvider
381         if (dataProvider == mStateResidencyDataProviders.end()) {
382             invalidInput = true;
383             continue;
384         }
385 
386         // get the results if we have not already done so.
387         if (stateResidencies.find(id) == stateResidencies.end()) {
388             if (!dataProvider->second->getResults(stateResidencies)) {
389                 filesystemError = true;
390             }
391         }
392 
393         // append results
394         auto stateResidency = stateResidencies.find(id);
395         if (stateResidency != stateResidencies.end()) {
396             results.emplace_back(stateResidency->second);
397         }
398     }
399 
400     auto ret = Status::SUCCESS;
401     if (filesystemError) {
402         ret = Status::FILESYSTEM_ERROR;
403     } else if (invalidInput) {
404         ret = Status::INVALID_INPUT;
405     }
406 
407     _hidl_cb(results, ret);
408     return Void();
409 }
410 
DumpResidencyDataToFd(const hidl_vec<PowerEntityInfo> & infos,const hidl_vec<PowerEntityStateSpace> & stateSpaces,const hidl_vec<PowerEntityStateResidencyResult> & results,int fd)411 bool DumpResidencyDataToFd(const hidl_vec<PowerEntityInfo>& infos,
412                            const hidl_vec<PowerEntityStateSpace>& stateSpaces,
413                            const hidl_vec<PowerEntityStateResidencyResult>& results, int fd) {
414     // construct lookup table of powerEntityId to name
415     std::unordered_map<uint32_t, std::string> entityNames;
416     for (auto info : infos) {
417         entityNames.emplace(info.powerEntityId, info.powerEntityName);
418     }
419 
420     // construct lookup table of powerEntityId, powerEntityStateId to state name
421     std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> stateNames;
422     for (auto stateSpace : stateSpaces) {
423         stateNames.emplace(stateSpace.powerEntityId, std::unordered_map<uint32_t, std::string>());
424         for (auto state : stateSpace.states) {
425             stateNames.at(stateSpace.powerEntityId)
426                     .emplace(state.powerEntityStateId, state.powerEntityStateName);
427         }
428     }
429 
430     std::ostringstream dumpStats;
431     dumpStats << "\n========== PowerStats HAL 1.0 state residencies ==========\n";
432 
433     const char* headerFormat = "  %14s   %14s   %16s   %15s   %16s\n";
434     const char* dataFormat =
435             "  %14s   %14s   %13" PRIu64 " ms   %15" PRIu64 "   %13" PRIu64 " ms\n";
436     dumpStats << android::base::StringPrintf(headerFormat, "Entity", "State", "Total time",
437                                              "Total entries", "Last entry timestamp");
438 
439     for (auto result : results) {
440         for (auto stateResidency : result.stateResidencyData) {
441             dumpStats << android::base::StringPrintf(
442                     dataFormat, entityNames.at(result.powerEntityId).c_str(),
443                     stateNames.at(result.powerEntityId)
444                             .at(stateResidency.powerEntityStateId)
445                             .c_str(),
446                     stateResidency.totalTimeInStateMs, stateResidency.totalStateEntryCount,
447                     stateResidency.lastEntryTimestampMs);
448         }
449     }
450 
451     dumpStats << "========== End of PowerStats HAL 1.0 state residencies ==========\n";
452 
453     return android::base::WriteStringToFd(dumpStats.str(), fd);
454 }
455 
debug(const hidl_handle & handle,const hidl_vec<hidl_string> &)456 Return<void> PowerStats::debug(const hidl_handle& handle, const hidl_vec<hidl_string>&) {
457     if (handle == nullptr || handle->numFds < 1) {
458         return Void();
459     }
460 
461     int fd = handle->data[0];
462     Status status;
463     hidl_vec<PowerEntityInfo> infos;
464 
465     // Get power entity information
466     getPowerEntityInfo([&status, &infos](auto rInfos, auto rStatus) {
467         status = rStatus;
468         infos = rInfos;
469     });
470     if (status != Status::SUCCESS) {
471         LOG(ERROR) << "Error getting power entity info";
472         return Void();
473     }
474 
475     // Get power entity state information
476     hidl_vec<PowerEntityStateSpace> stateSpaces;
477     getPowerEntityStateInfo({}, [&status, &stateSpaces](auto rStateSpaces, auto rStatus) {
478         status = rStatus;
479         stateSpaces = rStateSpaces;
480     });
481     if (status != Status::SUCCESS) {
482         LOG(ERROR) << "Error getting state info";
483         return Void();
484     }
485 
486     // Get power entity state residency data
487     hidl_vec<PowerEntityStateResidencyResult> results;
488     getPowerEntityStateResidencyData({}, [&status, &results](auto rResults, auto rStatus) {
489         status = rStatus;
490         results = rResults;
491     });
492 
493     // This implementation of getPowerEntityStateResidencyData supports the
494     // return of partial results if status == FILESYSTEM_ERROR.
495     if (status != Status::SUCCESS) {
496         LOG(ERROR) << "Error getting residency data -- Some results missing";
497     }
498 
499     if (!DumpResidencyDataToFd(infos, stateSpaces, results, fd)) {
500         PLOG(ERROR) << "Failed to dump residency data to fd";
501     }
502 
503     fsync(fd);
504 
505     return Void();
506 }
507 
508 }  // namespace implementation
509 }  // namespace V1_0
510 }  // namespace stats
511 }  // namespace power
512 }  // namespace hardware
513 }  // namespace android
514