1 /*
2  * Copyright (C) 2017 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 specic language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "libperfmgr"
18 
19 #include "perfmgr/HintManager.h"
20 
21 #include <android-base/file.h>
22 #include <android-base/logging.h>
23 #include <json/reader.h>
24 #include <json/value.h>
25 
26 #include <algorithm>
27 #include <set>
28 
29 #include "perfmgr/FileNode.h"
30 #include "perfmgr/PropertyNode.h"
31 
32 namespace android {
33 namespace perfmgr {
34 
ValidateHint(const std::string & hint_type) const35 bool HintManager::ValidateHint(const std::string& hint_type) const {
36     if (nm_.get() == nullptr) {
37         LOG(ERROR) << "NodeLooperThread not present";
38         return false;
39     }
40     return IsHintSupported(hint_type);
41 }
42 
IsHintSupported(const std::string & hint_type) const43 bool HintManager::IsHintSupported(const std::string& hint_type) const {
44     if (actions_.find(hint_type) == actions_.end()) {
45         LOG(INFO) << "Hint type not present in actions: " << hint_type;
46         return false;
47     }
48     return true;
49 }
50 
DoHint(const std::string & hint_type)51 bool HintManager::DoHint(const std::string& hint_type) {
52     LOG(VERBOSE) << "Do Powerhint: " << hint_type;
53     return ValidateHint(hint_type)
54                ? nm_->Request(actions_.at(hint_type), hint_type)
55                : false;
56 }
57 
DoHint(const std::string & hint_type,std::chrono::milliseconds timeout_ms_override)58 bool HintManager::DoHint(const std::string& hint_type,
59                          std::chrono::milliseconds timeout_ms_override) {
60     LOG(VERBOSE) << "Do Powerhint: " << hint_type << " for "
61                  << timeout_ms_override.count() << "ms";
62     if (!ValidateHint(hint_type)) {
63         return false;
64     }
65     std::vector<NodeAction> actions_override = actions_.at(hint_type);
66     for (auto& action : actions_override) {
67         action.timeout_ms = timeout_ms_override;
68     }
69     return nm_->Request(actions_override, hint_type);
70 }
71 
EndHint(const std::string & hint_type)72 bool HintManager::EndHint(const std::string& hint_type) {
73     LOG(VERBOSE) << "End Powerhint: " << hint_type;
74     return ValidateHint(hint_type)
75                ? nm_->Cancel(actions_.at(hint_type), hint_type)
76                : false;
77 }
78 
IsRunning() const79 bool HintManager::IsRunning() const {
80     return (nm_.get() == nullptr) ? false : nm_->isRunning();
81 }
82 
GetHints() const83 std::vector<std::string> HintManager::GetHints() const {
84     std::vector<std::string> hints;
85     for (auto const& action : actions_) {
86         hints.push_back(action.first);
87     }
88     return hints;
89 }
90 
DumpToFd(int fd)91 void HintManager::DumpToFd(int fd) {
92     std::string header(
93         "========== Begin perfmgr nodes ==========\n"
94         "Node Name\t"
95         "Node Path\t"
96         "Current Index\t"
97         "Current Value\n");
98     if (!android::base::WriteStringToFd(header, fd)) {
99         LOG(ERROR) << "Failed to dump fd: " << fd;
100     }
101     nm_->DumpToFd(fd);
102     std::string footer("==========  End perfmgr nodes  ==========\n");
103     if (!android::base::WriteStringToFd(footer, fd)) {
104         LOG(ERROR) << "Failed to dump fd: " << fd;
105     }
106     fsync(fd);
107 }
108 
Start()109 bool HintManager::Start() {
110     return nm_->Start();
111 }
112 
GetFromJSON(const std::string & config_path,bool start)113 std::unique_ptr<HintManager> HintManager::GetFromJSON(
114     const std::string& config_path, bool start) {
115     std::string json_doc;
116 
117     if (!android::base::ReadFileToString(config_path, &json_doc)) {
118         LOG(ERROR) << "Failed to read JSON config from " << config_path;
119         return nullptr;
120     }
121 
122     std::vector<std::unique_ptr<Node>> nodes = ParseNodes(json_doc);
123     if (nodes.empty()) {
124         LOG(ERROR) << "Failed to parse Nodes section from " << config_path;
125         return nullptr;
126     }
127     std::map<std::string, std::vector<NodeAction>> actions =
128         HintManager::ParseActions(json_doc, nodes);
129 
130     if (actions.empty()) {
131         LOG(ERROR) << "Failed to parse Actions section from " << config_path;
132         return nullptr;
133     }
134 
135     sp<NodeLooperThread> nm = new NodeLooperThread(std::move(nodes));
136     std::unique_ptr<HintManager> hm =
137         std::make_unique<HintManager>(std::move(nm), actions);
138 
139     LOG(INFO) << "Initialized HintManager from JSON config: " << config_path;
140 
141     if (start) {
142         hm->Start();
143     }
144     return hm;
145 }
146 
ParseNodes(const std::string & json_doc)147 std::vector<std::unique_ptr<Node>> HintManager::ParseNodes(
148     const std::string& json_doc) {
149     // function starts
150     std::vector<std::unique_ptr<Node>> nodes_parsed;
151     std::set<std::string> nodes_name_parsed;
152     std::set<std::string> nodes_path_parsed;
153     Json::Value root;
154     Json::Reader reader;
155 
156     if (!reader.parse(json_doc, root)) {
157         LOG(ERROR) << "Failed to parse JSON config";
158         return nodes_parsed;
159     }
160 
161     Json::Value nodes = root["Nodes"];
162     for (Json::Value::ArrayIndex i = 0; i < nodes.size(); ++i) {
163         std::string name = nodes[i]["Name"].asString();
164         LOG(VERBOSE) << "Node[" << i << "]'s Name: " << name;
165         if (name.empty()) {
166             LOG(ERROR) << "Failed to read "
167                        << "Node[" << i << "]'s Name";
168             nodes_parsed.clear();
169             return nodes_parsed;
170         }
171 
172         auto result = nodes_name_parsed.insert(name);
173         if (!result.second) {
174             LOG(ERROR) << "Duplicate Node[" << i << "]'s Name";
175             nodes_parsed.clear();
176             return nodes_parsed;
177         }
178 
179         std::string path = nodes[i]["Path"].asString();
180         LOG(VERBOSE) << "Node[" << i << "]'s Path: " << path;
181         if (path.empty()) {
182             LOG(ERROR) << "Failed to read "
183                        << "Node[" << i << "]'s Path";
184             nodes_parsed.clear();
185             return nodes_parsed;
186         }
187 
188         result = nodes_path_parsed.insert(path);
189         if (!result.second) {
190             LOG(ERROR) << "Duplicate Node[" << i << "]'s Path";
191             nodes_parsed.clear();
192             return nodes_parsed;
193         }
194 
195         bool is_file = true;
196         std::string node_type = nodes[i]["Type"].asString();
197         LOG(VERBOSE) << "Node[" << i << "]'s Type: " << node_type;
198         if (node_type.empty()) {
199             LOG(ERROR) << "Failed to read "
200                        << "Node[" << i << "]'s Type, set to 'File' as default";
201         } else if (node_type == "File") {
202             is_file = true;
203         } else if (node_type == "Property") {
204             is_file = false;
205         } else {
206             LOG(ERROR) << "Invalid Node[" << i
207                        << "]'s Type: only File and Property supported.";
208             nodes_parsed.clear();
209             return nodes_parsed;
210         }
211 
212         std::vector<RequestGroup> values_parsed;
213         std::set<std::string> values_set_parsed;
214         Json::Value values = nodes[i]["Values"];
215         for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
216             std::string value = values[j].asString();
217             LOG(VERBOSE) << "Node[" << i << "]'s Value[" << j << "]: " << value;
218             auto result = values_set_parsed.insert(value);
219             if (!result.second) {
220                 LOG(ERROR) << "Duplicate value parsed in Node[" << i
221                            << "]'s Value[" << j << "]";
222                 nodes_parsed.clear();
223                 return nodes_parsed;
224             }
225             if (is_file && value.empty()) {
226                 LOG(ERROR) << "Failed to read Node[" << i << "]'s Value[" << j
227                            << "]";
228                 nodes_parsed.clear();
229                 return nodes_parsed;
230             }
231             values_parsed.emplace_back(value);
232         }
233         if (values_parsed.size() < 1) {
234             LOG(ERROR) << "Failed to read Node[" << i << "]'s Values";
235             nodes_parsed.clear();
236             return nodes_parsed;
237         }
238 
239         Json::UInt64 default_index = values_parsed.size() - 1;
240         if (nodes[i]["DefaultIndex"].empty() ||
241             !nodes[i]["DefaultIndex"].isUInt64()) {
242             LOG(INFO) << "Failed to read Node[" << i
243                       << "]'s DefaultIndex, set to last index: "
244                       << default_index;
245         } else {
246             default_index = nodes[i]["DefaultIndex"].asUInt64();
247         }
248         if (default_index > values_parsed.size() - 1) {
249             default_index = values_parsed.size() - 1;
250             LOG(ERROR) << "Node[" << i
251                        << "]'s DefaultIndex out of bound, max value index: "
252                        << default_index;
253             nodes_parsed.clear();
254             return nodes_parsed;
255         }
256         LOG(VERBOSE) << "Node[" << i << "]'s DefaultIndex: " << default_index;
257 
258         bool reset = false;
259         if (nodes[i]["ResetOnInit"].empty() ||
260             !nodes[i]["ResetOnInit"].isBool()) {
261             LOG(INFO) << "Failed to read Node[" << i
262                       << "]'s ResetOnInit, set to 'false'";
263         } else {
264             reset = nodes[i]["ResetOnInit"].asBool();
265         }
266         LOG(VERBOSE) << "Node[" << i << "]'s ResetOnInit: " << std::boolalpha
267                      << reset << std::noboolalpha;
268 
269         if (is_file) {
270             bool hold_fd = false;
271             if (nodes[i]["HoldFd"].empty() || !nodes[i]["HoldFd"].isBool()) {
272                 LOG(INFO) << "Failed to read Node[" << i
273                           << "]'s HoldFd, set to 'false'";
274             } else {
275                 hold_fd = nodes[i]["HoldFd"].asBool();
276             }
277             LOG(VERBOSE) << "Node[" << i << "]'s HoldFd: " << std::boolalpha
278                          << hold_fd << std::noboolalpha;
279 
280             nodes_parsed.emplace_back(std::make_unique<FileNode>(
281                 name, path, values_parsed,
282                 static_cast<std::size_t>(default_index), reset, hold_fd));
283         } else {
284             nodes_parsed.emplace_back(std::make_unique<PropertyNode>(
285                 name, path, values_parsed,
286                 static_cast<std::size_t>(default_index), reset));
287         }
288     }
289     LOG(INFO) << nodes_parsed.size() << " Nodes parsed successfully";
290     return nodes_parsed;
291 }
292 
ParseActions(const std::string & json_doc,const std::vector<std::unique_ptr<Node>> & nodes)293 std::map<std::string, std::vector<NodeAction>> HintManager::ParseActions(
294     const std::string& json_doc,
295     const std::vector<std::unique_ptr<Node>>& nodes) {
296     // function starts
297     std::map<std::string, std::vector<NodeAction>> actions_parsed;
298     Json::Value root;
299     Json::Reader reader;
300 
301     if (!reader.parse(json_doc, root)) {
302         LOG(ERROR) << "Failed to parse JSON config";
303         return actions_parsed;
304     }
305 
306     Json::Value actions = root["Actions"];
307     std::size_t total_parsed = 0;
308 
309     std::map<std::string, std::size_t> nodes_index;
310     for (std::size_t i = 0; i < nodes.size(); ++i) {
311         nodes_index[nodes[i]->GetName()] = i;
312     }
313 
314     for (Json::Value::ArrayIndex i = 0; i < actions.size(); ++i) {
315         const std::string& hint_type = actions[i]["PowerHint"].asString();
316         LOG(VERBOSE) << "Action[" << i << "]'s PowerHint: " << hint_type;
317         if (hint_type.empty()) {
318             LOG(ERROR) << "Failed to read "
319                        << "Action[" << i << "]'s PowerHint";
320             actions_parsed.clear();
321             return actions_parsed;
322         }
323 
324         std::string node_name = actions[i]["Node"].asString();
325         LOG(VERBOSE) << "Action[" << i << "]'s Node: " << node_name;
326         std::size_t node_index;
327 
328         if (nodes_index.find(node_name) == nodes_index.end()) {
329             LOG(ERROR) << "Failed to find "
330                        << "Action[" << i << "]'s Node from Nodes section: ["
331                        << node_name << "]";
332             actions_parsed.clear();
333             return actions_parsed;
334         }
335         node_index = nodes_index[node_name];
336 
337         std::string value_name = actions[i]["Value"].asString();
338         LOG(VERBOSE) << "Action[" << i << "]'s Value: " << value_name;
339         std::size_t value_index = 0;
340 
341         if (!nodes[node_index]->GetValueIndex(value_name, &value_index)) {
342             LOG(ERROR) << "Failed to read Action[" << i << "]'s Value";
343             LOG(ERROR) << "Action[" << i << "]'s Value " << value_name
344                        << " is not defined in Node[" << node_name << "]";
345             actions_parsed.clear();
346             return actions_parsed;
347         }
348         LOG(VERBOSE) << "Action[" << i << "]'s ValueIndex: " << value_index;
349 
350         Json::UInt64 duration = 0;
351         if (actions[i]["Duration"].empty() ||
352             !actions[i]["Duration"].isUInt64()) {
353             LOG(ERROR) << "Failed to read Action[" << i << "]'s Duration";
354             actions_parsed.clear();
355             return actions_parsed;
356         } else {
357             duration = actions[i]["Duration"].asUInt64();
358         }
359         LOG(VERBOSE) << "Action[" << i << "]'s Duration: " << duration;
360 
361         if (actions_parsed.find(hint_type) == actions_parsed.end()) {
362             actions_parsed[hint_type] = std::vector<NodeAction>{
363                 {node_index, value_index, std::chrono::milliseconds(duration)}};
364         } else {
365             for (const auto& action : actions_parsed[hint_type]) {
366                 if (action.node_index == node_index) {
367                     LOG(ERROR)
368                         << "Action[" << i
369                         << "]'s NodeIndex is duplicated with another Action";
370                     actions_parsed.clear();
371                     return actions_parsed;
372                 }
373             }
374             actions_parsed[hint_type].emplace_back(
375                 node_index, value_index, std::chrono::milliseconds(duration));
376         }
377 
378         ++total_parsed;
379     }
380 
381     LOG(INFO) << total_parsed << " Actions parsed successfully";
382 
383     for (const auto& action : actions_parsed) {
384         LOG(INFO) << "PowerHint " << action.first << " has "
385                   << action.second.size() << " actions parsed";
386     }
387 
388     return actions_parsed;
389 }
390 
391 }  // namespace perfmgr
392 }  // namespace android
393