1 /*
2  * Copyright (C) 2020 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 ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
18 
19 #include <android-base/file.h>
20 #include <android-base/logging.h>
21 #include <android-base/stringprintf.h>
22 #include <android-base/strings.h>
23 #include <dataproviders/IioEnergyMeterDataProvider.h>
24 #include <dataproviders/IioEnergyMeterDataSelector.h>
25 #include <inttypes.h>
26 #include <utils/Trace.h>
27 
28 namespace aidl {
29 namespace android {
30 namespace hardware {
31 namespace power {
32 namespace stats {
33 
34 using aidl::android::hardware::power::stats::IioEnergyMeterDataSelector;
35 
36 #define MAX_RAIL_NAME_LEN 50
37 #define STR(s) #s
38 #define XSTR(s) STR(s)
39 
findIioEnergyMeterNodes()40 void IioEnergyMeterDataProvider::findIioEnergyMeterNodes() {
41     struct dirent *ent;
42 
43     DIR *iioDir = opendir(kIioRootDir.c_str());
44     if (!iioDir) {
45         PLOG(ERROR) << "Error opening directory" << kIioRootDir;
46         return;
47     }
48 
49     // Find any iio:devices that match the given kDeviceNames
50     while (ent = readdir(iioDir), ent) {
51         std::string devTypeDir = ent->d_name;
52         if (devTypeDir.find(kDeviceType) != std::string::npos) {
53             std::string devicePath = kIioRootDir + devTypeDir;
54             std::string deviceNameContents;
55 
56             if (!::android::base::ReadFileToString(devicePath + kNameNode, &deviceNameContents)) {
57                 LOG(WARNING) << "Failed to read device name from " << devicePath;
58             } else {
59                 for (const auto &deviceName : kDeviceNames) {
60                     if (deviceNameContents.find(deviceName) != std::string::npos) {
61                         mDevicePaths.emplace(devicePath, deviceName);
62                     }
63                 }
64             }
65         }
66     }
67 
68     closedir(iioDir);
69     return;
70 }
71 
parseEnabledRails()72 void IioEnergyMeterDataProvider::parseEnabledRails() {
73     std::string data;
74     int32_t id = 0;
75     for (const auto &path : mDevicePaths) {
76         // Get list of enabled rails
77         if (!::android::base::ReadFileToString(path.first + kEnabledRailsNode, &data)) {
78             LOG(ERROR) << "Error reading enabled rails from " << path.first;
79             continue;
80         }
81 
82         // Build RailInfos from list of enabled rails
83         std::istringstream railNames(data);
84         std::string line;
85         while (std::getline(railNames, line)) {
86             /* Format example: CH2[VSYS_PWR_RFFE]:Cellular */
87             std::vector<std::string> words = ::android::base::Split(line, ":][");
88             if (words.size() == 4) {
89                 const std::string channelName = words[1];
90                 const std::string subsystemName = words[3];
91                 if (mChannelIds.count(channelName) == 0) {
92                     mChannelInfos.push_back(
93                             {.id = id, .name = channelName, .subsystem = subsystemName});
94                     mChannelIds.emplace(channelName, id);
95                     id++;
96                 } else {
97                     LOG(WARNING) << "There exists rails with the same name (not supported): "
98                                  << channelName << ". Only the last occurrence of rail energy will "
99                                  << "be provided.";
100                 }
101             } else {
102                 LOG(WARNING) << "Unexpected enabled rail format in " << path.first;
103             }
104         }
105     }
106 }
107 
IioEnergyMeterDataProvider(const std::vector<std::string> & deviceNames,const bool useSelector)108 IioEnergyMeterDataProvider::IioEnergyMeterDataProvider(const std::vector<std::string> &deviceNames,
109                                                        const bool useSelector)
110     : kDeviceNames(std::move(deviceNames)) {
111     findIioEnergyMeterNodes();
112     if (useSelector) {
113         /* Run meter selection in constructor; object can be discarded afterwards */
114         IioEnergyMeterDataSelector selector(mDevicePaths);
115     }
116     parseEnabledRails();
117     mReading.resize(mChannelInfos.size());
118 }
119 
parseEnergyContents(const std::string & contents)120 int IioEnergyMeterDataProvider::parseEnergyContents(const std::string &contents) {
121     std::istringstream energyData(contents);
122     std::string line;
123 
124     int ret = 0;
125     uint64_t timestamp = 0;
126     bool timestampRead = false;
127 
128     while (std::getline(energyData, line)) {
129         bool parseLineSuccess = false;
130 
131         if (timestampRead == false) {
132             /* Read timestamp from boot (ms) */
133             if (sscanf(line.c_str(), "t=%" PRIu64, &timestamp) == 1) {
134                 if (timestamp == 0 || timestamp == ULLONG_MAX) {
135                     LOG(ERROR) << "Potentially wrong timestamp: " << timestamp;
136                 }
137                 timestampRead = true;
138                 parseLineSuccess = true;
139             }
140 
141         } else {
142             /* Read rail energy */
143             uint64_t energy = 0;
144             uint64_t duration = 0;
145             char railNameRaw[MAX_RAIL_NAME_LEN + 1];
146 
147             /* Format example: CH3(T=358356)[S2M_VDD_CPUCL2], 761330 */
148             if (sscanf(line.c_str(),
149                        "CH%*d(T=%" PRIu64 ")[%" XSTR(MAX_RAIL_NAME_LEN) "[^]]], %" PRIu64,
150                        &duration, railNameRaw, &energy) == 3) {
151                 std::string railName(railNameRaw);
152 
153                 /* If the count == 0, the rail may not be enabled */
154                 /* The count cannot be > 1; mChannelIds is a map */
155                 if (mChannelIds.count(railName) == 1) {
156                     size_t index = mChannelIds[railName];
157                     mReading[index].id = index;
158                     mReading[index].timestampMs = timestamp;
159                     mReading[index].durationMs = duration;
160                     mReading[index].energyUWs = energy;
161                     if (mReading[index].energyUWs == ULLONG_MAX) {
162                         LOG(ERROR) << "Potentially wrong energy value on rail: " << railName;
163                     }
164                     ATRACE_INT(railNameRaw, energy);
165                 }
166                 parseLineSuccess = true;
167             }
168         }
169 
170         if (parseLineSuccess == false) {
171             ret = -1;
172             break;
173         }
174     }
175 
176     return ret;
177 }
178 
parseEnergyValue(std::string path)179 int IioEnergyMeterDataProvider::parseEnergyValue(std::string path) {
180     int ret = 0;
181     std::string data;
182     if (!::android::base::ReadFileToString(path + kEnergyValueNode, &data)) {
183         LOG(ERROR) << "Error reading energy value in " << path;
184         return -1;
185     }
186 
187     ret = parseEnergyContents(data);
188     if (ret != 0) {
189         LOG(ERROR) << "Unexpected format in " << path;
190     }
191     return ret;
192 }
193 
readEnergyMeter(const std::vector<int32_t> & in_channelIds,std::vector<EnergyMeasurement> * _aidl_return)194 ndk::ScopedAStatus IioEnergyMeterDataProvider::readEnergyMeter(
195         const std::vector<int32_t> &in_channelIds, std::vector<EnergyMeasurement> *_aidl_return) {
196     std::scoped_lock lock(mLock);
197 
198     for (const auto &devicePath : mDevicePaths) {
199         if (parseEnergyValue(devicePath.first) < 0) {
200             LOG(ERROR) << "Error in parsing " << devicePath.first;
201             return ndk::ScopedAStatus::ok();
202         }
203     }
204 
205     if (in_channelIds.empty()) {
206         *_aidl_return = mReading;
207     } else {
208         _aidl_return->reserve(in_channelIds.size());
209         for (const auto &id : in_channelIds) {
210             // check for invalid ids
211             if (id < 0 || id >= mChannelInfos.size()) {
212                 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
213             }
214 
215             _aidl_return->emplace_back(mReading[id]);
216         }
217     }
218 
219     return ndk::ScopedAStatus::ok();
220 }
221 
getEnergyMeterInfo(std::vector<Channel> * _aidl_return)222 ndk::ScopedAStatus IioEnergyMeterDataProvider::getEnergyMeterInfo(
223         std::vector<Channel> *_aidl_return) {
224     std::scoped_lock lk(mLock);
225     *_aidl_return = mChannelInfos;
226 
227     return ndk::ScopedAStatus::ok();
228 }
229 
230 }  // namespace stats
231 }  // namespace power
232 }  // namespace hardware
233 }  // namespace android
234 }  // namespace aidl
235