/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "PerfHint-jni" #include "jni.h" #include #include #include #include #include #include "core_jni_helpers.h" namespace android { namespace { struct APerformanceHintManager; struct APerformanceHintSession; typedef APerformanceHintManager* (*APH_getManager)(); typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*, size_t, int64_t); typedef int64_t (*APH_getPreferredUpdateRateNanos)(APerformanceHintManager* manager); typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t); typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t); typedef void (*APH_closeSession)(APerformanceHintSession* session); typedef void (*APH_sendHint)(APerformanceHintSession*, int32_t); typedef void (*APH_setThreads)(APerformanceHintSession*, const pid_t*, size_t); typedef void (*APH_getThreadIds)(APerformanceHintSession*, int32_t* const, size_t* const); bool gAPerformanceHintBindingInitialized = false; APH_getManager gAPH_getManagerFn = nullptr; APH_createSession gAPH_createSessionFn = nullptr; APH_getPreferredUpdateRateNanos gAPH_getPreferredUpdateRateNanosFn = nullptr; APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr; APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr; APH_closeSession gAPH_closeSessionFn = nullptr; APH_sendHint gAPH_sendHintFn = nullptr; APH_setThreads gAPH_setThreadsFn = nullptr; APH_getThreadIds gAPH_getThreadIdsFn = nullptr; void ensureAPerformanceHintBindingInitialized() { if (gAPerformanceHintBindingInitialized) return; void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE); LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!"); gAPH_getManagerFn = (APH_getManager)dlsym(handle_, "APerformanceHint_getManager"); LOG_ALWAYS_FATAL_IF(gAPH_getManagerFn == nullptr, "Failed to find required symbol APerformanceHint_getManager!"); gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession"); LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr, "Failed to find required symbol APerformanceHint_createSession!"); gAPH_getPreferredUpdateRateNanosFn = (APH_getPreferredUpdateRateNanos)dlsym(handle_, "APerformanceHint_getPreferredUpdateRateNanos"); LOG_ALWAYS_FATAL_IF(gAPH_getPreferredUpdateRateNanosFn == nullptr, "Failed to find required symbol " "APerformanceHint_getPreferredUpdateRateNanos!"); gAPH_updateTargetWorkDurationFn = (APH_updateTargetWorkDuration)dlsym(handle_, "APerformanceHint_updateTargetWorkDuration"); LOG_ALWAYS_FATAL_IF(gAPH_updateTargetWorkDurationFn == nullptr, "Failed to find required symbol " "APerformanceHint_updateTargetWorkDuration!"); gAPH_reportActualWorkDurationFn = (APH_reportActualWorkDuration)dlsym(handle_, "APerformanceHint_reportActualWorkDuration"); LOG_ALWAYS_FATAL_IF(gAPH_reportActualWorkDurationFn == nullptr, "Failed to find required symbol " "APerformanceHint_reportActualWorkDuration!"); gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession"); LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr, "Failed to find required symbol APerformanceHint_closeSession!"); gAPH_sendHintFn = (APH_sendHint)dlsym(handle_, "APerformanceHint_sendHint"); LOG_ALWAYS_FATAL_IF(gAPH_sendHintFn == nullptr, "Failed to find required symbol " "APerformanceHint_sendHint!"); gAPH_setThreadsFn = (APH_setThreads)dlsym(handle_, "APerformanceHint_setThreads"); LOG_ALWAYS_FATAL_IF(gAPH_setThreadsFn == nullptr, "Failed to find required symbol APerformanceHint_setThreads!"); gAPH_getThreadIdsFn = (APH_getThreadIds)dlsym(handle_, "APerformanceHint_getThreadIds"); LOG_ALWAYS_FATAL_IF(gAPH_getThreadIdsFn == nullptr, "Failed to find required symbol APerformanceHint_getThreadIds!"); gAPerformanceHintBindingInitialized = true; } } // namespace static jlong nativeAcquireManager(JNIEnv* env, jclass clazz) { ensureAPerformanceHintBindingInitialized(); return reinterpret_cast(gAPH_getManagerFn()); } static jlong nativeGetPreferredUpdateRateNanos(JNIEnv* env, jclass clazz, jlong nativeManagerPtr) { ensureAPerformanceHintBindingInitialized(); return gAPH_getPreferredUpdateRateNanosFn( reinterpret_cast(nativeManagerPtr)); } static jlong nativeCreateSession(JNIEnv* env, jclass clazz, jlong nativeManagerPtr, jintArray tids, jlong initialTargetWorkDurationNanos) { ensureAPerformanceHintBindingInitialized(); if (tids == nullptr) return 0; std::vector tidsVector; ScopedIntArrayRO tidsArray(env, tids); for (size_t i = 0; i < tidsArray.size(); ++i) { tidsVector.push_back(static_cast(tidsArray[i])); } return reinterpret_cast( gAPH_createSessionFn(reinterpret_cast(nativeManagerPtr), tidsVector.data(), tidsVector.size(), initialTargetWorkDurationNanos)); } static void nativeUpdateTargetWorkDuration(JNIEnv* env, jclass clazz, jlong nativeSessionPtr, jlong targetDurationNanos) { ensureAPerformanceHintBindingInitialized(); gAPH_updateTargetWorkDurationFn(reinterpret_cast(nativeSessionPtr), targetDurationNanos); } static void nativeReportActualWorkDuration(JNIEnv* env, jclass clazz, jlong nativeSessionPtr, jlong actualDurationNanos) { ensureAPerformanceHintBindingInitialized(); gAPH_reportActualWorkDurationFn(reinterpret_cast(nativeSessionPtr), actualDurationNanos); } static void nativeCloseSession(JNIEnv* env, jclass clazz, jlong nativeSessionPtr) { ensureAPerformanceHintBindingInitialized(); gAPH_closeSessionFn(reinterpret_cast(nativeSessionPtr)); } static void nativeSendHint(JNIEnv* env, jclass clazz, jlong nativeSessionPtr, jint hint) { ensureAPerformanceHintBindingInitialized(); gAPH_sendHintFn(reinterpret_cast(nativeSessionPtr), hint); } static void nativeSetThreads(JNIEnv* env, jclass clazz, jlong nativeSessionPtr, jintArray tids) { ensureAPerformanceHintBindingInitialized(); if (tids == nullptr) { return; } ScopedIntArrayRO tidsArray(env, tids); std::vector tidsVector; tidsVector.reserve(tidsArray.size()); for (size_t i = 0; i < tidsArray.size(); ++i) { tidsVector.push_back(static_cast(tidsArray[i])); } gAPH_setThreadsFn(reinterpret_cast(nativeSessionPtr), tidsVector.data(), tidsVector.size()); } // This call should only be used for validation in tests only. This call will initiate two IPC // calls, the first one is used to determined the size of the thread ids list, the second one // is used to return the actual list. static jintArray nativeGetThreadIds(JNIEnv* env, jclass clazz, jlong nativeSessionPtr) { ensureAPerformanceHintBindingInitialized(); size_t size = 0; gAPH_getThreadIdsFn(reinterpret_cast(nativeSessionPtr), nullptr, &size); if (size == 0) { jintArray jintArr = env->NewIntArray(0); return jintArr; } std::vector tidsVector(size); gAPH_getThreadIdsFn(reinterpret_cast(nativeSessionPtr), tidsVector.data(), &size); jintArray jintArr = env->NewIntArray(size); if (jintArr == nullptr) { jniThrowException(env, "java/lang/OutOfMemoryError", nullptr); return nullptr; } jint* threadIds = env->GetIntArrayElements(jintArr, 0); for (int i = 0; i < size; ++i) { threadIds[i] = tidsVector[i]; } env->ReleaseIntArrayElements(jintArr, threadIds, 0); return jintArr; } static const JNINativeMethod gPerformanceHintMethods[] = { {"nativeAcquireManager", "()J", (void*)nativeAcquireManager}, {"nativeGetPreferredUpdateRateNanos", "(J)J", (void*)nativeGetPreferredUpdateRateNanos}, {"nativeCreateSession", "(J[IJ)J", (void*)nativeCreateSession}, {"nativeUpdateTargetWorkDuration", "(JJ)V", (void*)nativeUpdateTargetWorkDuration}, {"nativeReportActualWorkDuration", "(JJ)V", (void*)nativeReportActualWorkDuration}, {"nativeCloseSession", "(J)V", (void*)nativeCloseSession}, {"nativeSendHint", "(JI)V", (void*)nativeSendHint}, {"nativeSetThreads", "(J[I)V", (void*)nativeSetThreads}, {"nativeGetThreadIds", "(J)[I", (void*)nativeGetThreadIds}, }; int register_android_os_PerformanceHintManager(JNIEnv* env) { return RegisterMethodsOrDie(env, "android/os/PerformanceHintManager", gPerformanceHintMethods, NELEM(gPerformanceHintMethods)); } } // namespace android