Refactor HintSessionWrapper and move into CanvasContext

 * Move HintSessionWrapper into its own file
 * Refactor HintSessionWrapper initialization and lifecycle
 * Move HintSessionWrapper instance into CanvasContext
 * Have HintSessionWrapper do its own validity checks
 * Have HintSessionWrapper implicitly initialize when called

Bug: b/260267128
Bug: b/260224719
Test: manual
Change-Id: I69f89bb5ef601d1b5d9fca1a5da62256f2556da8
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 88cfed9..fcc0126 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -570,6 +570,7 @@
                 "renderthread/VulkanSurface.cpp",
                 "renderthread/RenderProxy.cpp",
                 "renderthread/RenderThread.cpp",
+                "renderthread/HintSessionWrapper.cpp",
                 "service/GraphicsStatsService.cpp",
                 "thread/CommonPool.cpp",
                 "utils/GLUtils.cpp",
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index 564ee4f..b15b6cb 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -104,6 +104,7 @@
         set(FrameInfoIndex::AnimationStart) = vsyncTime;
         set(FrameInfoIndex::PerformTraversalsStart) = vsyncTime;
         set(FrameInfoIndex::DrawStart) = vsyncTime;
+        set(FrameInfoIndex::FrameStartTime) = vsyncTime;
         set(FrameInfoIndex::FrameDeadline) = frameDeadline;
         set(FrameInfoIndex::FrameInterval) = frameInterval;
         return *this;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index d09bc47..64839d0 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -71,16 +71,19 @@
 } /* namespace */
 
 CanvasContext* CanvasContext::create(RenderThread& thread, bool translucent,
-                                     RenderNode* rootRenderNode, IContextFactory* contextFactory) {
+                                     RenderNode* rootRenderNode, IContextFactory* contextFactory,
+                                     int32_t uiThreadId, int32_t renderThreadId) {
     auto renderType = Properties::getRenderPipelineType();
 
     switch (renderType) {
         case RenderPipelineType::SkiaGL:
             return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
-                                     std::make_unique<skiapipeline::SkiaOpenGLPipeline>(thread));
+                                     std::make_unique<skiapipeline::SkiaOpenGLPipeline>(thread),
+                                     uiThreadId, renderThreadId);
         case RenderPipelineType::SkiaVulkan:
             return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
-                                     std::make_unique<skiapipeline::SkiaVulkanPipeline>(thread));
+                                     std::make_unique<skiapipeline::SkiaVulkanPipeline>(thread),
+                                     uiThreadId, renderThreadId);
         default:
             LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType);
             break;
@@ -110,7 +113,8 @@
 
 CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
                              IContextFactory* contextFactory,
-                             std::unique_ptr<IRenderPipeline> renderPipeline)
+                             std::unique_ptr<IRenderPipeline> renderPipeline, pid_t uiThreadId,
+                             pid_t renderThreadId)
         : mRenderThread(thread)
         , mGenerationID(0)
         , mOpaque(!translucent)
@@ -118,7 +122,8 @@
         , mJankTracker(&thread.globalProfileData())
         , mProfiler(mJankTracker.frames(), thread.timeLord().frameIntervalNanos())
         , mContentDrawBounds(0, 0, 0, 0)
