• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
18 #define LOG_TAG "libperfmgr"
19 
20 #include "perfmgr/HintManager.h"
21 
22 #include <android-base/file.h>
23 #include <android-base/logging.h>
24 #include <android-base/properties.h>
25 #include <android-base/stringprintf.h>
26 #include <inttypes.h>
27 #include <json/reader.h>
28 #include <json/value.h>
29 #include <utils/Trace.h>
30 
31 #include <algorithm>
32 #include <set>
33 #include <string>
34 
35 #include "perfmgr/EventNode.h"
36 #include "perfmgr/FileNode.h"
37 #include "perfmgr/PropertyNode.h"
38 
39 namespace android {
40 namespace perfmgr {
41 
42 namespace {
43 constexpr std::chrono::milliseconds kMilliSecondZero = std::chrono::milliseconds(0);
44 constexpr std::chrono::steady_clock::time_point kTimePointMax =
45         std::chrono::steady_clock::time_point::max();
46 }  // namespace
47 
48 using ::android::base::GetProperty;
49 using ::android::base::StringPrintf;
50 
51 constexpr char kPowerHalTruncateProp[] = "vendor.powerhal.truncate";
52 constexpr std::string_view kConfigDebugPathProperty("vendor.powerhal.config.debug");
53 constexpr std::string_view kConfigProperty("vendor.powerhal.config");
54 constexpr std::string_view kConfigDefaultFileName("powerhint.json");
55 constexpr char kAdpfEventNodePath[] = "<AdpfConfig>:";
56 
ValidateHint(const std::string & hint_type) const57 bool HintManager::ValidateHint(const std::string& hint_type) const {
58     if (nm_.get() == nullptr) {
59         LOG(ERROR) << "NodeLooperThread not present";
60         return false;
61     }
62     return IsHintSupported(hint_type);
63 }
64 
IsHintSupported(const std::string & hint_type) const65 bool HintManager::IsHintSupported(const std::string& hint_type) const {
66     if (actions_.find(hint_type) == actions_.end()) {
67         LOG(DEBUG) << "Hint type not present in actions: " << hint_type;
68         return false;
69     }
70     return true;
71 }
72 
IsHintEnabled(const std::string & hint_type) const73 bool HintManager::IsHintEnabled(const std::string &hint_type) const {
74     std::lock_guard<std::mutex> lock(actions_.at(hint_type).hint_lock);
75     return actions_.at(hint_type).mask_requesters.empty();
76 }
77 
InitHintStatus(const std::unique_ptr<HintManager> & hm)78 bool HintManager::InitHintStatus(const std::unique_ptr<HintManager> &hm) {
79     if (hm.get() == nullptr) {
80         return false;
81     }
82     for (auto &a : hm->actions_) {
83         // timeout_ms equaling kMilliSecondZero means forever until cancelling.
84         // As a result, if there's one NodeAction has timeout_ms of 0, we will store
85         // 0 instead of max. Also node actions could be empty, set to 0 in that case.
86         std::chrono::milliseconds timeout = kMilliSecondZero;
87         if (a.second.node_actions.size()) {
88             auto [min, max] =
89                     std::minmax_element(a.second.node_actions.begin(), a.second.node_actions.end(),
90                                         [](const auto act1, const auto act2) {
91                                             return act1.timeout_ms < act2.timeout_ms;
92                                         });
93             timeout = min->timeout_ms == kMilliSecondZero ? kMilliSecondZero : max->timeout_ms;
94         }
95         a.second.status.reset(new HintStatus(timeout));
96     }
97     return true;
98 }
99 
DoHintStatus(const std::string & hint_type,std::chrono::milliseconds timeout_ms)100 void HintManager::DoHintStatus(const std::string &hint_type, std::chrono::milliseconds timeout_ms) {
101     std::lock_guard<std::mutex> lock(actions_.at(hint_type).hint_lock);
102     actions_.at(hint_type).status->stats.count.fetch_add(1);
103     auto now = std::chrono::steady_clock::now();
104     ATRACE_INT(("H:" + hint_type).c_str(), (timeout_ms == kMilliSecondZero)
105                                                    ? std::numeric_limits<int>::max()
106                                                    : timeout_ms.count());
107     ATRACE_NAME(("H:" + hint_type + ":" + std::to_string((timeout_ms == kMilliSecondZero)
108                                                    ? std::numeric_limits<int>::max()
109                                                    : timeout_ms.count())).c_str());
110     if (now > actions_.at(hint_type).status->end_time) {
111         actions_.at(hint_type).status->stats.duration_ms.fetch_add(
112                 std::chrono::duration_cast<std::chrono::milliseconds>(
113                         actions_.at(hint_type).status->end_time -
114                         actions_.at(hint_type).status->start_time)
115                         .count());
116         actions_.at(hint_type).status->start_time = now;
117     }
118     actions_.at(hint_type).status->end_time =
119             (timeout_ms == kMilliSecondZero) ? kTimePointMax : now + timeout_ms;
120 }
121 
EndHintStatus(const std::string & hint_type)122 void HintManager::EndHintStatus(const std::string &hint_type) {
123     std::lock_guard<std::mutex> lock(actions_.at(hint_type).hint_lock);
124     // Update HintStats if the hint ends earlier than expected end_time
125     auto now = std::chrono::steady_clock::now();
126     ATRACE_INT(("H:" + hint_type).c_str(), 0);
127     ATRACE_NAME(("H:" + hint_type + ":0").c_str());
128     if (now < actions_.at(hint_type).status->end_time) {
129         actions_.at(hint_type).status->stats.duration_ms.fetch_add(
130                 std::chrono::duration_cast<std::chrono::milliseconds>(
131                         now - actions_.at(hint_type).status->start_time)
132                         .count());
133         actions_.at(hint_type).status->end_time = now;
134     }
135 }
136 
DoHintAction(const std::string & hint_type)137 void HintManager::DoHintAction(const std::string &hint_type) {
138     for (auto &action : actions_.at(hint_type).hint_actions) {
139         if (!action.enable_property.empty() &&
140             !android::base::GetBoolProperty(action.enable_property, true)) {
141             // Disabled action based on its control property
142             continue;
143         }
144         switch (action.type) {
145             case HintActionType::DoHint:
146                 DoHint(action.value);
147                 break;
148             case HintActionType::EndHint:
149                 EndHint(action.value);
150                 break;
151             case HintActionType::MaskHint:
152                 if (actions_.find(action.value) == actions_.end()) {
153                     LOG(ERROR) << "Failed to find " << action.value << " action";
154                 } else {
155                     std::lock_guard<std::mutex> lock(actions_.at(hint_type).hint_lock);
156                     actions_.at(action.value).mask_requesters.insert(hint_type);
157                 }
158                 break;
159             default:
160                 // should not reach here
161                 LOG(ERROR) << "Invalid "
162                            << static_cast<std::underlying_type<HintActionType>::type>(action.type)
163                            << " type";
164         }
165     }
166 }
167 
EndHintAction(const std::string & hint_type)168 void HintManager::EndHintAction(const std::string &hint_type) {
169     for (auto &action : actions_.at(hint_type).hint_actions) {
170         if (action.type == HintActionType::MaskHint &&
171             actions_.find(action.value) != actions_.end()) {
172             std::lock_guard<std::mutex> lock(actions_.at(hint_type).hint_lock);
173             actions_.at(action.value).mask_requesters.erase(hint_type);
174         }
175     }
176 }
177 
DoHint(const std::string & hint_type)178 bool HintManager::DoHint(const std::string& hint_type) {
179     LOG(VERBOSE) << "Do Powerhint: " << hint_type;
180     if (!ValidateHint(hint_type) || !IsHintEnabled(hint_type) ||
181         !nm_->Request(actions_.at(hint_type).node_actions, hint_type)) {
182         return false;
183     }
184     DoHintStatus(hint_type, actions_.at(hint_type).status->max_timeout);
185     DoHintAction(hint_type);
186     return true;
187 }
188 
DoHint(const std::string & hint_type,std::chrono::milliseconds timeout_ms_override)189 bool HintManager::DoHint(const std::string& hint_type,
190                          std::chrono::milliseconds timeout_ms_override) {
191     LOG(VERBOSE) << "Do Powerhint: " << hint_type << " for "
192                  << timeout_ms_override.count() << "ms";
193     if (!ValidateHint(hint_type) || !IsHintEnabled(hint_type)) {
194         return false;
195     }
196     std::vector<NodeAction> actions_override = actions_.at(hint_type).node_actions;
197     for (auto& action : actions_override) {
198         action.timeout_ms = timeout_ms_override;
199     }
200     if (!nm_->Request(actions_override, hint_type)) {
201         return false;
202     }
203     DoHintStatus(hint_type, timeout_ms_override);
204     DoHintAction(hint_type);
205     return true;
206 }
207 
EndHint(const std::string & hint_type)208 bool HintManager::EndHint(const std::string& hint_type) {
209     LOG(VERBOSE) << "End Powerhint: " << hint_type;
210     if (!ValidateHint(hint_type) || !nm_->Cancel(actions_.at(hint_type).node_actions, hint_type)) {
211         return false;
212     }
213     EndHintStatus(hint_type);
214     EndHintAction(hint_type);
215     return true;
216 }
217 
IsRunning() const218 bool HintManager::IsRunning() const {
219     return (nm_.get() == nullptr) ? false : nm_->isRunning();
220 }
221 
GetHints() const222 std::vector<std::string> HintManager::GetHints() const {
223     std::vector<std::string> hints;
224     for (auto const& action : actions_) {
225         hints.push_back(action.first);
226     }
227     return hints;
228 }
229 
GetHintStats(const std::string & hint_type) const230 HintStats HintManager::GetHintStats(const std::string &hint_type) const {
231     HintStats hint_stats;
232     if (ValidateHint(hint_type)) {
233         std::lock_guard<std::mutex> lock(actions_.at(hint_type).hint_lock);
234         hint_stats.count =
235                 actions_.at(hint_type).status->stats.count.load(std::memory_order_relaxed);
236         hint_stats.duration_ms =
237                 actions_.at(hint_type).status->stats.duration_ms.load(std::memory_order_relaxed);
238     }
239     return hint_stats;
240 }
241 
DumpToFd(int fd)242 void HintManager::DumpToFd(int fd) {
243     std::string header("========== Begin perfmgr nodes ==========\n");
244     if (!android::base::WriteStringToFd(header, fd)) {
245         LOG(ERROR) << "Failed to dump fd: " << fd;
246     }
247     nm_->DumpToFd(fd);
248     std::string footer("==========  End perfmgr nodes  ==========\n");
249     if (!android::base::WriteStringToFd(footer, fd)) {
250         LOG(ERROR) << "Failed to dump fd: " << fd;
251     }
252     header = "========== Begin perfmgr stats ==========\n"
253              "Hint Name\t"
254              "Counts\t"
255              "Duration\n";
256     if (!android::base::WriteStringToFd(header, fd)) {
257         LOG(ERROR) << "Failed to dump fd: " << fd;
258     }
259     std::string hint_stats_string;
260     std::vector<std::string> keys(GetHints());
261     std::sort(keys.begin(), keys.end());
262     for (const auto &ordered_key : keys) {
263         HintStats hint_stats(GetHintStats(ordered_key));
264         hint_stats_string += StringPrintf("%s\t%" PRIu32 "\t%" PRIu64 "\n", ordered_key.c_str(),
265                                           hint_stats.count, hint_stats.duration_ms);
266     }
267     if (!android::base::WriteStringToFd(hint_stats_string, fd)) {
268         LOG(ERROR) << "Failed to dump fd: " << fd;
269     }
270     footer = "==========  End perfmgr stats  ==========\n";
271     if (!android::base::WriteStringToFd(footer, fd)) {
272         LOG(ERROR) << "Failed to dump fd: " << fd;
273     }
274 
275     // Dump current ADPF profiles
276     if (IsAdpfSupported()) {
277         header = "========== ADPF Tag Profile begin ==========\n";
278         if (!android::base::WriteStringToFd(header, fd)) {
279             LOG(ERROR) << "Failed to dump fd: " << fd;
280         }
281 
282         header = "---- Default non-tagged adpf profile ----\n";
283         if (!android::base::WriteStringToFd(header, fd)) {
284             LOG(ERROR) << "Failed to dump fd: " << fd;
285         }
286         GetAdpfProfileFromDoHint()->dumpToFd(fd);
287 
288         for (const auto &tag_profile : tag_profile_map_) {
289             header = StringPrintf("---- Tagged ADPF Profile: %s ----\n", tag_profile.first.c_str());
290             if (!android::base::WriteStringToFd(header, fd)) {
291                 LOG(ERROR) << "Failed to dump fd: " << fd;
292             }
293             tag_profile.second->dumpToFd(fd);
294         }
295 
296         footer = "========== ADPF Tag Profile end ==========\n";
297         if (!android::base::WriteStringToFd(footer, fd)) {
298             LOG(ERROR) << "Failed to dump fd: " << fd;
299         }
300     } else {
301         header = "========== IsAdpfSupported: No ===========\n";
302         if (!android::base::WriteStringToFd(header, fd)) {
303             LOG(ERROR) << "Failed to dump fd: " << fd;
304         }
305     }
306     fsync(fd);
307 }
308 
Start()309 bool HintManager::Start() {
310     return nm_->Start();
311 }
312 
313 std::unique_ptr<HintManager> HintManager::sInstance = nullptr;
314 
Reload(bool start)315 void HintManager::Reload(bool start) {
316     std::string config_path = "/vendor/etc/";
317     if (android::base::GetBoolProperty(kConfigDebugPathProperty.data(), false)) {
318         config_path = "/data/vendor/etc/";
319         LOG(WARNING) << "Pixel Power HAL AIDL Service is using debug config from: " << config_path;
320     }
321     config_path.append(GetProperty(kConfigProperty.data(), kConfigDefaultFileName.data()));
322 
323     LOG(INFO) << "Pixel Power HAL AIDL Service with Extension is starting with config: "
324               << config_path;
325     // Reload and start the HintManager
326     HintManager::GetFromJSON(config_path, start);
327     if (!sInstance) {
328         LOG(FATAL) << "Invalid config: " << config_path;
329     }
330 }
331 
GetInstance()332 HintManager *HintManager::GetInstance() {
333     if (sInstance == nullptr) {
334         HintManager::Reload(false);
335     }
336     return sInstance.get();
337 }
338 
ParseGpuSysfsNode(const std::string & json_doc)339 static std::optional<std::string> ParseGpuSysfsNode(const std::string &json_doc) {
340     Json::Value root;
341     Json::CharReaderBuilder builder;
342     std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
343     std::string errorMessage;
344     if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
345         LOG(ERROR) << "Failed to parse JSON config: " << errorMessage;
346         return {};
347     }
348 
349     if (root["GpuSysfsPath"].empty() || !root["GpuSysfsPath"].isString()) {
350         return {};
351     }
352     return {root["GpuSysfsPath"].asString()};
353 }
354 
GetFromJSON(const std::string & config_path,bool start)355 HintManager *HintManager::GetFromJSON(const std::string &config_path, bool start) {
356     std::string json_doc;
357 
358     if (!android::base::ReadFileToString(config_path, &json_doc)) {
359         LOG(ERROR) << "Failed to read JSON config from " << config_path;
360         return nullptr;
361     }
362 
363     std::vector<std::unique_ptr<Node>> nodes = ParseNodes(json_doc);
364     if (nodes.empty()) {
365         LOG(ERROR) << "Failed to parse Nodes section from " << config_path;
366         return nullptr;
367     }
368     std::vector<std::shared_ptr<AdpfConfig>> adpfs = HintManager::ParseAdpfConfigs(json_doc);
369     if (adpfs.empty()) {
370         LOG(INFO) << "No AdpfConfig section in the " << config_path;
371     }
372 
373     std::unordered_map<std::string, Hint> actions = HintManager::ParseActions(json_doc, nodes);
374 
375     // Parse ADPF Event Node
376     std::unordered_map<std::string, std::shared_ptr<AdpfConfig>> tag_adpfs;
377     LOG(VERBOSE) << "Parse ADPF Hint Event Table from all nodes.";
378     for (std::size_t i = 0; i < nodes.size(); ++i) {
379         const std::string &node_name = nodes[i]->GetName();
380         const std::string &node_path = nodes[i]->GetPath();
381         if (node_path.starts_with(kAdpfEventNodePath)) {
382             std::string tag = node_path.substr(strlen(kAdpfEventNodePath));
383             std::size_t index = nodes[i]->GetDefaultIndex();
384             std::string profile_name = nodes[i]->GetValues()[index];
385             for (std::size_t j = 0; j < adpfs.size(); ++j) {
386                 if (adpfs[j]->mName == profile_name) {
387                     tag_adpfs[tag] = adpfs[j];
388                     LOG(INFO) << "[" << tag << ":" << node_name << "] set to '" << profile_name
389                               << "'";
390                     break;
391                 }
392             }
393             if (!tag_adpfs[tag]) {
394                 tag_adpfs[tag] = adpfs[0];
395                 LOG(INFO) << "[" << tag << ":" << node_name << "] fallback to '" << adpfs[0]->mName
396                           << "'";
397             }
398         }
399     }
400 
401     if (actions.empty()) {
402         LOG(ERROR) << "Failed to parse Actions section from " << config_path;
403         return nullptr;
404     }
405 
406     auto const gpu_sysfs_node = ParseGpuSysfsNode(json_doc);
407 
408     sp<NodeLooperThread> nm = new NodeLooperThread(std::move(nodes));
409     sInstance =
410             std::make_unique<HintManager>(std::move(nm), actions, adpfs, tag_adpfs, gpu_sysfs_node);
411 
412     if (!HintManager::InitHintStatus(sInstance)) {
413         LOG(ERROR) << "Failed to initialize hint status";
414         return nullptr;
415     }
416 
417     LOG(INFO) << "Initialized HintManager from JSON config: " << config_path;
418 
419     if (start) {
420         sInstance->Start();
421     }
422 
423     return HintManager::GetInstance();
424 }
425 
ParseNodes(const std::string & json_doc)426 std::vector<std::unique_ptr<Node>> HintManager::ParseNodes(const std::string &json_doc) {
427     // function starts
428     std::vector<std::unique_ptr<Node>> nodes_parsed;
429     std::set<std::string> nodes_name_parsed;
430     std::set<std::string> nodes_path_parsed;
431     Json::Value root;
432     Json::CharReaderBuilder builder;
433     std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
434     std::string errorMessage;
435 
436     if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
437         LOG(ERROR) << "Failed to parse JSON config: " << errorMessage;
438         return nodes_parsed;
439     }
440 
441     Json::Value nodes = root["Nodes"];
442     for (Json::Value::ArrayIndex i = 0; i < nodes.size(); ++i) {
443         std::string name = nodes[i]["Name"].asString();
444         LOG(VERBOSE) << "Node[" << i << "]'s Name: " << name;
445         if (name.empty()) {
446             LOG(ERROR) << "Failed to read "
447                        << "Node[" << i << "]'s Name";
448             nodes_parsed.clear();
449             return nodes_parsed;
450         }
451 
452         auto result = nodes_name_parsed.insert(name);
453         if (!result.second) {
454             LOG(ERROR) << "Duplicate Node[" << i << "]'s Name";
455             nodes_parsed.clear();
456             return nodes_parsed;
457         }
458 
459         std::string path = nodes[i]["Path"].asString();
460         LOG(VERBOSE) << "Node[" << i << "]'s Path: " << path;
461         if (path.empty()) {
462             LOG(ERROR) << "Failed to read "
463                        << "Node[" << i << "]'s Path";
464             nodes_parsed.clear();
465             return nodes_parsed;
466         }
467 
468         result = nodes_path_parsed.insert(path);
469         if (!result.second) {
470             LOG(ERROR) << "Duplicate Node[" << i << "]'s Path";
471             nodes_parsed.clear();
472             return nodes_parsed;
473         }
474 
475         bool is_event_node = false;
476         bool is_file = false;
477         std::string node_type = nodes[i]["Type"].asString();
478         LOG(VERBOSE) << "Node[" << i << "]'s Type: " << node_type;
479         if (node_type.empty()) {
480             is_file = true;
481             LOG(VERBOSE) << "Failed to read "
482                          << "Node[" << i << "]'s Type, set to 'File' as default";
483         } else if (node_type == "Event") {
484             is_event_node = true;
485         } else if (node_type == "File") {
486             is_file = true;
487         } else if (node_type == "Property") {
488             is_file = false;
489         } else {
490             LOG(ERROR) << "Invalid Node[" << i
491                        << "]'s Type: only File and Property supported.";
492             nodes_parsed.clear();
493             return nodes_parsed;
494         }
495 
496         std::vector<RequestGroup> values_parsed;
497         std::set<std::string> values_set_parsed;
498         Json::Value values = nodes[i]["Values"];
499         for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
500             std::string value = values[j].asString();
501             LOG(VERBOSE) << "Node[" << i << "]'s Value[" << j << "]: " << value;
502             auto result = values_set_parsed.insert(value);
503             if (!result.second) {
504                 LOG(ERROR) << "Duplicate value parsed in Node[" << i
505                            << "]'s Value[" << j << "]";
506                 nodes_parsed.clear();
507                 return nodes_parsed;
508             }
509             if (is_file && value.empty()) {
510                 LOG(ERROR) << "Failed to read Node[" << i << "]'s Value[" << j
511                            << "]";
512                 nodes_parsed.clear();
513                 return nodes_parsed;
514             }
515             values_parsed.emplace_back(value);
516         }
517         if (values_parsed.size() < 1) {
518             LOG(ERROR) << "Failed to read Node[" << i << "]'s Values";
519             nodes_parsed.clear();
520             return nodes_parsed;
521         }
522 
523         Json::UInt64 default_index = values_parsed.size() - 1;
524         if (nodes[i]["DefaultIndex"].empty() ||
525             !nodes[i]["DefaultIndex"].isUInt64()) {
526             LOG(INFO) << "Failed to read Node[" << i
527                       << "]'s DefaultIndex, set to last index: "
528                       << default_index;
529         } else {
530             default_index = nodes[i]["DefaultIndex"].asUInt64();
531         }
532         if (default_index > values_parsed.size() - 1) {
533             default_index = values_parsed.size() - 1;
534             LOG(ERROR) << "Node[" << i
535                        << "]'s DefaultIndex out of bound, max value index: "
536                        << default_index;
537             nodes_parsed.clear();
538             return nodes_parsed;
539         }
540         LOG(VERBOSE) << "Node[" << i << "]'s DefaultIndex: " << default_index;
541 
542         bool reset = false;
543         if (nodes[i]["ResetOnInit"].empty() ||
544             !nodes[i]["ResetOnInit"].isBool()) {
545             LOG(INFO) << "Failed to read Node[" << i
546                       << "]'s ResetOnInit, set to 'false'";
547         } else {
548             reset = nodes[i]["ResetOnInit"].asBool();
549         }
550         LOG(VERBOSE) << "Node[" << i << "]'s ResetOnInit: " << std::boolalpha
551                      << reset << std::noboolalpha;
552 
553         if (is_event_node) {
554             auto update_callback = [](const std::string &name, const std::string &path,
555                                       const std::string &val) {
556                 HintManager::GetInstance()->OnNodeUpdate(name, path, val);
557             };
558             nodes_parsed.emplace_back(std::make_unique<EventNode>(
559                     name, path, values_parsed, static_cast<std::size_t>(default_index), reset,
560                     update_callback));
561         } else if (is_file) {
562             bool truncate = android::base::GetBoolProperty(kPowerHalTruncateProp, true);
563             if (nodes[i]["Truncate"].empty() || !nodes[i]["Truncate"].isBool()) {
564                 LOG(INFO) << "Failed to read Node[" << i << "]'s Truncate, set to 'true'";
565             } else {
566                 truncate = nodes[i]["Truncate"].asBool();
567             }
568             LOG(VERBOSE) << "Node[" << i << "]'s Truncate: " << std::boolalpha << truncate
569                          << std::noboolalpha;
570 
571             bool hold_fd = false;
572             if (nodes[i]["HoldFd"].empty() || !nodes[i]["HoldFd"].isBool()) {
573                 LOG(INFO) << "Failed to read Node[" << i
574                           << "]'s HoldFd, set to 'false'";
575             } else {
576                 hold_fd = nodes[i]["HoldFd"].asBool();
577             }
578             LOG(VERBOSE) << "Node[" << i << "]'s HoldFd: " << std::boolalpha
579                          << hold_fd << std::noboolalpha;
580 
581             bool write_only = false;
582             if (nodes[i]["WriteOnly"].empty() || !nodes[i]["WriteOnly"].isBool()) {
583                 LOG(INFO) << "Failed to read Node[" << i
584                           << "]'s WriteOnly, set to 'false'";
585             } else {
586                 write_only = nodes[i]["WriteOnly"].asBool();
587             }
588             LOG(VERBOSE) << "Node[" << i << "]'s WriteOnly: " << std::boolalpha
589                          << write_only << std::noboolalpha;
590 
591             nodes_parsed.emplace_back(std::make_unique<FileNode>(
592                     name, path, values_parsed, static_cast<std::size_t>(default_index), reset,
593                     truncate, hold_fd, write_only));
594         } else {
595             nodes_parsed.emplace_back(std::make_unique<PropertyNode>(
596                     name, path, values_parsed, static_cast<std::size_t>(default_index), reset));
597         }
598     }
599     LOG(INFO) << nodes_parsed.size() << " Nodes parsed successfully";
600     return nodes_parsed;
601 }
602 
ParseActions(const std::string & json_doc,const std::vector<std::unique_ptr<Node>> & nodes)603 std::unordered_map<std::string, Hint> HintManager::ParseActions(
604         const std::string &json_doc, const std::vector<std::unique_ptr<Node>> &nodes) {
605     // function starts
606     std::unordered_map<std::string, Hint> actions_parsed;
607     Json::Value root;
608     Json::CharReaderBuilder builder;
609     std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
610     std::string errorMessage;
611 
612     if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
613         LOG(ERROR) << "Failed to parse JSON config";
614         return actions_parsed;
615     }
616 
617     Json::Value actions = root["Actions"];
618     std::size_t total_parsed = 0;
619 
620     std::map<std::string, std::size_t> nodes_index;
621     for (std::size_t i = 0; i < nodes.size(); ++i) {
622         nodes_index[nodes[i]->GetName()] = i;
623     }
624 
625     for (Json::Value::ArrayIndex i = 0; i < actions.size(); ++i) {
626         const std::string& hint_type = actions[i]["PowerHint"].asString();
627         LOG(VERBOSE) << "Action[" << i << "]'s PowerHint: " << hint_type;
628         if (hint_type.empty()) {
629             LOG(ERROR) << "Failed to read "
630                        << "Action[" << i << "]'s PowerHint";
631             actions_parsed.clear();
632             return actions_parsed;
633         }
634 
635         HintActionType action_type = HintActionType::Node;
636         std::string type_string = actions[i]["Type"].asString();
637         std::string enable_property = actions[i]["EnableProperty"].asString();
638         LOG(VERBOSE) << "Action[" << i << "]'s Type: " << type_string;
639         if (type_string.empty()) {
640             LOG(VERBOSE) << "Failed to read "
641                          << "Action[" << i << "]'s Type, set to 'Node' as default";
642         } else if (type_string == "DoHint") {
643             action_type = HintActionType::DoHint;
644         } else if (type_string == "EndHint") {
645             action_type = HintActionType::EndHint;
646         } else if (type_string == "MaskHint") {
647             action_type = HintActionType::MaskHint;
648         } else {
649             LOG(ERROR) << "Invalid Action[" << i << "]'s Type: " << type_string;
650             actions_parsed.clear();
651             return actions_parsed;
652         }
653         if (action_type == HintActionType::Node) {
654             std::string node_name = actions[i]["Node"].asString();
655             LOG(VERBOSE) << "Action[" << i << "]'s Node: " << node_name;
656             std::size_t node_index;
657 
658             if (nodes_index.find(node_name) == nodes_index.end()) {
659                 LOG(ERROR) << "Failed to find "
660                            << "Action[" << i << "]'s Node from Nodes section: [" << node_name
661                            << "]";
662                 actions_parsed.clear();
663                 return actions_parsed;
664             }
665             node_index = nodes_index[node_name];
666 
667             std::string value_name = actions[i]["Value"].asString();
668             LOG(VERBOSE) << "Action[" << i << "]'s Value: " << value_name;
669             std::size_t value_index = 0;
670 
671             if (!nodes[node_index]->GetValueIndex(value_name, &value_index)) {
672                 LOG(ERROR) << "Failed to read Action[" << i << "]'s Value";
673                 LOG(ERROR) << "Action[" << i << "]'s Value " << value_name
674                            << " is not defined in Node[" << node_name << "]";
675                 actions_parsed.clear();
676                 return actions_parsed;
677             }
678             LOG(VERBOSE) << "Action[" << i << "]'s ValueIndex: " << value_index;
679 
680             Json::UInt64 duration = 0;
681             if (actions[i]["Duration"].empty() || !actions[i]["Duration"].isUInt64()) {
682                 LOG(ERROR) << "Failed to read Action[" << i << "]'s Duration";
683                 actions_parsed.clear();
684                 return actions_parsed;
685             } else {
686                 duration = actions[i]["Duration"].asUInt64();
687             }
688             LOG(VERBOSE) << "Action[" << i << "]'s Duration: " << duration;
689 
690             for (const auto &action : actions_parsed[hint_type].node_actions) {
691                 if (action.node_index == node_index) {
692                     LOG(ERROR)
693                         << "Action[" << i
694                         << "]'s NodeIndex is duplicated with another Action";
695                     actions_parsed.clear();
696                     return actions_parsed;
697                 }
698             }
699             actions_parsed[hint_type].node_actions.emplace_back(
700                     node_index, value_index, std::chrono::milliseconds(duration), enable_property);
701 
702         } else {
703             const std::string &hint_value = actions[i]["Value"].asString();
704             LOG(VERBOSE) << "Action[" << i << "]'s Value: " << hint_value;
705             if (hint_value.empty()) {
706                 LOG(ERROR) << "Failed to read "
707                            << "Action[" << i << "]'s Value";
708                 actions_parsed.clear();
709                 return actions_parsed;
710             }
711             actions_parsed[hint_type].hint_actions.emplace_back(action_type, hint_value,
712                                                                 enable_property);
713         }
714 
715         ++total_parsed;
716     }
717 
718     LOG(INFO) << total_parsed << " actions parsed successfully";
719 
720     for (const auto& action : actions_parsed) {
721         LOG(INFO) << "PowerHint " << action.first << " has " << action.second.node_actions.size()
722                   << " node actions"
723                   << ", and " << action.second.hint_actions.size() << " hint actions parsed";
724     }
725 
726     return actions_parsed;
727 }
728 
729 #define ADPF_PARSE(VARIABLE, ENTRY, TYPE)                                                        \
730     static_assert(std::is_same<decltype(adpfs[i][ENTRY].as##TYPE()), decltype(VARIABLE)>::value, \
731                   "Parser type mismatch");                                                       \
732     if (adpfs[i][ENTRY].empty() || !adpfs[i][ENTRY].is##TYPE()) {                                \
733         LOG(ERROR) << "Failed to read AdpfConfig[" << name << "][" ENTRY "]'s Values";           \
734         adpfs_parsed.clear();                                                                    \
735         return adpfs_parsed;                                                                     \
736     }                                                                                            \
737     VARIABLE = adpfs[i][ENTRY].as##TYPE()
738 
739 #define ADPF_PARSE_OPTIONAL(VARIABLE, ENTRY, TYPE)                     \
740     static_assert(std::is_same<decltype(adpfs[i][ENTRY].as##TYPE()),   \
741                                decltype(VARIABLE)::value_type>::value, \
742                   "Parser type mismatch");                             \
743     if (!adpfs[i][ENTRY].empty() && adpfs[i][ENTRY].is##TYPE()) {      \
744         VARIABLE = adpfs[i][ENTRY].as##TYPE();                         \
745     }
746 
ParseAdpfConfigs(const std::string & json_doc)747 std::vector<std::shared_ptr<AdpfConfig>> HintManager::ParseAdpfConfigs(
748         const std::string &json_doc) {
749     // function starts
750     bool pidOn;
751     double pidPOver;
752     double pidPUnder;
753     double pidI;
754     double pidDOver;
755     double pidDUnder;
756     int64_t pidIInit;
757     int64_t pidIHighLimit;
758     int64_t pidILowLimit;
759     bool adpfUclamp;
760     uint32_t uclampMinInit;
761     uint32_t uclampMinHighLimit;
762     uint32_t uclampMinLowLimit;
763     uint64_t samplingWindowP;
764     uint64_t samplingWindowI;
765     uint64_t samplingWindowD;
766     double staleTimeFactor;
767     uint64_t reportingRate;
768     double targetTimeFactor;
769 
770     std::vector<std::shared_ptr<AdpfConfig>> adpfs_parsed;
771     std::set<std::string> name_parsed;
772     Json::Value root;
773     Json::CharReaderBuilder builder;
774     std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
775     std::string errorMessage;
776     if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
777         LOG(ERROR) << "Failed to parse JSON config: " << errorMessage;
778         return adpfs_parsed;
779     }
780     Json::Value adpfs = root["AdpfConfig"];
781     for (Json::Value::ArrayIndex i = 0; i < adpfs.size(); ++i) {
782         std::optional<bool> gpuBoost;
783         std::optional<uint64_t> gpuBoostCapacityMax;
784         uint64_t gpuCapacityLoadUpHeadroom = 0;
785         std::string name = adpfs[i]["Name"].asString();
786         LOG(VERBOSE) << "AdpfConfig[" << i << "]'s Name: " << name;
787         if (name.empty()) {
788             LOG(ERROR) << "Failed to read "
789                        << "AdpfConfig[" << i << "]'s Name";
790             adpfs_parsed.clear();
791             return adpfs_parsed;
792         }
793         auto result = name_parsed.insert(name);
794         if (!result.second) {
795             LOG(ERROR) << "Duplicate AdpfConfig[" << i << "]'s Name";
796             adpfs_parsed.clear();
797             return adpfs_parsed;
798         }
799 
800         // heuristic boost configs
801         std::optional<bool> heuristicBoostOn;
802         std::optional<uint32_t> hBoostModerateJankThreshold;
803         std::optional<double> hBoostOffMaxAvgDurRatio;
804         std::optional<double> hBoostSevereJankPidPu;
805         std::optional<uint32_t> hBoostSevereJankThreshold;
806         std::optional<std::pair<uint32_t, uint32_t>> hBoostUclampMinCeilingRange;
807         std::optional<std::pair<uint32_t, uint32_t>> hBoostUclampMinFloorRange;
808         std::optional<double> jankCheckTimeFactor;
809         std::optional<uint32_t> lowFrameRateThreshold;
810         std::optional<uint32_t> maxRecordsNum;
811         std::optional<bool> heuristicRampup;
812         std::optional<uint32_t> defaultRampupMult;
813         std::optional<uint32_t> highRampupMult;
814 
815         std::optional<uint32_t> uclampMinLoadUp;
816         std::optional<uint32_t> uclampMinLoadReset;
817         std::optional<int32_t> uclampMaxEfficientBase;
818         std::optional<int32_t> uclampMaxEfficientOffset;
819 
820         ADPF_PARSE(pidOn, "PID_On", Bool);
821         ADPF_PARSE(pidPOver, "PID_Po", Double);
822         ADPF_PARSE(pidPUnder, "PID_Pu", Double);
823         ADPF_PARSE(pidI, "PID_I", Double);
824         ADPF_PARSE(pidIInit, "PID_I_Init", Int64);
825         ADPF_PARSE(pidIHighLimit, "PID_I_High", Int64);
826         ADPF_PARSE(pidILowLimit, "PID_I_Low", Int64);
827         ADPF_PARSE(pidDOver, "PID_Do", Double);
828         ADPF_PARSE(pidDUnder, "PID_Du", Double);
829         ADPF_PARSE(adpfUclamp, "UclampMin_On", Bool);
830         ADPF_PARSE(uclampMinInit, "UclampMin_Init", UInt);
831         ADPF_PARSE_OPTIONAL(uclampMinLoadUp, "UclampMin_LoadUp", UInt);
832         ADPF_PARSE_OPTIONAL(uclampMinLoadReset, "UclampMin_LoadReset", UInt);
833         ADPF_PARSE(uclampMinHighLimit, "UclampMin_High", UInt);
834         ADPF_PARSE(uclampMinLowLimit, "UclampMin_Low", UInt);
835         ADPF_PARSE(samplingWindowP, "SamplingWindow_P", UInt64);
836         ADPF_PARSE(samplingWindowI, "SamplingWindow_I", UInt64);
837         ADPF_PARSE(samplingWindowD, "SamplingWindow_D", UInt64);
838         ADPF_PARSE(staleTimeFactor, "StaleTimeFactor", Double);
839         ADPF_PARSE(reportingRate, "ReportingRateLimitNs", UInt64);
840         ADPF_PARSE(targetTimeFactor, "TargetTimeFactor", Double);
841         ADPF_PARSE_OPTIONAL(heuristicBoostOn, "HeuristicBoost_On", Bool);
842         ADPF_PARSE_OPTIONAL(hBoostModerateJankThreshold, "HBoostModerateJankThreshold", UInt);
843         ADPF_PARSE_OPTIONAL(hBoostOffMaxAvgDurRatio, "HBoostOffMaxAvgDurRatio", Double);
844         ADPF_PARSE_OPTIONAL(hBoostSevereJankPidPu, "HBoostSevereJankPidPu", Double);
845         ADPF_PARSE_OPTIONAL(hBoostSevereJankThreshold, "HBoostSevereJankThreshold", UInt);
846         ADPF_PARSE_OPTIONAL(jankCheckTimeFactor, "JankCheckTimeFactor", Double);
847         ADPF_PARSE_OPTIONAL(lowFrameRateThreshold, "LowFrameRateThreshold", UInt);
848         ADPF_PARSE_OPTIONAL(maxRecordsNum, "MaxRecordsNum", UInt);
849         ADPF_PARSE_OPTIONAL(heuristicRampup, "HeuristicRampup", Bool);
850         ADPF_PARSE_OPTIONAL(defaultRampupMult, "DefaultRampupMult", UInt);
851         ADPF_PARSE_OPTIONAL(highRampupMult, "HighRampupMult", UInt);
852         ADPF_PARSE_OPTIONAL(uclampMaxEfficientBase, "UclampMax_EfficientBase", Int);
853         ADPF_PARSE_OPTIONAL(uclampMaxEfficientOffset, "UclampMax_EfficientOffset", Int);
854 
855         if (!adpfs[i]["GpuBoost"].empty() && adpfs[i]["GpuBoost"].isBool()) {
856             gpuBoost = adpfs[i]["GpuBoost"].asBool();
857         }
858         if (!adpfs[i]["GpuCapacityBoostMax"].empty() &&
859             adpfs[i]["GpuCapacityBoostMax"].isUInt64()) {
860             gpuBoostCapacityMax = adpfs[i]["GpuCapacityBoostMax"].asUInt64();
861         }
862         if (!adpfs[i]["GpuCapacityLoadUpHeadroom"].empty() &&
863             adpfs[i]["GpuCapacityLoadUpHeadroom"].isUInt64()) {
864             gpuCapacityLoadUpHeadroom = adpfs[i]["GpuCapacityLoadUpHeadroom"].asUInt64();
865         }
866 
867         if (!adpfs[i]["HBoostUclampMinCeilingRange"].empty()) {
868             Json::Value ceilRange = adpfs[i]["HBoostUclampMinCeilingRange"];
869             if (ceilRange.size() == 2 && ceilRange[0].isUInt() && ceilRange[1].isUInt()) {
870                 hBoostUclampMinCeilingRange =
871                         std::make_pair(ceilRange[0].asUInt(), ceilRange[1].asUInt());
872             }
873         }
874 
875         if (!adpfs[i]["HBoostUclampMinFloorRange"].empty()) {
876             Json::Value floorRange = adpfs[i]["HBoostUclampMinFloorRange"];
877             if (floorRange.size() == 2 && floorRange[0].isUInt() && floorRange[1].isUInt()) {
878                 hBoostUclampMinFloorRange =
879                         std::make_pair(floorRange[0].asUInt(), floorRange[1].asUInt());
880             }
881         }
882 
883         // Check all the heuristic configurations are there if heuristic boost is going to
884         // be used.
885         if (heuristicBoostOn.has_value()) {
886             if (!hBoostModerateJankThreshold.has_value() || !hBoostOffMaxAvgDurRatio.has_value() ||
887                 !hBoostSevereJankPidPu.has_value() || !hBoostSevereJankThreshold.has_value() ||
888                 !hBoostUclampMinCeilingRange.has_value() ||
889                 !hBoostUclampMinFloorRange.has_value() || !jankCheckTimeFactor.has_value() ||
890                 !lowFrameRateThreshold.has_value() || !maxRecordsNum.has_value()) {
891                 LOG(ERROR) << "Part of the heuristic boost configurations are missing!";
892                 adpfs_parsed.clear();
893                 return adpfs_parsed;
894             }
895 
896             // check heuristic rampup configurations.
897             if (heuristicRampup.has_value() &&
898                 (!defaultRampupMult.has_value() || !highRampupMult.has_value())) {
899                 LOG(ERROR) << "Part of the heuristic rampup configurations are missing!";
900                 adpfs_parsed.clear();
901                 return adpfs_parsed;
902             }
903         }
904 
905         if (uclampMaxEfficientBase.has_value() != uclampMaxEfficientBase.has_value()) {
906             LOG(ERROR) << "Part of the power efficiency configuration is missing!";
907             adpfs_parsed.clear();
908             return adpfs_parsed;
909         }
910 
911         if (!uclampMinLoadUp.has_value()) {
912             uclampMinLoadUp = uclampMinHighLimit;
913         }
914         if (!uclampMinLoadReset.has_value()) {
915             uclampMinLoadReset = uclampMinHighLimit;
916         }
917 
918         adpfs_parsed.emplace_back(std::make_shared<AdpfConfig>(
919                 name, pidOn, pidPOver, pidPUnder, pidI, pidIInit, pidIHighLimit, pidILowLimit,
920                 pidDOver, pidDUnder, adpfUclamp, uclampMinInit, uclampMinHighLimit,
921                 uclampMinLowLimit, samplingWindowP, samplingWindowI, samplingWindowD, reportingRate,
922                 targetTimeFactor, staleTimeFactor, gpuBoost, gpuBoostCapacityMax,
923                 gpuCapacityLoadUpHeadroom, heuristicBoostOn, hBoostModerateJankThreshold,
924                 hBoostOffMaxAvgDurRatio, hBoostSevereJankPidPu, hBoostSevereJankThreshold,
925                 hBoostUclampMinCeilingRange, hBoostUclampMinFloorRange, jankCheckTimeFactor,
926                 lowFrameRateThreshold, maxRecordsNum, heuristicRampup, defaultRampupMult,
927                 highRampupMult, uclampMinLoadUp.value(), uclampMinLoadReset.value(),
928                 uclampMaxEfficientBase, uclampMaxEfficientOffset));
929     }
930     LOG(INFO) << adpfs_parsed.size() << " AdpfConfigs parsed successfully";
931     return adpfs_parsed;
932 }
933 
934 // TODO(jimmyshiu@): Deprecated. Remove once all powerhint.json up-to-date.
GetAdpfProfileFromDoHint() const935 std::shared_ptr<AdpfConfig> HintManager::GetAdpfProfileFromDoHint() const {
936     if (adpfs_.empty())
937         return nullptr;
938     return adpfs_[adpf_index_];
939 }
940 
941 // TODO(jimmyshiu@): Deprecated. Remove once all powerhint.json up-to-date.
SetAdpfProfileFromDoHint(const std::string & profile_name)942 bool HintManager::SetAdpfProfileFromDoHint(const std::string &profile_name) {
943     for (std::size_t i = 0; i < adpfs_.size(); ++i) {
944         if (adpfs_[i]->mName == profile_name) {
945             if (adpf_index_ != i) {
946                 ATRACE_NAME(StringPrintf("%s %s:%s", __func__, adpfs_[adpf_index_]->mName.c_str(),
947                                          profile_name.c_str())
948                                     .c_str());
949                 adpf_index_ = i;
950             }
951             return true;
952         }
953     }
954     return false;
955 }
956 
IsAdpfSupported() const957 bool HintManager::IsAdpfSupported() const {
958     return !adpfs_.empty();
959 }
960 
GetAdpfProfile(const std::string & tag) const961 std::shared_ptr<AdpfConfig> HintManager::GetAdpfProfile(const std::string &tag) const {
962     if (adpfs_.empty())
963         return nullptr;
964     if (tag_profile_map_.find(tag) == tag_profile_map_.end()) {
965         // TODO(jimmyshiu@): `return adpfs_[0]` once the GetAdpfProfileFromDoHint() retired.
966         return GetAdpfProfileFromDoHint();
967     }
968     return tag_profile_map_.at(tag);
969 }
970 
SetAdpfProfile(const std::string & tag,const std::string & profile)971 bool HintManager::SetAdpfProfile(const std::string &tag, const std::string &profile) {
972     if (tag_profile_map_.find(tag) == tag_profile_map_.end()) {
973         LOG(WARNING) << "SetAdpfProfile('" << tag << "', " << profile << ") Invalidate Tag!!!";
974         return false;
975     }
976     if (tag_profile_map_[tag]->mName == profile) {
977         LOG(VERBOSE) << "SetAdpfProfile:(" << tag << ", " << profile << ") value not changed!";
978         return true;
979     }
980 
981     bool updated = false;
982     for (std::size_t i = 0; i < adpfs_.size(); ++i) {
983         if (adpfs_[i]->mName == profile) {
984             LOG(DEBUG) << "SetAdpfProfile('" << tag << "', '" << profile << "') Done!";
985             tag_profile_map_[tag] = adpfs_[i];
986             updated = true;
987             break;
988         }
989     }
990     if (!updated) {
991         LOG(WARNING) << "SetAdpfProfile(" << tag << ") failed to find profile:'" << profile << "'";
992     }
993     return updated;
994 }
995 
IsAdpfProfileSupported(const std::string & profile_name) const996 bool HintManager::IsAdpfProfileSupported(const std::string &profile_name) const {
997     for (std::size_t i = 0; i < adpfs_.size(); ++i) {
998         if (adpfs_[i]->mName == profile_name) {
999             return true;
1000         }
1001     }
1002     return false;
1003 }
1004 
OnNodeUpdate(const std::string & name,const std::string & path,const std::string & value)1005 void HintManager::OnNodeUpdate(const std::string &name,
1006                                __attribute__((unused)) const std::string &path,
1007                                const std::string &value) {
1008     // Check if the node is to update ADPF.
1009     if (path.starts_with(kAdpfEventNodePath)) {
1010         std::string tag = path.substr(strlen(kAdpfEventNodePath));
1011         bool updated = SetAdpfProfile(tag, value);
1012         if (!updated) {
1013             LOG(DEBUG) << "OnNodeUpdate:[" << name << "] failed to update '" << value << "'";
1014             return;
1015         }
1016         auto &callback_list = tag_update_callback_list_[tag];
1017         for (const auto &callback : callback_list) {
1018             (*callback)(tag_profile_map_[tag]);
1019         }
1020     }
1021 }
1022 
RegisterAdpfUpdateEvent(const std::string & tag,AdpfCallback * update_adpf_func)1023 void HintManager::RegisterAdpfUpdateEvent(const std::string &tag, AdpfCallback *update_adpf_func) {
1024     tag_update_callback_list_[tag].push_back(update_adpf_func);
1025 }
1026 
UnregisterAdpfUpdateEvent(const std::string & tag,AdpfCallback * update_adpf_func)1027 void HintManager::UnregisterAdpfUpdateEvent(const std::string &tag,
1028                                             AdpfCallback *update_adpf_func) {
1029     auto &callback_list = tag_update_callback_list_[tag];
1030     // Use std::find to locate the function object
1031     auto it = std::find_if(
1032             callback_list.begin(), callback_list.end(),
1033             [update_adpf_func](const std::function<void(const std::shared_ptr<AdpfConfig>)> *func) {
1034                 return func == update_adpf_func;
1035             });
1036     if (it != callback_list.end()) {
1037         // Erase the found function object
1038         callback_list.erase(it);
1039     }
1040 }
1041 
gpu_sysfs_config_path() const1042 std::optional<std::string> HintManager::gpu_sysfs_config_path() const {
1043     return gpu_sysfs_config_path_;
1044 }
1045 
1046 }  // namespace perfmgr
1047 }  // namespace android
1048