• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 #include <android/native_window_jni.h>
17 #include <android/performance_hint.h>
18 #include <android/surface_control_jni.h>
19 #include <errno.h>
20 #include <jni.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <unistd.h>
24 
25 #include <future>
26 #include <list>
27 #include <sstream>
28 #include <vector>
29 
toJString(JNIEnv * env,const char * c_str)30 static jstring toJString(JNIEnv *env, const char* c_str) {
31     return env->NewStringUTF(c_str);
32 }
33 
34 constexpr int64_t DEFAULT_TARGET_NS = 16666666L;
35 
36 #define CHECK_SESSION_RETURN(retval, session, situation)                               \
37     if (retval != 0 || session == nullptr) {                                           \
38         return toJString(env,                                                          \
39                          std::format(situation " returned status: {}{}", retval,       \
40                                      session == nullptr ? " with a null session" : "") \
41                                  .c_str());                                            \
42     }
43 
44 template <class T, void (*D)(T*)>
wrapSP(T * incoming)45 std::shared_ptr<T> wrapSP(T* incoming) {
46     return incoming == nullptr ? nullptr : std::shared_ptr<T>(incoming, [](T* ptr) { D(ptr); });
47 }
48 
49 // Preferable to template specialization bc if we wrap something unsupported, it's more obvious
50 constexpr auto&& wrapSession = wrapSP<APerformanceHintSession, APerformanceHint_closeSession>;
51 constexpr auto&& wrapConfig = wrapSP<ASessionCreationConfig, ASessionCreationConfig_release>;
52 constexpr auto&& wrapWorkDuration = wrapSP<AWorkDuration, AWorkDuration_release>;
53 constexpr auto&& wrapSurfaceControl = wrapSP<ASurfaceControl, ASurfaceControl_release>;
54 constexpr auto&& wrapNativeWindow = wrapSP<ANativeWindow, ANativeWindow_release>;
55 
createSession(APerformanceHintManager * manager)56 std::shared_ptr<APerformanceHintSession> createSession(APerformanceHintManager* manager) {
57     int32_t pid = getpid();
58     return wrapSession(APerformanceHint_createSession(manager, &pid, 1u, DEFAULT_TARGET_NS));
59 }
60 
createSessionWithConfig(APerformanceHintManager * manager,std::shared_ptr<ASessionCreationConfig> config,int * out)61 std::shared_ptr<APerformanceHintSession> createSessionWithConfig(
62         APerformanceHintManager* manager, std::shared_ptr<ASessionCreationConfig> config,
63         int* out) {
64     APerformanceHintSession* sessionOut;
65     *out = APerformanceHint_createSessionUsingConfig(manager, config.get(), &sessionOut);
66     return wrapSession(sessionOut);
67 }
68 
createConfig()69 std::shared_ptr<ASessionCreationConfig> createConfig() {
70     return wrapConfig(ASessionCreationConfig_create());
71 }
72 
73 struct WorkDurationCreator {
74     int64_t workPeriodStart;
75     int64_t totalDuration;
76     int64_t cpuDuration;
77     int64_t gpuDuration;
78 };
79 
80 struct ConfigCreator {
81     std::vector<int32_t> tids{getpid()};
82     int64_t targetDuration = DEFAULT_TARGET_NS;
83     bool powerEfficient = false;
84     bool graphicsPipeline = false;
85     std::vector<ANativeWindow*> nativeWindows{};
86     std::vector<ASurfaceControl*> surfaceControls{};
87     bool autoCpu = false;
88     bool autoGpu = false;
89 };
90 
91 struct SupportHelper {
92     bool hintSessions : 1;
93     bool powerEfficiency : 1;
94     bool bindToSurface : 1;
95     bool graphicsPipeline : 1;
96     bool autoCpu : 1;
97     bool autoGpu : 1;
98 };
99 
getSupportHelper()100 SupportHelper getSupportHelper() {
101     return {
102             .hintSessions = APerformanceHint_isFeatureSupported(APERF_HINT_SESSIONS),
103             .powerEfficiency = APerformanceHint_isFeatureSupported(APERF_HINT_POWER_EFFICIENCY),
104             .bindToSurface = APerformanceHint_isFeatureSupported(APERF_HINT_SURFACE_BINDING),
105             .graphicsPipeline = APerformanceHint_isFeatureSupported(APERF_HINT_GRAPHICS_PIPELINE),
106             .autoCpu = APerformanceHint_isFeatureSupported(APERF_HINT_AUTO_CPU),
107             .autoGpu = APerformanceHint_isFeatureSupported(APERF_HINT_AUTO_GPU),
108     };
109 }
110 
configFromCreator(ConfigCreator && creator)111 std::shared_ptr<ASessionCreationConfig> configFromCreator(ConfigCreator&& creator) {
112     auto config = createConfig();
113 
114     ASessionCreationConfig_setTids(config.get(), creator.tids.data(), creator.tids.size());
115     ASessionCreationConfig_setTargetWorkDurationNanos(config.get(), creator.targetDuration);
116     ASessionCreationConfig_setPreferPowerEfficiency(config.get(), creator.powerEfficient);
117     ASessionCreationConfig_setGraphicsPipeline(config.get(), creator.graphicsPipeline);
118     ASessionCreationConfig_setNativeSurfaces(config.get(),
119                                              creator.nativeWindows.size() > 0
120                                                      ? creator.nativeWindows.data()
121                                                      : nullptr,
122                                              creator.nativeWindows.size(),
123                                              creator.surfaceControls.size() > 0
124                                                      ? creator.surfaceControls.data()
125                                                      : nullptr,
126                                              creator.surfaceControls.size());
127     ASessionCreationConfig_setUseAutoTiming(config.get(), creator.autoCpu, creator.autoGpu);
128     return config;
129 }
130 
createWorkDuration(WorkDurationCreator creator)131 static std::shared_ptr<AWorkDuration> createWorkDuration(WorkDurationCreator creator) {
132     AWorkDuration* out = AWorkDuration_create();
133     AWorkDuration_setWorkPeriodStartTimestampNanos(out, creator.workPeriodStart);
134     AWorkDuration_setActualTotalDurationNanos(out, creator.totalDuration);
135     AWorkDuration_setActualCpuDurationNanos(out, creator.cpuDuration);
136     AWorkDuration_setActualGpuDurationNanos(out, creator.gpuDuration);
137     return wrapWorkDuration(out);
138 }
139 
nativeGetSessionsAreSupported()140 static jboolean nativeGetSessionsAreSupported() {
141     return APerformanceHint_isFeatureSupported(APERF_HINT_SESSIONS);
142 }
143 
nativeTestCreateHintSession(JNIEnv * env,jobject)144 static jstring nativeTestCreateHintSession(JNIEnv *env, jobject) {
145     APerformanceHintManager* manager = APerformanceHint_getManager();
146     if (!manager) return toJString(env, "null manager");
147     auto a = createSession(manager);
148     auto b = createSession(manager);
149     if (a == nullptr) {
150         return toJString(env, "a is null");
151     } else if (b == nullptr) {
152         return toJString(env, "b is null");
153     } else if (a.get() == b.get()) {
154         return toJString(env, "a and b matches");
155     }
156     return nullptr;
157 }
158 
nativeTestCreateHintSessionUsingConfig(JNIEnv * env,jobject)159 static jstring nativeTestCreateHintSessionUsingConfig(JNIEnv* env, jobject) {
160     APerformanceHintManager* manager = APerformanceHint_getManager();
161     if (!manager) return toJString(env, "null manager");
162     auto config = configFromCreator({});
163     if (config == nullptr) {
164         return toJString(env, "config is null");
165     }
166 
167     int returnVal = 0;
168     auto a = createSessionWithConfig(manager, config, &returnVal);
169     if (returnVal != 0) {
170         return toJString(env,
171                          std::format("Session creation failed with status: {}", returnVal).c_str());
172     }
173 
174     auto b = createSessionWithConfig(manager, config, &returnVal);
175     if (a == nullptr) {
176         return toJString(env, "a is null");
177     } else if (b == nullptr) {
178         return toJString(env, "b is null");
179     } else if (a.get() == b.get()) {
180         return toJString(env, "a and b matches");
181     }
182     return nullptr;
183 }
184 
nativeTestGetPreferredUpdateRateNanos(JNIEnv * env,jobject)185 static jstring nativeTestGetPreferredUpdateRateNanos(JNIEnv *env, jobject) {
186     APerformanceHintManager* manager = APerformanceHint_getManager();
187     if (!manager) return toJString(env, "null manager");
188     auto session = createSession(manager);
189     if (session != nullptr) {
190         bool positive = APerformanceHint_getPreferredUpdateRateNanos(manager) > 0;
191         if (!positive)
192           return toJString(env, "preferred rate is not positive");
193     } else {
194         if (APerformanceHint_getPreferredUpdateRateNanos(manager) != -1)
195           return toJString(env, "preferred rate is not -1");
196     }
197     return nullptr;
198 }
199 
nativeUpdateTargetWorkDuration(JNIEnv * env,jobject)200 static jstring nativeUpdateTargetWorkDuration(JNIEnv *env, jobject) {
201     APerformanceHintManager* manager = APerformanceHint_getManager();
202     if (!manager) return toJString(env, "null manager");
203     auto session = createSession(manager);
204     if (session == nullptr) return nullptr;
205 
206     int result = APerformanceHint_updateTargetWorkDuration(session.get(), 100);
207     if (result != 0) {
208         return toJString(env, "updateTargetWorkDuration did not return 0");
209     }
210     return nullptr;
211 }
212 
nativeUpdateTargetWorkDurationWithNegativeDuration(JNIEnv * env,jobject)213 static jstring nativeUpdateTargetWorkDurationWithNegativeDuration(JNIEnv *env, jobject) {
214     APerformanceHintManager* manager = APerformanceHint_getManager();
215     if (!manager) return toJString(env, "null manager");
216     auto session = createSession(manager);
217     if (session == nullptr) return nullptr;
218 
219     int result = APerformanceHint_updateTargetWorkDuration(session.get(), -1);
220     if (result != EINVAL) {
221         return toJString(env, "updateTargetWorkDuration did not return EINVAL");
222     }
223     return nullptr;
224 }
225 
nativeReportActualWorkDuration(JNIEnv * env,jobject)226 static jstring nativeReportActualWorkDuration(JNIEnv *env, jobject) {
227     APerformanceHintManager* manager = APerformanceHint_getManager();
228     if (!manager) return toJString(env, "null manager");
229     auto session = createSession(manager);
230     if (session == nullptr) return nullptr;
231 
232     int result = APerformanceHint_reportActualWorkDuration(session.get(), 100);
233     if (result != 0) {
234         return toJString(env, "reportActualWorkDuration(100) did not return 0");
235     }
236 
237     result = APerformanceHint_reportActualWorkDuration(session.get(), 1);
238     if (result != 0) {
239         return toJString(env, "reportActualWorkDuration(1) did not return 0");
240     }
241 
242     result = APerformanceHint_reportActualWorkDuration(session.get(), 100);
243     if (result != 0) {
244         return toJString(env, "reportActualWorkDuration(100) did not return 0");
245     }
246 
247     result = APerformanceHint_reportActualWorkDuration(session.get(), 1000);
248     if (result != 0) {
249         return toJString(env, "reportActualWorkDuration(1000) did not return 0");
250     }
251 
252     return nullptr;
253 }
254 
nativeReportActualWorkDurationWithIllegalArgument(JNIEnv * env,jobject)255 static jstring nativeReportActualWorkDurationWithIllegalArgument(JNIEnv *env, jobject) {
256     APerformanceHintManager* manager = APerformanceHint_getManager();
257     if (!manager) return toJString(env, "null manager");
258     auto session = createSession(manager);
259     if (session == nullptr) return nullptr;
260 
261     int result = APerformanceHint_reportActualWorkDuration(session.get(), -1);
262     if (result != EINVAL) {
263         return toJString(env, "reportActualWorkDuration did not return EINVAL");
264     }
265     return nullptr;
266 }
267 
nativeTestSetThreadsWithInvalidTid(JNIEnv * env,jobject)268 static jstring nativeTestSetThreadsWithInvalidTid(JNIEnv* env, jobject) {
269     APerformanceHintManager* manager = APerformanceHint_getManager();
270     if (!manager) {
271         return toJString(env, "null manager");
272     }
273     auto session = createSession(manager);
274     if (session == nullptr) {
275         return nullptr;
276     }
277 
278     std::vector<pid_t> tids;
279     tids.push_back(2);
280     int result = APerformanceHint_setThreads(session.get(), tids.data(), 1);
281     if (result != EPERM) {
282         return toJString(env, "setThreads did not return EPERM");
283     }
284     return nullptr;
285 }
286 
287 
nativeSetPreferPowerEfficiency(JNIEnv * env,jobject)288 static jstring nativeSetPreferPowerEfficiency(JNIEnv* env, jobject) {
289     APerformanceHintManager* manager = APerformanceHint_getManager();
290     if (!manager) return toJString(env, "null manager");
291     auto session = createSession(manager);
292     if (session == nullptr) return nullptr;
293 
294     int result = APerformanceHint_setPreferPowerEfficiency(session.get(), false);
295     if (result != 0) {
296         return toJString(env, "setPreferPowerEfficiency(false) did not return 0");
297     }
298 
299     result = APerformanceHint_setPreferPowerEfficiency(session.get(), true);
300     if (result != 0) {
301         return toJString(env, "setPreferPowerEfficiency(true) did not return 0");
302     }
303 
304     result = APerformanceHint_setPreferPowerEfficiency(session.get(), true);
305     if (result != 0) {
306         return toJString(env, "setPreferPowerEfficiency(true) did not return 0");
307     }
308     return nullptr;
309 }
310 
nativeTestReportActualWorkDuration2(JNIEnv * env,jobject)311 static jstring nativeTestReportActualWorkDuration2(JNIEnv* env, jobject) {
312     APerformanceHintManager* manager = APerformanceHint_getManager();
313     if (!manager) return toJString(env, "null manager");
314     auto session = createSession(manager);
315     if (session == nullptr) return nullptr;
316 
317     std::vector<WorkDurationCreator> testCases = {
318         {
319             .workPeriodStart = 1000,
320             .totalDuration = 14,
321             .cpuDuration = 11,
322             .gpuDuration = 8
323         },
324         {
325             .workPeriodStart = 1016,
326             .totalDuration = 14,
327             .cpuDuration = 12,
328             .gpuDuration = 4
329         },
330         {
331             .workPeriodStart = 1016,
332             .totalDuration = 14,
333             .cpuDuration = 12,
334             .gpuDuration = 4
335         },
336         {
337             .workPeriodStart = 900,
338             .totalDuration = 20,
339             .cpuDuration = 20,
340             .gpuDuration = 0
341         },
342         {
343             .workPeriodStart = 800,
344             .totalDuration = 20,
345             .cpuDuration = 0,
346             .gpuDuration = 20
347         }
348     };
349 
350     for (auto && testCase : testCases) {
351         auto&& testObj = createWorkDuration(testCase);
352         int result = APerformanceHint_reportActualWorkDuration2(session.get(), testObj.get());
353         if (result != 0) {
354             std::stringstream stream;
355             stream << "APerformanceHint_reportActualWorkDuration2({"
356                    << "workPeriodStartTimestampNanos = " << testCase.workPeriodStart << ", "
357                    << "actualTotalDurationNanos = " << testCase.totalDuration << ", "
358                    << "actualCpuDurationNanos = " << testCase.cpuDuration << ", "
359                    << "actualGpuDurationNanos = " << testCase.gpuDuration << "}) did not return 0";
360             return toJString(env, stream.str().c_str());
361         }
362     }
363 
364     return nullptr;
365 }
366 
nativeTestReportActualWorkDuration2WithIllegalArgument(JNIEnv * env,jobject)367 static jstring nativeTestReportActualWorkDuration2WithIllegalArgument(JNIEnv* env, jobject) {
368     APerformanceHintManager* manager = APerformanceHint_getManager();
369     if (!manager) return toJString(env, "null manager");
370     auto session = createSession(manager);
371     if (session == nullptr) return nullptr;
372 
373     std::vector<WorkDurationCreator> testCases = {
374         {
375             .workPeriodStart = -1,
376             .totalDuration = 14,
377             .cpuDuration = 11,
378             .gpuDuration = 8
379         },
380         {
381             .workPeriodStart = 1000,
382             .totalDuration = -1,
383             .cpuDuration = 11,
384             .gpuDuration = 8
385         },
386         {
387             .workPeriodStart = 1000,
388             .totalDuration = 14,
389             .cpuDuration = -1,
390             .gpuDuration = 8
391         },
392         {
393             .workPeriodStart = 1000,
394             .totalDuration = 14,
395             .cpuDuration = 11,
396             .gpuDuration = -1
397         },
398         {
399             .workPeriodStart = 1000,
400             .totalDuration = 14,
401             .cpuDuration = 0,
402             .gpuDuration = 0
403         }
404     };
405 
406     for (auto && testCase : testCases) {
407         auto&& testObj = createWorkDuration(testCase);
408         int result = APerformanceHint_reportActualWorkDuration2(session.get(), testObj.get());
409         if (result != EINVAL) {
410             std::stringstream stream;
411             stream << "APerformanceHint_reportActualWorkDuration2({"
412                    << "workPeriodStartTimestampNanos = " << testCase.workPeriodStart << ", "
413                    << "actualTotalDurationNanos = " << testCase.totalDuration << ", "
414                    << "actualCpuDurationNanos = " << testCase.cpuDuration << ", "
415                    << "actualGpuDurationNanos = " << testCase.gpuDuration
416                    << "}) did not return EINVAL";
417             return toJString(env, stream.str().c_str());
418         }
419     }
420 
421     return nullptr;
422 }
423 
nativeTestLoadHints(JNIEnv * env,jobject)424 static jstring nativeTestLoadHints(JNIEnv* env, jobject) {
425     APerformanceHintManager* manager = APerformanceHint_getManager();
426     if (!manager) return toJString(env, "null manager");
427     auto session = createSession(manager);
428     if (session == nullptr) return nullptr;
429 
430     int result = APerformanceHint_notifyWorkloadIncrease(session.get(), true, false, "CTS hint 1");
431     if (result != 0 && result != EBUSY) {
432         return toJString(env,
433                          std::format("notifyWorkloadIncrease(true, false) returned {}", result)
434                                  .c_str());
435     }
436 
437     result = APerformanceHint_notifyWorkloadReset(session.get(), false, true, "CTS hint 2");
438     if (result != 0 && result != EBUSY) {
439         return toJString(env,
440                          std::format("notifyWorkloadReset(false, true) returned {}", result)
441                                  .c_str());
442     }
443 
444     result = APerformanceHint_notifyWorkloadIncrease(session.get(), true, true, "CTS hint 3");
445     if (result != 0 && result != EBUSY) {
446         return toJString(env,
447                          std::format("notifyWorkloadIncrease(true, true) returned {}", result)
448                                  .c_str());
449     }
450 
451     result = APerformanceHint_notifyWorkloadIncrease(session.get(), false, false, "CTS hint 4");
452     if (result != 0 && result != EBUSY) {
453         return toJString(env,
454                          std::format("notifyWorkloadIncrease(false, false) returned {}", result)
455                                  .c_str());
456     }
457 
458     result = APerformanceHint_notifyWorkloadReset(session.get(), false, false, "CTS hint 5");
459     if (result != 0 && result != EBUSY) {
460         return toJString(env,
461                          std::format("notifyWorkloadReset(false, false) returned {}", result)
462                                  .c_str());
463     }
464 
465     result = APerformanceHint_notifyWorkloadSpike(session.get(), true, false, "CTS hint 6");
466     if (result != 0 && result != EBUSY) {
467         return toJString(env,
468                          std::format("notifyWorkloadSpike(true, false) returned {}", result)
469                                  .c_str());
470     }
471 
472     result = APerformanceHint_notifyWorkloadSpike(session.get(), false, true, "CTS hint 7");
473     if (result != 0 && result != EBUSY) {
474         return toJString(env,
475                          std::format("notifyWorkloadSpike(false, true) returned {}", result)
476                                  .c_str());
477     }
478 
479     result = APerformanceHint_notifyWorkloadSpike(session.get(), false, false, "CTS hint 8");
480     if (result != 0 && result != EBUSY) {
481         return toJString(env,
482                          std::format("notifyWorkloadSpike(false, false) returned {}", result)
483                                  .c_str());
484     }
485 
486     return nullptr;
487 }
488 
nativeBorrowSessionFromJava(JNIEnv * env,jobject,jobject sessionObj)489 static jlong nativeBorrowSessionFromJava(JNIEnv* env, jobject, jobject sessionObj) {
490     APerformanceHintManager* manager = APerformanceHint_getManager();
491     if (!manager) return 0;
492 
493     APerformanceHintSession* session = APerformanceHint_borrowSessionFromJava(env, sessionObj);
494 
495     // Test a basic synchronous operation to ensure the session is valid.
496     int32_t pid = getpid();
497     int retval = APerformanceHint_setThreads(session, &pid, 1u);
498     if (retval != 0) {
499         return 0;
500     }
501 
502     return reinterpret_cast<jlong>(session);
503 }
504 
nativeTestCreateGraphicsPipelineSession(JNIEnv * env,jobject)505 static jstring nativeTestCreateGraphicsPipelineSession(JNIEnv* env, jobject) {
506     auto supportInfo = getSupportHelper();
507     if (!supportInfo.graphicsPipeline) {
508         return nullptr;
509     }
510 
511     APerformanceHintManager* manager = APerformanceHint_getManager();
512     if (!manager) return toJString(env, "null manager");
513 
514     int errCode = 0;
515     const int count = APerformanceHint_getMaxGraphicsPipelineThreadsCount(manager);
516     auto config = configFromCreator({.graphicsPipeline = true});
517     auto session = createSessionWithConfig(manager, config, &errCode);
518     CHECK_SESSION_RETURN(errCode, session, "Graphics pipeline session creation");
519 
520     return nullptr;
521 }
522 
nativeTestSetNativeSurfaces(JNIEnv * env,jobject,jobject surfaceControlFromJava,jobject surfaceFromJava)523 static jstring nativeTestSetNativeSurfaces(JNIEnv* env, jobject, jobject surfaceControlFromJava,
524                                            jobject surfaceFromJava) {
525     auto supportInfo = getSupportHelper();
526     if (!supportInfo.bindToSurface) {
527         return nullptr;
528     }
529 
530     APerformanceHintManager* manager = APerformanceHint_getManager();
531     if (!manager) return toJString(env, "null manager");
532 
533     int errCode = 0;
534 
535     auto surfaceControl = wrapSurfaceControl(ASurfaceControl_fromJava(env, surfaceControlFromJava));
536     auto nativeWindow = wrapNativeWindow(ANativeWindow_fromSurface(env, surfaceFromJava));
537 
538     // Test native window session creation
539     auto nativeWindowConfig = configFromCreator({
540             .graphicsPipeline = true,
541             .nativeWindows = {nativeWindow.get()},
542     });
543     auto nativeWindowSession = createSessionWithConfig(manager, nativeWindowConfig, &errCode);
544     CHECK_SESSION_RETURN(errCode, nativeWindowSession, "Session creation with an ANativeWindow");
545 
546     // Test surfaceControl session creation
547     auto surfaceControlConfig = configFromCreator({
548             .graphicsPipeline = true,
549             .surfaceControls = {surfaceControl.get()},
550     });
551     auto surfaceControlSession = createSessionWithConfig(manager, surfaceControlConfig, &errCode);
552     CHECK_SESSION_RETURN(errCode, nativeWindowSession, "Session creation with an ASurfaceControl");
553 
554     // Test both
555     auto bothConfig = configFromCreator({
556             .graphicsPipeline = true,
557             .nativeWindows = {nativeWindow.get()},
558             .surfaceControls = {surfaceControl.get()},
559     });
560     auto bothSession = createSessionWithConfig(manager, bothConfig, &errCode);
561     CHECK_SESSION_RETURN(errCode, bothSession,
562                          "Session creation with both ASurfaceControl and ANativeWindow");
563 
564     // Test unsetting
565     errCode = APerformanceHint_setNativeSurfaces(bothSession.get(), nullptr, 0, nullptr, 0);
566     if (errCode != 0) {
567         return toJString(env,
568                          std::format("Setting empty native surfaces failed with: {}", errCode)
569                                  .c_str());
570     }
571 
572     // Test setting it back
573     ANativeWindow* windowArr[]{nativeWindow.get()};
574     ASurfaceControl* controlArr[]{surfaceControl.get()};
575 
576     errCode = APerformanceHint_setNativeSurfaces(bothSession.get(), windowArr, 1, controlArr, 1);
577     if (errCode != 0) {
578         return toJString(env,
579                          std::format("Setting native surfaces on re-used empty session failed "
580                                      "with: {}",
581                                      errCode)
582                                  .c_str());
583     }
584 
585     // Create new empty sessions
586     auto unassociatedConfig = configFromCreator({
587             .graphicsPipeline = true,
588     });
589 
590     auto unassociatedSession = createSessionWithConfig(manager, unassociatedConfig, &errCode);
591     CHECK_SESSION_RETURN(errCode, unassociatedSession, "Session creation while unassociated");
592 
593     errCode = APerformanceHint_setNativeSurfaces(nativeWindowSession.get(), windowArr, 1,
594                                                  controlArr, 1);
595     if (errCode != 0) {
596         return toJString(env,
597                          std::format("Setting native surfaces on fresh unassociated session failed "
598                                      "with: {}",
599                                      errCode)
600                                  .c_str());
601     }
602 
603     return nullptr;
604 }
605 
nativeTestAutoSessionTiming(JNIEnv * env,jobject,jobject surfaceControlFromJava)606 static jstring nativeTestAutoSessionTiming(JNIEnv* env, jobject, jobject surfaceControlFromJava) {
607     APerformanceHintManager* manager = APerformanceHint_getManager();
608     if (!manager) return toJString(env, "null manager");
609 
610     auto supportInfo = getSupportHelper();
611 
612     std::string out;
613     int errCode;
614 
615     auto surfaceControl = wrapSurfaceControl(ASurfaceControl_fromJava(env, surfaceControlFromJava));
616 
617     if (supportInfo.autoCpu) {
618         // Create a surfaceControl-associated session for auto CPU
619         auto autoCpuSessionConfig = configFromCreator({
620                 .graphicsPipeline = true,
621                 .surfaceControls = {surfaceControl.get()},
622                 .autoCpu = true,
623         });
624         if (autoCpuSessionConfig == nullptr) {
625             return toJString(env, out.c_str());
626         }
627 
628         auto autoCpuSession = createSessionWithConfig(manager, autoCpuSessionConfig, &errCode);
629         CHECK_SESSION_RETURN(errCode, autoCpuSession, "Cpu auto session creation");
630     }
631 
632     if (supportInfo.autoGpu) {
633         // Create a surfaceControl-associated session for auto GPU
634         auto autoGpuSessionConfig = configFromCreator({
635                 .graphicsPipeline = true,
636                 .surfaceControls = {surfaceControl.get()},
637                 .autoGpu = true,
638         });
639         if (autoGpuSessionConfig == nullptr) {
640             return toJString(env, out.c_str());
641         }
642 
643         auto autoGpuSession = createSessionWithConfig(manager, autoGpuSessionConfig, &errCode);
644         CHECK_SESSION_RETURN(errCode, autoGpuSession, "Gpu auto session creation");
645     }
646 
647     if (supportInfo.autoCpu && supportInfo.autoGpu) {
648         // Create a surfaceControl-associated session for both
649         auto fullAutoSessionConfig = configFromCreator({
650                 .graphicsPipeline = true,
651                 .surfaceControls = {surfaceControl.get()},
652                 .autoCpu = true,
653                 .autoGpu = true,
654         });
655         if (fullAutoSessionConfig == nullptr) {
656             return toJString(env, out.c_str());
657         }
658 
659         auto fullAutoSession = createSessionWithConfig(manager, fullAutoSessionConfig, &errCode);
660         CHECK_SESSION_RETURN(errCode, fullAutoSession, "Full auto session creation");
661     }
662 
663     return nullptr;
664 }
665 
nativeTestSupportChecking(JNIEnv * env,jobject)666 static jstring nativeTestSupportChecking(JNIEnv* env, jobject) {
667     union {
668         // This checks every single support enum
669         SupportHelper supportInfo = getSupportHelper();
670         int32_t supportInt;
671     };
672     if (!supportInfo.hintSessions) {
673         // If any mode other than hintSessions is enabled
674         if (supportInt & -2) {
675             return toJString(env, "Exposed support for session functionality but not for sessions");
676         }
677     } else if (supportInfo.autoCpu || supportInfo.autoGpu) {
678         if (!supportInfo.graphicsPipeline) {
679             return toJString(env,
680                              "Exposed support for auto timing without support for graphics "
681                              "pipelines");
682         } else if (!supportInfo.bindToSurface) {
683             return toJString(env,
684                              "Exposed support for auto timing without support for native surface"
685                              "binding");
686         }
687     }
688     return nullptr;
689 }
690 
691 static JNINativeMethod gMethods[] = {
nativeGetSessionsAreSupported()692         {"nativeGetSessionsAreSupported", "()Z",
693          (void*)nativeGetSessionsAreSupported},
nativeTestCreateHintSession()694         {"nativeTestCreateHintSession", "()Ljava/lang/String;",
695          (void*)nativeTestCreateHintSession},
nativeTestCreateGraphicsPipelineSession()696         {"nativeTestCreateGraphicsPipelineSession", "()Ljava/lang/String;",
697          (void*)nativeTestCreateGraphicsPipelineSession},
nativeTestCreateHintSessionUsingConfig()698         {"nativeTestCreateHintSessionUsingConfig", "()Ljava/lang/String;",
699          (void*)nativeTestCreateHintSessionUsingConfig},
nativeTestGetPreferredUpdateRateNanos()700         {"nativeTestGetPreferredUpdateRateNanos", "()Ljava/lang/String;",
701          (void*)nativeTestGetPreferredUpdateRateNanos},
nativeUpdateTargetWorkDuration()702         {"nativeUpdateTargetWorkDuration", "()Ljava/lang/String;",
703          (void*)nativeUpdateTargetWorkDuration},
nativeUpdateTargetWorkDurationWithNegativeDuration()704         {"nativeUpdateTargetWorkDurationWithNegativeDuration", "()Ljava/lang/String;",
705          (void*)nativeUpdateTargetWorkDurationWithNegativeDuration},
nativeReportActualWorkDuration()706         {"nativeReportActualWorkDuration", "()Ljava/lang/String;",
707          (void*)nativeReportActualWorkDuration},
nativeReportActualWorkDurationWithIllegalArgument()708         {"nativeReportActualWorkDurationWithIllegalArgument", "()Ljava/lang/String;",
709          (void*)nativeReportActualWorkDurationWithIllegalArgument},
nativeTestSetThreadsWithInvalidTid()710         {"nativeTestSetThreadsWithInvalidTid", "()Ljava/lang/String;",
711          (void*)nativeTestSetThreadsWithInvalidTid},
nativeSetPreferPowerEfficiency()712         {"nativeSetPreferPowerEfficiency", "()Ljava/lang/String;",
713          (void*)nativeSetPreferPowerEfficiency},
nativeTestReportActualWorkDuration2()714         {"nativeTestReportActualWorkDuration2", "()Ljava/lang/String;",
715          (void*)nativeTestReportActualWorkDuration2},
nativeTestReportActualWorkDuration2WithIllegalArgument()716         {"nativeTestReportActualWorkDuration2WithIllegalArgument", "()Ljava/lang/String;",
717          (void*)nativeTestReportActualWorkDuration2WithIllegalArgument},
nativeTestLoadHints()718         {"nativeTestLoadHints", "()Ljava/lang/String;", (void*)nativeTestLoadHints},
nativeBorrowSessionFromJava(Landroid/os/PerformanceHintManager$Session;)719         {"nativeBorrowSessionFromJava", "(Landroid/os/PerformanceHintManager$Session;)J",
720          (void*)nativeBorrowSessionFromJava},
nativeTestSetNativeSurfaces(Landroid/view/SurfaceControl;Landroid/view/Surface;)721         {"nativeTestSetNativeSurfaces",
722          "(Landroid/view/SurfaceControl;Landroid/view/Surface;)Ljava/lang/String;",
723          (void*)nativeTestSetNativeSurfaces},
nativeTestAutoSessionTiming(Landroid/view/SurfaceControl;)724         {"nativeTestAutoSessionTiming", "(Landroid/view/SurfaceControl;)Ljava/lang/String;",
725          (void*)nativeTestAutoSessionTiming},
nativeTestSupportChecking()726         {"nativeTestSupportChecking", "()Ljava/lang/String;", (void*)nativeTestSupportChecking},
727 };
728 
register_android_os_cts_PerformanceHintManagerTest(JNIEnv * env)729 int register_android_os_cts_PerformanceHintManagerTest(JNIEnv* env) {
730     jclass clazz = env->FindClass("android/os/cts/PerformanceHintManagerTest");
731 
732     return env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(JNINativeMethod));
733 }
734