Add GPU completion to FrameMetrics (1/3)

- Add SurfaceStatsCallback to TransactionCompletedListener
- Register a callback in RenderProxy to be called when we have
surface stats from SF via the BLAST callback.
- Instead of finishing a frame for frame metrics reporting
immediately, wait until BLAST callback fires, note GPU completion
time and finish frame.
- Expose GPU_COMPLETION in FrameMetrics
- Modify TOTAL_DURATION to also include GPU_COMPLETION

Test: FrameMetricsListenerTest
Fixes: 171046219
Change-Id: I16fa1d80cfc4e7a5527c18fec7e885409f17ee4d
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index f481228..42c6d7c 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -480,6 +480,8 @@
 
     target: {
         android: {
+            header_libs: ["libandroid_headers_private" ],
+
             srcs: [
                 "hwui/AnimatedImageThread.cpp",
                 "pipeline/skia/ATraceMemoryDump.cpp",
@@ -567,6 +569,7 @@
     name: "hwui_test_defaults",
     defaults: ["hwui_defaults"],
     test_suites: ["device-tests"],
+    header_libs: ["libandroid_headers_private"],
     target: {
         android: {
             shared_libs: [
@@ -604,7 +607,6 @@
     shared_libs: [
         "libmemunreachable",
     ],
-
     srcs: [
         "tests/unit/main.cpp",
         "tests/unit/ABitmapTests.cpp",
diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp
index 8b20492..ce9b288 100644
--- a/libs/hwui/FrameInfo.cpp
+++ b/libs/hwui/FrameInfo.cpp
@@ -42,7 +42,7 @@
         "GpuCompleted",
 };
 
-static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 19,
+static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 20,
               "Must update value in FrameMetrics.java#FRAME_STATS_COUNT (and here)");
 
 void FrameInfo::importUiThreadInfo(int64_t* info) {
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index ee7d15a..45a367f 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -55,6 +55,7 @@
     QueueBufferDuration,
 
     GpuCompleted,
+    SwapBuffersCompleted,
 
     // Must be the last value!
     // Also must be kept in sync with FrameMetrics.java#FRAME_STATS_COUNT
@@ -120,6 +121,10 @@
 
     void markSwapBuffers() { set(FrameInfoIndex::SwapBuffers) = systemTime(SYSTEM_TIME_MONOTONIC); }
 
+    void markSwapBuffersCompleted() {
+        set(FrameInfoIndex::SwapBuffersCompleted) = systemTime(SYSTEM_TIME_MONOTONIC);
+    }
+
     void markFrameCompleted() { set(FrameInfoIndex::FrameCompleted) = systemTime(SYSTEM_TIME_MONOTONIC); }
 
     void addFlag(int frameInfoFlag) {
diff --git a/libs/hwui/FrameMetricsReporter.h b/libs/hwui/FrameMetricsReporter.h
index 75b8038..0643e79 100644
--- a/libs/hwui/FrameMetricsReporter.h
+++ b/libs/hwui/FrameMetricsReporter.h
@@ -16,14 +16,16 @@
 
 #pragma once
 
+#include <utils/Mutex.h>
 #include <utils/Log.h>
 #include <utils/RefBase.h>
 
+#include <ui/FatVector.h>
+
 #include "FrameInfo.h"
 #include "FrameMetricsObserver.h"
 
 #include <string.h>
-#include <vector>
 
 namespace android {
 namespace uirenderer {
@@ -32,9 +34,13 @@
 public:
     FrameMetricsReporter() {}
 
-    void addObserver(FrameMetricsObserver* observer) { mObservers.push_back(observer); }
+    void addObserver(FrameMetricsObserver* observer) {
+        std::lock_guard lock(mObserversLock);
+        mObservers.push_back(observer);
+    }
 
     bool removeObserver(FrameMetricsObserver* observer) {
+        std::lock_guard lock(mObserversLock);
         for (size_t i = 0; i < mObservers.size(); i++) {
             if (mObservers[i].get() == observer) {
                 mObservers.erase(mObservers.begin() + i);
@@ -44,16 +50,28 @@
         return false;
     }
 
-    bool hasObservers() { return mObservers.size() > 0; }
+    bool hasObservers() {
+        std::lock_guard lock(mObserversLock);
+        return mObservers.size() > 0;
+    }
 
     void reportFrameMetrics(const int64_t* stats) {
-        for (size_t i = 0; i < mObservers.size(); i++) {
-            mObservers[i]->notify(stats);
+        FatVector<sp<FrameMetricsObserver>, 10> copy;
+        {
+            std::lock_guard lock(mObserversLock);
+            copy.reserve(mObservers.size());
+            for (size_t i = 0; i < mObservers.size(); i++) {
+                copy.push_back(mObservers[i]);
+            }
+        }
+        for (size_t i = 0; i < copy.size(); i++) {
+            copy[i]->notify(stats);
         }
     }
 
 private:
-    std::vector<sp<FrameMetricsObserver> > mObservers;
+    FatVector<sp<FrameMetricsObserver>, 10> mObservers GUARDED_BY(mObserversLock);
+    std::mutex mObserversLock;
 };
 
 }  // namespace uirenderer
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index ccce403..4a2e30d 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -79,7 +79,9 @@
 // and filter it out of the frame profile data
 static FrameInfoIndex sFrameStart = FrameInfoIndex::IntendedVsync;
 
-JankTracker::JankTracker(ProfileDataContainer* globalData) {
+JankTracker::JankTracker(ProfileDataContainer* globalData)
+        : mData(globalData->getDataMutex())
+        , mDataMutex(globalData->getDataMutex()) {
     mGlobalData = globalData;
     nsecs_t frameIntervalNanos = DeviceInfo::getVsyncPeriod();
     nsecs_t sfOffset = DeviceInfo::getCompositorOffset();
@@ -107,6 +109,8 @@
 }
 
 void JankTracker::finishFrame(const FrameInfo& frame) {
+    std::lock_guard lock(mDataMutex);
+
     // Fast-path for jank-free frames
     int64_t totalDuration = frame.duration(sFrameStart, FrameInfoIndex::FrameCompleted);
     if (mDequeueTimeForgiveness && frame[FrameInfoIndex::DequeueBufferDuration] > 500_us) {
@@ -125,7 +129,11 @@
         }
     }
 
-    LOG_ALWAYS_FATAL_IF(totalDuration <= 0, "Impossible totalDuration %" PRId64, totalDuration);
+    LOG_ALWAYS_FATAL_IF(totalDuration <= 0, "Impossible totalDuration %" PRId64 " start=%" PRIi64
+                        " gpuComplete=%" PRIi64, totalDuration,
+                        frame[FrameInfoIndex::IntendedVsync],
+                        frame[FrameInfoIndex::GpuCompleted]);
+
     mData->reportFrame(totalDuration);
     (*mGlobalData)->reportFrame(totalDuration);
 
@@ -188,6 +196,7 @@
 
 void JankTracker::dumpData(int fd, const ProfileDataDescription* description,
                            const ProfileData* data) {
+
     if (description) {
         switch (description->type) {
             case JankTrackerType::Generic:
@@ -227,6 +236,7 @@
 }
 
 void JankTracker::reset() {
+    std::lock_guard lock(mDataMutex);
     mFrames.clear();
     mData->reset();
     (*mGlobalData)->reset();
@@ -235,6 +245,7 @@
 }
 
 void JankTracker::finishGpuDraw(const FrameInfo& frame) {
+    std::lock_guard lock(mDataMutex);
     int64_t totalGPUDrawTime = frame.gpuDrawTime();
     if (totalGPUDrawTime >= 0) {
         mData->reportGPUFrame(totalGPUDrawTime);
diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h
index b3fbbfe..0964553 100644
--- a/libs/hwui/JankTracker.h
+++ b/libs/hwui/JankTracker.h
@@ -84,12 +84,15 @@
     // This is only used if we are in pipelined mode and are using HWC2,
     // otherwise it's 0.
     nsecs_t mDequeueTimeForgiveness = 0;
-    ProfileDataContainer mData;
-    ProfileDataContainer* mGlobalData;
+    ProfileDataContainer mData GUARDED_BY(mDataMutex);
+    ProfileDataContainer* mGlobalData GUARDED_BY(mDataMutex);
     ProfileDataDescription mDescription;
 
     // Ring buffer large enough for 2 seconds worth of frames
     RingBuffer<FrameInfo, 120> mFrames;
+
+    // Mutex to protect acccess to mData and mGlobalData obtained from mGlobalData->getDataMutex
+    std::mutex& mDataMutex;
 };
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/ProfileDataContainer.cpp b/libs/hwui/ProfileDataContainer.cpp
index 38e0f0a..41afc0e 100644
--- a/libs/hwui/ProfileDataContainer.cpp
+++ b/libs/hwui/ProfileDataContainer.cpp
@@ -38,6 +38,8 @@
 }
 
 void ProfileDataContainer::rotateStorage() {
+    std::lock_guard lock(mJankDataMutex);
+
     // If we are mapped we want to stop using the ashmem backend and switch to malloc
     // We are expecting a switchStorageToAshmem call to follow this, but it's not guaranteed
     // If we aren't sitting on top of ashmem then just do a reset() as it's functionally
@@ -50,6 +52,7 @@
 }
 
 void ProfileDataContainer::switchStorageToAshmem(int ashmemfd) {
+    std::lock_guard lock(mJankDataMutex);
     int regionSize = ashmem_get_size_region(ashmemfd);
     if (regionSize < 0) {
         int err = errno;
@@ -70,7 +73,9 @@
         return;
     }
 
-    newData->mergeWith(*mData);
+    if (mData != nullptr) {
+        newData->mergeWith(*mData);
+    }
     freeData();
     mData = newData;
     mIsMapped = true;
diff --git a/libs/hwui/ProfileDataContainer.h b/libs/hwui/ProfileDataContainer.h
index a398694..a61b8dc 100644
--- a/libs/hwui/ProfileDataContainer.h
+++ b/libs/hwui/ProfileDataContainer.h
@@ -19,6 +19,9 @@
 #include "ProfileData.h"
 #include "utils/Macros.h"
 
+#include <mutex>
+#include <utils/Mutex.h>
+
 namespace android {
 namespace uirenderer {
 
@@ -26,7 +29,8 @@
     PREVENT_COPY_AND_ASSIGN(ProfileDataContainer);
 
 public:
-    explicit ProfileDataContainer() {}
+    explicit ProfileDataContainer(std::mutex& jankDataMutex)
+            : mData(new ProfileData()), mJankDataMutex(jankDataMutex) {}
 
     ~ProfileDataContainer() { freeData(); }
 
@@ -36,13 +40,16 @@
     ProfileData* get() { return mData; }
     ProfileData* operator->() { return mData; }
 
+    std::mutex& getDataMutex() { return mJankDataMutex; }
+
 private:
     void freeData();
 
     // By default this will use malloc memory. It may be moved later to ashmem
     // if there is shared space for it and a request comes in to do that.
-    ProfileData* mData = new ProfileData;
+    ProfileData* mData GUARDED_BY(mJankDataMutex);
     bool mIsMapped = false;
+    std::mutex& mJankDataMutex;
 };
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 9543d47..b760db2 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -178,12 +178,16 @@
     if (surfaceControl == mSurfaceControl) return;
 
     auto funcs = mRenderThread.getASurfaceControlFunctions();
+
     if (mSurfaceControl != nullptr) {
+        funcs.unregisterListenerFunc(this, &onSurfaceStatsAvailable);
         funcs.releaseFunc(mSurfaceControl);
     }
     mSurfaceControl = surfaceControl;
+    mExpectSurfaceStats = surfaceControl != nullptr;
     if (mSurfaceControl != nullptr) {
         funcs.acquireFunc(mSurfaceControl);
+        funcs.registerListenerFunc(surfaceControl, this, &onSurfaceStatsAvailable);
     }
 }
 
@@ -332,8 +336,8 @@
     // just keep using the previous frame's structure instead
     if (!wasSkipped(mCurrentFrameInfo)) {
         mCurrentFrameInfo = mJankTracker.startFrame();
-        mLast4FrameInfos.next().first = mCurrentFrameInfo;
     }
+
     mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo);
     mCurrentFrameInfo->set(FrameInfoIndex::SyncQueued) = syncQueued;
     mCurrentFrameInfo->markSyncStart();
@@ -538,17 +542,14 @@
         }
         mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration) = swap.dequeueDuration;
         mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration) = swap.queueDuration;
-        mLast4FrameInfos[-1].second = frameCompleteNr;
         mHaveNewSurface = false;
         mFrameNumber = -1;
     } else {
         mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration) = 0;
         mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration) = 0;
-        mLast4FrameInfos[-1].second = -1;
     }
 
-    // TODO: Use a fence for real completion?
-    mCurrentFrameInfo->markFrameCompleted();
+    mCurrentFrameInfo->markSwapBuffersCompleted();
 
 #if LOG_FRAMETIME_MMA
     float thisFrame = mCurrentFrameInfo->duration(FrameInfoIndex::IssueDrawCommandsStart,
@@ -572,30 +573,73 @@
         mFrameCompleteCallbacks.clear();
     }
 
-    mJankTracker.finishFrame(*mCurrentFrameInfo);
-    if (CC_UNLIKELY(mFrameMetricsReporter.get() != nullptr)) {
-        mFrameMetricsReporter->reportFrameMetrics(mCurrentFrameInfo->data());
-    }
-
-    if (mLast4FrameInfos.size() == mLast4FrameInfos.capacity()) {
-        // By looking 4 frames back, we guarantee all SF stats are available. There are at
-        // most 3 buffers in BufferQueue. Surface object keeps stats for the last 8 frames.
-        FrameInfo* forthBehind = mLast4FrameInfos.front().first;
-        int64_t composedFrameId = mLast4FrameInfos.front().second;
-        nsecs_t acquireTime = -1;
-        if (mNativeSurface) {
-            native_window_get_frame_timestamps(mNativeSurface->getNativeWindow(), composedFrameId,
-                                               nullptr, &acquireTime, nullptr, nullptr, nullptr,
-                                               nullptr, nullptr, nullptr, nullptr);
+    if (requireSwap) {
+        if (mExpectSurfaceStats) {
+            std::lock_guard lock(mLast4FrameInfosMutex);
+            std::pair<FrameInfo*, int64_t>& next = mLast4FrameInfos.next();
+            next.first = mCurrentFrameInfo;
+            next.second = frameCompleteNr;
+        } else {
+            mCurrentFrameInfo->markFrameCompleted();
+            mCurrentFrameInfo->set(FrameInfoIndex::GpuCompleted)
+                    = mCurrentFrameInfo->get(FrameInfoIndex::FrameCompleted);
+            finishFrame(mCurrentFrameInfo);
         }
-        // Ignore default -1, NATIVE_WINDOW_TIMESTAMP_INVALID and NATIVE_WINDOW_TIMESTAMP_PENDING
-        forthBehind->set(FrameInfoIndex::GpuCompleted) = acquireTime > 0 ? acquireTime : -1;
-        mJankTracker.finishGpuDraw(*forthBehind);
     }
 
     mRenderThread.cacheManager().onFrameCompleted();
 }
 
+void CanvasContext::finishFrame(FrameInfo* frameInfo) {
+
+    // TODO (b/169858044): Consolidate this into a single call.
+    mJankTracker.finishFrame(*frameInfo);
+    mJankTracker.finishGpuDraw(*frameInfo);
+
+    // TODO (b/169858044): Move this into JankTracker to adjust deadline when queue is
+    // double-stuffed.
+    if (CC_UNLIKELY(mFrameMetricsReporter.get() != nullptr)) {
+        mFrameMetricsReporter->reportFrameMetrics(frameInfo->data());
+    }
+}
+
+void CanvasContext::onSurfaceStatsAvailable(void* context, ASurfaceControl* control,
+            ASurfaceControlStats* stats) {
+
+    CanvasContext* instance = static_cast<CanvasContext*>(context);
+
+    const ASurfaceControlFunctions& functions =
+            instance->mRenderThread.getASurfaceControlFunctions();
+
+    nsecs_t gpuCompleteTime = functions.getAcquireTimeFunc(stats);
+    uint64_t frameNumber = functions.getFrameNumberFunc(stats);
+
+    FrameInfo* frameInfo = nullptr;
+    {
+        std::lock_guard(instance->mLast4FrameInfosMutex);
+        for (size_t i = 0; i < instance->mLast4FrameInfos.size(); i++) {
+            if (instance->mLast4FrameInfos[i].second == frameNumber) {
+                frameInfo = instance->mLast4FrameInfos[i].first;
+                break;
+            }
+        }
+    }
+    if (frameInfo != nullptr) {
+        if (gpuCompleteTime == -1) {
+            gpuCompleteTime = frameInfo->get(FrameInfoIndex::SwapBuffersCompleted);
+        }
+        if (gpuCompleteTime < frameInfo->get(FrameInfoIndex::SwapBuffers)) {
+            // TODO (b/180488606): Investigate why this can happen for first frames.
+            ALOGW("Impossible GPU complete time swapBuffers=%" PRIi64 " gpuComplete=%" PRIi64,
+                    frameInfo->get(FrameInfoIndex::SwapBuffers), gpuCompleteTime);
+            gpuCompleteTime = frameInfo->get(FrameInfoIndex::SwapBuffersCompleted);
+        }
+        frameInfo->set(FrameInfoIndex::FrameCompleted) = gpuCompleteTime;
+        frameInfo->set(FrameInfoIndex::GpuCompleted) = gpuCompleteTime;
+        instance->finishFrame(frameInfo);
+    }
+}
+
 // Called by choreographer to do an RT-driven animation
 void CanvasContext::doFrame() {
     if (!mRenderPipeline->isSurfaceReady()) return;
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 917b00c..2e7b2f6 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -37,6 +37,7 @@
 #include <SkSize.h>
 #include <cutils/compiler.h>
 #include <utils/Functor.h>
+#include <utils/Mutex.h>
 
 #include <functional>
 #include <future>
@@ -196,6 +197,10 @@
 
     SkISize getNextFrameSize() const;
 
+    // Called when SurfaceStats are available.
+    static void onSurfaceStatsAvailable(void* context, ASurfaceControl* control,
+            ASurfaceControlStats* stats);
+
 private:
     CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
                   IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline);
@@ -212,6 +217,7 @@
     void setupPipelineSurface();
 
     SkRect computeDirtyRect(const Frame& frame, SkRect* dirty);
+    void finishFrame(FrameInfo* frameInfo);
 
     // The same type as Frame.mWidth and Frame.mHeight
     int32_t mLastFrameWidth = 0;
@@ -261,7 +267,12 @@
     std::vector<sp<RenderNode>> mRenderNodes;
 
     FrameInfo* mCurrentFrameInfo = nullptr;
-    RingBuffer<std::pair<FrameInfo*, int64_t>, 4> mLast4FrameInfos;
+
+    // List of frames that are awaiting GPU completion reporting
+    RingBuffer<std::pair<FrameInfo*, int64_t>, 4> mLast4FrameInfos
+            GUARDED_BY(mLast4FrameInfosMutex);
+    std::mutex mLast4FrameInfosMutex;
+
     std::string mName;
     JankTracker mJankTracker;
     FrameInfoVisualizer mProfiler;
@@ -276,6 +287,9 @@
     std::unique_ptr<IRenderPipeline> mRenderPipeline;
 
     std::vector<std::function<void(int64_t)>> mFrameCompleteCallbacks;
+
+    // If set to true, we expect that callbacks into onSurfaceStatsAvailable
+    bool mExpectSurfaceStats = false;
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index e14842f..b9568fc 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -215,6 +215,7 @@
 
 void RenderProxy::dumpProfileInfo(int fd, int dumpFlags) {
     mRenderThread.queue().runSync([&]() {
+        std::lock_guard lock(mRenderThread.getJankDataMutex());
         mContext->profiler().dumpData(fd);
         if (dumpFlags & DumpFlags::FrameStats) {
             mContext->dumpFrames(fd);
@@ -234,6 +235,7 @@
 
 uint32_t RenderProxy::frameTimePercentile(int percentile) {
     return mRenderThread.queue().runSync([&]() -> auto {
+        std::lock_guard lock(mRenderThread.globalProfileData().getDataMutex());
         return mRenderThread.globalProfileData()->findPercentile(percentile);
     });
 }
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 2610186..5dc02e8 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -59,6 +59,26 @@
     releaseFunc = (ASC_release) dlsym(handle_, "ASurfaceControl_release");
     LOG_ALWAYS_FATAL_IF(releaseFunc == nullptr,
             "Failed to find required symbol ASurfaceControl_release!");
+
+    registerListenerFunc = (ASC_registerSurfaceStatsListener) dlsym(handle_,
+            "ASurfaceControl_registerSurfaceStatsListener");
+    LOG_ALWAYS_FATAL_IF(registerListenerFunc == nullptr,
+            "Failed to find required symbol ASurfaceControl_registerSurfaceStatsListener!");
+
+    unregisterListenerFunc = (ASC_unregisterSurfaceStatsListener) dlsym(handle_,
+            "ASurfaceControl_unregisterSurfaceStatsListener");
+    LOG_ALWAYS_FATAL_IF(unregisterListenerFunc == nullptr,
+            "Failed to find required symbol ASurfaceControl_unregisterSurfaceStatsListener!");
+
+    getAcquireTimeFunc = (ASCStats_getAcquireTime) dlsym(handle_,
+            "ASurfaceControlStats_getAcquireTime");
+    LOG_ALWAYS_FATAL_IF(getAcquireTimeFunc == nullptr,
+            "Failed to find required symbol ASurfaceControlStats_getAcquireTime!");
+
+    getFrameNumberFunc = (ASCStats_getFrameNumber) dlsym(handle_,
+            "ASurfaceControlStats_getFrameNumber");
+    LOG_ALWAYS_FATAL_IF(getFrameNumberFunc == nullptr,
+            "Failed to find required symbol ASurfaceControlStats_getFrameNumber!");
 }
 
 void RenderThread::frameCallback(int64_t frameTimeNanos, void* data) {
@@ -146,7 +166,8 @@
         , mFrameCallbackTaskPending(false)
         , mRenderState(nullptr)
         , mEglManager(nullptr)
-        , mFunctorManager(WebViewFunctorManager::instance()) {
+        , mFunctorManager(WebViewFunctorManager::instance())
+        , mGlobalProfileData(mJankDataMutex) {
     Properties::load();
     start("RenderThread");
 }
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index bb7c518..a7d1ba8 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -17,6 +17,7 @@
 #ifndef RENDERTHREAD_H_
 #define RENDERTHREAD_H_
 
+#include <surface_control_private.h>
 #include <GrDirectContext.h>
 #include <SkBitmap.h>
 #include <cutils/compiler.h>
@@ -81,11 +82,22 @@
 typedef void (*ASC_acquire)(ASurfaceControl* control);
 typedef void (*ASC_release)(ASurfaceControl* control);
 
+typedef void (*ASC_registerSurfaceStatsListener)(ASurfaceControl* control, void* context,
+        ASurfaceControl_SurfaceStatsListener func);
+typedef void (*ASC_unregisterSurfaceStatsListener)(void* context,
+                                       ASurfaceControl_SurfaceStatsListener func);
+
+typedef int64_t (*ASCStats_getAcquireTime)(ASurfaceControlStats* stats);
+typedef uint64_t (*ASCStats_getFrameNumber)(ASurfaceControlStats* stats);
+
 struct ASurfaceControlFunctions {
     ASurfaceControlFunctions();
-
     ASC_acquire acquireFunc;
     ASC_release releaseFunc;
+    ASC_registerSurfaceStatsListener registerListenerFunc;
+    ASC_unregisterSurfaceStatsListener unregisterListenerFunc;
+    ASCStats_getAcquireTime getAcquireTimeFunc;
+    ASCStats_getFrameNumber getFrameNumberFunc;
 };
 
 class ChoreographerSource;
@@ -114,6 +126,7 @@
     RenderState& renderState() const { return *mRenderState; }
     EglManager& eglManager() const { return *mEglManager; }
     ProfileDataContainer& globalProfileData() { return mGlobalProfileData; }
+    std::mutex& getJankDataMutex() { return mJankDataMutex; }
     Readback& readback();
 
     GrDirectContext* getGrContext() const { return mGrContext.get(); }
@@ -205,6 +218,7 @@
     sp<VulkanManager> mVkManager;
 
     ASurfaceControlFunctions mASurfaceControlFunctions;
+    std::mutex mJankDataMutex;
 };
 
 } /* namespace renderthread */