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 "jni.h"
20
21 #include <dlfcn.h>
22 #include <nativehelper/JNIHelp.h>
23 #include <nativehelper/ScopedPrimitiveArray.h>
24 #include <utils/Log.h>
25 #include <vector>
26
27 #include "core_jni_helpers.h"
28
29 namespace android {
30
31 namespace {
32
33 struct APerformanceHintManager;
34 struct APerformanceHintSession;
35
36 typedef APerformanceHintManager* (*APH_getManager)();
37 typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*,
38 size_t, int64_t);
39 typedef int64_t (*APH_getPreferredUpdateRateNanos)(APerformanceHintManager* manager);
40 typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t);
41 typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t);
42 typedef void (*APH_closeSession)(APerformanceHintSession* session);
43 typedef void (*APH_sendHint)(APerformanceHintSession*, int32_t);
44 typedef void (*APH_setThreads)(APerformanceHintSession*, const pid_t*, size_t);
45 typedef void (*APH_getThreadIds)(APerformanceHintSession*, int32_t* const, size_t* const);
46
47 bool gAPerformanceHintBindingInitialized = false;
48 APH_getManager gAPH_getManagerFn = nullptr;
49 APH_createSession gAPH_createSessionFn = nullptr;
50 APH_getPreferredUpdateRateNanos gAPH_getPreferredUpdateRateNanosFn = nullptr;
51 APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr;
52 APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr;
53 APH_closeSession gAPH_closeSessionFn = nullptr;
54 APH_sendHint gAPH_sendHintFn = nullptr;
55 APH_setThreads gAPH_setThreadsFn = nullptr;
56 APH_getThreadIds gAPH_getThreadIdsFn = nullptr;
57
ensureAPerformanceHintBindingInitialized()58 void ensureAPerformanceHintBindingInitialized() {
59 if (gAPerformanceHintBindingInitialized) return;
60
61 void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
62 LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!");
63
64 gAPH_getManagerFn = (APH_getManager)dlsym(handle_, "APerformanceHint_getManager");
65 LOG_ALWAYS_FATAL_IF(gAPH_getManagerFn == nullptr,
66 "Failed to find required symbol APerformanceHint_getManager!");
67
68 gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession");
69 LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr,
70 "Failed to find required symbol APerformanceHint_createSession!");
71
72 gAPH_getPreferredUpdateRateNanosFn =
73 (APH_getPreferredUpdateRateNanos)dlsym(handle_,
74 "APerformanceHint_getPreferredUpdateRateNanos");
75 LOG_ALWAYS_FATAL_IF(gAPH_getPreferredUpdateRateNanosFn == nullptr,
76 "Failed to find required symbol "
77 "APerformanceHint_getPreferredUpdateRateNanos!");
78
79 gAPH_updateTargetWorkDurationFn =
80 (APH_updateTargetWorkDuration)dlsym(handle_,
81 "APerformanceHint_updateTargetWorkDuration");
82 LOG_ALWAYS_FATAL_IF(gAPH_updateTargetWorkDurationFn == nullptr,
83 "Failed to find required symbol "
84 "APerformanceHint_updateTargetWorkDuration!");
85
86 gAPH_reportActualWorkDurationFn =
87 (APH_reportActualWorkDuration)dlsym(handle_,
88 "APerformanceHint_reportActualWorkDuration");
89 LOG_ALWAYS_FATAL_IF(gAPH_reportActualWorkDurationFn == nullptr,
90 "Failed to find required symbol "
91 "APerformanceHint_reportActualWorkDuration!");
92
93 gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession");
94 LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr,
95 "Failed to find required symbol APerformanceHint_closeSession!");
96
97 gAPH_sendHintFn = (APH_sendHint)dlsym(handle_, "APerformanceHint_sendHint");
98 LOG_ALWAYS_FATAL_IF(gAPH_sendHintFn == nullptr,
99 "Failed to find required symbol "
100 "APerformanceHint_sendHint!");
101
102 gAPH_setThreadsFn = (APH_setThreads)dlsym(handle_, "APerformanceHint_setThreads");
103 LOG_ALWAYS_FATAL_IF(gAPH_setThreadsFn == nullptr,
104 "Failed to find required symbol APerformanceHint_setThreads!");
105
106 gAPH_getThreadIdsFn = (APH_getThreadIds)dlsym(handle_, "APerformanceHint_getThreadIds");
107 LOG_ALWAYS_FATAL_IF(gAPH_getThreadIdsFn == nullptr,
108 "Failed to find required symbol APerformanceHint_getThreadIds!");
109
110 gAPerformanceHintBindingInitialized = true;
111 }
112
113 } // namespace
114
nativeAcquireManager(JNIEnv * env,jclass clazz)115 static jlong nativeAcquireManager(JNIEnv* env, jclass clazz) {
116 ensureAPerformanceHintBindingInitialized();
117 return reinterpret_cast<jlong>(gAPH_getManagerFn());
118 }
119
nativeGetPreferredUpdateRateNanos(JNIEnv * env,jclass clazz,jlong nativeManagerPtr)120 static jlong nativeGetPreferredUpdateRateNanos(JNIEnv* env, jclass clazz, jlong nativeManagerPtr) {
121 ensureAPerformanceHintBindingInitialized();
122 return gAPH_getPreferredUpdateRateNanosFn(
123 reinterpret_cast<APerformanceHintManager*>(nativeManagerPtr));
124 }
125
nativeCreateSession(JNIEnv * env,jclass clazz,jlong nativeManagerPtr,jintArray tids,jlong initialTargetWorkDurationNanos)126 static jlong nativeCreateSession(JNIEnv* env, jclass clazz, jlong nativeManagerPtr, jintArray tids,
127 jlong initialTargetWorkDurationNanos) {
128 ensureAPerformanceHintBindingInitialized();
129 if (tids == nullptr) return 0;
130 std::vector<int32_t> tidsVector;
131 ScopedIntArrayRO tidsArray(env, tids);
132 for (size_t i = 0; i < tidsArray.size(); ++i) {
133 tidsVector.push_back(static_cast<int32_t>(tidsArray[i]));
134 }
135 return reinterpret_cast<jlong>(
136 gAPH_createSessionFn(reinterpret_cast<APerformanceHintManager*>(nativeManagerPtr),
137 tidsVector.data(), tidsVector.size(),
138 initialTargetWorkDurationNanos));
139 }
140
nativeUpdateTargetWorkDuration(JNIEnv * env,jclass clazz,jlong nativeSessionPtr,jlong targetDurationNanos)141 static void nativeUpdateTargetWorkDuration(JNIEnv* env, jclass clazz, jlong nativeSessionPtr,
142 jlong targetDurationNanos) {
143 ensureAPerformanceHintBindingInitialized();
144 gAPH_updateTargetWorkDurationFn(reinterpret_cast<APerformanceHintSession*>(nativeSessionPtr),
145 targetDurationNanos);
146 }
147
nativeReportActualWorkDuration(JNIEnv * env,jclass clazz,jlong nativeSessionPtr,jlong actualDurationNanos)148 static void nativeReportActualWorkDuration(JNIEnv* env, jclass clazz, jlong nativeSessionPtr,
149 jlong actualDurationNanos) {
150 ensureAPerformanceHintBindingInitialized();
151 gAPH_reportActualWorkDurationFn(reinterpret_cast<APerformanceHintSession*>(nativeSessionPtr),
152 actualDurationNanos);
153 }
154
nativeCloseSession(JNIEnv * env,jclass clazz,jlong nativeSessionPtr)155 static void nativeCloseSession(JNIEnv* env, jclass clazz, jlong nativeSessionPtr) {
156 ensureAPerformanceHintBindingInitialized();
157 gAPH_closeSessionFn(reinterpret_cast<APerformanceHintSession*>(nativeSessionPtr));
158 }
159
nativeSendHint(JNIEnv * env,jclass clazz,jlong nativeSessionPtr,jint hint)160 static void nativeSendHint(JNIEnv* env, jclass clazz, jlong nativeSessionPtr, jint hint) {
161 ensureAPerformanceHintBindingInitialized();
162 gAPH_sendHintFn(reinterpret_cast<APerformanceHintSession*>(nativeSessionPtr), hint);
163 }
164
nativeSetThreads(JNIEnv * env,jclass clazz,jlong nativeSessionPtr,jintArray tids)165 static void nativeSetThreads(JNIEnv* env, jclass clazz, jlong nativeSessionPtr, jintArray tids) {
166 ensureAPerformanceHintBindingInitialized();
167
168 if (tids == nullptr) {
169 return;
170 }
171 ScopedIntArrayRO tidsArray(env, tids);
172 std::vector<int32_t> tidsVector;
173 tidsVector.reserve(tidsArray.size());
174 for (size_t i = 0; i < tidsArray.size(); ++i) {
175 tidsVector.push_back(static_cast<int32_t>(tidsArray[i]));
176 }
177 gAPH_setThreadsFn(reinterpret_cast<APerformanceHintSession*>(nativeSessionPtr),
178 tidsVector.data(), tidsVector.size());
179 }
180
181 // This call should only be used for validation in tests only. This call will initiate two IPC
182 // calls, the first one is used to determined the size of the thread ids list, the second one
183 // is used to return the actual list.
nativeGetThreadIds(JNIEnv * env,jclass clazz,jlong nativeSessionPtr)184 static jintArray nativeGetThreadIds(JNIEnv* env, jclass clazz, jlong nativeSessionPtr) {
185 ensureAPerformanceHintBindingInitialized();
186 size_t size = 0;
187 gAPH_getThreadIdsFn(reinterpret_cast<APerformanceHintSession*>(nativeSessionPtr), nullptr,
188 &size);
189 if (size == 0) {
190 jintArray jintArr = env->NewIntArray(0);
191 return jintArr;
192 }
193 std::vector<int32_t> tidsVector(size);
194 gAPH_getThreadIdsFn(reinterpret_cast<APerformanceHintSession*>(nativeSessionPtr),
195 tidsVector.data(), &size);
196 jintArray jintArr = env->NewIntArray(size);
197 if (jintArr == nullptr) {
198 jniThrowException(env, "java/lang/OutOfMemoryError", nullptr);
199 return nullptr;
200 }
201 jint* threadIds = env->GetIntArrayElements(jintArr, 0);
202 for (int i = 0; i < size; ++i) {
203 threadIds[i] = tidsVector[i];
204 }
205 env->ReleaseIntArrayElements(jintArr, threadIds, 0);
206 return jintArr;
207 }
208
209 static const JNINativeMethod gPerformanceHintMethods[] = {
210 {"nativeAcquireManager", "()J", (void*)nativeAcquireManager},
211 {"nativeGetPreferredUpdateRateNanos", "(J)J", (void*)nativeGetPreferredUpdateRateNanos},
212 {"nativeCreateSession", "(J[IJ)J", (void*)nativeCreateSession},
213 {"nativeUpdateTargetWorkDuration", "(JJ)V", (void*)nativeUpdateTargetWorkDuration},
214 {"nativeReportActualWorkDuration", "(JJ)V", (void*)nativeReportActualWorkDuration},
215 {"nativeCloseSession", "(J)V", (void*)nativeCloseSession},
216 {"nativeSendHint", "(JI)V", (void*)nativeSendHint},
217 {"nativeSetThreads", "(J[I)V", (void*)nativeSetThreads},
218 {"nativeGetThreadIds", "(J)[I", (void*)nativeGetThreadIds},
219 };
220
register_android_os_PerformanceHintManager(JNIEnv * env)221 int register_android_os_PerformanceHintManager(JNIEnv* env) {
222 return RegisterMethodsOrDie(env, "android/os/PerformanceHintManager", gPerformanceHintMethods,
223 NELEM(gPerformanceHintMethods));
224 }
225
226 } // namespace android
227