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