1 /*
2  * Copyright 2021 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 "powerhal-libperfmgr"
18 #define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
19 
20 #include "PowerSessionManager.h"
21 
22 #include <android-base/file.h>
23 #include <log/log.h>
24 #include <perfmgr/HintManager.h>
25 #include <private/android_filesystem_config.h>
26 #include <processgroup/processgroup.h>
27 #include <sys/syscall.h>
28 #include <utils/Trace.h>
29 
30 #include "AppDescriptorTrace.h"
31 #include "AppHintDesc.h"
32 #include "tests/mocks/MockHintManager.h"
33 
34 namespace aidl {
35 namespace google {
36 namespace hardware {
37 namespace power {
38 namespace impl {
39 namespace pixel {
40 
41 using ::android::perfmgr::HintManager;
42 constexpr char kGameModeName[] = "GAME";
43 
44 namespace {
45 /* there is no glibc or bionic wrapper */
46 struct sched_attr {
47     __u32 size;
48     __u32 sched_policy;
49     __u64 sched_flags;
50     __s32 sched_nice;
51     __u32 sched_priority;
52     __u64 sched_runtime;
53     __u64 sched_deadline;
54     __u64 sched_period;
55     __u32 sched_util_min;
56     __u32 sched_util_max;
57 };
58 
set_uclamp(int tid,UclampRange range)59 static int set_uclamp(int tid, UclampRange range) {
60     // Ensure min and max are bounded by the range limits and each other
61     range.uclampMin = std::min(std::max(kUclampMin, range.uclampMin), kUclampMax);
62     range.uclampMax = std::min(std::max(range.uclampMax, range.uclampMin), kUclampMax);
63     sched_attr attr = {};
64     attr.size = sizeof(attr);
65 
66     attr.sched_flags =
67             (SCHED_FLAG_KEEP_ALL | SCHED_FLAG_UTIL_CLAMP_MIN | SCHED_FLAG_UTIL_CLAMP_MAX);
68     attr.sched_util_min = range.uclampMin;
69     attr.sched_util_max = range.uclampMax;
70 
71     const int ret = syscall(__NR_sched_setattr, tid, attr, 0);
72     if (ret) {
73         ALOGW("sched_setattr failed for thread %d, err=%d", tid, errno);
74         return errno;
75     }
76     return 0;
77 }
78 }  // namespace
79 
80 template <class HintManagerT>
updateHintMode(const std::string & mode,bool enabled)81 void PowerSessionManager<HintManagerT>::updateHintMode(const std::string &mode, bool enabled) {
82     ALOGD("%s %s:%b", __func__, mode.c_str(), enabled);
83     if (mode.compare(kGameModeName) == 0) {
84         mGameModeEnabled = enabled;
85     }
86 
87     // TODO(jimmyshiu@): Deprecated. Remove once all powerhint.json up-to-date.
88     if (enabled && HintManager::GetInstance()->GetAdpfProfileFromDoHint()) {
89         HintManager::GetInstance()->SetAdpfProfileFromDoHint(mode);
90     }
91 }
92 
93 template <class HintManagerT>
getGameModeEnableState()94 bool PowerSessionManager<HintManagerT>::getGameModeEnableState() {
95     return mGameModeEnabled;
96 }
97 
98 template <class HintManagerT>
addPowerSession(const std::string & idString,const std::shared_ptr<AppHintDesc> & sessionDescriptor,const std::shared_ptr<AppDescriptorTrace> & sessionTrace,const std::vector<int32_t> & threadIds,const ProcessTag procTag)99 void PowerSessionManager<HintManagerT>::addPowerSession(
100         const std::string &idString, const std::shared_ptr<AppHintDesc> &sessionDescriptor,
101         const std::shared_ptr<AppDescriptorTrace> &sessionTrace,
102         const std::vector<int32_t> &threadIds, const ProcessTag procTag) {
103     if (!sessionDescriptor) {
104         ALOGE("sessionDescriptor is null. PowerSessionManager failed to add power session: %s",
105               idString.c_str());
106         return;
107     }
108     const auto timeNow = std::chrono::steady_clock::now();
109     SessionValueEntry sve;
110     sve.tgid = sessionDescriptor->tgid;
111     sve.uid = sessionDescriptor->uid;
112     sve.idString = idString;
113     sve.isActive = sessionDescriptor->is_active;
114     sve.isAppSession = sessionDescriptor->uid >= AID_APP_START;
115     sve.lastUpdatedTime = timeNow;
116     sve.votes = std::make_shared<Votes>();
117     sve.sessionTrace = sessionTrace;
118     sve.votes->add(
119             static_cast<std::underlying_type_t<AdpfVoteType>>(AdpfVoteType::CPU_VOTE_DEFAULT),
120             CpuVote(false, timeNow, sessionDescriptor->targetNs, kUclampMin, kUclampMax));
121 
122     bool addedRes = false;
123     {
124         std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
125         addedRes = mSessionTaskMap.add(sessionDescriptor->sessionId, sve, {});
126     }
127     if (!addedRes) {
128         ALOGE("sessionTaskMap failed to add power session: %" PRId64, sessionDescriptor->sessionId);
129     }
130 
131     setThreadsFromPowerSession(sessionDescriptor->sessionId, threadIds, procTag);
132 }
133 
134 template <class HintManagerT>
removePowerSession(int64_t sessionId,const ProcessTag procTag)135 void PowerSessionManager<HintManagerT>::removePowerSession(int64_t sessionId,
136                                                            const ProcessTag procTag) {
137     // To remove a session we also need to undo the effects the session
138     // has on currently enabled votes which means setting vote to inactive
139     // and then forceing a uclamp update to occur
140     forceSessionActive(sessionId, false);
141 
142     std::vector<pid_t> addedThreads;
143     std::vector<pid_t> removedThreads;
144 
145     {
146         // Wait till end to remove session because it needs to be around for apply U clamp
147         // to work above since applying the uclamp needs a valid session id
148         std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
149         mSessionTaskMap.replace(sessionId, {}, &addedThreads, &removedThreads);
150         mSessionTaskMap.remove(sessionId);
151     }
152 
153     if (procTag == ProcessTag::SYSTEM_UI) {
154         for (auto tid : removedThreads) {
155             if (!SetTaskProfiles(tid, {"SCHED_QOS_SENSITIVE_EXTREME_CLEAR"})) {
156                 ALOGE("Failed to set SCHED_QOS_SENSITIVE_EXTREME_CLEAR task profile for tid:%d",
157                       tid);
158             }
159         }
160     } else {
161         for (auto tid : removedThreads) {
162             if (!SetTaskProfiles(tid, {"SCHED_QOS_SENSITIVE_STANDARD_CLEAR"})) {
163                 ALOGE("Failed to set SCHED_QOS_SENSITIVE_STANDARD_CLEAR task profile for tid:%d",
164                       tid);
165             }
166         }
167     }
168 
169     unregisterSession(sessionId);
170 }
171 
172 template <class HintManagerT>
setThreadsFromPowerSession(int64_t sessionId,const std::vector<int32_t> & threadIds,const ProcessTag procTag)173 void PowerSessionManager<HintManagerT>::setThreadsFromPowerSession(
174         int64_t sessionId, const std::vector<int32_t> &threadIds, const ProcessTag procTag) {
175     std::vector<pid_t> addedThreads;
176     std::vector<pid_t> removedThreads;
177     forceSessionActive(sessionId, false);
178     {
179         std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
180         mSessionTaskMap.replace(sessionId, threadIds, &addedThreads, &removedThreads);
181     }
182     if (procTag == ProcessTag::SYSTEM_UI) {
183         for (auto tid : addedThreads) {
184             if (!SetTaskProfiles(tid, {"SCHED_QOS_SENSITIVE_EXTREME_SET"})) {
185                 ALOGE("Failed to set SCHED_QOS_SENSITIVE_EXTREME_SET task profile for tid:%d", tid);
186             }
187         }
188     } else {
189         for (auto tid : addedThreads) {
190             if (!SetTaskProfiles(tid, {"SCHED_QOS_SENSITIVE_STANDARD_SET"})) {
191                 ALOGE("Failed to set SCHED_QOS_SENSITIVE_STANDARD_SET task profile for tid:%d",
192                       tid);
193             }
194         }
195     }
196     if (procTag == ProcessTag::SYSTEM_UI) {
197         for (auto tid : removedThreads) {
198             if (!SetTaskProfiles(tid, {"SCHED_QOS_SENSITIVE_EXTREME_CLEAR"})) {
199                 ALOGE("Failed to set SCHED_QOS_SENSITIVE_EXTREME_CLEAR task profile for tid:%d",
200                       tid);
201             }
202         }
203     } else {
204         for (auto tid : removedThreads) {
205             if (!SetTaskProfiles(tid, {"SCHED_QOS_SENSITIVE_STANDARD_CLEAR"})) {
206                 ALOGE("Failed to set SCHED_QOS_SENSITIVE_STANDARD_CLEAR task profile for tid:%d",
207                       tid);
208             }
209         }
210     }
211     forceSessionActive(sessionId, true);
212 }
213 
214 template <class HintManagerT>
isAnyAppSessionActive()215 std::optional<bool> PowerSessionManager<HintManagerT>::isAnyAppSessionActive() {
216     bool isAnyAppSessionActive = false;
217     {
218         std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
219         isAnyAppSessionActive =
220                 mSessionTaskMap.isAnyAppSessionActive(std::chrono::steady_clock::now());
221     }
222     return isAnyAppSessionActive;
223 }
224 
225 template <class HintManagerT>
updateUniversalBoostMode()226 void PowerSessionManager<HintManagerT>::updateUniversalBoostMode() {
227     const auto active = isAnyAppSessionActive();
228     if (!active.has_value()) {
229         return;
230     }
231     if (active.value()) {
232         disableSystemTopAppBoost();
233     } else {
234         enableSystemTopAppBoost();
235     }
236 }
237 
238 template <class HintManagerT>
dumpToFd(int fd)239 void PowerSessionManager<HintManagerT>::dumpToFd(int fd) {
240     std::ostringstream dump_buf;
241     dump_buf << "========== Begin PowerSessionManager ADPF list ==========\n";
242     std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
243     mSessionTaskMap.forEachSessionValTasks(
244             [&](auto /* sessionId */, const auto &sessionVal, const auto &tasks) {
245                 sessionVal.dump(dump_buf);
246                 dump_buf << " Tid:Ref[";
247 
248                 size_t tasksLen = tasks.size();
249                 for (auto taskId : tasks) {
250                     dump_buf << taskId << ":";
251                     const auto &sessionIds = mSessionTaskMap.getSessionIds(taskId);
252                     if (!sessionIds.empty()) {
253                         dump_buf << sessionIds.size();
254                     }
255                     if (tasksLen > 0) {
256                         dump_buf << ", ";
257                         --tasksLen;
258                     }
259                 }
260                 dump_buf << "]\n";
261             });
262     dump_buf << "========== End PowerSessionManager ADPF list ==========\n";
263     if (!::android::base::WriteStringToFd(dump_buf.str(), fd)) {
264         ALOGE("Failed to dump one of session list to fd:%d", fd);
265     }
266 }
267 
268 template <class HintManagerT>
pause(int64_t sessionId)269 void PowerSessionManager<HintManagerT>::pause(int64_t sessionId) {
270     {
271         std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
272         auto sessValPtr = mSessionTaskMap.findSession(sessionId);
273         if (nullptr == sessValPtr) {
274             ALOGW("Pause failed, session is null %" PRId64, sessionId);
275             return;
276         }
277 
278         if (!sessValPtr->isActive) {
279             ALOGW("Sess(%" PRId64 "), cannot pause, already inActive", sessionId);
280             return;
281         }
282         sessValPtr->isActive = false;
283     }
284     applyCpuAndGpuVotes(sessionId, std::chrono::steady_clock::now());
285     updateUniversalBoostMode();
286 }
287 
288 template <class HintManagerT>
resume(int64_t sessionId)289 void PowerSessionManager<HintManagerT>::resume(int64_t sessionId) {
290     {
291         std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
292         auto sessValPtr = mSessionTaskMap.findSession(sessionId);
293         if (nullptr == sessValPtr) {
294             ALOGW("Resume failed, session is null %" PRId64, sessionId);
295             return;
296         }
297 
298         if (sessValPtr->isActive) {
299             ALOGW("Sess(%" PRId64 "), cannot resume, already active", sessionId);
300             return;
301         }
302         sessValPtr->isActive = true;
303     }
304     applyCpuAndGpuVotes(sessionId, std::chrono::steady_clock::now());
305     updateUniversalBoostMode();
306 }
307 
308 template <class HintManagerT>
updateTargetWorkDuration(int64_t sessionId,AdpfVoteType voteId,std::chrono::nanoseconds durationNs)309 void PowerSessionManager<HintManagerT>::updateTargetWorkDuration(
310         int64_t sessionId, AdpfVoteType voteId, std::chrono::nanoseconds durationNs) {
311     int voteIdInt = static_cast<std::underlying_type_t<AdpfVoteType>>(voteId);
312     std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
313     auto sessValPtr = mSessionTaskMap.findSession(sessionId);
314     if (nullptr == sessValPtr) {
315         ALOGE("Failed to updateTargetWorkDuration, session val is null id: %" PRId64, sessionId);
316         return;
317     }
318 
319     sessValPtr->votes->updateDuration(voteIdInt, durationNs);
320     // Note, for now we are not recalculating and applying uclamp because
321     // that maintains behavior from before.  In the future we may want to
322     // revisit that decision.
323 }
324 
325 template <typename T>
shouldScheduleTimeout(Votes const & votes,int vote_id,std::chrono::time_point<T> deadline)326 auto shouldScheduleTimeout(Votes const &votes, int vote_id, std::chrono::time_point<T> deadline)
327         -> bool {
328     return !votes.voteIsActive(vote_id) || deadline < votes.voteTimeout(vote_id);
329 }
330 
331 template <class HintManagerT>
voteSet(int64_t sessionId,AdpfVoteType voteId,int uclampMin,int uclampMax,std::chrono::steady_clock::time_point startTime,std::chrono::nanoseconds durationNs)332 void PowerSessionManager<HintManagerT>::voteSet(int64_t sessionId, AdpfVoteType voteId,
333                                                 int uclampMin, int uclampMax,
334                                                 std::chrono::steady_clock::time_point startTime,
335                                                 std::chrono::nanoseconds durationNs) {
336     const int voteIdInt = static_cast<std::underlying_type_t<AdpfVoteType>>(voteId);
337     const auto timeoutDeadline = startTime + durationNs;
338     bool scheduleTimeout = false;
339 
340     {
341         std::lock_guard lock(mSessionTaskMapMutex);
342         auto session = mSessionTaskMap.findSession(sessionId);
343         if (!session) {
344             // Because of the async nature of some events an event for a session
345             // that has been removed is a possibility
346             return;
347         }
348         scheduleTimeout = shouldScheduleTimeout(*session->votes, voteIdInt, timeoutDeadline),
349         mSessionTaskMap.addVote(sessionId, voteIdInt, uclampMin, uclampMax, startTime, durationNs);
350         if (ATRACE_ENABLED()) {
351             ATRACE_INT(session->sessionTrace->trace_votes[voteIdInt].c_str(), uclampMin);
352         }
353         session->lastUpdatedTime = startTime;
354         applyUclampLocked(sessionId, startTime);
355     }
356 
357     if (scheduleTimeout) {
358         mEventSessionTimeoutWorker.schedule(
359                 {.timeStamp = startTime, .sessionId = sessionId, .voteId = voteIdInt},
360                 timeoutDeadline);
361     }
362 }
363 
364 template <class HintManagerT>
voteSet(int64_t sessionId,AdpfVoteType voteId,Cycles capacity,std::chrono::steady_clock::time_point startTime,std::chrono::nanoseconds durationNs)365 void PowerSessionManager<HintManagerT>::voteSet(int64_t sessionId, AdpfVoteType voteId,
366                                                 Cycles capacity,
367                                                 std::chrono::steady_clock::time_point startTime,
368                                                 std::chrono::nanoseconds durationNs) {
369     const int voteIdInt = static_cast<std::underlying_type_t<AdpfVoteType>>(voteId);
370     const auto timeoutDeadline = startTime + durationNs;
371     bool scheduleTimeout = false;
372 
373     {
374         std::lock_guard lock(mSessionTaskMapMutex);
375         auto session = mSessionTaskMap.findSession(sessionId);
376         if (!session) {
377             return;
378         }
379         scheduleTimeout = shouldScheduleTimeout(*session->votes, voteIdInt, timeoutDeadline),
380         mSessionTaskMap.addGpuVote(sessionId, voteIdInt, capacity, startTime, durationNs);
381         if (ATRACE_ENABLED()) {
382             ATRACE_INT(session->sessionTrace->trace_votes[voteIdInt].c_str(),
383                        static_cast<int>(capacity));
384         }
385         session->lastUpdatedTime = startTime;
386         applyGpuVotesLocked(sessionId, startTime);
387     }
388 
389     if (scheduleTimeout) {
390         mEventSessionTimeoutWorker.schedule(
391                 {.timeStamp = startTime, .sessionId = sessionId, .voteId = voteIdInt},
392                 timeoutDeadline);
393     }
394 }
395 
396 template <class HintManagerT>
disableBoosts(int64_t sessionId)397 void PowerSessionManager<HintManagerT>::disableBoosts(int64_t sessionId) {
398     {
399         std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
400         auto sessValPtr = mSessionTaskMap.findSession(sessionId);
401         if (nullptr == sessValPtr) {
402             // Because of the async nature of some events an event for a session
403             // that has been removed is a possibility
404             return;
405         }
406 
407         // sessValPtr->disableBoosts();
408         for (auto vid : {AdpfVoteType::CPU_LOAD_UP, AdpfVoteType::CPU_LOAD_RESET,
409                          AdpfVoteType::CPU_LOAD_RESUME, AdpfVoteType::VOTE_POWER_EFFICIENCY,
410                          AdpfVoteType::GPU_LOAD_UP, AdpfVoteType::GPU_LOAD_RESET}) {
411             auto vint = static_cast<std::underlying_type_t<AdpfVoteType>>(vid);
412             sessValPtr->votes->setUseVote(vint, false);
413             if (ATRACE_ENABLED()) {
414                 ATRACE_INT(sessValPtr->sessionTrace->trace_votes[vint].c_str(), 0);
415             }
416         }
417     }
418 }
419 
420 template <class HintManagerT>
enableSystemTopAppBoost()421 void PowerSessionManager<HintManagerT>::enableSystemTopAppBoost() {
422     if (HintManagerT::GetInstance()->IsHintSupported(kDisableBoostHintName)) {
423         ALOGV("PowerSessionManager::enableSystemTopAppBoost!!");
424         HintManagerT::GetInstance()->EndHint(kDisableBoostHintName);
425     }
426 }
427 
428 template <class HintManagerT>
disableSystemTopAppBoost()429 void PowerSessionManager<HintManagerT>::disableSystemTopAppBoost() {
430     if (HintManagerT::GetInstance()->IsHintSupported(kDisableBoostHintName)) {
431         ALOGV("PowerSessionManager::disableSystemTopAppBoost!!");
432         HintManagerT::GetInstance()->DoHint(kDisableBoostHintName);
433     }
434 }
435 
436 template <class HintManagerT>
handleEvent(const EventSessionTimeout & eventTimeout)437 void PowerSessionManager<HintManagerT>::handleEvent(const EventSessionTimeout &eventTimeout) {
438     bool recalcUclamp = false;
439     const auto tNow = std::chrono::steady_clock::now();
440     {
441         std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
442         auto sessValPtr = mSessionTaskMap.findSession(eventTimeout.sessionId);
443         if (nullptr == sessValPtr) {
444             // It is ok for session timeouts to fire after a session has been
445             // removed
446             return;
447         }
448 
449         // To minimize the number of events pushed into the queue, we are using
450         // the following logic to make use of a single timeout event which will
451         // requeue itself if the timeout has been changed since it was added to
452         // the work queue.  Requeue Logic:
453         // if vote active and vote timeout <= sched time
454         //    then deactivate vote and recalc uclamp (near end of function)
455         // if vote active and vote timeout > sched time
456         //    then requeue timeout event for new deadline (which is vote timeout)
457         const bool voteIsActive = sessValPtr->votes->voteIsActive(eventTimeout.voteId);
458         const auto voteTimeout = sessValPtr->votes->voteTimeout(eventTimeout.voteId);
459 
460         if (voteIsActive) {
461             if (voteTimeout <= tNow) {
462                 sessValPtr->votes->setUseVote(eventTimeout.voteId, false);
463                 recalcUclamp = true;
464                 if (ATRACE_ENABLED()) {
465                     ATRACE_INT(sessValPtr->sessionTrace->trace_votes[eventTimeout.voteId].c_str(),
466                                0);
467                 }
468             } else {
469                 // Can unlock sooner than we do
470                 auto eventTimeout2 = eventTimeout;
471                 mEventSessionTimeoutWorker.schedule(eventTimeout2, voteTimeout);
472             }
473         }
474     }
475 
476     if (!recalcUclamp) {
477         return;
478     }
479 
480     // It is important to use the correct time here, time now is more reasonable
481     // than trying to use the event's timestamp which will be slightly off given
482     // the background priority queue introduces latency
483     applyCpuAndGpuVotes(eventTimeout.sessionId, tNow);
484     updateUniversalBoostMode();
485 }
486 
487 template <class HintManagerT>
applyUclampLocked(int64_t sessionId,std::chrono::steady_clock::time_point timePoint)488 void PowerSessionManager<HintManagerT>::applyUclampLocked(
489         int64_t sessionId, std::chrono::steady_clock::time_point timePoint) {
490     auto config = HintManagerT::GetInstance()->GetAdpfProfile();
491     {
492         // TODO(kevindubois) un-indent this in followup patch to reduce churn.
493         auto sessValPtr = mSessionTaskMap.findSession(sessionId);
494         if (nullptr == sessValPtr) {
495             return;
496         }
497 
498         if (!config->mUclampMinOn) {
499             ALOGV("PowerSessionManager::set_uclamp: skip");
500         } else {
501             auto &threadList = mSessionTaskMap.getTaskIds(sessionId);
502             auto tidIter = threadList.begin();
503             while (tidIter != threadList.end()) {
504                 UclampRange uclampRange;
505                 mSessionTaskMap.getTaskVoteRange(*tidIter, timePoint, uclampRange,
506                                                  config->mUclampMaxEfficientBase,
507                                                  config->mUclampMaxEfficientOffset);
508                 int stat = set_uclamp(*tidIter, uclampRange);
509                 if (stat == ESRCH) {
510                     ALOGV("Removing dead thread %d from hint session %s.", *tidIter,
511                           sessValPtr->idString.c_str());
512                     if (mSessionTaskMap.removeDeadTaskSessionMap(sessionId, *tidIter)) {
513                         ALOGV("Removed dead thread-session map.");
514                     }
515                     tidIter = threadList.erase(tidIter);
516                 } else {
517                     tidIter++;
518                 }
519             }
520         }
521 
522         sessValPtr->lastUpdatedTime = timePoint;
523     }
524 }
525 
526 template <class HintManagerT>
applyGpuVotesLocked(int64_t sessionId,std::chrono::steady_clock::time_point timePoint)527 void PowerSessionManager<HintManagerT>::applyGpuVotesLocked(
528         int64_t sessionId, std::chrono::steady_clock::time_point timePoint) {
529     auto const sessValPtr = mSessionTaskMap.findSession(sessionId);
530     if (!sessValPtr) {
531         return;
532     }
533 
534     auto const gpuVotingOn = HintManagerT::GetInstance()->GetAdpfProfile()->mGpuBoostOn;
535     if (mGpuCapacityNode && gpuVotingOn) {
536         auto const capacity = mSessionTaskMap.getSessionsGpuCapacity(timePoint);
537         (*mGpuCapacityNode)->set_gpu_capacity(capacity);
538     }
539 
540     sessValPtr->lastUpdatedTime = timePoint;
541 }
542 
543 template <class HintManagerT>
applyCpuAndGpuVotes(int64_t sessionId,std::chrono::steady_clock::time_point timePoint)544 void PowerSessionManager<HintManagerT>::applyCpuAndGpuVotes(
545         int64_t sessionId, std::chrono::steady_clock::time_point timePoint) {
546     std::lock_guard lock(mSessionTaskMapMutex);
547     applyUclampLocked(sessionId, timePoint);
548     applyGpuVotesLocked(sessionId, timePoint);
549 }
550 
551 template <class HintManagerT>
gpuFrequency() const552 std::optional<Frequency> PowerSessionManager<HintManagerT>::gpuFrequency() const {
553     if (mGpuCapacityNode) {
554         return (*mGpuCapacityNode)->gpu_frequency();
555     }
556     return {};
557 }
558 
559 template <class HintManagerT>
forceSessionActive(int64_t sessionId,bool isActive)560 void PowerSessionManager<HintManagerT>::forceSessionActive(int64_t sessionId, bool isActive) {
561     {
562         std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
563         auto sessValPtr = mSessionTaskMap.findSession(sessionId);
564         if (nullptr == sessValPtr) {
565             return;
566         }
567         sessValPtr->isActive = isActive;
568     }
569 
570     // As currently written, call needs to occur synchronously so as to ensure
571     // that the SessionId remains valid and mapped to the proper threads/tasks
572     // which enables apply u clamp to work correctly
573     applyCpuAndGpuVotes(sessionId, std::chrono::steady_clock::now());
574     updateUniversalBoostMode();
575 }
576 
577 template <class HintManagerT>
setPreferPowerEfficiency(int64_t sessionId,bool enabled)578 void PowerSessionManager<HintManagerT>::setPreferPowerEfficiency(int64_t sessionId, bool enabled) {
579     std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
580     auto sessValPtr = mSessionTaskMap.findSession(sessionId);
581     if (nullptr == sessValPtr) {
582         return;
583     }
584     if (enabled != sessValPtr->isPowerEfficient) {
585         sessValPtr->isPowerEfficient = enabled;
586         applyUclampLocked(sessionId, std::chrono::steady_clock::now());
587     }
588 }
589 
590 template <class HintManagerT>
registerSession(std::shared_ptr<void> session,int64_t sessionId)591 void PowerSessionManager<HintManagerT>::registerSession(std::shared_ptr<void> session,
592                                                         int64_t sessionId) {
593     std::lock_guard<std::mutex> lock(mSessionMapMutex);
594     mSessionMap[sessionId] = session;
595 }
596 
597 template <class HintManagerT>
unregisterSession(int64_t sessionId)598 void PowerSessionManager<HintManagerT>::unregisterSession(int64_t sessionId) {
599     std::lock_guard<std::mutex> lock(mSessionMapMutex);
600     mSessionMap.erase(sessionId);
601 }
602 
603 template <class HintManagerT>
getSession(int64_t sessionId)604 std::shared_ptr<void> PowerSessionManager<HintManagerT>::getSession(int64_t sessionId) {
605     std::scoped_lock lock(mSessionMapMutex);
606     auto ptr = mSessionMap.find(sessionId);
607     if (ptr == mSessionMap.end()) {
608         return nullptr;
609     }
610     std::shared_ptr<void> out = ptr->second.lock();
611     if (!out) {
612         mSessionMap.erase(sessionId);
613         return nullptr;
614     }
615     return out;
616 }
617 
618 template <class HintManagerT>
clear()619 void PowerSessionManager<HintManagerT>::clear() {
620     std::scoped_lock lock(mSessionMapMutex);
621     mSessionMap.clear();
622 }
623 
624 template <class HintManagerT>
updateFrameBuckets(int64_t sessionId,const FrameBuckets & lastReportedFrames)625 void PowerSessionManager<HintManagerT>::updateFrameBuckets(int64_t sessionId,
626                                                            const FrameBuckets &lastReportedFrames) {
627     std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
628     auto sessValPtr = mSessionTaskMap.findSession(sessionId);
629     if (nullptr == sessValPtr) {
630         return;
631     }
632 
633     sessValPtr->sessFrameBuckets.addUpNewFrames(lastReportedFrames);
634 }
635 
636 template <class HintManagerT>
updateHboostStatistics(int64_t sessionId,SessionJankyLevel jankyLevel,int32_t numOfFrames)637 void PowerSessionManager<HintManagerT>::updateHboostStatistics(int64_t sessionId,
638                                                                SessionJankyLevel jankyLevel,
639                                                                int32_t numOfFrames) {
640     std::lock_guard<std::mutex> lock(mSessionTaskMapMutex);
641     auto sessValPtr = mSessionTaskMap.findSession(sessionId);
642     if (nullptr == sessValPtr) {
643         return;
644     }
645     switch (jankyLevel) {
646         case SessionJankyLevel::LIGHT:
647             sessValPtr->hBoostModeDist.lightModeFrames += numOfFrames;
648             break;
649         case SessionJankyLevel::MODERATE:
650             sessValPtr->hBoostModeDist.moderateModeFrames += numOfFrames;
651             break;
652         case SessionJankyLevel::SEVERE:
653             sessValPtr->hBoostModeDist.severeModeFrames += numOfFrames;
654             break;
655         default:
656             ALOGW("Unknown janky level during updateHboostStatistics");
657     }
658 }
659 
660 template class PowerSessionManager<>;
661 template class PowerSessionManager<testing::NiceMock<mock::pixel::MockHintManager>>;
662 
663 }  // namespace pixel
664 }  // namespace impl
665 }  // namespace power
666 }  // namespace hardware
667 }  // namespace google
668 }  // namespace aidl
669