Switch HWUI to use native performance hint API

Test: None
Bug: 194204196
Change-Id: I80dfdb5d56921c465406cc4534e82738c668d46d
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index c4cdb7d..54367b8 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -44,8 +44,6 @@
 #include <utils/StrongPointer.h>
 #include <utils/Timers.h>
 
-#include <pthread.h>
-
 #include <algorithm>
 #include <atomic>
 #include <vector>
@@ -60,10 +58,6 @@
 struct {
     jclass clazz;
     jmethodID invokePictureCapturedCallback;
-    jmethodID createHintSession;
-    jmethodID updateTargetWorkDuration;
-    jmethodID reportActualWorkDuration;
-    jmethodID closeHintSession;
 } gHardwareRenderer;
 
 struct {
@@ -90,14 +84,6 @@
     return env;
 }
 
-static bool hasExceptionAndClear(JNIEnv* env) {
-    if (GraphicsJNI::hasException(env)) {
-        env->ExceptionClear();
-        return true;
-    }
-    return false;
-}
-
 typedef ANativeWindow* (*ANW_fromSurface)(JNIEnv* env, jobject surface);
 ANW_fromSurface fromSurface;
 
@@ -147,67 +133,6 @@
     }
 };
 
-class HintSessionWrapper : public LightRefBase<HintSessionWrapper> {
-public:
-    static sp<HintSessionWrapper> create(JNIEnv* env, RenderProxy* proxy) {
-        if (!Properties::useHintManager) return nullptr;
-
-        // Include UI thread (self), render thread, and thread pool.
-        std::vector<int> tids = CommonPool::getThreadIds();
-        tids.push_back(proxy->getRenderThreadTid());
-        tids.push_back(pthread_gettid_np(pthread_self()));
-
-        jintArray tidsArray = env->NewIntArray(tids.size());
-        if (hasExceptionAndClear(env)) return nullptr;
-        env->SetIntArrayRegion(tidsArray, 0, tids.size(), reinterpret_cast<jint*>(tids.data()));
-        if (hasExceptionAndClear(env)) return nullptr;
-        jobject session = env->CallStaticObjectMethod(
-                gHardwareRenderer.clazz, gHardwareRenderer.createHintSession, tidsArray);
-        if (hasExceptionAndClear(env) || !session) return nullptr;
-        return new HintSessionWrapper(env, session);
-    }
-
-    ~HintSessionWrapper() {
-        if (!mSession) return;
-        JNIEnv* env = getenv(mVm);
-        env->CallStaticVoidMethod(gHardwareRenderer.clazz, gHardwareRenderer.closeHintSession,
-                                  mSession);
-        hasExceptionAndClear(env);
-        env->DeleteGlobalRef(mSession);
-        mSession = nullptr;
-    }
-
-    void updateTargetWorkDuration(long targetDurationNanos) {
-        if (!mSession) return;
-        JNIEnv* env = getenv(mVm);
-        env->CallStaticVoidMethod(gHardwareRenderer.clazz,
-                                  gHardwareRenderer.updateTargetWorkDuration, mSession,
-                                  static_cast<jlong>(targetDurationNanos));
-        hasExceptionAndClear(env);
-    }
-
-    void reportActualWorkDuration(long actualDurationNanos) {
-        if (!mSession) return;
-        JNIEnv* env = getenv(mVm);
-        env->CallStaticVoidMethod(gHardwareRenderer.clazz,
-                                  gHardwareRenderer.reportActualWorkDuration, mSession,
-                                  static_cast<jlong>(actualDurationNanos));
-        hasExceptionAndClear(env);
-    }
-
-private:
-    HintSessionWrapper(JNIEnv* env, jobject jobject) {
-        env->GetJavaVM(&mVm);
-        if (jobject) {
-            mSession = env->NewGlobalRef(jobject);
-            LOG_ALWAYS_FATAL_IF(!mSession, "Failed to make global ref");
-        }
-    }
-
-    JavaVM* mVm = nullptr;
-    jobject mSession = nullptr;
-};
-
 static void android_view_ThreadedRenderer_rotateProcessStatsBuffer(JNIEnv* env, jobject clazz) {
     RenderProxy::rotateProcessStatsBuffer();
 }
