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