-        , mRenderPipeline(std::move(renderPipeline)) {
+        , mRenderPipeline(std::move(renderPipeline))
+        , mHintSessionWrapper(uiThreadId, renderThreadId) {
     mRenderThread.cacheManager().registerCanvasContext(this);
     rootRenderNode->makeRoot();
     mRenderNodes.emplace_back(rootRenderNode);
@@ -472,16 +477,22 @@
     mRenderThread.pushBackFrameCallback(this);
 }
 
-std::optional<nsecs_t> CanvasContext::draw() {
+void CanvasContext::draw() {
     if (auto grContext = getGrContext()) {
         if (grContext->abandoned()) {
             LOG_ALWAYS_FATAL("GrContext is abandoned/device lost at start of CanvasContext::draw");
-            return std::nullopt;
+            return;
         }
     }
     SkRect dirty;
     mDamageAccumulator.finish(&dirty);
 
+    // reset syncDelayDuration each time we draw
+    nsecs_t syncDelayDuration = mSyncDelayDuration;
+    nsecs_t idleDuration = mIdleDuration;
+    mSyncDelayDuration = 0;
+    mIdleDuration = 0;
+
     if (!Properties::isDrawingEnabled() ||
         (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw())) {
         mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
@@ -498,7 +509,7 @@
             std::invoke(func, false /* didProduceBuffer */);
         }
         mFrameCommitCallbacks.clear();
-        return std::nullopt;
+        return;
     }
 
     ScopedActiveContext activeContext(this);
@@ -650,10 +661,25 @@
         }
     }
 
+    int64_t intendedVsync = mCurrentFrameInfo->get(FrameInfoIndex::IntendedVsync);
+    int64_t frameDeadline = mCurrentFrameInfo->get(FrameInfoIndex::FrameDeadline);
+    int64_t dequeueBufferDuration = mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration);
+
+    mHintSessionWrapper.updateTargetWorkDuration(frameDeadline - intendedVsync);
+
+    if (didDraw) {
+        int64_t frameStartTime = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime);
+        int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime;
+        int64_t actualDuration = frameDuration -
+                                 (std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
+                                 dequeueBufferDuration - idleDuration;
+        mHintSessionWrapper.reportActualWorkDuration(actualDuration);
+    }
+
+    mLastDequeueBufferDuration = dequeueBufferDuration;
+
     mRenderThread.cacheManager().onFrameCompleted();
-    return didDraw ? std::make_optional(
-                             mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration))
-                   : std::nullopt;
+    return;
 }
 
 void CanvasContext::reportMetricsWithPresentTime() {
@@ -766,6 +792,8 @@
 // Called by choreographer to do an RT-driven animation
 void CanvasContext::doFrame() {
     if (!mRenderPipeline->isSurfaceReady()) return;
+    mIdleDuration =
+            systemTime(SYSTEM_TIME_MONOTONIC) - mRenderThread.timeLord().computeFrameTimeNanos();
     prepareAndDraw(nullptr);
 }
 
@@ -974,6 +1002,14 @@
     }
 }
 
+void CanvasContext::sendLoadResetHint() {
+    mHintSessionWrapper.sendLoadResetHint();
+}
+
+void CanvasContext::setSyncDelayDuration(nsecs_t duration) {
+    mSyncDelayDuration = duration;
+}
+
 } /* namespace renderthread */
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index db96cfb..e875c42 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -16,22 +16,6 @@
 
 #pragma once
 
-#include "DamageAccumulator.h"
-#include "FrameInfo.h"
-#include "FrameInfoVisualizer.h"
-#include "FrameMetricsReporter.h"
-#include "IContextFactory.h"
-#include "IRenderPipeline.h"
-#include "JankTracker.h"
-#include "LayerUpdateQueue.h"
-#include "Lighting.h"
-#include "ReliableSurface.h"
-#include "RenderNode.h"
-#include "renderthread/RenderTask.h"
-#include "renderthread/RenderThread.h"
-#include "utils/RingBuffer.h"
-#include "ColorMode.h"
-
 #include <SkBitmap.h>
 #include <SkRect.h>
 #include <SkSize.h>
@@ -46,6 +30,23 @@
 #include <utility>
 #include <vector>
 
