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