Add support for converting java hint sessions to native hint sessions
Add an API called "APerformanceHint_getSessionFromJava" for Java
clients to access the native APerformanceHintSession objects that
Java hint sessions wrap. This should will make the Java and Native APIs
substantially more interoperable.
This patch also makes the native code aware of when it is being used in
Java, to allow it to have slightly different behavior, especially when
it is being directly accessed through JNI. The main place where this
mostly matters is in preventing clients from closing Java-owned sessions
through the JNI, as that could cause weird behavior otherwise.
Bug: 367803904
Test: atest HintManagerServiceTest
Test: atest PerformanceHintNativeTestCases
Flag: EXEMPT NDK API
Change-Id: I126e8663fc2719d9b7c866cf56f9827c25c2a53c
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 2d1fbf9..6b41ddd 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -363,6 +363,7 @@
APerformanceHint_reportActualWorkDuration2; # introduced=VanillaIceCream
APerformanceHint_notifyWorkloadIncrease; # introduced=36
APerformanceHint_notifyWorkloadReset; # introduced=36
+ APerformanceHint_borrowSessionFromJava; # introduced=36
AWorkDuration_create; # introduced=VanillaIceCream
AWorkDuration_release; # introduced=VanillaIceCream
AWorkDuration_setWorkPeriodStartTimestampNanos; # introduced=VanillaIceCream
@@ -383,6 +384,8 @@
APerformanceHint_setUseFMQForTesting;
APerformanceHint_getRateLimiterPropertiesForTesting;
APerformanceHint_setUseNewLoadHintBehaviorForTesting;
+ APerformanceHint_closeSessionFromJava;
+ APerformanceHint_createSessionFromJava;
extern "C++" {
ASurfaceControl_registerSurfaceStatsListener*;
ASurfaceControl_unregisterSurfaceStatsListener*;
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index e2fa94d..bc1945e 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -36,6 +36,7 @@
#include <cutils/trace.h>
#include <fmq/AidlMessageQueue.h>
#include <inttypes.h>
+#include <jni_wrappers.h>
#include <performance_hint_private.h>
#include <utils/SystemClock.h>
@@ -137,10 +138,14 @@
APerformanceHintSession* createSession(const int32_t* threadIds, size_t size,
int64_t initialTargetWorkDurationNanos,
- hal::SessionTag tag = hal::SessionTag::APP);
+ hal::SessionTag tag = hal::SessionTag::APP,
+ bool isJava = false);
+ APerformanceHintSession* getSessionFromJava(JNIEnv* _Nonnull env, jobject _Nonnull sessionObj);
+
int64_t getPreferredRateNanos() const;
FMQWrapper& getFMQWrapper();
bool canSendLoadHints(std::vector<hal::SessionHint>& hints, int64_t now) REQUIRES(sHintMutex);
+ void initJava(JNIEnv* _Nonnull env);
private:
// Necessary to create an empty binder object
@@ -161,13 +166,16 @@
FMQWrapper mFMQWrapper;
double mHintBudget = kMaxLoadHintsPerInterval;
int64_t mLastBudgetReplenish = 0;
+ bool mJavaInitialized = false;
+ jclass mJavaSessionClazz;
+ jfieldID mJavaSessionNativePtr;
};
struct APerformanceHintSession {
public:
APerformanceHintSession(std::shared_ptr<IHintManager> hintManager,
std::shared_ptr<IHintSession> session, int64_t preferredRateNanos,
- int64_t targetDurationNanos,
+ int64_t targetDurationNanos, bool isJava,
std::optional<hal::SessionConfig> sessionConfig);
APerformanceHintSession() = delete;
~APerformanceHintSession();
@@ -181,6 +189,7 @@
int getThreadIds(int32_t* const threadIds, size_t* size);
int setPreferPowerEfficiency(bool enabled);
int reportActualWorkDuration(AWorkDuration* workDuration);
+ bool isJava();
private:
friend struct APerformanceHintManager;
@@ -203,6 +212,8 @@
std::vector<int64_t> mLastHintSentTimestamp GUARDED_BY(sHintMutex);
// Cached samples
std::vector<hal::WorkDuration> mActualWorkDurations GUARDED_BY(sHintMutex);
+ // Is this session backing an SDK wrapper object
+ const bool mIsJava;
std::string mSessionName;
static int64_t sIDCounter GUARDED_BY(sHintMutex);
// The most recent set of thread IDs
@@ -299,7 +310,7 @@
APerformanceHintSession* APerformanceHintManager::createSession(
const int32_t* threadIds, size_t size, int64_t initialTargetWorkDurationNanos,
- hal::SessionTag tag) {
+ hal::SessionTag tag, bool isJava) {
std::vector<int32_t> tids(threadIds, threadIds + size);
std::shared_ptr<IHintSession> session;
ndk::ScopedAStatus ret;
@@ -312,7 +323,7 @@
return nullptr;
}
auto out = new APerformanceHintSession(mHintManager, std::move(session), mPreferredRateNanos,
- initialTargetWorkDurationNanos,
+ initialTargetWorkDurationNanos, isJava,
sessionConfig.id == -1
? std::nullopt
: std::make_optional<hal::SessionConfig>(
@@ -324,6 +335,18 @@
return out;
}
+APerformanceHintSession* APerformanceHintManager::getSessionFromJava(JNIEnv* env,
+ jobject sessionObj) {
+ initJava(env);
+ LOG_ALWAYS_FATAL_IF(!env->IsInstanceOf(sessionObj, mJavaSessionClazz),
+ "Wrong java type passed to APerformanceHint_getSessionFromJava");
+ APerformanceHintSession* out = reinterpret_cast<APerformanceHintSession*>(
+ env->GetLongField(sessionObj, mJavaSessionNativePtr));
+ LOG_ALWAYS_FATAL_IF(out == nullptr, "Java-wrapped native hint session is nullptr");
+ LOG_ALWAYS_FATAL_IF(!out->isJava(), "Unmanaged native hint session returned from Java SDK");
+ return out;
+}
+
int64_t APerformanceHintManager::getPreferredRateNanos() const {
return mPreferredRateNanos;
}
@@ -332,13 +355,23 @@
return mFMQWrapper;
}
+void APerformanceHintManager::initJava(JNIEnv* _Nonnull env) {
+ if (mJavaInitialized) {
+ return;
+ }
+ jclass sessionClazz = FindClassOrDie(env, "android/os/PerformanceHintManager$Session");
+ mJavaSessionClazz = MakeGlobalRefOrDie(env, sessionClazz);
+ mJavaSessionNativePtr = GetFieldIDOrDie(env, mJavaSessionClazz, "mNativeSessionPtr", "J");
+ mJavaInitialized = true;
+}
+
// ===================================== APerformanceHintSession implementation
constexpr int kNumEnums = enum_size<hal::SessionHint>();
APerformanceHintSession::APerformanceHintSession(std::shared_ptr<IHintManager> hintManager,
std::shared_ptr<IHintSession> session,
int64_t preferredRateNanos,
- int64_t targetDurationNanos,
+ int64_t targetDurationNanos, bool isJava,
std::optional<hal::SessionConfig> sessionConfig)
: mHintManager(hintManager),
mHintSession(std::move(session)),
@@ -347,6 +380,7 @@
mFirstTargetMetTimestamp(0),
mLastTargetMetTimestamp(0),
mLastHintSentTimestamp(std::vector<int64_t>(kNumEnums, 0)),
+ mIsJava(isJava),
mSessionConfig(sessionConfig) {
if (sessionConfig->id > INT32_MAX) {
ALOGE("Session ID too large, must fit 32-bit integer");
@@ -401,6 +435,10 @@
return reportActualWorkDurationInternal(static_cast<AWorkDuration*>(&workDuration));
}
+bool APerformanceHintSession::isJava() {
+ return mIsJava;
+}
+
int APerformanceHintSession::sendHints(std::vector<hal::SessionHint>& hints, int64_t now,
const char*) {
std::scoped_lock lock(sHintMutex);
@@ -826,6 +864,22 @@
static_cast<hal::SessionTag>(tag));
}
+APerformanceHintSession* APerformanceHint_createSessionFromJava(
+ APerformanceHintManager* manager, const int32_t* threadIds, size_t size,
+ int64_t initialTargetWorkDurationNanos) {
+ VALIDATE_PTR(manager)
+ VALIDATE_PTR(threadIds)
+ return manager->createSession(threadIds, size, initialTargetWorkDurationNanos,
+ hal::SessionTag::APP, true);
+}
+
+APerformanceHintSession* APerformanceHint_borrowSessionFromJava(JNIEnv* env,
+ jobject sessionObj) {
+ VALIDATE_PTR(env)
+ VALIDATE_PTR(sessionObj)
+ return APerformanceHintManager::getInstance()->getSessionFromJava(env, sessionObj);
+}
+
int64_t APerformanceHint_getPreferredUpdateRateNanos(APerformanceHintManager* manager) {
VALIDATE_PTR(manager)
return manager->getPreferredRateNanos();
@@ -846,6 +900,16 @@
void APerformanceHint_closeSession(APerformanceHintSession* session) {
VALIDATE_PTR(session)
+ if (session->isJava()) {
+ LOG_ALWAYS_FATAL("%s: Java-owned PerformanceHintSession cannot be closed in native",
+ __FUNCTION__);
+ return;
+ }
+ delete session;
+}
+
+void APerformanceHint_closeSessionFromJava(APerformanceHintSession* session) {
+ VALIDATE_PTR(session)
delete session;
}