+#include "ColorMode.h"
+#include "DamageAccumulator.h"
+#include "FrameInfo.h"
+#include "FrameInfoVisualizer.h"
+#include "FrameMetricsReporter.h"
+#include "HintSessionWrapper.h"
+#include "IContextFactory.h"
+#include "IRenderPipeline.h"
+#include "JankTracker.h"
+#include "LayerUpdateQueue.h"
+#include "Lighting.h"
+#include "ReliableSurface.h"
+#include "RenderNode.h"
+#include "renderthread/RenderTask.h"
+#include "renderthread/RenderThread.h"
+#include "utils/RingBuffer.h"
+
 namespace android {
 namespace uirenderer {
 
@@ -66,7 +67,8 @@
 class CanvasContext : public IFrameCallback {
 public:
     static CanvasContext* create(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
-                                 IContextFactory* contextFactory);
+                                 IContextFactory* contextFactory, pid_t uiThreadId,
+                                 pid_t renderThreadId);
     virtual ~CanvasContext();
 
     /**
@@ -138,7 +140,7 @@
     bool makeCurrent();
     void prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued, RenderNode* target);
     // Returns the DequeueBufferDuration.
-    std::optional<nsecs_t> draw();
+    void draw();
     void destroy();
 
     // IFrameCallback, Choreographer-driven frame callback entry point
@@ -214,9 +216,14 @@
 
     static CanvasContext* getActiveContext();
 
+    void sendLoadResetHint();
+
+    void setSyncDelayDuration(nsecs_t duration);
+
 private:
     CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
-                  IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline);
+                  IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline,
+                  pid_t uiThreadId, pid_t renderThreadId);
 
     friend class RegisterFrameCallbackTask;
     // TODO: Replace with something better for layer & other GL object
@@ -330,6 +337,11 @@
 
     std::function<bool(int64_t, int64_t, int64_t)> mASurfaceTransactionCallback;
     std::function<void()> mPrepareSurfaceControlForWebviewCallback;
+
+    HintSessionWrapper mHintSessionWrapper;
+    nsecs_t mLastDequeueBufferDuration = 0;
+    nsecs_t mSyncDelayDuration = 0;
+    nsecs_t mIdleDuration = 0;
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index cb30614..1cc82fd 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -16,7 +16,6 @@
 
 #include "DrawFrameTask.h"
 
-#include <dlfcn.h>
 #include <gui/TraceUtils.h>
 #include <utils/Log.h>
 
@@ -28,70 +27,11 @@
 #include "../RenderNode.h"
 #include "CanvasContext.h"
 #include "RenderThread.h"
-#include "thread/CommonPool.h"
-#include "utils/TimeUtils.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_sendHint)(APerformanceHintSession* session, int32_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_sendHint gAPH_sendHintFn = 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_sendHintFn = (APH_sendHint)dlsym(handle_, "APerformanceHint_sendHint");
-    LOG_ALWAYS_FATAL_IF(gAPH_sendHintFn == nullptr,
-                        "Failed to find required symbol APerformanceHint_sendHint!");
-
-    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)
@@ -100,13 +40,11 @@
 
 DrawFrameTask::~DrawFrameTask() {}
 
-void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode,
-                               int32_t uiThreadId, int32_t renderThreadId) {
+void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context,
+                               RenderNode* targetNode) {
     mRenderThread = thread;
     mContext = context;
     mTargetNode = targetNode;
-    mUiThreadId = uiThreadId;
-    mRenderThreadId = renderThreadId;
 }
 
 void DrawFrameTask::pushLayerUpdate(DeferredLayerUpdater* layer) {
@@ -150,11 +88,11 @@
 void DrawFrameTask::run() {
     const int64_t vsyncId = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameTimelineVsyncId)];
     ATRACE_FORMAT("DrawFrames %" PRId64, vsyncId);
-    nsecs_t syncDelayDuration = systemTime(SYSTEM_TIME_MONOTONIC) - mSyncQueued;
+
+    mContext->setSyncDelayDuration(systemTime(SYSTEM_TIME_MONOTONIC) - mSyncQueued);
 
     bool canUnblockUiThread;
     bool canDrawThisFrame;
-    bool didDraw = false;
     {
         TreeInfo info(TreeInfo::MODE_FULL, *mContext);
         info.forceDrawFrame = mForceDrawFrame;
@@ -175,9 +113,6 @@
     std::function<void()> frameCompleteCallback = std::move(mFrameCompleteCallback);
     mFrameCallback = nullptr;
     mFrameCompleteCallback = nullptr;
-    int64_t intendedVsync = mFrameInfo[static_cast<int>(FrameInfoIndex::IntendedVsync)];
-    int64_t frameDeadline = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameDeadline)];
-    int64_t frameStartTime = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameStartTime)];
 
     // From this point on anything in "this" is *UNSAFE TO ACCESS*
     if (canUnblockUiThread) {
@@ -188,18 +123,15 @@
     if (CC_UNLIKELY(frameCallback)) {
         context->enqueueFrameWork([frameCallback, context, syncResult = mSyncResult,
                                    frameNr = context->getFrameNumber()]() {
-            auto frameCommitCallback = std::move(frameCallback(syncResult, frameNr));
+            auto frameCommitCallback = frameCallback(syncResult, frameNr);
             if (frameCommitCallback) {
                 context->addFrameCommitListener(std::move(frameCommitCallback));
             }
         });
     }
 
-    nsecs_t dequeueBufferDuration = 0;
     if (CC_LIKELY(canDrawThisFrame)) {
-        std::optional<nsecs_t> drawResult = context->draw();
-        didDraw = drawResult.has_value();
-        dequeueBufferDuration = drawResult.value_or(0);
+        context->draw();
     } else {
         // Do a flush in case syncFrameState performed any texture uploads. Since we skipped
         // the draw() call, those uploads (or deletes) will end up sitting in the queue.
@@ -218,41 +150,6 @@
     if (!canUnblockUiThread) {
         unblockUiThread();
     }
-
-    if (!mHintSessionWrapper) mHintSessionWrapper.emplace(mUiThreadId, mRenderThreadId);
-
-    constexpr int64_t kSanityCheckLowerBound = 100_us;
-    constexpr int64_t kSanityCheckUpperBound = 10_s;
-    int64_t targetWorkDuration = frameDeadline - intendedVsync;
-    targetWorkDuration = targetWorkDuration * Properties::targetCpuTimePercentage / 100;
-    if (targetWorkDuration > kSanityCheckLowerBound &&
-        targetWorkDuration < kSanityCheckUpperBound &&
-        targetWorkDuration != mLastTargetWorkDuration) {
-        mLastTargetWorkDuration = targetWorkDuration;
-        mHintSessionWrapper->updateTargetWorkDuration(targetWorkDuration);
-    }
-
-    if (didDraw) {
-        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;
-}
-
-void DrawFrameTask::sendLoadResetHint() {
-    if (!(Properties::useHintManager && Properties::isDrawingEnabled())) return;
-    if (!mHintSessionWrapper) mHintSessionWrapper.emplace(mUiThreadId, mRenderThreadId);
-    nsecs_t now = systemTime();
-    if (now - mLastFrameNotification > kResetHintTimeout) {
-        mHintSessionWrapper->sendHint(SessionHint::CPU_LOAD_RESET);
-    }
-    mLastFrameNotification = now;
 }
 
 bool DrawFrameTask::syncFrameState(TreeInfo& info) {
@@ -305,50 +202,6 @@
     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);
-    }
-}
-
-void DrawFrameTask::HintSessionWrapper::sendHint(SessionHint hint) {
-    if (mHintSession && Properties::isDrawingEnabled()) {
-        gAPH_sendHintFn(mHintSession, static_cast<int>(hint));
-    }
-}
-
 } /* namespace renderthread */
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index 7eae41c..fafab24 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -16,7 +16,6 @@
 #ifndef DRAWFRAMETASK_H
 #define DRAWFRAMETASK_H
 
-#include <android/performance_hint.h>
 #include <utils/Condition.h>
 #include <utils/Mutex.h>
 #include <utils/StrongPointer.h>
@@ -28,7 +27,6 @@
 #include "../Rect.h"
 #include "../TreeInfo.h"
 #include "RenderTask.h"
-#include "utils/TimeUtils.h"
 
 namespace android {
 namespace uirenderer {
@@ -62,8 +60,7 @@
     DrawFrameTask();
     virtual ~DrawFrameTask();
 
-    void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode,
-                    int32_t uiThreadId, int32_t renderThreadId);
+    void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode);
     void setContentDrawBounds(int left, int top, int right, int bottom) {
         mContentDrawBounds.set(left, top, right, bottom);
     }
@@ -91,22 +88,7 @@
 
     void forceDrawNextFrame() { mForceDrawFrame = true; }
 
-    void sendLoadResetHint();
-
 private:
-    class HintSessionWrapper {
-    public:
-        HintSessionWrapper(int32_t uiThreadId, int32_t renderThreadId);
-        ~HintSessionWrapper();
-
-        void updateTargetWorkDuration(long targetDurationNanos);
-        void reportActualWorkDuration(long actualDurationNanos);
-        void sendHint(SessionHint hint);
-
-    private:
-        APerformanceHintSession* mHintSession = nullptr;
-    };
-
     void postAndWait();
     bool syncFrameState(TreeInfo& info);
     void unblockUiThread();
@@ -117,8 +99,6 @@
     RenderThread* mRenderThread;
     CanvasContext* mContext;
     RenderNode* mTargetNode = nullptr;
-    int32_t mUiThreadId = -1;
-    int32_t mRenderThreadId = -1;
     Rect mContentDrawBounds;
 
     /*********************************************
@@ -135,13 +115,6 @@
     std::function<void(bool)> mFrameCommitCallback;
     std::function<void()> mFrameCompleteCallback;
 
-    nsecs_t mLastDequeueBufferDuration = 0;
-    nsecs_t mLastTargetWorkDuration = 0;
-    std::optional<HintSessionWrapper> mHintSessionWrapper;
-
-    nsecs_t mLastFrameNotification = 0;
-    nsecs_t kResetHintTimeout = 100_ms;
-
     bool mForceDrawFrame = false;
 };
 
diff --git a/libs/hwui/renderthread/HintSessionWrapper.cpp b/libs/hwui/renderthread/HintSessionWrapper.cpp
new file mode 100644
index 0000000..edacef0
--- /dev/null
+++ b/libs/hwui/renderthread/HintSessionWrapper.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include "HintSessionWrapper.h"
+
+#include <dlfcn.h>
+#include <utils/Log.h>
+
+#include <vector>
+
+#include "../Properties.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_closeSession)(APerformanceHintSession* session);
+typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t);
+typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t);
+typedef void (*APH_sendHint)(APerformanceHintSession* session, int32_t);
+
+bool gAPerformanceHintBindingInitialized = false;
+APH_getManager gAPH_getManagerFn = nullptr;
+APH_createSession gAPH_createSessionFn = nullptr;
+APH_closeSession gAPH_closeSessionFn = nullptr;
+APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr;
+APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr;
+APH_sendHint gAPH_sendHintFn = 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_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession");
+    LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr,
+                        "Failed to find required symbol APerformanceHint_closeSession!");
+
+    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_sendHintFn = (APH_sendHint)dlsym(handle_, "APerformanceHint_sendHint");
+    LOG_ALWAYS_FATAL_IF(gAPH_sendHintFn == nullptr,
+                        "Failed to find required symbol APerformanceHint_sendHint!");
+
+    gAPerformanceHintBindingInitialized = true;
+}
+
+}  // namespace
+
+HintSessionWrapper::HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId)
+        : mUiThreadId(uiThreadId), mRenderThreadId(renderThreadId) {}
+
+HintSessionWrapper::~HintSessionWrapper() {
+    if (mHintSession) {
+        gAPH_closeSessionFn(mHintSession);
+    }
+}
+
+bool HintSessionWrapper::useHintSession() {
+    if (!Properties::useHintManager || !Properties::isDrawingEnabled()) return false;
+    if (mHintSession) return true;
+    // If session does not exist, create it;
+    // this defers session creation until we try to actually use it.
+    if (!mSessionValid) return false;
+    return init();
+}
+
+bool HintSessionWrapper::init() {
+    if (mUiThreadId < 0 || mRenderThreadId < 0) return false;
+
+    // Assume that if we return before the end, it broke
+    mSessionValid = false;
+
+    ensureAPerformanceHintBindingInitialized();
+
+    APerformanceHintManager* manager = gAPH_getManagerFn();
+    if (!manager) return false;
+
+    std::vector<pid_t> tids = CommonPool::getThreadIds();
+    tids.push_back(mUiThreadId);
+    tids.push_back(mRenderThreadId);
+
+    // Use a placeholder target value to initialize,
+    // this will always be replaced elsewhere before it gets used
+    int64_t defaultTargetDurationNanos = 16666667;
+    mHintSession =
+            gAPH_createSessionFn(manager, tids.data(), tids.size(), defaultTargetDurationNanos);
+
+    mSessionValid = !!mHintSession;
+    return mSessionValid;
+}
+
+void HintSessionWrapper::updateTargetWorkDuration(long targetWorkDurationNanos) {
+    if (!useHintSession()) return;
+    targetWorkDurationNanos = targetWorkDurationNanos * Properties::targetCpuTimePercentage / 100;
+    if (targetWorkDurationNanos != mLastTargetWorkDuration &&
+        targetWorkDurationNanos > kSanityCheckLowerBound &&
+        targetWorkDurationNanos < kSanityCheckUpperBound) {
+        mLastTargetWorkDuration = targetWorkDurationNanos;
+        gAPH_updateTargetWorkDurationFn(mHintSession, targetWorkDurationNanos);
+    }
+    mLastFrameNotification = systemTime();
+}
+
+void HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) {
+    if (!useHintSession()) return;
+    if (actualDurationNanos > kSanityCheckLowerBound &&
+        actualDurationNanos < kSanityCheckUpperBound) {
+        gAPH_reportActualWorkDurationFn(mHintSession, actualDurationNanos);
+    }
+}
+
+void HintSessionWrapper::sendLoadResetHint() {
+    if (!useHintSession()) return;
+    nsecs_t now = systemTime();
+    if (now - mLastFrameNotification > kResetHintTimeout) {
+        gAPH_sendHintFn(mHintSession, static_cast<int>(SessionHint::CPU_LOAD_RESET));
+    }
+    mLastFrameNotification = now;
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/HintSessionWrapper.h b/libs/hwui/renderthread/HintSessionWrapper.h
new file mode 100644
index 0000000..fcbc101
--- /dev/null
+++ b/libs/hwui/renderthread/HintSessionWrapper.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#pragma once
+
+#include <android/performance_hint.h>
+
+#include "utils/TimeUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+namespace renderthread {
+
+class HintSessionWrapper {
+public:
+    HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId);
+    ~HintSessionWrapper();
+
+    void updateTargetWorkDuration(long targetDurationNanos);
+    void reportActualWorkDuration(long actualDurationNanos);
+    void sendLoadResetHint();
+
+private:
+    bool useHintSession();
+    bool init();
+    APerformanceHintSession* mHintSession = nullptr;
+
+    nsecs_t mLastFrameNotification = 0;
+    nsecs_t mLastTargetWorkDuration = 0;
+
+    pid_t mUiThreadId;
+    pid_t mRenderThreadId;
+
+    bool mSessionValid = true;
+
+    static constexpr nsecs_t kResetHintTimeout = 100_ms;
+    static constexpr int64_t kSanityCheckLowerBound = 100_us;
+    static constexpr int64_t kSanityCheckUpperBound = 10_s;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 03a2bc9..07f5a78 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -42,11 +42,13 @@
 RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode,
                          IContextFactory* contextFactory)
         : mRenderThread(RenderThread::getInstance()), mContext(nullptr) {
-    mContext = mRenderThread.queue().runSync([&]() -> CanvasContext* {
-        return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory);
+    pid_t uiThreadId = pthread_gettid_np(pthread_self());
+    pid_t renderThreadId = getRenderThreadTid();
+    mContext = mRenderThread.queue().runSync([=, this]() -> CanvasContext* {
+        return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory,
+                                     uiThreadId, renderThreadId);
     });
-    mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode,
-                              pthread_gettid_np(pthread_self()), getRenderThreadTid());
+    mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode);
 }
 
 RenderProxy::~RenderProxy() {
@@ -55,7 +57,7 @@
 
 void RenderProxy::destroyContext() {
     if (mContext) {
-        mDrawFrameTask.setContext(nullptr, nullptr, nullptr, -1, -1);
+        mDrawFrameTask.setContext(nullptr, nullptr, nullptr);
         // 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; });
@@ -237,7 +239,7 @@
 }
 
 void RenderProxy::notifyCallbackPending() {
-    mDrawFrameTask.sendLoadResetHint();
+    mRenderThread.queue().post([this]() { mContext->sendLoadResetHint(); });
 }
 
 void RenderProxy::dumpProfileInfo(int fd, int dumpFlags) {
diff --git a/libs/hwui/tests/unit/CanvasContextTests.cpp b/libs/hwui/tests/unit/CanvasContextTests.cpp
index 1771c35..88420a5 100644
--- a/libs/hwui/tests/unit/CanvasContextTests.cpp
+++ b/libs/hwui/tests/unit/CanvasContextTests.cpp
@@ -36,7 +36,7 @@
     auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(
-            CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+            CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
 
     ASSERT_FALSE(canvasContext->hasSurface());
 
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index ec949b8..67b4d2c 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -334,7 +334,7 @@
             "A");
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(
-            CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
+            CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
     TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
     DamageAccumulator damageAccumulator;
     info.damageAccumulator = &damageAccumulator;
@@ -398,7 +398,7 @@
                                       "A");
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(
-            CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
+            CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
     TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
     DamageAccumulator damageAccumulator;
     info.damageAccumulator = &damageAccumulator;
@@ -518,7 +518,7 @@
     // prepareTree is required to find, which receivers have backward projected nodes
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(
-            CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
+            CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
     TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
     DamageAccumulator damageAccumulator;
     info.damageAccumulator = &damageAccumulator;
@@ -618,7 +618,7 @@
     // prepareTree is required to find, which receivers have backward projected nodes
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(
-            CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
+            CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
     TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
     DamageAccumulator damageAccumulator;
     info.damageAccumulator = &damageAccumulator;
@@ -634,7 +634,7 @@
 static int drawNode(RenderThread& renderThread, const sp<RenderNode>& renderNode) {
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(
-            CanvasContext::create(renderThread, false, renderNode.get(), &contextFactory));
+            CanvasContext::create(renderThread, false, renderNode.get(), &contextFactory, 0, 0));
     TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
     DamageAccumulator damageAccumulator;
     info.damageAccumulator = &damageAccumulator;
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index 61bd646..80796f4 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -274,7 +274,7 @@
     auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(
-            CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+            CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
     TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
     DamageAccumulator damageAccumulator;
     info.damageAccumulator = &damageAccumulator;
@@ -310,7 +310,7 @@
             });
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(
-            CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+            CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
     canvasContext->setSurface(nullptr);
     TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
     DamageAccumulator damageAccumulator;
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 3d5aca4..f825d7c 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -142,7 +142,7 @@
     auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(
-            CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+            CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
     TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get());
     DamageAccumulator damageAccumulator;
     info.damageAccumulator = &damageAccumulator;
@@ -201,7 +201,7 @@
     auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(
-            CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+            CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
 
     // Set up a Surface so that we can position the VectorDrawable offscreen.
     test::TestContext testContext;