@@ -235,12 +160,6 @@
     RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootRenderNodePtr);
     ContextFactoryImpl factory(rootRenderNode);
     RenderProxy* proxy = new RenderProxy(translucent, rootRenderNode, &factory);
-    sp<HintSessionWrapper> wrapper = HintSessionWrapper::create(env, proxy);
-    if (wrapper) {
-        proxy->setHintSessionCallbacks(
-                [wrapper](int64_t nanos) { wrapper->updateTargetWorkDuration(nanos); },
-                [wrapper](int64_t nanos) { wrapper->reportActualWorkDuration(nanos); });
-    }
     return (jlong) proxy;
 }
 
@@ -1059,18 +978,6 @@
     gHardwareRenderer.invokePictureCapturedCallback = GetStaticMethodIDOrDie(env, hardwareRenderer,
             "invokePictureCapturedCallback",
             "(JLandroid/graphics/HardwareRenderer$PictureCapturedCallback;)V");
-    gHardwareRenderer.createHintSession =
-            GetStaticMethodIDOrDie(env, hardwareRenderer, "createHintSession",
-                                   "([I)Landroid/os/PerformanceHintManager$Session;");
-    gHardwareRenderer.updateTargetWorkDuration =
-            GetStaticMethodIDOrDie(env, hardwareRenderer, "updateTargetWorkDuration",
-                                   "(Landroid/os/PerformanceHintManager$Session;J)V");
-    gHardwareRenderer.reportActualWorkDuration =
-            GetStaticMethodIDOrDie(env, hardwareRenderer, "reportActualWorkDuration",
-                                   "(Landroid/os/PerformanceHintManager$Session;J)V");
-    gHardwareRenderer.closeHintSession =
-            GetStaticMethodIDOrDie(env, hardwareRenderer, "closeHintSession",
-                                   "(Landroid/os/PerformanceHintManager$Session;)V");
 
     jclass aSurfaceTransactionCallbackClass =
             FindClassOrDie(env, "android/graphics/HardwareRenderer$ASurfaceTransactionCallback");
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index db29e34..e7081df 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -16,6 +16,7 @@
 
 #include "DrawFrameTask.h"
 
+#include <dlfcn.h>
 #include <gui/TraceUtils.h>
 #include <utils/Log.h>
 #include <algorithm>
@@ -26,11 +27,63 @@
 #include "../RenderNode.h"
 #include "CanvasContext.h"
 #include "RenderThread.h"
+#include "thread/CommonPool.h"
 
 namespace android {
 namespace uirenderer {
 namespace renderthread {
 
+namespace {
+
+typedef APerformanceHintManager* (*APH_getManager)();
+typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*,
+                                                      size_t, int64_t);
+typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t);
+typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t);
+typedef void (*APH_closeSession)(APerformanceHintSession* session);
+
+bool gAPerformanceHintBindingInitialized = false;
+APH_getManager gAPH_getManagerFn = nullptr;
+APH_createSession gAPH_createSessionFn = nullptr;
+APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr;
+APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr;
+APH_closeSession gAPH_closeSessionFn = 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_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!");
+
+    gAPerformanceHintBindingInitialized = true;
+}
+
+}  // namespace
+
 DrawFrameTask::DrawFrameTask()
         : mRenderThread(nullptr)
         , mContext(nullptr)
@@ -39,17 +92,13 @@
 
 DrawFrameTask::~DrawFrameTask() {}
 
-void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context,
-                               RenderNode* targetNode) {
+void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode,
+                               int32_t uiThreadId, int32_t renderThreadId) {
     mRenderThread = thread;
     mContext = context;
     mTargetNode = targetNode;
-}
-
-void DrawFrameTask::setHintSessionCallbacks(std::function<void(int64_t)> updateTargetWorkDuration,
-                                            std::function<void(int64_t)> reportActualWorkDuration) {
-    mUpdateTargetWorkDuration = std::move(updateTargetWorkDuration);
-    mReportActualWorkDuration = std::move(reportActualWorkDuration);
+    mUiThreadId = uiThreadId;
+    mRenderThreadId = renderThreadId;
 }
 
 void DrawFrameTask::pushLayerUpdate(DeferredLayerUpdater* layer) {
@@ -144,27 +193,25 @@
         unblockUiThread();
     }
 
