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