1 /*
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 "PerfHint-jni"
18
19 #include <android/performance_hint.h>
20 #include <dlfcn.h>
21 #include <nativehelper/JNIHelp.h>
22 #include <nativehelper/ScopedPrimitiveArray.h>
23 #include <utils/Log.h>
24
25 #include <vector>
26
27 #include "core_jni_helpers.h"
28 #include "jni.h"
29
30 namespace android {
31
32 namespace {
33
34 struct APerformanceHintManager;
35 struct APerformanceHintSession;
36
37 typedef APerformanceHintManager* (*APH_getManager)();
38 typedef int64_t (*APH_getPreferredUpdateRateNanos)(APerformanceHintManager* manager);
39 typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*,
40 size_t, int64_t);
41 typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t);
42 typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t);
43 typedef void (*APH_closeSession)(APerformanceHintSession* session);
44 typedef void (*APH_sendHint)(APerformanceHintSession*, int32_t);
45 typedef int (*APH_setThreads)(APerformanceHintSession*, const pid_t*, size_t);
46 typedef void (*APH_getThreadIds)(APerformanceHintSession*, int32_t* const, size_t* const);
47 typedef void (*APH_setPreferPowerEfficiency)(APerformanceHintSession*, bool);
48 typedef void (*APH_reportActualWorkDuration2)(APerformanceHintSession*, AWorkDuration*);
49
50 typedef AWorkDuration* (*AWD_create)();
51 typedef void (*AWD_setTimeNanos)(AWorkDuration*, int64_t);
52 typedef void (*AWD_release)(AWorkDuration*);
53
54 bool gAPerformanceHintBindingInitialized = false;
55 APH_getManager gAPH_getManagerFn = nullptr;
56 APH_getPreferredUpdateRateNanos gAPH_getPreferredUpdateRateNanosFn = nullptr;
57 APH_createSession gAPH_createSessionFn = nullptr;
58 APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr;
59 APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr;
60 APH_closeSession gAPH_closeSessionFn = nullptr;
61 APH_sendHint gAPH_sendHintFn = nullptr;
62 APH_setThreads gAPH_setThreadsFn = nullptr;
63 APH_getThreadIds gAPH_getThreadIdsFn = nullptr;
64 APH_setPreferPowerEfficiency gAPH_setPreferPowerEfficiencyFn = nullptr;
65 APH_reportActualWorkDuration2 gAPH_reportActualWorkDuration2Fn = nullptr;
66
67 AWD_create gAWD_createFn = nullptr;
68 AWD_setTimeNanos gAWD_setWorkPeriodStartTimestampNanosFn = nullptr;
69 AWD_setTimeNanos gAWD_setActualTotalDurationNanosFn = nullptr;
70 AWD_setTimeNanos gAWD_setActualCpuDurationNanosFn = nullptr;
71 AWD_setTimeNanos gAWD_setActualGpuDurationNanosFn = nullptr;
72 AWD_release gAWD_releaseFn = nullptr;
73
ensureAPerformanceHintBindingInitialized()74 void ensureAPerformanceHintBindingInitialized() {
75 if (gAPerformanceHintBindingInitialized) return;
76
77 void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
78 LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!");
79
80 gAPH_getManagerFn = (APH_getManager)dlsym(handle_, "APerformanceHint_getManager");
81 LOG_ALWAYS_FATAL_IF(gAPH_getManagerFn == nullptr,
82 "Failed to find required symbol APerformanceHint_getManager!");
83
84 gAPH_getPreferredUpdateRateNanosFn =
85 (APH_getPreferredUpdateRateNanos)dlsym(handle_,
86 "APerformanceHint_getPreferredUpdateRateNanos");
87 LOG_ALWAYS_FATAL_IF(gAPH_getPreferredUpdateRateNanosFn == nullptr,
88 "Failed to find required symbol "
89 "APerformanceHint_getPreferredUpdateRateNanos!");
90
91 gAPH_createSessionFn =
92 (APH_createSession)dlsym(handle_, "APerformanceHint_createSessionFromJava");
93 LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr,
94 "Failed to find required symbol APerformanceHint_createSessionFromJava!");
95
96 gAPH_updateTargetWorkDurationFn =
97 (APH_updateTargetWorkDuration)dlsym(handle_,
98 "APerformanceHint_updateTargetWorkDuration");
99 LOG_ALWAYS_FATAL_IF(gAPH_updateTargetWorkDurationFn == nullptr,
100 "Failed to find required symbol "
101 "APerformanceHint_updateTargetWorkDuration!");
102
103 gAPH_reportActualWorkDurationFn =
104 (APH_reportActualWorkDuration)dlsym(handle_,
105 "APerformanceHint_reportActualWorkDuration");
106 LOG_ALWAYS_FATAL_IF(gAPH_reportActualWorkDurationFn == nullptr,
107 "Failed to find required symbol "
108 "APerformanceHint_reportActualWorkDuration!");
109
110 gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSessionFromJava");
111 LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr,
112 "Failed to find required symbol APerformanceHint_closeSessionFromJava!");
113
114 gAPH_sendHintFn = (APH_sendHint)dlsym(handle_, "APerformanceHint_sendHint");
115 LOG_ALWAYS_FATAL_IF(gAPH_sendHintFn == nullptr,
116 "Failed to find required symbol APerformanceHint_sendHint!");
117
118 gAPH_setThreadsFn = (APH_setThreads)dlsym(handle_, "APerformanceHint_setThreads");
119 LOG_ALWAYS_FATAL_IF(gAPH_setThreadsFn == nullptr,
120 "Failed to find required symbol APerformanceHint_setThreads!");
121
122 gAPH_getThreadIdsFn = (APH_getThreadIds)dlsym(handle_, "APerformanceHint_getThreadIds");
123 LOG_ALWAYS_FATAL_IF(gAPH_getThreadIdsFn == nullptr,
124 "Failed to find required symbol APerformanceHint_getThreadIds!");
125
126 gAPH_setPreferPowerEfficiencyFn =
127 (APH_setPreferPowerEfficiency)dlsym(handle_,
128 "APerformanceHint_setPreferPowerEfficiency");
129 LOG_ALWAYS_FATAL_IF(gAPH_setPreferPowerEfficiencyFn == nullptr,
130 "Failed to find required symbol "
131 "APerformanceHint_setPreferPowerEfficiency!");
132
133 gAPH_reportActualWorkDuration2Fn =
134 (APH_reportActualWorkDuration2)dlsym(handle_,
135 "APerformanceHint_reportActualWorkDuration2");
136 LOG_ALWAYS_FATAL_IF(gAPH_reportActualWorkDuration2Fn == nullptr,
137 "Failed to find required symbol "
138 "APerformanceHint_reportActualWorkDuration2!");
139
140 gAWD_createFn = (AWD_create)dlsym(handle_, "AWorkDuration_create");
141 LOG_ALWAYS_FATAL_IF(gAWD_createFn == nullptr,
142 "Failed to find required symbol AWorkDuration_create!");
143
144 gAWD_setWorkPeriodStartTimestampNanosFn =
145 (AWD_setTimeNanos)dlsym(handle_, "AWorkDuration_setWorkPeriodStartTimestampNanos");
146 LOG_ALWAYS_FATAL_IF(gAWD_setWorkPeriodStartTimestampNanosFn == nullptr,
147 "Failed to find required symbol "
148 "AWorkDuration_setWorkPeriodStartTimestampNanos!");
149
150 gAWD_setActualTotalDurationNanosFn =
151 (AWD_setTimeNanos)dlsym(handle_, "AWorkDuration_setActualTotalDurationNanos");
152 LOG_ALWAYS_FATAL_IF(gAWD_setActualTotalDurationNanosFn == nullptr,
153 "Failed to find required symbol "
154 "AWorkDuration_setActualTotalDurationNanos!");
155
156 gAWD_setActualCpuDurationNanosFn =
157 (AWD_setTimeNanos)dlsym(handle_, "AWorkDuration_setActualCpuDurationNanos");
158 LOG_ALWAYS_FATAL_IF(gAWD_setActualCpuDurationNanosFn == nullptr,
159 "Failed to find required symbol AWorkDuration_setActualCpuDurationNanos!");
160
161 gAWD_setActualGpuDurationNanosFn =
162 (AWD_setTimeNanos)dlsym(handle_, "AWorkDuration_setActualGpuDurationNanos");
163 LOG_ALWAYS_FATAL_IF(gAWD_setActualGpuDurationNanosFn == nullptr,
164 "Failed to find required symbol AWorkDuration_setActualGpuDurationNanos!");
165
166 gAWD_releaseFn = (AWD_release)dlsym(handle_, "AWorkDuration_release");
167 LOG_ALWAYS_FATAL_IF(gAWD_releaseFn == nullptr,
168 "Failed to find required symbol AWorkDuration_release!");
169
170 gAPerformanceHintBindingInitialized = true;
171 }
172
173 } // namespace
174
throwExceptionForErrno(JNIEnv * env,int err,const std::string & msg)175 static void throwExceptionForErrno(JNIEnv* env, int err, const std::string& msg) {
176 switch (err) {
177 case EINVAL:
178 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", msg.c_str());
179 break;
180 case EPERM:
181 jniThrowExceptionFmt(env, "java/lang/SecurityException", msg.c_str());
182 break;
183 default:
184 jniThrowException(env, "java/lang/RuntimeException", msg.c_str());
185 break;
186 }
187 }
188
nativeAcquireManager(JNIEnv * env,jclass clazz)189 static jlong nativeAcquireManager(JNIEnv* env, jclass clazz) {
190 ensureAPerformanceHintBindingInitialized();
191 return reinterpret_cast<jlong>(gAPH_getManagerFn());
192 }
193
nativeGetPreferredUpdateRateNanos(JNIEnv * env,jclass clazz,jlong nativeManagerPtr)194 static jlong nativeGetPreferredUpdateRateNanos(JNIEnv* env, jclass clazz, jlong nativeManagerPtr) {
195 ensureAPerformanceHintBindingInitialized();
196 return gAPH_getPreferredUpdateRateNanosFn(
197 reinterpret_cast<APerformanceHintManager*>(nativeManagerPtr));
198 }
199
nativeCreateSession(JNIEnv * env,jclass clazz,jlong nativeManagerPtr,jintArray tids,jlong initialTargetWorkDurationNanos)200 static jlong nativeCreateSession(JNIEnv* env, jclass clazz, jlong nativeManagerPtr, jintArray tids,
201 jlong initialTargetWorkDurationNanos) {
202 ensureAPerformanceHintBindingInitialized();
203 if (tids == nullptr) return 0;
204 std::vector<int32_t> tidsVector;
205 ScopedIntArrayRO tidsArray(env, tids);
206 for (size_t i = 0; i < tidsArray.size(); ++i) {
207 tidsVector.push_back(static_cast<int32_t>(tidsArray[i]));
208 }
209 return reinterpret_cast<jlong>(
210 gAPH_createSessionFn(reinterpret_cast<APerformanceHintManager*>(nativeManagerPtr),
211 tidsVector.data(), tidsVector.size(),
212 initialTargetWorkDurationNanos));
213 }
214
nativeUpdateTargetWorkDuration(JNIEnv * env,jclass clazz,jlong nativeSessionPtr,jlong targetDurationNanos)215 static void nativeUpdateTargetWorkDuration(JNIEnv* env, jclass clazz, jlong nativeSessionPtr,
216 jlong targetDurationNanos) {
217 ensureAPerformanceHintBindingInitialized();
218 gAPH_updateTargetWorkDurationFn(reinterpret_cast<APerformanceHintSession*>(nativeSessionPtr),
219 targetDurationNanos);
220 }
221
nativeReportActualWorkDuration(JNIEnv * env,jclass clazz,jlong nativeSessionPtr,jlong actualDurationNanos)222 static void nativeReportActualWorkDuration(JNIEnv* env, jclass clazz, jlong nativeSessionPtr,
223 jlong actualDurationNanos) {
224 ensureAPerformanceHintBindingInitialized();
225 gAPH_reportActualWorkDurationFn(reinterpret_cast<APerformanceHintSession*>(nativeSessionPtr),
226 actualDurationNanos);
227 }
228
nativeCloseSession(JNIEnv * env,jclass clazz,jlong nativeSessionPtr)229 static void nativeCloseSession(JNIEnv* env, jclass clazz, jlong nativeSessionPtr) {
230 ensureAPerformanceHintBindingInitialized();
231 gAPH_closeSessionFn(reinterpret_cast<APerformanceHintSession*>(nativeSessionPtr));
232 }
233
nativeSendHint(JNIEnv * env,jclass clazz,jlong nativeSessionPtr,jint hint)234 static void nativeSendHint(JNIEnv* env, jclass clazz, jlong nativeSessionPtr, jint hint) {
235 ensureAPerformanceHintBindingInitialized();
236 gAPH_sendHintFn(reinterpret_cast<APerformanceHintSession*>(nativeSessionPtr), hint);
237 }
238
nativeSetThreads(JNIEnv * env,jclass clazz,jlong nativeSessionPtr,jintArray tids)239 static void nativeSetThreads(JNIEnv* env, jclass clazz, jlong nativeSessionPtr, jintArray tids) {
240 ensureAPerformanceHintBindingInitialized();
241
242 if (tids == nullptr) {
243 return;
244 }
245 ScopedIntArrayRO tidsArray(env, tids);
246 std::vector<int32_t> tidsVector;
247 tidsVector.reserve(tidsArray.size());
248 for (size_t i = 0; i < tidsArray.size(); ++i) {
249 tidsVector.push_back(static_cast<int32_t>(tidsArray[i]));
250 }
251 int err = gAPH_setThreadsFn(reinterpret_cast<APerformanceHintSession*>(nativeSessionPtr),
252 tidsVector.data(), tidsVector.size());
253 if (err != 0) {
254 throwExceptionForErrno(env, err, "Failed to set threads for hint session");
255 }
256 }
257
258 // This call should only be used for validation in tests only. This call will initiate two IPC
259 // calls, the first one is used to determined the size of the thread ids list, the second one
260 // is used to return the actual list.
nativeGetThreadIds(JNIEnv * env,jclass clazz,jlong nativeSessionPtr)261 static jintArray nativeGetThreadIds(JNIEnv* env, jclass clazz, jlong nativeSessionPtr) {
262 ensureAPerformanceHintBindingInitialized();
263 size_t size = 0;
264 gAPH_getThreadIdsFn(reinterpret_cast<APerformanceHintSession*>(nativeSessionPtr), nullptr,
265 &size);
266 if (size == 0) {
267 jintArray jintArr = env->NewIntArray(0);
268 return jintArr;
269 }
270 std::vector<int32_t> tidsVector(size);
271 gAPH_getThreadIdsFn(reinterpret_cast<APerformanceHintSession*>(nativeSessionPtr),
272 tidsVector.data(), &size);
273 jintArray jintArr = env->NewIntArray(size);
274 if (jintArr == nullptr) {
275 jniThrowException(env, "java/lang/OutOfMemoryError", nullptr);
276 return nullptr;
277 }
278 jint* threadIds = env->GetIntArrayElements(jintArr, 0);
279 for (size_t i = 0; i < size; ++i) {
280 threadIds[i] = tidsVector[i];
281 }
282 env->ReleaseIntArrayElements(jintArr, threadIds, 0);
283 return jintArr;
284 }
285
nativeSetPreferPowerEfficiency(JNIEnv * env,jclass clazz,jlong nativeSessionPtr,jboolean enabled)286 static void nativeSetPreferPowerEfficiency(JNIEnv* env, jclass clazz, jlong nativeSessionPtr,
287 jboolean enabled) {
288 ensureAPerformanceHintBindingInitialized();
289 gAPH_setPreferPowerEfficiencyFn(reinterpret_cast<APerformanceHintSession*>(nativeSessionPtr),
290 enabled);
291 }
292
nativeReportActualWorkDuration2(JNIEnv * env,jclass clazz,jlong nativeSessionPtr,jlong workPeriodStartTimestampNanos,jlong actualTotalDurationNanos,jlong actualCpuDurationNanos,jlong actualGpuDurationNanos)293 static void nativeReportActualWorkDuration2(JNIEnv* env, jclass clazz, jlong nativeSessionPtr,
294 jlong workPeriodStartTimestampNanos,
295 jlong actualTotalDurationNanos,
296 jlong actualCpuDurationNanos,
297 jlong actualGpuDurationNanos) {
298 ensureAPerformanceHintBindingInitialized();
299
300 AWorkDuration* workDuration = gAWD_createFn();
301 gAWD_setWorkPeriodStartTimestampNanosFn(workDuration, workPeriodStartTimestampNanos);
302 gAWD_setActualTotalDurationNanosFn(workDuration, actualTotalDurationNanos);
303 gAWD_setActualCpuDurationNanosFn(workDuration, actualCpuDurationNanos);
304 gAWD_setActualGpuDurationNanosFn(workDuration, actualGpuDurationNanos);
305
306 gAPH_reportActualWorkDuration2Fn(reinterpret_cast<APerformanceHintSession*>(nativeSessionPtr),
307 workDuration);
308
309 gAWD_releaseFn(workDuration);
310 }
311
312 static const JNINativeMethod gPerformanceHintMethods[] = {
313 {"nativeAcquireManager", "()J", (void*)nativeAcquireManager},
314 {"nativeGetPreferredUpdateRateNanos", "(J)J", (void*)nativeGetPreferredUpdateRateNanos},
315 {"nativeCreateSession", "(J[IJ)J", (void*)nativeCreateSession},
316 {"nativeUpdateTargetWorkDuration", "(JJ)V", (void*)nativeUpdateTargetWorkDuration},
317 {"nativeReportActualWorkDuration", "(JJ)V", (void*)nativeReportActualWorkDuration},
318 {"nativeCloseSession", "(J)V", (void*)nativeCloseSession},
319 {"nativeSendHint", "(JI)V", (void*)nativeSendHint},
320 {"nativeSetThreads", "(J[I)V", (void*)nativeSetThreads},
321 {"nativeGetThreadIds", "(J)[I", (void*)nativeGetThreadIds},
322 {"nativeSetPreferPowerEfficiency", "(JZ)V", (void*)nativeSetPreferPowerEfficiency},
323 {"nativeReportActualWorkDuration", "(JJJJJ)V", (void*)nativeReportActualWorkDuration2},
324 };
325
register_android_os_PerformanceHintManager(JNIEnv * env)326 int register_android_os_PerformanceHintManager(JNIEnv* env) {
327 return RegisterMethodsOrDie(env, "android/os/PerformanceHintManager", gPerformanceHintMethods,
328 NELEM(gPerformanceHintMethods));
329 }
330
331 } // namespace android
332