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 "PowerHintSession.h"
21 
22 #include <android-base/file.h>
23 #include <android-base/logging.h>
24 #include <android-base/parsedouble.h>
25 #include <android-base/properties.h>
26 #include <android-base/stringprintf.h>
27 #include <private/android_filesystem_config.h>
28 #include <sys/syscall.h>
29 #include <time.h>
30 #include <utils/Trace.h>
31 
32 #include <atomic>
33 
34 #include "GpuCalculationHelpers.h"
35 #include "tests/mocks/MockHintManager.h"
36 #include "tests/mocks/MockPowerSessionManager.h"
37 
38 namespace aidl {
39 namespace google {
40 namespace hardware {
41 namespace power {
42 namespace impl {
43 namespace pixel {
44 
45 using ::android::base::StringPrintf;
46 using ::android::perfmgr::AdpfConfig;
47 using ::android::perfmgr::HintManager;
48 using std::chrono::duration_cast;
49 using std::chrono::nanoseconds;
50 
51 using std::operator""ms;
52 
53 namespace {
54 
55 static std::atomic<int64_t> sSessionIDCounter{0};
56 
ns_to_100us(int64_t ns)57 static inline int64_t ns_to_100us(int64_t ns) {
58     return ns / 100000;
59 }
60 
61 static const char systemSessionCheckPath[] = "/proc/vendor_sched/is_tgid_system_ui";
62 static const bool systemSessionCheckNodeExist = access(systemSessionCheckPath, W_OK) == 0;
63 static constexpr int32_t kTargetDurationChangeThreshold = 30;  // Percentage change threshold
64 
65 }  // namespace
66 
67 template <class HintManagerT, class PowerSessionManagerT>
convertWorkDurationToBoostByPid(const std::vector<WorkDuration> & actualDurations)68 int64_t PowerHintSession<HintManagerT, PowerSessionManagerT>::convertWorkDurationToBoostByPid(
69         const std::vector<WorkDuration> &actualDurations) {
70     std::shared_ptr<AdpfConfig> adpfConfig = getAdpfProfile();
71     const nanoseconds &targetDuration = mDescriptor->targetNs;
72     int64_t &integral_error = mDescriptor->integral_error;
73     int64_t &previous_error = mDescriptor->previous_error;
74     uint64_t samplingWindowP = adpfConfig->mSamplingWindowP;
75     uint64_t samplingWindowI = adpfConfig->mSamplingWindowI;
76     uint64_t samplingWindowD = adpfConfig->mSamplingWindowD;
77     int64_t targetDurationNanos = (int64_t)targetDuration.count();
78     int64_t length = actualDurations.size();
79     int64_t p_start =
80             samplingWindowP == 0 || samplingWindowP > length ? 0 : length - samplingWindowP;
81     int64_t i_start =
82             samplingWindowI == 0 || samplingWindowI > length ? 0 : length - samplingWindowI;
83     int64_t d_start =
84             samplingWindowD == 0 || samplingWindowD > length ? 0 : length - samplingWindowD;
85     int64_t dt = ns_to_100us(targetDurationNanos);
86     int64_t err_sum = 0;
87     int64_t derivative_sum = 0;
88     for (int64_t i = std::min({p_start, i_start, d_start}); i < length; i++) {
89         int64_t actualDurationNanos = actualDurations[i].durationNanos;
90         if (std::abs(actualDurationNanos) > targetDurationNanos * 20) {
91             ALOGW("The actual duration is way far from the target (%" PRId64 " >> %" PRId64 ")",
92                   actualDurationNanos, targetDurationNanos);
93         }
94         // PID control algorithm
95         int64_t error = ns_to_100us(actualDurationNanos - targetDurationNanos);
96         if (i >= d_start) {
97             derivative_sum += error - previous_error;
98         }
99         if (i >= p_start) {
100             err_sum += error;
101         }
102         if (i >= i_start) {
103             integral_error += error * dt;
104             integral_error = std::min(adpfConfig->getPidIHighDivI(), integral_error);
105             integral_error = std::max(adpfConfig->getPidILowDivI(), integral_error);
106         }
107         previous_error = error;
108     }
109 
110     auto pid_pu_active = adpfConfig->mPidPu;
111     if (adpfConfig->mHeuristicBoostOn.has_value() && adpfConfig->mHeuristicBoostOn.value()) {
112         auto hboostPidPu = std::min(adpfConfig->mHBoostSevereJankPidPu.value(), adpfConfig->mPidPu);
113         if (mJankyLevel == SessionJankyLevel::MODERATE) {
114             double JankyFactor =
115                     mJankyFrameNum < adpfConfig->mHBoostModerateJankThreshold.value()
116                             ? 0.0
117                             : (mJankyFrameNum - adpfConfig->mHBoostModerateJankThreshold.value()) *
118                                       1.0 /
119                                       (adpfConfig->mHBoostSevereJankThreshold.value() -
120                                        adpfConfig->mHBoostModerateJankThreshold.value());
121             pid_pu_active = adpfConfig->mPidPu + JankyFactor * (hboostPidPu - adpfConfig->mPidPu);
122         } else if (mJankyLevel == SessionJankyLevel::SEVERE) {
123             pid_pu_active = hboostPidPu;
124         }
125         ATRACE_INT(mAppDescriptorTrace->trace_hboost_pid_pu.c_str(), pid_pu_active * 100);
126     }
127     int64_t pOut = static_cast<int64_t>((err_sum > 0 ? adpfConfig->mPidPo : pid_pu_active) *
128                                         err_sum / (length - p_start));
129     int64_t iOut = static_cast<int64_t>(adpfConfig->mPidI * integral_error);
130     int64_t dOut =
131             static_cast<int64_t>((derivative_sum > 0 ? adpfConfig->mPidDo : adpfConfig->mPidDu) *
132                                  derivative_sum / dt / (length - d_start));
133 
134     int64_t output = pOut + iOut + dOut;
135     ATRACE_INT(mAppDescriptorTrace->trace_pid_err.c_str(), err_sum / (length - p_start));
136     ATRACE_INT(mAppDescriptorTrace->trace_pid_integral.c_str(), integral_error);
137     ATRACE_INT(mAppDescriptorTrace->trace_pid_derivative.c_str(),
138                derivative_sum / dt / (length - d_start));
139     ATRACE_INT(mAppDescriptorTrace->trace_pid_pOut.c_str(), pOut);
140     ATRACE_INT(mAppDescriptorTrace->trace_pid_iOut.c_str(), iOut);
141     ATRACE_INT(mAppDescriptorTrace->trace_pid_dOut.c_str(), dOut);
142     ATRACE_INT(mAppDescriptorTrace->trace_pid_output.c_str(), output);
143     return output;
144 }
145 
146 template <class HintManagerT, class PowerSessionManagerT>
getProcessTag(int32_t tgid)147 ProcessTag PowerHintSession<HintManagerT, PowerSessionManagerT>::getProcessTag(int32_t tgid) {
148     if (!systemSessionCheckNodeExist) {
149         ALOGD("Vendor system session checking node doesn't exist");
150         return ProcessTag::DEFAULT;
151     }
152 
153     int flags = O_WRONLY | O_TRUNC | O_CLOEXEC;
154     ::android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(systemSessionCheckPath, flags)));
155     if (fd == -1) {
156         ALOGW("Can't open system session checking node %s", systemSessionCheckPath);
157         return ProcessTag::DEFAULT;
158     }
159     // The file-write return status is true if the task belongs to systemUI or Launcher. Other task
160     // or invalid tgid will return a false value.
161     auto stat = ::android::base::WriteStringToFd(std::to_string(tgid), fd);
162     ALOGD("System session checking result: %d - %d", tgid, stat);
163     if (stat) {
164         return ProcessTag::SYSTEM_UI;
165     } else {
166         return ProcessTag::DEFAULT;
167     }
168 }
169 
170 template <class HintManagerT, class PowerSessionManagerT>
PowerHintSession(int32_t tgid,int32_t uid,const std::vector<int32_t> & threadIds,int64_t durationNs,SessionTag tag)171 PowerHintSession<HintManagerT, PowerSessionManagerT>::PowerHintSession(
172         int32_t tgid, int32_t uid, const std::vector<int32_t> &threadIds, int64_t durationNs,
173         SessionTag tag)
174     : mPSManager(PowerSessionManagerT::getInstance()),
175       mSessionId(++sSessionIDCounter),
176       mSessTag(tag),
177       mProcTag(getProcessTag(tgid)),
178       mIdString(StringPrintf("%" PRId32 "-%" PRId32 "-%" PRId64 "-%s-%" PRId32, tgid, uid,
179                              mSessionId, toString(tag).c_str(), static_cast<int32_t>(mProcTag))),
180       mDescriptor(std::make_shared<AppHintDesc>(mSessionId, tgid, uid, threadIds, tag,
181                                                 std::chrono::nanoseconds(durationNs))),
182       mAppDescriptorTrace(std::make_shared<AppDescriptorTrace>(mIdString)),
183       mAdpfProfile(mProcTag != ProcessTag::DEFAULT
184                            ? HintManager::GetInstance()->GetAdpfProfile(toString(mProcTag))
185                            : HintManager::GetInstance()->GetAdpfProfile(toString(mSessTag))),
186       mOnAdpfUpdate(
187               [this](const std::shared_ptr<AdpfConfig> config) { this->setAdpfProfile(config); }),
188       mSessionRecords(getAdpfProfile()->mHeuristicBoostOn.has_value() &&
189                                       getAdpfProfile()->mHeuristicBoostOn.value()
190                               ? std::make_unique<SessionRecords>(
191                                         getAdpfProfile()->mMaxRecordsNum.value(),
192                                         getAdpfProfile()->mJankCheckTimeFactor.value())
193                               : nullptr) {
194     ATRACE_CALL();
195     ATRACE_INT(mAppDescriptorTrace->trace_target.c_str(), mDescriptor->targetNs.count());
196     ATRACE_INT(mAppDescriptorTrace->trace_active.c_str(), mDescriptor->is_active.load());
197 
198     if (mProcTag != ProcessTag::DEFAULT) {
199         HintManager::GetInstance()->RegisterAdpfUpdateEvent(toString(mProcTag), &mOnAdpfUpdate);
200     } else {
201         HintManager::GetInstance()->RegisterAdpfUpdateEvent(toString(mSessTag), &mOnAdpfUpdate);
202     }
203 
204     mLastUpdatedTime = std::chrono::steady_clock::now();
205     mPSManager->addPowerSession(mIdString, mDescriptor, mAppDescriptorTrace, threadIds, mProcTag);
206     // init boost
207     auto adpfConfig = getAdpfProfile();
208     mPSManager->voteSet(
209             mSessionId, AdpfVoteType::CPU_LOAD_RESET, adpfConfig->mUclampMinLoadReset, kUclampMax,
210             std::chrono::steady_clock::now(),
211             duration_cast<nanoseconds>(mDescriptor->targetNs * adpfConfig->mStaleTimeFactor / 2.0));
212 
213     mPSManager->voteSet(mSessionId, AdpfVoteType::CPU_VOTE_DEFAULT, adpfConfig->mUclampMinInit,
214                         kUclampMax, std::chrono::steady_clock::now(), mDescriptor->targetNs);
215     ALOGV("PowerHintSession created: %s", mDescriptor->toString().c_str());
216 }
217 
218 template <class HintManagerT, class PowerSessionManagerT>
~PowerHintSession()219 PowerHintSession<HintManagerT, PowerSessionManagerT>::~PowerHintSession() {
220     ATRACE_CALL();
221     close();
222     ALOGV("PowerHintSession deleted: %s", mDescriptor->toString().c_str());
223     ATRACE_INT(mAppDescriptorTrace->trace_target.c_str(), 0);
224     ATRACE_INT(mAppDescriptorTrace->trace_actl_last.c_str(), 0);
225     ATRACE_INT(mAppDescriptorTrace->trace_active.c_str(), 0);
226 }
227 
228 template <class HintManagerT, class PowerSessionManagerT>
isAppSession()229 bool PowerHintSession<HintManagerT, PowerSessionManagerT>::isAppSession() {
230     // Check if uid is in range reserved for applications
231     return mDescriptor->uid >= AID_APP_START;
232 }
233 
234 template <class HintManagerT, class PowerSessionManagerT>
updatePidControlVariable(int pidControlVariable,bool updateVote)235 void PowerHintSession<HintManagerT, PowerSessionManagerT>::updatePidControlVariable(
236         int pidControlVariable, bool updateVote) {
237     mDescriptor->pidControlVariable = pidControlVariable;
238     if (updateVote) {
239         auto adpfConfig = getAdpfProfile();
240         mPSManager->voteSet(mSessionId, AdpfVoteType::CPU_VOTE_DEFAULT, pidControlVariable,
241                             kUclampMax, std::chrono::steady_clock::now(),
242                             std::max(duration_cast<nanoseconds>(mDescriptor->targetNs *
243                                                                 adpfConfig->mStaleTimeFactor),
244                                      nanoseconds(adpfConfig->mReportingRateLimitNs) * 2));
245     }
246     ATRACE_INT(mAppDescriptorTrace->trace_min.c_str(), pidControlVariable);
247 }
248 
249 template <class HintManagerT, class PowerSessionManagerT>
tryToSendPowerHint(std::string hint)250 void PowerHintSession<HintManagerT, PowerSessionManagerT>::tryToSendPowerHint(std::string hint) {
251     if (!mSupportedHints[hint].has_value()) {
252         mSupportedHints[hint] = HintManagerT::GetInstance()->IsHintSupported(hint);
253     }
254     if (mSupportedHints[hint].value()) {
255         HintManagerT::GetInstance()->DoHint(hint);
256     }
257 }
258 
259 template <class HintManagerT, class PowerSessionManagerT>
dumpToStream(std::ostream & stream)260 void PowerHintSession<HintManagerT, PowerSessionManagerT>::dumpToStream(std::ostream &stream) {
261     std::scoped_lock lock{mPowerHintSessionLock};
262     stream << "ID.Min.Act.Timeout(" << mIdString;
263     stream << ", " << mDescriptor->pidControlVariable;
264     stream << ", " << mDescriptor->is_active;
265     stream << ", " << isTimeout() << ")";
266 }
267 
268 template <class HintManagerT, class PowerSessionManagerT>
pause()269 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::pause() {
270     std::scoped_lock lock{mPowerHintSessionLock};
271     if (mSessionClosed) {
272         ALOGE("Error: session is dead");
273         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
274     }
275     if (!mDescriptor->is_active.load())
276         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
277     // Reset to default uclamp value.
278     mPSManager->setThreadsFromPowerSession(mSessionId, {}, mProcTag);
279     mDescriptor->is_active.store(false);
280     mPSManager->pause(mSessionId);
281     ATRACE_INT(mAppDescriptorTrace->trace_active.c_str(), false);
282     ATRACE_INT(mAppDescriptorTrace->trace_min.c_str(), 0);
283     return ndk::ScopedAStatus::ok();
284 }
285 
286 template <class HintManagerT, class PowerSessionManagerT>
resume()287 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::resume() {
288     std::scoped_lock lock{mPowerHintSessionLock};
289     if (mSessionClosed) {
290         ALOGE("Error: session is dead");
291         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
292     }
293     if (mDescriptor->is_active.load()) {
294         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
295     }
296     mPSManager->setThreadsFromPowerSession(mSessionId, mDescriptor->thread_ids, mProcTag);
297     mDescriptor->is_active.store(true);
298     // resume boost
299     mPSManager->resume(mSessionId);
300     ATRACE_INT(mAppDescriptorTrace->trace_active.c_str(), true);
301     ATRACE_INT(mAppDescriptorTrace->trace_min.c_str(), mDescriptor->pidControlVariable);
302     return ndk::ScopedAStatus::ok();
303 }
304 
305 template <class HintManagerT, class PowerSessionManagerT>
close()306 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::close() {
307     std::scoped_lock lock{mPowerHintSessionLock};
308     if (mSessionClosed) {
309         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
310     }
311     mSessionClosed = true;
312     // Remove the session from PowerSessionManager first to avoid racing.
313     mPSManager->removePowerSession(mSessionId, mProcTag);
314     mDescriptor->is_active.store(false);
315 
316     if (mProcTag != ProcessTag::DEFAULT) {
317         HintManager::GetInstance()->UnregisterAdpfUpdateEvent(toString(mProcTag), &mOnAdpfUpdate);
318     } else {
319         HintManager::GetInstance()->UnregisterAdpfUpdateEvent(toString(mSessTag), &mOnAdpfUpdate);
320     }
321     ATRACE_INT(mAppDescriptorTrace->trace_min.c_str(), 0);
322     return ndk::ScopedAStatus::ok();
323 }
324 
325 template <class HintManagerT, class PowerSessionManagerT>
updateTargetWorkDuration(int64_t targetDurationNanos)326 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::updateTargetWorkDuration(
327         int64_t targetDurationNanos) {
328     std::scoped_lock lock{mPowerHintSessionLock};
329     if (mSessionClosed) {
330         ALOGE("Error: session is dead");
331         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
332     }
333     if (targetDurationNanos <= 0) {
334         ALOGE("Error: targetDurationNanos(%" PRId64 ") should bigger than 0", targetDurationNanos);
335         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
336     }
337     targetDurationNanos = targetDurationNanos * getAdpfProfile()->mTargetTimeFactor;
338 
339     // Reset session records and heuristic boost states when the percentage change of target
340     // duration is over the threshold.
341     if (targetDurationNanos != mDescriptor->targetNs.count() &&
342         getAdpfProfile()->mHeuristicBoostOn.has_value() &&
343         getAdpfProfile()->mHeuristicBoostOn.value()) {
344         auto lastTargetNs = mDescriptor->targetNs.count();
345         if (abs(targetDurationNanos - lastTargetNs) >
346             lastTargetNs / 100 * kTargetDurationChangeThreshold) {
347             resetSessionHeuristicStates();
348         }
349     }
350 
351     mDescriptor->targetNs = std::chrono::nanoseconds(targetDurationNanos);
352     mPSManager->updateTargetWorkDuration(mSessionId, AdpfVoteType::CPU_VOTE_DEFAULT,
353                                          mDescriptor->targetNs);
354     ATRACE_INT(mAppDescriptorTrace->trace_target.c_str(), targetDurationNanos);
355 
356     return ndk::ScopedAStatus::ok();
357 }
358 
359 template <class HintManagerT, class PowerSessionManagerT>
resetSessionHeuristicStates()360 void PowerHintSession<HintManagerT, PowerSessionManagerT>::resetSessionHeuristicStates() {
361     mSessionRecords->resetRecords();
362     mJankyLevel = SessionJankyLevel::LIGHT;
363     mJankyFrameNum = 0;
364     ATRACE_INT(mAppDescriptorTrace->trace_hboost_janky_level.c_str(),
365                static_cast<int32_t>(mJankyLevel));
366     ATRACE_INT(mAppDescriptorTrace->trace_missed_cycles.c_str(), mJankyFrameNum);
367     ATRACE_INT(mAppDescriptorTrace->trace_avg_duration.c_str(), 0);
368     ATRACE_INT(mAppDescriptorTrace->trace_max_duration.c_str(), 0);
369     ATRACE_INT(mAppDescriptorTrace->trace_low_frame_rate.c_str(), false);
370 }
371 
372 template <class HintManagerT, class PowerSessionManagerT>
updateSessionJankState(SessionJankyLevel oldState,int32_t numOfJankFrames,double durationVariance,bool isLowFPS)373 SessionJankyLevel PowerHintSession<HintManagerT, PowerSessionManagerT>::updateSessionJankState(
374         SessionJankyLevel oldState, int32_t numOfJankFrames, double durationVariance,
375         bool isLowFPS) {
376     SessionJankyLevel newState = SessionJankyLevel::LIGHT;
377     if (isLowFPS) {
378         newState = SessionJankyLevel::LIGHT;
379         return newState;
380     }
381 
382     auto adpfConfig = getAdpfProfile();
383     if (numOfJankFrames < adpfConfig->mHBoostModerateJankThreshold.value()) {
384         if (oldState == SessionJankyLevel::LIGHT ||
385             durationVariance < adpfConfig->mHBoostOffMaxAvgDurRatio.value()) {
386             newState = SessionJankyLevel::LIGHT;
387         } else {
388             newState = SessionJankyLevel::MODERATE;
389         }
390     } else if (numOfJankFrames < adpfConfig->mHBoostSevereJankThreshold.value()) {
391         newState = SessionJankyLevel::MODERATE;
392     } else {
393         newState = SessionJankyLevel::SEVERE;
394     }
395 
396     return newState;
397 }
398 
399 template <class HintManagerT, class PowerSessionManagerT>
updateHeuristicBoost()400 void PowerHintSession<HintManagerT, PowerSessionManagerT>::updateHeuristicBoost() {
401     auto maxDurationUs = mSessionRecords->getMaxDuration();  // micro seconds
402     auto avgDurationUs = mSessionRecords->getAvgDuration();  // micro seconds
403     auto numOfJankFrames = mSessionRecords->getNumOfMissedCycles();
404 
405     if (!maxDurationUs.has_value() || !avgDurationUs.has_value() || avgDurationUs.value() <= 0) {
406         // No history data stored or invalid average duration.
407         return;
408     }
409 
410     auto maxToAvgRatio = maxDurationUs.value() * 1.0 / avgDurationUs.value();
411     auto isLowFPS =
412             mSessionRecords->isLowFrameRate(getAdpfProfile()->mLowFrameRateThreshold.value());
413 
414     mJankyLevel = updateSessionJankState(mJankyLevel, numOfJankFrames, maxToAvgRatio, isLowFPS);
415     mJankyFrameNum = numOfJankFrames;
416 
417     ATRACE_INT(mAppDescriptorTrace->trace_hboost_janky_level.c_str(),
418                static_cast<int32_t>(mJankyLevel));
419     ATRACE_INT(mAppDescriptorTrace->trace_missed_cycles.c_str(), mJankyFrameNum);
420     ATRACE_INT(mAppDescriptorTrace->trace_avg_duration.c_str(), avgDurationUs.value());
421     ATRACE_INT(mAppDescriptorTrace->trace_max_duration.c_str(), maxDurationUs.value());
422     ATRACE_INT(mAppDescriptorTrace->trace_low_frame_rate.c_str(), isLowFPS);
423     if (mSessTag == SessionTag::SURFACEFLINGER) {
424         ATRACE_INT(mAppDescriptorTrace->trace_game_mode_fps.c_str(),
425                    mSessionRecords->getLatestFPS());
426         ATRACE_INT(mAppDescriptorTrace->trace_game_mode_fps_jitters.c_str(),
427                    mSessionRecords->getNumOfFPSJitters());
428     }
429 }
430 
431 template <class HintManagerT, class PowerSessionManagerT>
reportActualWorkDuration(const std::vector<WorkDuration> & actualDurations)432 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::reportActualWorkDuration(
433         const std::vector<WorkDuration> &actualDurations) {
434     std::scoped_lock lock{mPowerHintSessionLock};
435     if (mSessionClosed) {
436         ALOGE("Error: session is dead");
437         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
438     }
439     if (mDescriptor->targetNs.count() == 0LL) {
440         ALOGE("Expect to call updateTargetWorkDuration() first.");
441         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
442     }
443     if (actualDurations.empty()) {
444         ALOGE("Error: durations shouldn't be empty.");
445         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
446     }
447     if (!mDescriptor->is_active.load()) {
448         ALOGE("Error: shouldn't report duration during pause state.");
449         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
450     }
451     auto adpfConfig = getAdpfProfile();
452     mDescriptor->update_count++;
453     bool isFirstFrame = isTimeout();
454     ATRACE_INT(mAppDescriptorTrace->trace_batch_size.c_str(), actualDurations.size());
455     ATRACE_INT(mAppDescriptorTrace->trace_actl_last.c_str(), actualDurations.back().durationNanos);
456     ATRACE_INT(mAppDescriptorTrace->trace_target.c_str(), mDescriptor->targetNs.count());
457     ATRACE_INT(mAppDescriptorTrace->trace_hint_count.c_str(), mDescriptor->update_count);
458     ATRACE_INT(mAppDescriptorTrace->trace_hint_overtime.c_str(),
459                actualDurations.back().durationNanos - mDescriptor->targetNs.count() > 0);
460     ATRACE_INT(mAppDescriptorTrace->trace_is_first_frame.c_str(), (isFirstFrame) ? (1) : (0));
461     ATRACE_INT(mAppDescriptorTrace->trace_cpu_duration.c_str(),
462                actualDurations.back().cpuDurationNanos);
463     ATRACE_INT(mAppDescriptorTrace->trace_gpu_duration.c_str(),
464                actualDurations.back().gpuDurationNanos);
465 
466     mLastUpdatedTime = std::chrono::steady_clock::now();
467     if (isFirstFrame) {
468         mPSManager->updateUniversalBoostMode();
469     }
470 
471     mPSManager->disableBoosts(mSessionId);
472 
473     if (!adpfConfig->mPidOn) {
474         updatePidControlVariable(adpfConfig->mUclampMinHigh);
475         return ndk::ScopedAStatus::ok();
476     }
477 
478     bool hboostEnabled =
479             adpfConfig->mHeuristicBoostOn.has_value() && adpfConfig->mHeuristicBoostOn.value();
480 
481     if (hboostEnabled) {
482         FrameBuckets newFramesInBuckets;
483         mSessionRecords->addReportedDurations(
484                 actualDurations, mDescriptor->targetNs.count(), newFramesInBuckets,
485                 mSessTag == SessionTag::SURFACEFLINGER && mPSManager->getGameModeEnableState());
486         mPSManager->updateHboostStatistics(mSessionId, mJankyLevel, actualDurations.size());
487         mPSManager->updateFrameBuckets(mSessionId, newFramesInBuckets);
488         updateHeuristicBoost();
489     }
490 
491     int64_t output = convertWorkDurationToBoostByPid(actualDurations);
492 
493     // Apply to all the threads in the group
494     auto uclampMinFloor = adpfConfig->mUclampMinLow;
495     auto uclampMinCeiling = adpfConfig->mUclampMinHigh;
496     if (hboostEnabled) {
497         auto hboostMinUclampMinFloor = std::max(
498                 adpfConfig->mUclampMinLow, adpfConfig->mHBoostUclampMinFloorRange.value().first);
499         auto hboostMaxUclampMinFloor = std::max(
500                 adpfConfig->mUclampMinLow, adpfConfig->mHBoostUclampMinFloorRange.value().second);
501         auto hboostMinUclampMinCeiling = std::max(
502                 adpfConfig->mUclampMinHigh, adpfConfig->mHBoostUclampMinCeilingRange.value().first);
503         auto hboostMaxUclampMinCeiling =
504                 std::max(adpfConfig->mUclampMinHigh,
505                          adpfConfig->mHBoostUclampMinCeilingRange.value().second);
506         if (mJankyLevel == SessionJankyLevel::MODERATE) {
507             double JankyFactor =
508                     mJankyFrameNum < adpfConfig->mHBoostModerateJankThreshold.value()
509                             ? 0.0
510                             : (mJankyFrameNum - adpfConfig->mHBoostModerateJankThreshold.value()) *
511                                       1.0 /
512                                       (adpfConfig->mHBoostSevereJankThreshold.value() -
513                                        adpfConfig->mHBoostModerateJankThreshold.value());
514             uclampMinFloor = hboostMinUclampMinFloor +
515                              (hboostMaxUclampMinFloor - hboostMinUclampMinFloor) * JankyFactor;
516             uclampMinCeiling =
517                     hboostMinUclampMinCeiling +
518                     (hboostMaxUclampMinCeiling - hboostMinUclampMinCeiling) * JankyFactor;
519         } else if (mJankyLevel == SessionJankyLevel::SEVERE) {
520             uclampMinFloor = hboostMaxUclampMinFloor;
521             uclampMinCeiling = hboostMaxUclampMinCeiling;
522         }
523         ATRACE_INT(mAppDescriptorTrace->trace_uclamp_min_ceiling.c_str(), uclampMinCeiling);
524         ATRACE_INT(mAppDescriptorTrace->trace_uclamp_min_floor.c_str(), uclampMinFloor);
525     }
526 
527     int next_min = std::min(static_cast<int>(uclampMinCeiling),
528                             mDescriptor->pidControlVariable + static_cast<int>(output));
529     next_min = std::max(static_cast<int>(uclampMinFloor), next_min);
530 
531     updatePidControlVariable(next_min);
532 
533     if (!adpfConfig->mGpuBoostOn.value_or(false) || !adpfConfig->mGpuBoostCapacityMax ||
534         !actualDurations.back().gpuDurationNanos) {
535         return ndk::ScopedAStatus::ok();
536     }
537 
538     auto const gpu_freq = mPSManager->gpuFrequency();
539     if (!gpu_freq) {
540         return ndk::ScopedAStatus::ok();
541     }
542     auto const additional_gpu_capacity =
543             calculate_capacity(actualDurations.back(), mDescriptor->targetNs, *gpu_freq);
544     ATRACE_INT(mAppDescriptorTrace->trace_gpu_capacity.c_str(),
545                static_cast<int>(additional_gpu_capacity));
546 
547     auto const additional_gpu_capacity_clamped = std::clamp(
548             additional_gpu_capacity, Cycles(0), Cycles(*adpfConfig->mGpuBoostCapacityMax));
549 
550     mPSManager->voteSet(
551             mSessionId, AdpfVoteType::GPU_CAPACITY, additional_gpu_capacity_clamped,
552             std::chrono::steady_clock::now(),
553             duration_cast<nanoseconds>(mDescriptor->targetNs * adpfConfig->mStaleTimeFactor));
554 
555     return ndk::ScopedAStatus::ok();
556 }
557 
558 template <class HintManagerT, class PowerSessionManagerT>
sendHint(SessionHint hint)559 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::sendHint(
560         SessionHint hint) {
561     {
562         std::scoped_lock lock{mPowerHintSessionLock};
563         if (mSessionClosed) {
564             ALOGE("Error: session is dead");
565             return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
566         }
567         if (mDescriptor->targetNs.count() == 0LL) {
568             ALOGE("Expect to call updateTargetWorkDuration() first.");
569             return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
570         }
571         auto adpfConfig = getAdpfProfile();
572 
573         switch (hint) {
574             case SessionHint::CPU_LOAD_UP:
575                 updatePidControlVariable(mDescriptor->pidControlVariable);
576                 mPSManager->voteSet(mSessionId, AdpfVoteType::CPU_LOAD_UP,
577                                     adpfConfig->mUclampMinLoadUp, kUclampMax,
578                                     std::chrono::steady_clock::now(), mDescriptor->targetNs * 2);
579                 break;
580             case SessionHint::CPU_LOAD_DOWN:
581                 updatePidControlVariable(adpfConfig->mUclampMinLow);
582                 break;
583             case SessionHint::CPU_LOAD_RESET:
584                 updatePidControlVariable(
585                         std::max(adpfConfig->mUclampMinInit,
586                                  static_cast<uint32_t>(mDescriptor->pidControlVariable)),
587                         false);
588                 mPSManager->voteSet(mSessionId, AdpfVoteType::CPU_LOAD_RESET,
589                                     adpfConfig->mUclampMinLoadReset, kUclampMax,
590                                     std::chrono::steady_clock::now(),
591                                     duration_cast<nanoseconds>(mDescriptor->targetNs *
592                                                                adpfConfig->mStaleTimeFactor / 2.0));
593                 break;
594             case SessionHint::CPU_LOAD_RESUME:
595                 mPSManager->voteSet(mSessionId, AdpfVoteType::CPU_LOAD_RESUME,
596                                     mDescriptor->pidControlVariable, kUclampMax,
597                                     std::chrono::steady_clock::now(),
598                                     duration_cast<nanoseconds>(mDescriptor->targetNs *
599                                                                adpfConfig->mStaleTimeFactor / 2.0));
600                 break;
601             case SessionHint::POWER_EFFICIENCY:
602                 setModeLocked(SessionMode::POWER_EFFICIENCY, true);
603                 break;
604             case SessionHint::GPU_LOAD_UP:
605                 mPSManager->voteSet(mSessionId, AdpfVoteType::GPU_LOAD_UP,
606                                     Cycles(adpfConfig->mGpuCapacityLoadUpHeadroom),
607                                     std::chrono::steady_clock::now(), mDescriptor->targetNs);
608                 break;
609             case SessionHint::GPU_LOAD_DOWN:
610                 // TODO(kevindubois): add impl
611                 break;
612             case SessionHint::GPU_LOAD_RESET:
613                 // TODO(kevindubois): add impl
614                 break;
615             case SessionHint::CPU_LOAD_SPIKE:
616                 // TODO(mattbuckley): add impl
617                 break;
618             case SessionHint::GPU_LOAD_SPIKE:
619                 // TODO(kevindubois): add impl
620                 break;
621             default:
622                 ALOGE("Error: hint is invalid");
623                 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
624         }
625         mLastUpdatedTime = std::chrono::steady_clock::now();
626     }
627     // Don't hold a lock (mPowerHintSession) while DoHint will try to take another
628     // lock(NodeLooperThread).
629     tryToSendPowerHint(toString(hint));
630     return ndk::ScopedAStatus::ok();
631 }
632 
633 template <class HintManagerT, class PowerSessionManagerT>
setMode(SessionMode mode,bool enabled)634 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::setMode(SessionMode mode,
635                                                                                  bool enabled) {
636     std::scoped_lock lock{mPowerHintSessionLock};
637     return setModeLocked(mode, enabled);
638 }
639 
640 template <class HintManagerT, class PowerSessionManagerT>
setModeLocked(SessionMode mode,bool enabled)641 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::setModeLocked(
642         SessionMode mode, bool enabled) {
643     if (mSessionClosed) {
644         ALOGE("Error: session is dead");
645         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
646     }
647 
648     switch (mode) {
649         case SessionMode::POWER_EFFICIENCY:
650             mPSManager->setPreferPowerEfficiency(mSessionId, enabled);
651             break;
652         default:
653             ALOGE("Error: mode is invalid");
654             return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
655     }
656 
657     mModes[static_cast<size_t>(mode)] = enabled;
658     ATRACE_INT(mAppDescriptorTrace->trace_modes[static_cast<size_t>(mode)].c_str(), enabled);
659     mLastUpdatedTime = std::chrono::steady_clock::now();
660     return ndk::ScopedAStatus::ok();
661 }
662 
663 template <class HintManagerT, class PowerSessionManagerT>
setThreads(const std::vector<int32_t> & threadIds)664 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::setThreads(
665         const std::vector<int32_t> &threadIds) {
666     std::scoped_lock lock{mPowerHintSessionLock};
667     if (mSessionClosed) {
668         ALOGE("Error: session is dead");
669         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
670     }
671     if (threadIds.empty()) {
672         ALOGE("Error: threadIds should not be empty");
673         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
674     }
675     mDescriptor->thread_ids = threadIds;
676     mPSManager->setThreadsFromPowerSession(mSessionId, threadIds, mProcTag);
677     // init boost
678     updatePidControlVariable(getAdpfProfile()->mUclampMinInit);
679     return ndk::ScopedAStatus::ok();
680 }
681 
682 template <class HintManagerT, class PowerSessionManagerT>
getSessionConfig(SessionConfig * _aidl_return)683 ndk::ScopedAStatus PowerHintSession<HintManagerT, PowerSessionManagerT>::getSessionConfig(
684         SessionConfig *_aidl_return) {
685     _aidl_return->id = mSessionId;
686     return ndk::ScopedAStatus::ok();
687 }
688 
689 template <class HintManagerT, class PowerSessionManagerT>
getSessionTag() const690 SessionTag PowerHintSession<HintManagerT, PowerSessionManagerT>::getSessionTag() const {
691     return mSessTag;
692 }
693 
694 template <class HintManagerT, class PowerSessionManagerT>
695 const std::shared_ptr<AdpfConfig>
getAdpfProfile() const696 PowerHintSession<HintManagerT, PowerSessionManagerT>::getAdpfProfile() const {
697     if (!mAdpfProfile) {
698         return mProcTag == ProcessTag::DEFAULT
699                        ? HintManager::GetInstance()->GetAdpfProfile(toString(mSessTag))
700                        : HintManager::GetInstance()->GetAdpfProfile(toString(mProcTag));
701     }
702     return mAdpfProfile;
703 }
704 
705 template <class HintManagerT, class PowerSessionManagerT>
setAdpfProfile(const std::shared_ptr<AdpfConfig> profile)706 void PowerHintSession<HintManagerT, PowerSessionManagerT>::setAdpfProfile(
707         const std::shared_ptr<AdpfConfig> profile) {
708     // Must prevent profile from being changed in a binder call duration.
709     std::scoped_lock lock{mPowerHintSessionLock};
710     mAdpfProfile = profile;
711 }
712 
toString() const713 std::string AppHintDesc::toString() const {
714     std::string out = StringPrintf("session %" PRId64 "\n", sessionId);
715     out.append(
716             StringPrintf("  duration: %" PRId64 " ns\n", static_cast<int64_t>(targetNs.count())));
717     out.append(StringPrintf("  uclamp.min: %d \n", pidControlVariable));
718     out.append(StringPrintf("  uid: %d, tgid: %d\n", uid, tgid));
719     return out;
720 }
721 
722 template <class HintManagerT, class PowerSessionManagerT>
isTimeout()723 bool PowerHintSession<HintManagerT, PowerSessionManagerT>::isTimeout() {
724     auto now = std::chrono::steady_clock::now();
725     time_point<steady_clock> staleTime =
726             mLastUpdatedTime +
727             nanoseconds(static_cast<int64_t>(mDescriptor->targetNs.count() *
728                                              getAdpfProfile()->mStaleTimeFactor));
729     return now >= staleTime;
730 }
731 
732 template class PowerHintSession<>;
733 template class PowerHintSession<testing::NiceMock<mock::pixel::MockHintManager>,
734                                 testing::NiceMock<mock::pixel::MockPowerSessionManager>>;
735 template class PowerHintSession<
736         testing::NiceMock<mock::pixel::MockHintManager>,
737         PowerSessionManager<testing::NiceMock<mock::pixel::MockHintManager>>>;
738 }  // namespace pixel
739 }  // namespace impl
740 }  // namespace power
741 }  // namespace hardware
742 }  // namespace google
743 }  // namespace aidl
744