blob: c4c81284780e332583a7e8ccfab408e715132578 [file] [log] [blame]
Bo Liu44267722021-07-16 17:03:20 -04001/*
2 * Copyright (C) 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 "perf_hint"
18
Matt Buckley56093a72022-11-07 21:50:50 +000019#include <aidl/android/hardware/power/SessionHint.h>
Matt Buckley423c1b32023-06-28 19:13:42 +000020#include <aidl/android/hardware/power/SessionMode.h>
Peiyong Lin70de0852023-10-25 21:12:35 +000021#include <android/WorkDuration.h>
Bo Liu44267722021-07-16 17:03:20 -040022#include <android/os/IHintManager.h>
23#include <android/os/IHintSession.h>
Bo Liu2b739bb2021-11-10 19:20:03 -050024#include <android/performance_hint.h>
Bo Liu44267722021-07-16 17:03:20 -040025#include <binder/Binder.h>
26#include <binder/IBinder.h>
27#include <binder/IServiceManager.h>
Peiyong Lin70de0852023-10-25 21:12:35 +000028#include <inttypes.h>
Bo Liu44267722021-07-16 17:03:20 -040029#include <performance_hint_private.h>
30#include <utils/SystemClock.h>
31
Matt Buckley56093a72022-11-07 21:50:50 +000032#include <chrono>
Bo Liu2b739bb2021-11-10 19:20:03 -050033#include <utility>
34#include <vector>
35
Bo Liu44267722021-07-16 17:03:20 -040036using namespace android;
37using namespace android::os;
38
Matt Buckley56093a72022-11-07 21:50:50 +000039using namespace std::chrono_literals;
40
41using AidlSessionHint = aidl::android::hardware::power::SessionHint;
Matt Buckley423c1b32023-06-28 19:13:42 +000042using AidlSessionMode = aidl::android::hardware::power::SessionMode;
Matt Buckley56093a72022-11-07 21:50:50 +000043
Bo Liu44267722021-07-16 17:03:20 -040044struct APerformanceHintSession;
45
Matt Buckley56093a72022-11-07 21:50:50 +000046constexpr int64_t SEND_HINT_TIMEOUT = std::chrono::nanoseconds(100ms).count();
47
Bo Liu44267722021-07-16 17:03:20 -040048struct APerformanceHintManager {
49public:
50 static APerformanceHintManager* getInstance();
51 APerformanceHintManager(sp<IHintManager> service, int64_t preferredRateNanos);
52 APerformanceHintManager() = delete;
53 ~APerformanceHintManager() = default;
54
55 APerformanceHintSession* createSession(const int32_t* threadIds, size_t size,
56 int64_t initialTargetWorkDurationNanos);
57 int64_t getPreferredRateNanos() const;
58
59private:
60 static APerformanceHintManager* create(sp<IHintManager> iHintManager);
61
62 sp<IHintManager> mHintManager;
Bo Liu9acc5582022-02-17 16:47:32 -050063 const sp<IBinder> mToken = sp<BBinder>::make();
Bo Liu44267722021-07-16 17:03:20 -040064 const int64_t mPreferredRateNanos;
65};
66
67struct APerformanceHintSession {
68public:
Peiyong Lin095de762022-11-11 18:28:12 +000069 APerformanceHintSession(sp<IHintManager> hintManager, sp<IHintSession> session,
70 int64_t preferredRateNanos, int64_t targetDurationNanos);
Bo Liu44267722021-07-16 17:03:20 -040071 APerformanceHintSession() = delete;
72 ~APerformanceHintSession();
73
74 int updateTargetWorkDuration(int64_t targetDurationNanos);
75 int reportActualWorkDuration(int64_t actualDurationNanos);
Steven Moreland42a8cce2023-03-03 23:31:17 +000076 int sendHint(SessionHint hint);
Peiyong Lin095de762022-11-11 18:28:12 +000077 int setThreads(const int32_t* threadIds, size_t size);
78 int getThreadIds(int32_t* const threadIds, size_t* size);
Matt Buckley423c1b32023-06-28 19:13:42 +000079 int setPreferPowerEfficiency(bool enabled);
Peiyong Lin70de0852023-10-25 21:12:35 +000080 int reportActualWorkDuration(AWorkDuration* workDuration);
Bo Liu44267722021-07-16 17:03:20 -040081
82private:
83 friend struct APerformanceHintManager;
84
Peiyong Lin70de0852023-10-25 21:12:35 +000085 int reportActualWorkDurationInternal(WorkDuration* workDuration);
86
Peiyong Lin095de762022-11-11 18:28:12 +000087 sp<IHintManager> mHintManager;
Bo Liu44267722021-07-16 17:03:20 -040088 sp<IHintSession> mHintSession;
89 // HAL preferred update rate
90 const int64_t mPreferredRateNanos;
91 // Target duration for choosing update rate
92 int64_t mTargetDurationNanos;
Wei Wang00feb502022-10-18 10:56:59 -070093 // First target hit timestamp
94 int64_t mFirstTargetMetTimestamp;
95 // Last target hit timestamp
96 int64_t mLastTargetMetTimestamp;
Matt Buckley56093a72022-11-07 21:50:50 +000097 // Last hint reported from sendHint indexed by hint value
98 std::vector<int64_t> mLastHintSentTimestamp;
Bo Liu44267722021-07-16 17:03:20 -040099 // Cached samples
Peiyong Lin70de0852023-10-25 21:12:35 +0000100 std::vector<WorkDuration> mActualWorkDurations;
Bo Liu44267722021-07-16 17:03:20 -0400101};
102
103static IHintManager* gIHintManagerForTesting = nullptr;
104static APerformanceHintManager* gHintManagerForTesting = nullptr;
105
106// ===================================== APerformanceHintManager implementation
107APerformanceHintManager::APerformanceHintManager(sp<IHintManager> manager,
108 int64_t preferredRateNanos)
109 : mHintManager(std::move(manager)), mPreferredRateNanos(preferredRateNanos) {}
110
111APerformanceHintManager* APerformanceHintManager::getInstance() {
112 if (gHintManagerForTesting) return gHintManagerForTesting;
113 if (gIHintManagerForTesting) {
114 APerformanceHintManager* manager = create(gIHintManagerForTesting);
115 gIHintManagerForTesting = nullptr;
116 return manager;
117 }
118 static APerformanceHintManager* instance = create(nullptr);
119 return instance;
120}
121
122APerformanceHintManager* APerformanceHintManager::create(sp<IHintManager> manager) {
123 if (!manager) {
124 manager = interface_cast<IHintManager>(
125 defaultServiceManager()->checkService(String16("performance_hint")));
126 }
127 if (manager == nullptr) {
128 ALOGE("%s: PerformanceHint service is not ready ", __FUNCTION__);
129 return nullptr;
130 }
131 int64_t preferredRateNanos = -1L;
132 binder::Status ret = manager->getHintSessionPreferredRate(&preferredRateNanos);
133 if (!ret.isOk()) {
134 ALOGE("%s: PerformanceHint cannot get preferred rate. %s", __FUNCTION__,
135 ret.exceptionMessage().c_str());
136 return nullptr;
137 }
138 if (preferredRateNanos <= 0) {
Bo Liud6a09602021-07-26 14:48:41 -0400139 preferredRateNanos = -1L;
Bo Liu44267722021-07-16 17:03:20 -0400140 }
141 return new APerformanceHintManager(std::move(manager), preferredRateNanos);
142}
143
144APerformanceHintSession* APerformanceHintManager::createSession(
145 const int32_t* threadIds, size_t size, int64_t initialTargetWorkDurationNanos) {
Bo Liu44267722021-07-16 17:03:20 -0400146 std::vector<int32_t> tids(threadIds, threadIds + size);
147 sp<IHintSession> session;
148 binder::Status ret =
Bo Liu9acc5582022-02-17 16:47:32 -0500149 mHintManager->createHintSession(mToken, tids, initialTargetWorkDurationNanos, &session);
Bo Liu44267722021-07-16 17:03:20 -0400150 if (!ret.isOk() || !session) {
151 return nullptr;
152 }
Peiyong Lin095de762022-11-11 18:28:12 +0000153 return new APerformanceHintSession(mHintManager, std::move(session), mPreferredRateNanos,
Bo Liu44267722021-07-16 17:03:20 -0400154 initialTargetWorkDurationNanos);
155}
156
157int64_t APerformanceHintManager::getPreferredRateNanos() const {
158 return mPreferredRateNanos;
159}
160
161// ===================================== APerformanceHintSession implementation
162
Peiyong Lin095de762022-11-11 18:28:12 +0000163APerformanceHintSession::APerformanceHintSession(sp<IHintManager> hintManager,
164 sp<IHintSession> session,
Bo Liu44267722021-07-16 17:03:20 -0400165 int64_t preferredRateNanos,
166 int64_t targetDurationNanos)
Peiyong Lin095de762022-11-11 18:28:12 +0000167 : mHintManager(hintManager),
168 mHintSession(std::move(session)),
Bo Liu44267722021-07-16 17:03:20 -0400169 mPreferredRateNanos(preferredRateNanos),
170 mTargetDurationNanos(targetDurationNanos),
Wei Wang00feb502022-10-18 10:56:59 -0700171 mFirstTargetMetTimestamp(0),
Matt Buckley56093a72022-11-07 21:50:50 +0000172 mLastTargetMetTimestamp(0) {
173 const std::vector<AidlSessionHint> sessionHintRange{ndk::enum_range<AidlSessionHint>().begin(),
174 ndk::enum_range<AidlSessionHint>().end()};
175
176 mLastHintSentTimestamp = std::vector<int64_t>(sessionHintRange.size(), 0);
177}
Bo Liu44267722021-07-16 17:03:20 -0400178
179APerformanceHintSession::~APerformanceHintSession() {
180 binder::Status ret = mHintSession->close();
181 if (!ret.isOk()) {
182 ALOGE("%s: HintSession close failed: %s", __FUNCTION__, ret.exceptionMessage().c_str());
183 }
184}
185
186int APerformanceHintSession::updateTargetWorkDuration(int64_t targetDurationNanos) {
187 if (targetDurationNanos <= 0) {
188 ALOGE("%s: targetDurationNanos must be positive", __FUNCTION__);
189 return EINVAL;
190 }
191 binder::Status ret = mHintSession->updateTargetWorkDuration(targetDurationNanos);
192 if (!ret.isOk()) {
Matt Buckley354cc0a2022-09-28 20:54:46 +0000193 ALOGE("%s: HintSession updateTargetWorkDuration failed: %s", __FUNCTION__,
Bo Liu44267722021-07-16 17:03:20 -0400194 ret.exceptionMessage().c_str());
195 return EPIPE;
196 }
197 mTargetDurationNanos = targetDurationNanos;
198 /**
199 * Most of the workload is target_duration dependent, so now clear the cached samples
200 * as they are most likely obsolete.
201 */
Peiyong Lin70de0852023-10-25 21:12:35 +0000202 mActualWorkDurations.clear();
Wei Wang00feb502022-10-18 10:56:59 -0700203 mFirstTargetMetTimestamp = 0;
204 mLastTargetMetTimestamp = 0;
Bo Liu44267722021-07-16 17:03:20 -0400205 return 0;
206}
207
208int APerformanceHintSession::reportActualWorkDuration(int64_t actualDurationNanos) {
209 if (actualDurationNanos <= 0) {
210 ALOGE("%s: actualDurationNanos must be positive", __FUNCTION__);
211 return EINVAL;
212 }
Bo Liu44267722021-07-16 17:03:20 -0400213
Peiyong Lin70de0852023-10-25 21:12:35 +0000214 WorkDuration workDuration(0, actualDurationNanos, actualDurationNanos, 0);
Bo Liu44267722021-07-16 17:03:20 -0400215
Peiyong Lin70de0852023-10-25 21:12:35 +0000216 return reportActualWorkDurationInternal(&workDuration);
Bo Liu44267722021-07-16 17:03:20 -0400217}
218
Steven Moreland42a8cce2023-03-03 23:31:17 +0000219int APerformanceHintSession::sendHint(SessionHint hint) {
Matt Buckley56093a72022-11-07 21:50:50 +0000220 if (hint < 0 || hint >= static_cast<int32_t>(mLastHintSentTimestamp.size())) {
221 ALOGE("%s: invalid session hint %d", __FUNCTION__, hint);
Matt Buckley354cc0a2022-09-28 20:54:46 +0000222 return EINVAL;
223 }
Matt Buckley56093a72022-11-07 21:50:50 +0000224 int64_t now = elapsedRealtimeNano();
225
226 // Limit sendHint to a pre-detemined rate for safety
227 if (now < (mLastHintSentTimestamp[hint] + SEND_HINT_TIMEOUT)) {
228 return 0;
229 }
Matt Buckley354cc0a2022-09-28 20:54:46 +0000230
231 binder::Status ret = mHintSession->sendHint(hint);
232
233 if (!ret.isOk()) {
234 ALOGE("%s: HintSession sendHint failed: %s", __FUNCTION__, ret.exceptionMessage().c_str());
235 return EPIPE;
236 }
Matt Buckley56093a72022-11-07 21:50:50 +0000237 mLastHintSentTimestamp[hint] = now;
Matt Buckley354cc0a2022-09-28 20:54:46 +0000238 return 0;
239}
240
Peiyong Lin095de762022-11-11 18:28:12 +0000241int APerformanceHintSession::setThreads(const int32_t* threadIds, size_t size) {
242 if (size == 0) {
243 ALOGE("%s: the list of thread ids must not be empty.", __FUNCTION__);
244 return EINVAL;
245 }
246 std::vector<int32_t> tids(threadIds, threadIds + size);
247 binder::Status ret = mHintManager->setHintSessionThreads(mHintSession, tids);
248 if (!ret.isOk()) {
249 ALOGE("%s: failed: %s", __FUNCTION__, ret.exceptionMessage().c_str());
Xiang Wangbee6f162023-07-18 17:58:10 -0700250 if (ret.exceptionCode() == binder::Status::Exception::EX_ILLEGAL_ARGUMENT) {
Peiyong Lin095de762022-11-11 18:28:12 +0000251 return EINVAL;
Xiang Wangbee6f162023-07-18 17:58:10 -0700252 } else if (ret.exceptionCode() == binder::Status::Exception::EX_SECURITY) {
253 return EPERM;
Peiyong Lin095de762022-11-11 18:28:12 +0000254 }
255 return EPIPE;
256 }
257 return 0;
258}
259
260int APerformanceHintSession::getThreadIds(int32_t* const threadIds, size_t* size) {
261 std::vector<int32_t> tids;
262 binder::Status ret = mHintManager->getHintSessionThreadIds(mHintSession, &tids);
263 if (!ret.isOk()) {
264 ALOGE("%s: failed: %s", __FUNCTION__, ret.exceptionMessage().c_str());
265 return EPIPE;
266 }
267
268 // When threadIds is nullptr, this is the first call to determine the size
269 // of the thread ids list.
270 if (threadIds == nullptr) {
271 *size = tids.size();
272 return 0;
273 }
274
275 // Second call to return the actual list of thread ids.
276 *size = tids.size();
277 for (size_t i = 0; i < *size; ++i) {
278 threadIds[i] = tids[i];
279 }
280 return 0;
281}
282
Matt Buckley423c1b32023-06-28 19:13:42 +0000283int APerformanceHintSession::setPreferPowerEfficiency(bool enabled) {
284 binder::Status ret =
285 mHintSession->setMode(static_cast<int32_t>(AidlSessionMode::POWER_EFFICIENCY), enabled);
286
287 if (!ret.isOk()) {
288 ALOGE("%s: HintSession setPreferPowerEfficiency failed: %s", __FUNCTION__,
289 ret.exceptionMessage().c_str());
290 return EPIPE;
291 }
292 return OK;
293}
294
Peiyong Lin70de0852023-10-25 21:12:35 +0000295int APerformanceHintSession::reportActualWorkDuration(AWorkDuration* aWorkDuration) {
296 WorkDuration* workDuration = static_cast<WorkDuration*>(aWorkDuration);
297 if (workDuration->workPeriodStartTimestampNanos <= 0) {
298 ALOGE("%s: workPeriodStartTimestampNanos must be positive", __FUNCTION__);
299 return EINVAL;
300 }
301 if (workDuration->actualTotalDurationNanos <= 0) {
302 ALOGE("%s: actualDurationNanos must be positive", __FUNCTION__);
303 return EINVAL;
304 }
305 if (workDuration->actualCpuDurationNanos <= 0) {
306 ALOGE("%s: cpuDurationNanos must be positive", __FUNCTION__);
307 return EINVAL;
308 }
309 if (workDuration->actualGpuDurationNanos < 0) {
310 ALOGE("%s: gpuDurationNanos must be non negative", __FUNCTION__);
311 return EINVAL;
312 }
313
314 return reportActualWorkDurationInternal(workDuration);
315}
316
317int APerformanceHintSession::reportActualWorkDurationInternal(WorkDuration* workDuration) {
318 int64_t actualTotalDurationNanos = workDuration->actualTotalDurationNanos;
319 int64_t now = uptimeNanos();
320 workDuration->timestampNanos = now;
321 mActualWorkDurations.push_back(std::move(*workDuration));
322
323 if (actualTotalDurationNanos >= mTargetDurationNanos) {
324 // Reset timestamps if we are equal or over the target.
325 mFirstTargetMetTimestamp = 0;
326 } else {
327 // Set mFirstTargetMetTimestamp for first time meeting target.
328 if (!mFirstTargetMetTimestamp || !mLastTargetMetTimestamp ||
329 (now - mLastTargetMetTimestamp > 2 * mPreferredRateNanos)) {
330 mFirstTargetMetTimestamp = now;
331 }
332 /**
333 * Rate limit the change if the update is over mPreferredRateNanos since first
334 * meeting target and less than mPreferredRateNanos since last meeting target.
335 */
336 if (now - mFirstTargetMetTimestamp > mPreferredRateNanos &&
337 now - mLastTargetMetTimestamp <= mPreferredRateNanos) {
338 return 0;
339 }
340 mLastTargetMetTimestamp = now;
341 }
342
343 binder::Status ret = mHintSession->reportActualWorkDuration2(mActualWorkDurations);
344 if (!ret.isOk()) {
345 ALOGE("%s: HintSession reportActualWorkDuration failed: %s", __FUNCTION__,
346 ret.exceptionMessage().c_str());
347 mFirstTargetMetTimestamp = 0;
348 mLastTargetMetTimestamp = 0;
349 return ret.exceptionCode() == binder::Status::EX_ILLEGAL_ARGUMENT ? EINVAL : EPIPE;
350 }
351 mActualWorkDurations.clear();
352
353 return 0;
354}
355
Bo Liu44267722021-07-16 17:03:20 -0400356// ===================================== C API
357APerformanceHintManager* APerformanceHint_getManager() {
358 return APerformanceHintManager::getInstance();
359}
360
361APerformanceHintSession* APerformanceHint_createSession(APerformanceHintManager* manager,
362 const int32_t* threadIds, size_t size,
363 int64_t initialTargetWorkDurationNanos) {
364 return manager->createSession(threadIds, size, initialTargetWorkDurationNanos);
365}
366
367int64_t APerformanceHint_getPreferredUpdateRateNanos(APerformanceHintManager* manager) {
368 return manager->getPreferredRateNanos();
369}
370
371int APerformanceHint_updateTargetWorkDuration(APerformanceHintSession* session,
372 int64_t targetDurationNanos) {
373 return session->updateTargetWorkDuration(targetDurationNanos);
374}
375
376int APerformanceHint_reportActualWorkDuration(APerformanceHintSession* session,
377 int64_t actualDurationNanos) {
378 return session->reportActualWorkDuration(actualDurationNanos);
379}
380
Bo Liu44267722021-07-16 17:03:20 -0400381void APerformanceHint_closeSession(APerformanceHintSession* session) {
382 delete session;
383}
384
Steven Moreland42a8cce2023-03-03 23:31:17 +0000385int APerformanceHint_sendHint(void* session, SessionHint hint) {
Matt Buckley61726a32022-12-06 23:44:45 +0000386 return reinterpret_cast<APerformanceHintSession*>(session)->sendHint(hint);
387}
388
Peiyong Lin7ed6de32023-01-26 00:52:54 +0000389int APerformanceHint_setThreads(APerformanceHintSession* session, const pid_t* threadIds,
Peiyong Lin095de762022-11-11 18:28:12 +0000390 size_t size) {
391 if (session == nullptr) {
392 return EINVAL;
393 }
394 return session->setThreads(threadIds, size);
395}
396
397int APerformanceHint_getThreadIds(void* aPerformanceHintSession, int32_t* const threadIds,
398 size_t* const size) {
399 if (aPerformanceHintSession == nullptr) {
400 return EINVAL;
401 }
402 return static_cast<APerformanceHintSession*>(aPerformanceHintSession)
403 ->getThreadIds(threadIds, size);
404}
405
Matt Buckley423c1b32023-06-28 19:13:42 +0000406int APerformanceHint_setPreferPowerEfficiency(APerformanceHintSession* session, bool enabled) {
407 return session->setPreferPowerEfficiency(enabled);
408}
409
Peiyong Lin70de0852023-10-25 21:12:35 +0000410int APerformanceHint_reportActualWorkDuration2(APerformanceHintSession* session,
411 AWorkDuration* workDuration) {
412 if (session == nullptr || workDuration == nullptr) {
413 ALOGE("Invalid value: (session %p, workDuration %p)", session, workDuration);
414 return EINVAL;
415 }
416 return session->reportActualWorkDuration(workDuration);
417}
418
419AWorkDuration* AWorkDuration_create() {
420 WorkDuration* workDuration = new WorkDuration();
421 return static_cast<AWorkDuration*>(workDuration);
422}
423
424void AWorkDuration_release(AWorkDuration* aWorkDuration) {
425 if (aWorkDuration == nullptr) {
426 ALOGE("%s: aWorkDuration is nullptr", __FUNCTION__);
427 }
428 delete aWorkDuration;
429}
430
431void AWorkDuration_setWorkPeriodStartTimestampNanos(AWorkDuration* aWorkDuration,
432 int64_t workPeriodStartTimestampNanos) {
433 if (aWorkDuration == nullptr || workPeriodStartTimestampNanos <= 0) {
434 ALOGE("%s: Invalid value. (AWorkDuration: %p, workPeriodStartTimestampNanos: %" PRIi64 ")",
435 __FUNCTION__, aWorkDuration, workPeriodStartTimestampNanos);
436 }
437 static_cast<WorkDuration*>(aWorkDuration)->workPeriodStartTimestampNanos =
438 workPeriodStartTimestampNanos;
439}
440
441void AWorkDuration_setActualTotalDurationNanos(AWorkDuration* aWorkDuration,
442 int64_t actualTotalDurationNanos) {
443 if (aWorkDuration == nullptr || actualTotalDurationNanos <= 0) {
444 ALOGE("%s: Invalid value. (AWorkDuration: %p, actualTotalDurationNanos: %" PRIi64 ")",
445 __FUNCTION__, aWorkDuration, actualTotalDurationNanos);
446 }
447 static_cast<WorkDuration*>(aWorkDuration)->actualTotalDurationNanos = actualTotalDurationNanos;
448}
449
450void AWorkDuration_setActualCpuDurationNanos(AWorkDuration* aWorkDuration,
451 int64_t actualCpuDurationNanos) {
452 if (aWorkDuration == nullptr || actualCpuDurationNanos <= 0) {
453 ALOGE("%s: Invalid value. (AWorkDuration: %p, actualCpuDurationNanos: %" PRIi64 ")",
454 __FUNCTION__, aWorkDuration, actualCpuDurationNanos);
455 }
456 static_cast<WorkDuration*>(aWorkDuration)->actualCpuDurationNanos = actualCpuDurationNanos;
457}
458
459void AWorkDuration_setActualGpuDurationNanos(AWorkDuration* aWorkDuration,
460 int64_t actualGpuDurationNanos) {
461 if (aWorkDuration == nullptr || actualGpuDurationNanos < 0) {
462 ALOGE("%s: Invalid value. (AWorkDuration: %p, actualGpuDurationNanos: %" PRIi64 ")",
463 __FUNCTION__, aWorkDuration, actualGpuDurationNanos);
464 }
465 static_cast<WorkDuration*>(aWorkDuration)->actualGpuDurationNanos = actualGpuDurationNanos;
466}
467
Bo Liu44267722021-07-16 17:03:20 -0400468void APerformanceHint_setIHintManagerForTesting(void* iManager) {
469 delete gHintManagerForTesting;
470 gHintManagerForTesting = nullptr;
471 gIHintManagerForTesting = static_cast<IHintManager*>(iManager);
472}