xref: /aosp_15_r20/frameworks/base/core/jni/android_os_PerformanceHintManager.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
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