-    // These member callbacks are effectively const as they are set once during init, so it's safe
-    // to use these directly instead of making local copies.
-    if (mUpdateTargetWorkDuration && mReportActualWorkDuration) {
-        constexpr int64_t kSanityCheckLowerBound = 100000;       // 0.1ms
-        constexpr int64_t kSanityCheckUpperBound = 10000000000;  // 10s
-        int64_t targetWorkDuration = frameDeadline - intendedVsync;
-        targetWorkDuration = targetWorkDuration * Properties::targetCpuTimePercentage / 100;
-        if (targetWorkDuration > kSanityCheckLowerBound &&
-            targetWorkDuration < kSanityCheckUpperBound &&
-            targetWorkDuration != mLastTargetWorkDuration) {
-            mLastTargetWorkDuration = targetWorkDuration;
-            mUpdateTargetWorkDuration(targetWorkDuration);
-        }
-        int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime;
-        int64_t actualDuration = frameDuration -
-                                 (std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
-                                 dequeueBufferDuration;
-        if (actualDuration > kSanityCheckLowerBound && actualDuration < kSanityCheckUpperBound) {
-            mReportActualWorkDuration(actualDuration);
-        }
+    if (!mHintSessionWrapper) mHintSessionWrapper.emplace(mUiThreadId, mRenderThreadId);
+    constexpr int64_t kSanityCheckLowerBound = 100000;       // 0.1ms
+    constexpr int64_t kSanityCheckUpperBound = 10000000000;  // 10s
+    int64_t targetWorkDuration = frameDeadline - intendedVsync;
+    targetWorkDuration = targetWorkDuration * Properties::targetCpuTimePercentage / 100;
+    if (targetWorkDuration > kSanityCheckLowerBound &&
+        targetWorkDuration < kSanityCheckUpperBound &&
+        targetWorkDuration != mLastTargetWorkDuration) {
+        mLastTargetWorkDuration = targetWorkDuration;
+        mHintSessionWrapper->updateTargetWorkDuration(targetWorkDuration);
     }
+    int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime;
+    int64_t actualDuration = frameDuration -
+                             (std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
+                             dequeueBufferDuration;
+    if (actualDuration > kSanityCheckLowerBound && actualDuration < kSanityCheckUpperBound) {
+        mHintSessionWrapper->reportActualWorkDuration(actualDuration);
+    }
+
     mLastDequeueBufferDuration = dequeueBufferDuration;
 }
 
@@ -216,6 +263,44 @@
     mSignal.signal();
 }
 
+DrawFrameTask::HintSessionWrapper::HintSessionWrapper(int32_t uiThreadId, int32_t renderThreadId) {
+    if (!Properties::useHintManager) return;
+    if (uiThreadId < 0 || renderThreadId < 0) return;
+
+    ensureAPerformanceHintBindingInitialized();
+
+    APerformanceHintManager* manager = gAPH_getManagerFn();
+    if (!manager) return;
+
+    std::vector<int32_t> tids = CommonPool::getThreadIds();
+    tids.push_back(uiThreadId);
+    tids.push_back(renderThreadId);
+
+    // DrawFrameTask code will always set a target duration before reporting actual durations.
+    // So this is just a placeholder value that's never used.
+    int64_t dummyTargetDurationNanos = 16666667;
+    mHintSession =
+            gAPH_createSessionFn(manager, tids.data(), tids.size(), dummyTargetDurationNanos);
+}
+
+DrawFrameTask::HintSessionWrapper::~HintSessionWrapper() {
+    if (mHintSession) {
+        gAPH_closeSessionFn(mHintSession);
+    }
+}
+
+void DrawFrameTask::HintSessionWrapper::updateTargetWorkDuration(long targetDurationNanos) {
+    if (mHintSession) {
+        gAPH_updateTargetWorkDurationFn(mHintSession, targetDurationNanos);
+    }
+}
+
+void DrawFrameTask::HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) {
+    if (mHintSession) {
+        gAPH_reportActualWorkDurationFn(mHintSession, actualDurationNanos);
+    }
+}
+
 } /* namespace renderthread */
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index 2455ea8..6a61a2b 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -16,8 +16,10 @@
 #ifndef DRAWFRAMETASK_H
 #define DRAWFRAMETASK_H
 
