blob: c25df6e08fd050fde5250cdbb559690760395509 [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>
Bo Liu44267722021-07-16 17:03:20 -040021#include <android/os/IHintManager.h>
22#include <android/os/IHintSession.h>
Bo Liu2b739bb2021-11-10 19:20:03 -050023#include <android/performance_hint.h>
Bo Liu44267722021-07-16 17:03:20 -040024#include <binder/Binder.h>
25#include <binder/IBinder.h>
26#include <binder/IServiceManager.h>
27#include <performance_hint_private.h>
28#include <utils/SystemClock.h>
29
Matt Buckley56093a72022-11-07 21:50:50 +000030#include <chrono>
Bo Liu2b739bb2021-11-10 19:20:03 -050031#include <utility>
32#include <vector>
33
Bo Liu44267722021-07-16 17:03:20 -040034using namespace android;
35using namespace android::os;
36
Matt Buckley56093a72022-11-07 21:50:50 +000037using namespace std::chrono_literals;
38
39using AidlSessionHint = aidl::android::hardware::power::SessionHint;
Matt Buckley423c1b32023-06-28 19:13:42 +000040using AidlSessionMode = aidl::android::hardware::power::SessionMode;
Matt Buckley56093a72022-11-07 21:50:50 +000041
Bo Liu44267722021-07-16 17:03:20 -040042struct APerformanceHintSession;
43
Matt Buckley56093a72022-11-07 21:50:50 +000044constexpr int64_t SEND_HINT_TIMEOUT = std::chrono::nanoseconds(100ms).count();
45
Bo Liu44267722021-07-16 17:03:20 -040046struct APerformanceHintManager {
47public:
48 static APerformanceHintManager* getInstance();
49 APerformanceHintManager(sp<IHintManager> service, int64_t preferredRateNanos);
50 APerformanceHintManager() = delete;
51 ~APerformanceHintManager() = default;
52
53 APerformanceHintSession* createSession(const int32_t* threadIds, size_t size,
54 int64_t initialTargetWorkDurationNanos);
55 int64_t getPreferredRateNanos() const;
56
57private:
58 static APerformanceHintManager* create(sp<IHintManager> iHintManager);
59
60 sp<IHintManager> mHintManager;
Bo Liu9acc5582022-02-17 16:47:32 -050061 const sp<IBinder> mToken = sp<BBinder>::make();
Bo Liu44267722021-07-16 17:03:20 -040062 const int64_t mPreferredRateNanos;
63};
64
65struct APerformanceHintSession {
66public:
Peiyong Lin095de762022-11-11 18:28:12 +000067 APerformanceHintSession(sp<IHintManager> hintManager, sp<IHintSession> session,
68 int64_t preferredRateNanos, int64_t targetDurationNanos);
Bo Liu44267722021-07-16 17:03:20 -040069 APerformanceHintSession() = delete;
70 ~APerformanceHintSession();
71
72 int updateTargetWorkDuration(int64_t targetDurationNanos);
73 int reportActualWorkDuration(int64_t actualDurationNanos);
Steven Moreland42a8cce2023-03-03 23:31:17 +000074 int sendHint(SessionHint hint);
Peiyong Lin095de762022-11-11 18:28:12 +000075 int setThreads(const int32_t* threadIds, size_t size);
76 int getThreadIds(int32_t* const threadIds, size_t* size);
Matt Buckley423c1b32023-06-28 19:13:42 +000077 int setPreferPowerEfficiency(bool enabled);
Bo Liu44267722021-07-16 17:03:20 -040078
79private:
80 friend struct APerformanceHintManager;
81
Peiyong Lin095de762022-11-11 18:28:12 +000082 sp<IHintManager> mHintManager;
Bo Liu44267722021-07-16 17:03:20 -040083 sp<IHintSession> mHintSession;
84 // HAL preferred update rate
85 const int64_t mPreferredRateNanos;
86 // Target duration for choosing update rate
87 int64_t mTargetDurationNanos;
Wei Wang00feb502022-10-18 10:56:59 -070088 // First target hit timestamp
89 int64_t mFirstTargetMetTimestamp;
90 // Last target hit timestamp
91 int64_t mLastTargetMetTimestamp;
Matt Buckley56093a72022-11-07 21:50:50 +000092 // Last hint reported from sendHint indexed by hint value
93 std::vector<int64_t> mLastHintSentTimestamp;
Bo Liu44267722021-07-16 17:03:20 -040094 // Cached samples
95 std::vector<int64_t> mActualDurationsNanos;
96 std::vector<int64_t> mTimestampsNanos;
97};
98
99static IHintManager* gIHintManagerForTesting = nullptr;
100static APerformanceHintManager* gHintManagerForTesting = nullptr;
101
102// ===================================== APerformanceHintManager implementation
103APerformanceHintManager::APerformanceHintManager(sp<IHintManager> manager,
104 int64_t preferredRateNanos)
105 : mHintManager(std::move(manager)), mPreferredRateNanos(preferredRateNanos) {}
106
107APerformanceHintManager* APerformanceHintManager::getInstance() {
108 if (gHintManagerForTesting) return gHintManagerForTesting;
109 if (gIHintManagerForTesting) {
110 APerformanceHintManager* manager = create(gIHintManagerForTesting);
111 gIHintManagerForTesting = nullptr;
112 return manager;
113 }
114 static APerformanceHintManager* instance = create(nullptr);
115 return instance;
116}
117
118APerformanceHintManager* APerformanceHintManager::create(sp<IHintManager> manager) {
119 if (!manager) {
120 manager = interface_cast<IHintManager>(
121 defaultServiceManager()->checkService(String16("performance_hint")));
122 }
123 if (manager == nullptr) {
124 ALOGE("%s: PerformanceHint service is not ready ", __FUNCTION__);
125 return nullptr;
126 }
127 int64_t preferredRateNanos = -1L;
128 binder::Status ret = manager->getHintSessionPreferredRate(&preferredRateNanos);
129 if (!ret.isOk()) {
130 ALOGE("%s: PerformanceHint cannot get preferred rate. %s", __FUNCTION__,
131 ret.exceptionMessage().c_str());
132 return nullptr;
133 }
134 if (preferredRateNanos <= 0) {
Bo Liud6a09602021-07-26 14:48:41 -0400135 preferredRateNanos = -1L;
Bo Liu44267722021-07-16 17:03:20 -0400136 }
137 return new APerformanceHintManager(std::move(manager), preferredRateNanos);
138}
139
140APerformanceHintSession* APerformanceHintManager::createSession(
141 const int32_t* threadIds, size_t size, int64_t initialTargetWorkDurationNanos) {
Bo Liu44267722021-07-16 17:03:20 -0400142 std::vector<int32_t> tids(threadIds, threadIds + size);
143 sp<IHintSession> session;
144 binder::Status ret =
Bo Liu9acc5582022-02-17 16:47:32 -0500145 mHintManager->createHintSession(mToken, tids, initialTargetWorkDurationNanos, &session);
Bo Liu44267722021-07-16 17:03:20 -0400146 if (!ret.isOk() || !session) {
147 return nullptr;
148 }
Peiyong Lin095de762022-11-11 18:28:12 +0000149 return new APerformanceHintSession(mHintManager, std::move(session), mPreferredRateNanos,
Bo Liu44267722021-07-16 17:03:20 -0400150 initialTargetWorkDurationNanos);
151}
152
153int64_t APerformanceHintManager::getPreferredRateNanos() const {
154 return mPreferredRateNanos;
155}
156
157// ===================================== APerformanceHintSession implementation
158
Peiyong Lin095de762022-11-11 18:28:12 +0000159APerformanceHintSession::APerformanceHintSession(sp<IHintManager> hintManager,
160 sp<IHintSession> session,
Bo Liu44267722021-07-16 17:03:20 -0400161 int64_t preferredRateNanos,
162 int64_t targetDurationNanos)
Peiyong Lin095de762022-11-11 18:28:12 +0000163 : mHintManager(hintManager),
164 mHintSession(std::move(session)),
Bo Liu44267722021-07-16 17:03:20 -0400165 mPreferredRateNanos(preferredRateNanos),
166 mTargetDurationNanos(targetDurationNanos),
Wei Wang00feb502022-10-18 10:56:59 -0700167 mFirstTargetMetTimestamp(0),
Matt Buckley56093a72022-11-07 21:50:50 +0000168 mLastTargetMetTimestamp(0) {
169 const std::vector<AidlSessionHint> sessionHintRange{ndk::enum_range<AidlSessionHint>().begin(),
170 ndk::enum_range<AidlSessionHint>().end()};
171
172 mLastHintSentTimestamp = std::vector<int64_t>(sessionHintRange.size(), 0);
173}
Bo Liu44267722021-07-16 17:03:20 -0400174
175APerformanceHintSession::~APerformanceHintSession() {
176 binder::Status ret = mHintSession->close();
177 if (!ret.isOk()) {
178 ALOGE("%s: HintSession close failed: %s", __FUNCTION__, ret.exceptionMessage().c_str());
179 }
180}
181
182int APerformanceHintSession::updateTargetWorkDuration(int64_t targetDurationNanos) {
183 if (targetDurationNanos <= 0) {
184 ALOGE("%s: targetDurationNanos must be positive", __FUNCTION__);
185 return EINVAL;
186 }
187 binder::Status ret = mHintSession->updateTargetWorkDuration(targetDurationNanos);
188 if (!ret.isOk()) {
Matt Buckley354cc0a2022-09-28 20:54:46 +0000189 ALOGE("%s: HintSession updateTargetWorkDuration failed: %s", __FUNCTION__,
Bo Liu44267722021-07-16 17:03:20 -0400190 ret.exceptionMessage().c_str());
191 return EPIPE;
192 }
193 mTargetDurationNanos = targetDurationNanos;
194 /**
195 * Most of the workload is target_duration dependent, so now clear the cached samples
196 * as they are most likely obsolete.
197 */
198 mActualDurationsNanos.clear();
199 mTimestampsNanos.clear();
Wei Wang00feb502022-10-18 10:56:59 -0700200 mFirstTargetMetTimestamp = 0;
201 mLastTargetMetTimestamp = 0;
Bo Liu44267722021-07-16 17:03:20 -0400202 return 0;
203}
204
205int APerformanceHintSession::reportActualWorkDuration(int64_t actualDurationNanos) {
206 if (actualDurationNanos <= 0) {
207 ALOGE("%s: actualDurationNanos must be positive", __FUNCTION__);
208 return EINVAL;
209 }
210 int64_t now = elapsedRealtimeNano();
211 mActualDurationsNanos.push_back(actualDurationNanos);
212 mTimestampsNanos.push_back(now);
213
Wei Wang00feb502022-10-18 10:56:59 -0700214 if (actualDurationNanos >= mTargetDurationNanos) {
215 // Reset timestamps if we are equal or over the target.
216 mFirstTargetMetTimestamp = 0;
217 } else {
218 // Set mFirstTargetMetTimestamp for first time meeting target.
219 if (!mFirstTargetMetTimestamp || !mLastTargetMetTimestamp ||
220 (now - mLastTargetMetTimestamp > 2 * mPreferredRateNanos)) {
221 mFirstTargetMetTimestamp = now;
222 }
223 /**
224 * Rate limit the change if the update is over mPreferredRateNanos since first
225 * meeting target and less than mPreferredRateNanos since last meeting target.
226 */
227 if (now - mFirstTargetMetTimestamp > mPreferredRateNanos &&
228 now - mLastTargetMetTimestamp <= mPreferredRateNanos) {
229 return 0;
230 }
231 mLastTargetMetTimestamp = now;
Wei Wang9f174f42021-11-11 22:56:05 -0800232 }
Bo Liu44267722021-07-16 17:03:20 -0400233
234 binder::Status ret =
235 mHintSession->reportActualWorkDuration(mActualDurationsNanos, mTimestampsNanos);
Bo Liu44267722021-07-16 17:03:20 -0400236 if (!ret.isOk()) {
237 ALOGE("%s: HintSession reportActualWorkDuration failed: %s", __FUNCTION__,
238 ret.exceptionMessage().c_str());
Wei Wang00feb502022-10-18 10:56:59 -0700239 mFirstTargetMetTimestamp = 0;
240 mLastTargetMetTimestamp = 0;
Bo Liu44267722021-07-16 17:03:20 -0400241 return EPIPE;
242 }
Wei Wang00feb502022-10-18 10:56:59 -0700243 mActualDurationsNanos.clear();
244 mTimestampsNanos.clear();
245
Bo Liu44267722021-07-16 17:03:20 -0400246 return 0;
247}
248
Steven Moreland42a8cce2023-03-03 23:31:17 +0000249int APerformanceHintSession::sendHint(SessionHint hint) {
Matt Buckley56093a72022-11-07 21:50:50 +0000250 if (hint < 0 || hint >= static_cast<int32_t>(mLastHintSentTimestamp.size())) {
251 ALOGE("%s: invalid session hint %d", __FUNCTION__, hint);
Matt Buckley354cc0a2022-09-28 20:54:46 +0000252 return EINVAL;
253 }
Matt Buckley56093a72022-11-07 21:50:50 +0000254 int64_t now = elapsedRealtimeNano();
255
256 // Limit sendHint to a pre-detemined rate for safety
257 if (now < (mLastHintSentTimestamp[hint] + SEND_HINT_TIMEOUT)) {
258 return 0;
259 }
Matt Buckley354cc0a2022-09-28 20:54:46 +0000260
261 binder::Status ret = mHintSession->sendHint(hint);
262
263 if (!ret.isOk()) {
264 ALOGE("%s: HintSession sendHint failed: %s", __FUNCTION__, ret.exceptionMessage().c_str());
265 return EPIPE;
266 }
Matt Buckley56093a72022-11-07 21:50:50 +0000267 mLastHintSentTimestamp[hint] = now;
Matt Buckley354cc0a2022-09-28 20:54:46 +0000268 return 0;
269}
270
Peiyong Lin095de762022-11-11 18:28:12 +0000271int APerformanceHintSession::setThreads(const int32_t* threadIds, size_t size) {
272 if (size == 0) {
273 ALOGE("%s: the list of thread ids must not be empty.", __FUNCTION__);
274 return EINVAL;
275 }
276 std::vector<int32_t> tids(threadIds, threadIds + size);
277 binder::Status ret = mHintManager->setHintSessionThreads(mHintSession, tids);
278 if (!ret.isOk()) {
279 ALOGE("%s: failed: %s", __FUNCTION__, ret.exceptionMessage().c_str());
Xiang Wangbee6f162023-07-18 17:58:10 -0700280 if (ret.exceptionCode() == binder::Status::Exception::EX_ILLEGAL_ARGUMENT) {
Peiyong Lin095de762022-11-11 18:28:12 +0000281 return EINVAL;
Xiang Wangbee6f162023-07-18 17:58:10 -0700282 } else if (ret.exceptionCode() == binder::Status::Exception::EX_SECURITY) {
283 return EPERM;
Peiyong Lin095de762022-11-11 18:28:12 +0000284 }
285 return EPIPE;
286 }
287 return 0;
288}
289
290int APerformanceHintSession::getThreadIds(int32_t* const threadIds, size_t* size) {
291 std::vector<int32_t> tids;
292 binder::Status ret = mHintManager->getHintSessionThreadIds(mHintSession, &tids);
293 if (!ret.isOk()) {
294 ALOGE("%s: failed: %s", __FUNCTION__, ret.exceptionMessage().c_str());
295 return EPIPE;
296 }
297
298 // When threadIds is nullptr, this is the first call to determine the size
299 // of the thread ids list.
300 if (threadIds == nullptr) {
301 *size = tids.size();
302 return 0;
303 }
304
305 // Second call to return the actual list of thread ids.
306 *size = tids.size();
307 for (size_t i = 0; i < *size; ++i) {
308 threadIds[i] = tids[i];
309 }
310 return 0;
311}
312
Matt Buckley423c1b32023-06-28 19:13:42 +0000313int APerformanceHintSession::setPreferPowerEfficiency(bool enabled) {
314 binder::Status ret =
315 mHintSession->setMode(static_cast<int32_t>(AidlSessionMode::POWER_EFFICIENCY), enabled);
316
317 if (!ret.isOk()) {
318 ALOGE("%s: HintSession setPreferPowerEfficiency failed: %s", __FUNCTION__,
319 ret.exceptionMessage().c_str());
320 return EPIPE;
321 }
322 return OK;
323}
324
Bo Liu44267722021-07-16 17:03:20 -0400325// ===================================== C API
326APerformanceHintManager* APerformanceHint_getManager() {
327 return APerformanceHintManager::getInstance();
328}
329
330APerformanceHintSession* APerformanceHint_createSession(APerformanceHintManager* manager,
331 const int32_t* threadIds, size_t size,
332 int64_t initialTargetWorkDurationNanos) {
333 return manager->createSession(threadIds, size, initialTargetWorkDurationNanos);
334}
335
336int64_t APerformanceHint_getPreferredUpdateRateNanos(APerformanceHintManager* manager) {
337 return manager->getPreferredRateNanos();
338}
339
340int APerformanceHint_updateTargetWorkDuration(APerformanceHintSession* session,
341 int64_t targetDurationNanos) {
342 return session->updateTargetWorkDuration(targetDurationNanos);
343}
344
345int APerformanceHint_reportActualWorkDuration(APerformanceHintSession* session,
346 int64_t actualDurationNanos) {
347 return session->reportActualWorkDuration(actualDurationNanos);
348}
349
Bo Liu44267722021-07-16 17:03:20 -0400350void APerformanceHint_closeSession(APerformanceHintSession* session) {
351 delete session;
352}
353
Steven Moreland42a8cce2023-03-03 23:31:17 +0000354int APerformanceHint_sendHint(void* session, SessionHint hint) {
Matt Buckley61726a32022-12-06 23:44:45 +0000355 return reinterpret_cast<APerformanceHintSession*>(session)->sendHint(hint);
356}
357
Peiyong Lin7ed6de32023-01-26 00:52:54 +0000358int APerformanceHint_setThreads(APerformanceHintSession* session, const pid_t* threadIds,
Peiyong Lin095de762022-11-11 18:28:12 +0000359 size_t size) {
360 if (session == nullptr) {
361 return EINVAL;
362 }
363 return session->setThreads(threadIds, size);
364}
365
366int APerformanceHint_getThreadIds(void* aPerformanceHintSession, int32_t* const threadIds,
367 size_t* const size) {
368 if (aPerformanceHintSession == nullptr) {
369 return EINVAL;
370 }
371 return static_cast<APerformanceHintSession*>(aPerformanceHintSession)
372 ->getThreadIds(threadIds, size);
373}
374
Matt Buckley423c1b32023-06-28 19:13:42 +0000375int APerformanceHint_setPreferPowerEfficiency(APerformanceHintSession* session, bool enabled) {
376 return session->setPreferPowerEfficiency(enabled);
377}
378
Bo Liu44267722021-07-16 17:03:20 -0400379void APerformanceHint_setIHintManagerForTesting(void* iManager) {
380 delete gHintManagerForTesting;
381 gHintManagerForTesting = nullptr;
382 gIHintManagerForTesting = static_cast<IHintManager*>(iManager);
383}