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 TAG "HintManagerService-JNI"
18 
19 //#define LOG_NDEBUG 0
20 
21 #include <aidl/android/hardware/power/IPower.h>
22 #include <aidl/android/os/IHintManager.h>
23 #include <android-base/stringprintf.h>
24 #include <android/binder_manager.h>
25 #include <android/binder_parcel.h>
26 #include <android/binder_parcel_utils.h>
27 #include <nativehelper/JNIHelp.h>
28 #include <nativehelper/ScopedPrimitiveArray.h>
29 #include <powermanager/PowerHalController.h>
30 #include <powermanager/PowerHintSessionWrapper.h>
31 #include <utils/Log.h>
32 
33 #include <unordered_map>
34 
35 #include "jni.h"
36 
37 namespace hal = aidl::android::hardware::power;
38 
39 using android::power::PowerHintSessionWrapper;
40 
41 namespace android {
42 
43 static struct {
44     jclass clazz{};
45     jfieldID workPeriodStartTimestampNanos{};
46     jfieldID durationNanos{};
47     jfieldID cpuDurationNanos{};
48     jfieldID gpuDurationNanos{};
49     jfieldID timeStampNanos{};
50 } gWorkDurationInfo;
51 
52 static power::PowerHalController gPowerHalController;
53 static std::mutex gSessionMapLock;
54 static std::unordered_map<jlong, std::shared_ptr<PowerHintSessionWrapper>> gSessionMap
55         GUARDED_BY(gSessionMapLock);
56 
getHintSessionPreferredRate()57 static int64_t getHintSessionPreferredRate() {
58     int64_t rate = -1;
59     auto result = gPowerHalController.getHintSessionPreferredRate();
60     if (result.isOk()) {
61         rate = result.value();
62     }
63     return rate;
64 }
65 
throwUnsupported(JNIEnv * env,const char * msg)66 void throwUnsupported(JNIEnv* env, const char* msg) {
67     env->ThrowNew(env->FindClass("java/lang/UnsupportedOperationException"), msg);
68 }
69 
throwFailed(JNIEnv * env,const char * msg)70 void throwFailed(JNIEnv* env, const char* msg) {
71     // We throw IllegalStateException for all errors other than the "unsupported" ones
72     env->ThrowNew(env->FindClass("java/lang/IllegalStateException"), msg);
73 }
74 
createHintSession(JNIEnv * env,int32_t tgid,int32_t uid,std::vector<int32_t> & threadIds,int64_t durationNanos)75 static jlong createHintSession(JNIEnv* env, int32_t tgid, int32_t uid,
76                                std::vector<int32_t>& threadIds, int64_t durationNanos) {
77     auto result = gPowerHalController.createHintSession(tgid, uid, threadIds, durationNanos);
78     if (result.isOk()) {
79         jlong session_ptr = reinterpret_cast<jlong>(result.value().get());
80         std::scoped_lock sessionLock(gSessionMapLock);
81         auto res = gSessionMap.insert({session_ptr, result.value()});
82         return res.second ? session_ptr : 0;
83     } else if (result.isFailed()) {
84         ALOGW("createHintSession failed with message: %s", result.errorMessage());
85         throwFailed(env, result.errorMessage());
86     } else if (result.isUnsupported()) {
87         throwUnsupported(env, result.errorMessage());
88         return -1;
89     }
90     return 0;
91 }
92 
createHintSessionWithConfig(JNIEnv * env,int32_t tgid,int32_t uid,std::vector<int32_t> threadIds,int64_t durationNanos,int32_t sessionTag,hal::SessionConfig & config)93 static jlong createHintSessionWithConfig(JNIEnv* env, int32_t tgid, int32_t uid,
94                                          std::vector<int32_t> threadIds, int64_t durationNanos,
95                                          int32_t sessionTag, hal::SessionConfig& config) {
96     auto result =
97             gPowerHalController.createHintSessionWithConfig(tgid, uid, threadIds, durationNanos,
98                                                             static_cast<hal::SessionTag>(
99                                                                     sessionTag),
100                                                             &config);
101     if (result.isOk()) {
102         jlong session_ptr = reinterpret_cast<jlong>(result.value().get());
103         std::scoped_lock sessionLock(gSessionMapLock);
104         auto res = gSessionMap.insert({session_ptr, result.value()});
105         if (!res.second) {
106             throwFailed(env, "PowerHAL provided an invalid session");
107             return 0;
108         }
109         return session_ptr;
110     } else if (result.isUnsupported()) {
111         throwUnsupported(env, result.errorMessage());
112         return -1;
113     }
114     throwFailed(env, result.errorMessage());
115     return 0;
116 }
117 
pauseHintSession(JNIEnv * env,int64_t session_ptr)118 static void pauseHintSession(JNIEnv* env, int64_t session_ptr) {
119     auto appSession = reinterpret_cast<PowerHintSessionWrapper*>(session_ptr);
120     appSession->pause();
121 }
122 
resumeHintSession(JNIEnv * env,int64_t session_ptr)123 static void resumeHintSession(JNIEnv* env, int64_t session_ptr) {
124     auto appSession = reinterpret_cast<PowerHintSessionWrapper*>(session_ptr);
125     appSession->resume();
126 }
127 
closeHintSession(JNIEnv * env,int64_t session_ptr)128 static void closeHintSession(JNIEnv* env, int64_t session_ptr) {
129     auto appSession = reinterpret_cast<PowerHintSessionWrapper*>(session_ptr);
130     appSession->close();
131     std::scoped_lock sessionLock(gSessionMapLock);
132     gSessionMap.erase(session_ptr);
133 }
134 
updateTargetWorkDuration(int64_t session_ptr,int64_t targetDurationNanos)135 static void updateTargetWorkDuration(int64_t session_ptr, int64_t targetDurationNanos) {
136     auto appSession = reinterpret_cast<PowerHintSessionWrapper*>(session_ptr);
137     appSession->updateTargetWorkDuration(targetDurationNanos);
138 }
139 
reportActualWorkDuration(int64_t session_ptr,const std::vector<hal::WorkDuration> & actualDurations)140 static void reportActualWorkDuration(int64_t session_ptr,
141                                      const std::vector<hal::WorkDuration>& actualDurations) {
142     auto appSession = reinterpret_cast<PowerHintSessionWrapper*>(session_ptr);
143     appSession->reportActualWorkDuration(actualDurations);
144 }
145 
sendHint(int64_t session_ptr,hal::SessionHint hint)146 static void sendHint(int64_t session_ptr, hal::SessionHint hint) {
147     auto appSession = reinterpret_cast<PowerHintSessionWrapper*>(session_ptr);
148     appSession->sendHint(hint);
149 }
150 
setThreads(int64_t session_ptr,const std::vector<int32_t> & threadIds)151 static void setThreads(int64_t session_ptr, const std::vector<int32_t>& threadIds) {
152     auto appSession = reinterpret_cast<PowerHintSessionWrapper*>(session_ptr);
153     appSession->setThreads(threadIds);
154 }
155 
setMode(int64_t session_ptr,hal::SessionMode mode,bool enabled)156 static void setMode(int64_t session_ptr, hal::SessionMode mode, bool enabled) {
157     auto appSession = reinterpret_cast<PowerHintSessionWrapper*>(session_ptr);
158     appSession->setMode(mode, enabled);
159 }
160 
161 // ----------------------------------------------------------------------------
nativeInit(JNIEnv * env,jobject obj)162 static void nativeInit(JNIEnv* env, jobject obj) {
163     gPowerHalController.init();
164 }
165 
nativeGetHintSessionPreferredRate(JNIEnv *,jclass)166 static jlong nativeGetHintSessionPreferredRate(JNIEnv* /* env */, jclass /* clazz */) {
167     return static_cast<jlong>(getHintSessionPreferredRate());
168 }
169 
nativeCreateHintSession(JNIEnv * env,jclass,jint tgid,jint uid,jintArray tids,jlong durationNanos)170 static jlong nativeCreateHintSession(JNIEnv* env, jclass /* clazz */, jint tgid, jint uid,
171                                      jintArray tids, jlong durationNanos) {
172     ScopedIntArrayRO tidArray(env, tids);
173     if (nullptr == tidArray.get() || tidArray.size() == 0) {
174         ALOGW("nativeCreateHintSession: GetIntArrayElements returns nullptr.");
175         return 0;
176     }
177     std::vector<int32_t> threadIds(tidArray.get(), tidArray.get() + tidArray.size());
178     return createHintSession(env, tgid, uid, threadIds, durationNanos);
179 }
180 
nativeCreateHintSessionWithConfig(JNIEnv * env,jclass,jint tgid,jint uid,jintArray tids,jlong durationNanos,jint sessionTag,jobject sessionConfig)181 static jlong nativeCreateHintSessionWithConfig(JNIEnv* env, jclass /* clazz */, jint tgid, jint uid,
182                                                jintArray tids, jlong durationNanos, jint sessionTag,
183                                                jobject sessionConfig) {
184     ScopedIntArrayRO tidArray(env, tids);
185     if (nullptr == tidArray.get() || tidArray.size() == 0) {
186         ALOGW("nativeCreateHintSessionWithConfig: GetIntArrayElements returns nullptr.");
187         return 0;
188     }
189     std::vector<int32_t> threadIds(tidArray.get(), tidArray.get() + tidArray.size());
190     hal::SessionConfig config;
191     jlong out = createHintSessionWithConfig(env, tgid, uid, std::move(threadIds), durationNanos,
192                                             sessionTag, config);
193     if (out <= 0) {
194         return out;
195     }
196     static jclass configClass = env->FindClass("android/hardware/power/SessionConfig");
197     static jfieldID fid = env->GetFieldID(configClass, "id", "J");
198     env->SetLongField(sessionConfig, fid, config.id);
199     return out;
200 }
201 
nativePauseHintSession(JNIEnv * env,jclass,jlong session_ptr)202 static void nativePauseHintSession(JNIEnv* env, jclass /* clazz */, jlong session_ptr) {
203     pauseHintSession(env, session_ptr);
204 }
205 
nativeResumeHintSession(JNIEnv * env,jclass,jlong session_ptr)206 static void nativeResumeHintSession(JNIEnv* env, jclass /* clazz */, jlong session_ptr) {
207     resumeHintSession(env, session_ptr);
208 }
209 
nativeCloseHintSession(JNIEnv * env,jclass,jlong session_ptr)210 static void nativeCloseHintSession(JNIEnv* env, jclass /* clazz */, jlong session_ptr) {
211     closeHintSession(env, session_ptr);
212 }
213 
nativeUpdateTargetWorkDuration(JNIEnv *,jclass,jlong session_ptr,jlong targetDurationNanos)214 static void nativeUpdateTargetWorkDuration(JNIEnv* /* env */, jclass /* clazz */, jlong session_ptr,
215                                            jlong targetDurationNanos) {
216     updateTargetWorkDuration(session_ptr, targetDurationNanos);
217 }
218 
nativeReportActualWorkDuration(JNIEnv * env,jclass,jlong session_ptr,jlongArray actualDurations,jlongArray timeStamps)219 static void nativeReportActualWorkDuration(JNIEnv* env, jclass /* clazz */, jlong session_ptr,
220                                            jlongArray actualDurations, jlongArray timeStamps) {
221     ScopedLongArrayRO arrayActualDurations(env, actualDurations);
222     ScopedLongArrayRO arrayTimeStamps(env, timeStamps);
223 
224     std::vector<hal::WorkDuration> actualList(arrayActualDurations.size());
225     for (size_t i = 0; i < arrayActualDurations.size(); i++) {
226         actualList[i].timeStampNanos = arrayTimeStamps[i];
227         actualList[i].durationNanos = arrayActualDurations[i];
228     }
229     reportActualWorkDuration(session_ptr, actualList);
230 }
231 
nativeSendHint(JNIEnv * env,jclass,jlong session_ptr,jint hint)232 static void nativeSendHint(JNIEnv* env, jclass /* clazz */, jlong session_ptr, jint hint) {
233     sendHint(session_ptr, static_cast<hal::SessionHint>(hint));
234 }
235 
nativeSetThreads(JNIEnv * env,jclass,jlong session_ptr,jintArray tids)236 static void nativeSetThreads(JNIEnv* env, jclass /* clazz */, jlong session_ptr, jintArray tids) {
237     ScopedIntArrayRO tidArray(env, tids);
238 
239     std::vector<int32_t> threadIds(tidArray.get(), tidArray.get() + tidArray.size());
240     setThreads(session_ptr, threadIds);
241 }
242 
nativeSetMode(JNIEnv * env,jclass,jlong session_ptr,jint mode,jboolean enabled)243 static void nativeSetMode(JNIEnv* env, jclass /* clazz */, jlong session_ptr, jint mode,
244                           jboolean enabled) {
245     setMode(session_ptr, static_cast<hal::SessionMode>(mode), enabled);
246 }
247 
nativeReportActualWorkDuration2(JNIEnv * env,jclass,jlong session_ptr,jobjectArray jWorkDurations)248 static void nativeReportActualWorkDuration2(JNIEnv* env, jclass /* clazz */, jlong session_ptr,
249                                             jobjectArray jWorkDurations) {
250     int size = env->GetArrayLength(jWorkDurations);
251     std::vector<hal::WorkDuration> workDurations(size);
252     for (int i = 0; i < size; i++) {
253         jobject workDuration = env->GetObjectArrayElement(jWorkDurations, i);
254         workDurations[i].workPeriodStartTimestampNanos =
255                 env->GetLongField(workDuration, gWorkDurationInfo.workPeriodStartTimestampNanos);
256         workDurations[i].durationNanos =
257                 env->GetLongField(workDuration, gWorkDurationInfo.durationNanos);
258         workDurations[i].cpuDurationNanos =
259                 env->GetLongField(workDuration, gWorkDurationInfo.cpuDurationNanos);
260         workDurations[i].gpuDurationNanos =
261                 env->GetLongField(workDuration, gWorkDurationInfo.gpuDurationNanos);
262         workDurations[i].timeStampNanos =
263                 env->GetLongField(workDuration, gWorkDurationInfo.timeStampNanos);
264     }
265     reportActualWorkDuration(session_ptr, workDurations);
266 }
267 
268 // ----------------------------------------------------------------------------
269 static const JNINativeMethod sHintManagerServiceMethods[] = {
270         /* name, signature, funcPtr */
271         {"nativeInit", "()V", (void*)nativeInit},
272         {"nativeGetHintSessionPreferredRate", "()J", (void*)nativeGetHintSessionPreferredRate},
273         {"nativeCreateHintSession", "(II[IJ)J", (void*)nativeCreateHintSession},
274         {"nativeCreateHintSessionWithConfig", "(II[IJILandroid/hardware/power/SessionConfig;)J",
275          (void*)nativeCreateHintSessionWithConfig},
276         {"nativePauseHintSession", "(J)V", (void*)nativePauseHintSession},
277         {"nativeResumeHintSession", "(J)V", (void*)nativeResumeHintSession},
278         {"nativeCloseHintSession", "(J)V", (void*)nativeCloseHintSession},
279         {"nativeUpdateTargetWorkDuration", "(JJ)V", (void*)nativeUpdateTargetWorkDuration},
280         {"nativeReportActualWorkDuration", "(J[J[J)V", (void*)nativeReportActualWorkDuration},
281         {"nativeSendHint", "(JI)V", (void*)nativeSendHint},
282         {"nativeSetThreads", "(J[I)V", (void*)nativeSetThreads},
283         {"nativeSetMode", "(JIZ)V", (void*)nativeSetMode},
284         {"nativeReportActualWorkDuration", "(J[Landroid/hardware/power/WorkDuration;)V",
285          (void*)nativeReportActualWorkDuration2},
286 };
287 
register_android_server_HintManagerService(JNIEnv * env)288 int register_android_server_HintManagerService(JNIEnv* env) {
289     gWorkDurationInfo.clazz = env->FindClass("android/hardware/power/WorkDuration");
290     gWorkDurationInfo.workPeriodStartTimestampNanos =
291             env->GetFieldID(gWorkDurationInfo.clazz, "workPeriodStartTimestampNanos", "J");
292     gWorkDurationInfo.durationNanos =
293             env->GetFieldID(gWorkDurationInfo.clazz, "durationNanos", "J");
294     gWorkDurationInfo.cpuDurationNanos =
295             env->GetFieldID(gWorkDurationInfo.clazz, "cpuDurationNanos", "J");
296     gWorkDurationInfo.gpuDurationNanos =
297             env->GetFieldID(gWorkDurationInfo.clazz, "gpuDurationNanos", "J");
298     gWorkDurationInfo.timeStampNanos =
299             env->GetFieldID(gWorkDurationInfo.clazz, "timeStampNanos", "J");
300 
301     return jniRegisterNativeMethods(env,
302                                     "com/android/server/power/hint/"
303                                     "HintManagerService$NativeWrapper",
304                                     sHintManagerServiceMethods, NELEM(sHintManagerServiceMethods));
305 }
306 
307 } /* namespace android */
308