+#include <optional>
 #include <vector>
 
+#include <performance_hint_private.h>
 #include <utils/Condition.h>
 #include <utils/Mutex.h>
 #include <utils/StrongPointer.h>
@@ -60,9 +62,8 @@
     DrawFrameTask();
     virtual ~DrawFrameTask();
 
-    void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode);
-    void setHintSessionCallbacks(std::function<void(int64_t)> updateTargetWorkDuration,
-                                 std::function<void(int64_t)> reportActualWorkDuration);
+    void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode,
+                    int32_t uiThreadId, int32_t renderThreadId);
     void setContentDrawBounds(int left, int top, int right, int bottom) {
         mContentDrawBounds.set(left, top, right, bottom);
     }
@@ -85,6 +86,18 @@
     }
 
 private:
+    class HintSessionWrapper {
+    public:
+        HintSessionWrapper(int32_t uiThreadId, int32_t renderThreadId);
+        ~HintSessionWrapper();
+
+        void updateTargetWorkDuration(long targetDurationNanos);
+        void reportActualWorkDuration(long actualDurationNanos);
+
+    private:
+        APerformanceHintSession* mHintSession = nullptr;
+    };
+
     void postAndWait();
     bool syncFrameState(TreeInfo& info);
     void unblockUiThread();
@@ -95,6 +108,8 @@
     RenderThread* mRenderThread;
     CanvasContext* mContext;
     RenderNode* mTargetNode = nullptr;
+    int32_t mUiThreadId = -1;
+    int32_t mRenderThreadId = -1;
     Rect mContentDrawBounds;
 
     /*********************************************
@@ -112,8 +127,7 @@
 
     nsecs_t mLastDequeueBufferDuration = 0;
     nsecs_t mLastTargetWorkDuration = 0;
-    std::function<void(int64_t)> mUpdateTargetWorkDuration;
-    std::function<void(int64_t)> mReportActualWorkDuration;
+    std::optional<HintSessionWrapper> mHintSessionWrapper;
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index a77b5b5..c485ce2 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -29,6 +29,8 @@
 #include "utils/Macros.h"
 #include "utils/TimeUtils.h"
 
+#include <pthread.h>
+
 namespace android {
 namespace uirenderer {
 namespace renderthread {
@@ -39,7 +41,8 @@
     mContext = mRenderThread.queue().runSync([&]() -> CanvasContext* {
         return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory);
     });
-    mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode);
+    mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode,
+                              pthread_gettid_np(pthread_self()), getRenderThreadTid());
 }
 
 RenderProxy::~RenderProxy() {
@@ -48,7 +51,7 @@
 
 void RenderProxy::destroyContext() {
     if (mContext) {
-        mDrawFrameTask.setContext(nullptr, nullptr, nullptr);
+        mDrawFrameTask.setContext(nullptr, nullptr, nullptr, -1, -1);
         // This is also a fence as we need to be certain that there are no
         // outstanding mDrawFrame tasks posted before it is destroyed
         mRenderThread.queue().runSync([this]() { delete mContext; });
@@ -76,12 +79,6 @@
     mRenderThread.queue().runSync([this, name]() { mContext->setName(std::string(name)); });
 }
 
-void RenderProxy::setHintSessionCallbacks(std::function<void(int64_t)> updateTargetWorkDuration,
-                                          std::function<void(int64_t)> reportActualWorkDuration) {
-    mDrawFrameTask.setHintSessionCallbacks(std::move(updateTargetWorkDuration),
-                                           std::move(reportActualWorkDuration));
-}
-
 void RenderProxy::setSurface(ANativeWindow* window, bool enableTimeout) {
     if (window) { ANativeWindow_acquire(window); }
     mRenderThread.queue().post([this, win = window, enableTimeout]() mutable {
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 1b0f22e..2b5405c 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -71,8 +71,6 @@
     void setSwapBehavior(SwapBehavior swapBehavior);
     bool loadSystemProperties();
     void setName(const char* name);
-    void setHintSessionCallbacks(std::function<void(int64_t)> updateTargetWorkDuration,
-                                 std::function<void(int64_t)> reportActualWorkDuration);
 
     void setSurface(ANativeWindow* window, bool enableTimeout = true);
     void setSurfaceControl(ASurfaceControl* surfaceControl);