OSDN Git Service

android_view_ThreadedRenderer: prevent Null Pointer Exception in createBitmap
[android-x86/frameworks-base.git] / core / jni / android_view_ThreadedRenderer.cpp
index 47132f4..29226c3 100644 (file)
 #define LOG_TAG "ThreadedRenderer"
 
 #include <algorithm>
+#include <atomic>
 
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
 #include "core_jni_helpers.h"
 #include <GraphicsJNI.h>
-#include <ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+
+#include <gui/BufferItemConsumer.h>
+#include <gui/BufferQueue.h>
+#include <gui/Surface.h>
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
-#include <EGL/egl_cache.h>
+#include <private/EGL/cache.h>
 
+#include <utils/Looper.h>
+#include <utils/RefBase.h>
 #include <utils/StrongPointer.h>
+#include <utils/Timers.h>
 #include <android_runtime/android_view_Surface.h>
 #include <system/window.h>
 
-#include "android_view_GraphicBuffer.h"
+#include "android_os_MessageQueue.h"
 
 #include <Animator.h>
 #include <AnimationContext.h>
+#include <FrameInfo.h>
+#include <FrameMetricsObserver.h>
 #include <IContextFactory.h>
-#include <JankTracker.h>
+#include <PropertyValuesAnimatorSet.h>
 #include <RenderNode.h>
 #include <renderthread/CanvasContext.h>
 #include <renderthread/RenderProxy.h>
 #include <renderthread/RenderTask.h>
 #include <renderthread/RenderThread.h>
-#include <Vector.h>
 
 namespace android {
 
 using namespace android::uirenderer;
 using namespace android::uirenderer::renderthread;
 
+struct {
+    jfieldID frameMetrics;
+    jfieldID timingDataBuffer;
+    jfieldID messageQueue;
+    jmethodID callback;
+} gFrameMetricsObserverClassInfo;
+
 static JNIEnv* getenv(JavaVM* vm) {
     JNIEnv* env;
     if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
@@ -68,7 +84,7 @@ public:
 
 class InvokeAnimationListeners : public MessageHandler {
 public:
-    InvokeAnimationListeners(std::vector<OnFinishedEvent>& events) {
+    explicit InvokeAnimationListeners(std::vector<OnFinishedEvent>& events) {
         mOnFinishedEvents.swap(events);
     }
 
@@ -85,6 +101,31 @@ private:
     std::vector<OnFinishedEvent> mOnFinishedEvents;
 };
 
+class FinishAndInvokeListener : public MessageHandler {
+public:
+    explicit FinishAndInvokeListener(PropertyValuesAnimatorSet* anim)
+            : mAnimator(anim) {
+        mListener = anim->getOneShotListener();
+        mRequestId = anim->getRequestId();
+    }
+
+    virtual void handleMessage(const Message& message) {
+        if (mAnimator->getRequestId() == mRequestId) {
+            // Request Id has not changed, meaning there's no animation lifecyle change since the
+            // message is posted, so go ahead and call finish to make sure the PlayState is properly
+            // updated. This is needed because before the next frame comes in from UI thread to
+            // trigger an animation update, there could be reverse/cancel etc. So we need to update
+            // the playstate in time to ensure all the subsequent events get chained properly.
+            mAnimator->end();
+        }
+        mListener->onAnimationFinished(nullptr);
+    }
+private:
+    sp<PropertyValuesAnimatorSet> mAnimator;
+    sp<AnimationListener> mListener;
+    uint32_t mRequestId;
+};
+
 class RenderingException : public MessageHandler {
 public:
     RenderingException(JavaVM* vm, const std::string& message)
@@ -108,7 +149,7 @@ private:
 
 class RootRenderNode : public RenderNode, ErrorHandler {
 public:
-    RootRenderNode(JNIEnv* env) : RenderNode() {
+    explicit RootRenderNode(JNIEnv* env) : RenderNode() {
         mLooper = Looper::getForThread();
         LOG_ALWAYS_FATAL_IF(!mLooper.get(),
                 "Must create RootRenderNode on a thread with a looper!");
@@ -117,24 +158,80 @@ public:
 
     virtual ~RootRenderNode() {}
 
-    virtual void onError(const std::string& message) {
+    virtual void onError(const std::string& message) override {
         mLooper->sendMessage(new RenderingException(mVm, message), 0);
     }
 
-    virtual void prepareTree(TreeInfo& info) {
+    virtual void prepareTree(TreeInfo& info) override {
         info.errorHandler = this;
+
+        for (auto& anim : mRunningVDAnimators) {
+            // Assume that the property change in VD from the animators will not be consumed. Mark
+            // otherwise if the VDs are found in the display list tree. For VDs that are not in
+            // the display list tree, we stop providing animation pulses by 1) removing them from
+            // the animation list, 2) post a delayed message to end them at end time so their
+            // listeners can receive the corresponding callbacks.
+            anim->getVectorDrawable()->setPropertyChangeWillBeConsumed(false);
+            // Mark the VD dirty so it will damage itself during prepareTree.
+            anim->getVectorDrawable()->markDirty();
+        }
+        if (info.mode == TreeInfo::MODE_FULL) {
+            for (auto &anim : mPausedVDAnimators) {
+                anim->getVectorDrawable()->setPropertyChangeWillBeConsumed(false);
+                anim->getVectorDrawable()->markDirty();
+            }
+        }
+        // TODO: This is hacky
+        info.updateWindowPositions = true;
         RenderNode::prepareTree(info);
-        info.errorHandler = NULL;
+        info.updateWindowPositions = false;
+        info.errorHandler = nullptr;
     }
 
     void sendMessage(const sp<MessageHandler>& handler) {
         mLooper->sendMessage(handler, 0);
     }
 
+    void sendMessageDelayed(const sp<MessageHandler>& handler, nsecs_t delayInMs) {
+        mLooper->sendMessageDelayed(ms2ns(delayInMs), handler, 0);
+    }
+
     void attachAnimatingNode(RenderNode* animatingNode) {
         mPendingAnimatingRenderNodes.push_back(animatingNode);
     }
 
+    void attachPendingVectorDrawableAnimators() {
+        mRunningVDAnimators.insert(mPendingVectorDrawableAnimators.begin(),
+                mPendingVectorDrawableAnimators.end());
+        mPendingVectorDrawableAnimators.clear();
+    }
+
+    void detachAnimators() {
+        // Remove animators from the list and post a delayed message in future to end the animator
+        // For infinite animators, remove the listener so we no longer hold a global ref to the AVD
+        // java object, and therefore the AVD objects in both native and Java can be properly
+        // released.
+        for (auto& anim : mRunningVDAnimators) {
+            detachVectorDrawableAnimator(anim.get());
+            anim->clearOneShotListener();
+        }
+        for (auto& anim : mPausedVDAnimators) {
+            anim->clearOneShotListener();
+        }
+        mRunningVDAnimators.clear();
+        mPausedVDAnimators.clear();
+    }
+
+    // Move all the animators to the paused list, and send a delayed message to notify the finished
+    // listener.
+    void pauseAnimators() {
+        mPausedVDAnimators.insert(mRunningVDAnimators.begin(), mRunningVDAnimators.end());
+        for (auto& anim : mRunningVDAnimators) {
+            detachVectorDrawableAnimator(anim.get());
+        }
+        mRunningVDAnimators.clear();
+    }
+
     void doAttachAnimatingNodes(AnimationContext* context) {
         for (size_t i = 0; i < mPendingAnimatingRenderNodes.size(); i++) {
             RenderNode* node = mPendingAnimatingRenderNodes[i].get();
@@ -143,10 +240,144 @@ public:
         mPendingAnimatingRenderNodes.clear();
     }
 
+    // Run VectorDrawable animators after prepareTree.
+    void runVectorDrawableAnimators(AnimationContext* context, TreeInfo& info) {
+        // Push staging.
+        if (info.mode == TreeInfo::MODE_FULL) {
+            pushStagingVectorDrawableAnimators(context);
+        }
+
+        // Run the animators in the running list.
+        for (auto it = mRunningVDAnimators.begin(); it != mRunningVDAnimators.end();) {
+            if ((*it)->animate(*context)) {
+                it = mRunningVDAnimators.erase(it);
+            } else {
+                it++;
+            }
+        }
+
+        // Run the animators in paused list during full sync.
+        if (info.mode == TreeInfo::MODE_FULL) {
+            // During full sync we also need to pulse paused animators, in case their targets
+            // have been added back to the display list. All the animators that passed the
+            // scheduled finish time will be removed from the paused list.
+            for (auto it = mPausedVDAnimators.begin(); it != mPausedVDAnimators.end();) {
+                if ((*it)->animate(*context)) {
+                    // Animator has finished, remove from the list.
+                    it = mPausedVDAnimators.erase(it);
+                } else {
+                    it++;
+                }
+            }
+        }
+
+        // Move the animators with a target not in DisplayList to paused list.
+        for (auto it = mRunningVDAnimators.begin(); it != mRunningVDAnimators.end();) {
+            if (!(*it)->getVectorDrawable()->getPropertyChangeWillBeConsumed()) {
+                // Vector Drawable is not in the display list, we should remove this animator from
+                // the list, put it in the paused list, and post a delayed message to end the
+                // animator.
+                detachVectorDrawableAnimator(it->get());
+                mPausedVDAnimators.insert(*it);
+                it = mRunningVDAnimators.erase(it);
+            } else {
+                it++;
+            }
+        }
+
+        // Move the animators with a target in DisplayList from paused list to running list, and
+        // trim paused list.
+        if (info.mode == TreeInfo::MODE_FULL) {
+            // Check whether any paused animator's target is back in Display List. If so, put the
+            // animator back in the running list.
+            for (auto it = mPausedVDAnimators.begin(); it != mPausedVDAnimators.end();) {
+                if ((*it)->getVectorDrawable()->getPropertyChangeWillBeConsumed()) {
+                    mRunningVDAnimators.insert(*it);
+                    it = mPausedVDAnimators.erase(it);
+                } else {
+                    it++;
+                }
+            }
+            // Trim paused VD animators at full sync, so that when Java loses reference to an
+            // animator, we know we won't be requested to animate it any more, then we remove such
+            // animators from the paused list so they can be properly freed. We also remove the
+            // animators from paused list when the time elapsed since start has exceeded duration.
+            trimPausedVDAnimators(context);
+        }
+
+        info.out.hasAnimations |= !mRunningVDAnimators.empty();
+    }
+
+    void trimPausedVDAnimators(AnimationContext* context) {
+        // Trim paused vector drawable animator list.
+        for (auto it = mPausedVDAnimators.begin(); it != mPausedVDAnimators.end();) {
+            // Remove paused VD animator if no one else is referencing it. Note that animators that
+            // have passed scheduled finish time are removed from list when they are being pulsed
+            // before prepare tree.
+            // TODO: this is a bit hacky, need to figure out a better way to track when the paused
+            // animators should be freed.
+            if ((*it)->getStrongCount() == 1) {
+                it = mPausedVDAnimators.erase(it);
+            } else {
+                it++;
+            }
+        }
+    }
+
+    void pushStagingVectorDrawableAnimators(AnimationContext* context) {
+        for (auto& anim : mRunningVDAnimators) {
+            anim->pushStaging(*context);
+        }
+    }
+
+    void destroy() {
+        for (auto& renderNode : mPendingAnimatingRenderNodes) {
+            renderNode->animators().endAllStagingAnimators();
+        }
+        mPendingAnimatingRenderNodes.clear();
+        mPendingVectorDrawableAnimators.clear();
+    }
+
+    void addVectorDrawableAnimator(PropertyValuesAnimatorSet* anim) {
+        mPendingVectorDrawableAnimators.insert(anim);
+    }
+
 private:
     sp<Looper> mLooper;
     JavaVM* mVm;
     std::vector< sp<RenderNode> > mPendingAnimatingRenderNodes;
+    std::set< sp<PropertyValuesAnimatorSet> > mPendingVectorDrawableAnimators;
+    std::set< sp<PropertyValuesAnimatorSet> > mRunningVDAnimators;
+    // mPausedVDAnimators stores a list of animators that have not yet passed the finish time, but
+    // their VectorDrawable targets are no longer in the DisplayList. We skip these animators when
+    // render thread runs animators independent of UI thread (i.e. RT_ONLY mode). These animators
+    // need to be re-activated once their VD target is added back into DisplayList. Since that could
+    // only happen when we do a full sync, we need to make sure to pulse these paused animators at
+    // full sync. If any animator's VD target is found in DisplayList during a full sync, we move
+    // the animator back to the running list.
+    std::set< sp<PropertyValuesAnimatorSet> > mPausedVDAnimators;
+    void detachVectorDrawableAnimator(PropertyValuesAnimatorSet* anim) {
+        if (anim->isInfinite() || !anim->isRunning()) {
+            // Do not need to post anything if the animation is infinite (i.e. no meaningful
+            // end listener action), or if the animation has already ended.
+            return;
+        }
+        nsecs_t remainingTimeInMs = anim->getRemainingPlayTime();
+        // Post a delayed onFinished event that is scheduled to be handled when the animator ends.
+        if (anim->getOneShotListener()) {
+            // VectorDrawable's oneshot listener is updated when there are user triggered animation
+            // lifecycle changes, such as start(), end(), etc. By using checking and clearing
+            // one shot listener, we ensure the same end listener event gets posted only once.
+            // Therefore no duplicates. Another benefit of using one shot listener is that no
+            // removal is necessary: the end time of animation will not change unless triggered by
+            // user events, in which case the already posted listener's id will become stale, and
+            // the onFinished callback will then be ignored.
+            sp<FinishAndInvokeListener> message
+                    = new FinishAndInvokeListener(anim);
+            sendMessageDelayed(message, remainingTimeInMs);
+            anim->clearOneShotListener();
+        }
+    }
 };
 
 class AnimationContextBridge : public AnimationContext {
@@ -162,6 +393,7 @@ public:
     virtual void startFrame(TreeInfo::TraversalMode mode) {
         if (mode == TreeInfo::MODE_FULL) {
             mRootNode->doAttachAnimatingNodes(this);
+            mRootNode->attachPendingVectorDrawableAnimators();
         }
         AnimationContext::startFrame(mode);
     }
@@ -169,9 +401,14 @@ public:
     // Runs any animations still left in mCurrentFrameAnimations
     virtual void runRemainingAnimations(TreeInfo& info) {
         AnimationContext::runRemainingAnimations(info);
+        mRootNode->runVectorDrawableAnimators(this, info);
         postOnFinishedEvents();
     }
 
+    virtual void pauseAnimators() override {
+        mRootNode->pauseAnimators();
+    }
+
     virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener) {
         OnFinishedEvent event(animator, listener);
         mOnFinishedEvents.push_back(event);
@@ -179,6 +416,7 @@ public:
 
     virtual void destroy() {
         AnimationContext::destroy();
+        mRootNode->detachAnimators();
         postOnFinishedEvents();
     }
 
@@ -197,7 +435,7 @@ private:
 
 class ContextFactoryImpl : public IContextFactory {
 public:
-    ContextFactoryImpl(RootRenderNode* rootNode) : mRootNode(rootNode) {}
+    explicit ContextFactoryImpl(RootRenderNode* rootNode) : mRootNode(rootNode) {}
 
     virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) {
         return new AnimationContextBridge(clock, mRootNode);
@@ -207,25 +445,155 @@ private:
     RootRenderNode* mRootNode;
 };
 
-static void android_view_ThreadedRenderer_setAtlas(JNIEnv* env, jobject clazz,
-        jlong proxyPtr, jobject graphicBuffer, jlongArray atlasMapArray) {
-    sp<GraphicBuffer> buffer = graphicBufferForJavaObject(env, graphicBuffer);
-    jsize len = env->GetArrayLength(atlasMapArray);
-    if (len <= 0) {
-        ALOGW("Failed to initialize atlas, invalid map length: %d", len);
-        return;
+class ObserverProxy;
+
+class NotifyHandler : public MessageHandler {
+public:
+    NotifyHandler(JavaVM* vm, ObserverProxy* observer) : mVm(vm), mObserver(observer) {}
+
+    virtual void handleMessage(const Message& message);
+
+private:
+    JavaVM* const mVm;
+    ObserverProxy* const mObserver;
+};
+
+static jlongArray get_metrics_buffer(JNIEnv* env, jobject observer) {
+    jobject frameMetrics = env->GetObjectField(
+            observer, gFrameMetricsObserverClassInfo.frameMetrics);
+    LOG_ALWAYS_FATAL_IF(frameMetrics == nullptr, "unable to retrieve data sink object");
+    jobject buffer = env->GetObjectField(
+            frameMetrics, gFrameMetricsObserverClassInfo.timingDataBuffer);
+    LOG_ALWAYS_FATAL_IF(buffer == nullptr, "unable to retrieve data sink buffer");
+    return reinterpret_cast<jlongArray>(buffer);
+}
+
+/*
+ * Implements JNI layer for hwui frame metrics reporting.
+ */
+class ObserverProxy : public FrameMetricsObserver {
+public:
+    ObserverProxy(JavaVM *vm, jobject observer) : mVm(vm) {
+        JNIEnv* env = getenv(mVm);
+
+        mObserverWeak = env->NewWeakGlobalRef(observer);
+        LOG_ALWAYS_FATAL_IF(mObserverWeak == nullptr,
+                "unable to create frame stats observer reference");
+
+        jlongArray buffer = get_metrics_buffer(env, observer);
+        jsize bufferSize = env->GetArrayLength(reinterpret_cast<jarray>(buffer));
+        LOG_ALWAYS_FATAL_IF(bufferSize != kBufferSize,
+                "Mismatched Java/Native FrameMetrics data format.");
+
+        jobject messageQueueLocal = env->GetObjectField(
+                observer, gFrameMetricsObserverClassInfo.messageQueue);
+        mMessageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueLocal);
+        LOG_ALWAYS_FATAL_IF(mMessageQueue == nullptr, "message queue not available");
+
+        mMessageHandler = new NotifyHandler(mVm, this);
+        LOG_ALWAYS_FATAL_IF(mMessageHandler == nullptr,
+                "OOM: unable to allocate NotifyHandler");
+    }
+
+    ~ObserverProxy() {
+        JNIEnv* env = getenv(mVm);
+        env->DeleteWeakGlobalRef(mObserverWeak);
     }
-    int64_t* map = new int64_t[len];
-    env->GetLongArrayRegion(atlasMapArray, 0, len, map);
 
-    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    proxy->setTextureAtlas(buffer, map, len);
+    jweak getObserverReference() {
+        return mObserverWeak;
+    }
+
+    bool getNextBuffer(JNIEnv* env, jlongArray sink, int* dropCount) {
+        FrameMetricsNotification& elem = mRingBuffer[mNextInQueue];
+
+        if (elem.hasData.load()) {
+            env->SetLongArrayRegion(sink, 0, kBufferSize, elem.buffer);
+            *dropCount = elem.dropCount;
+            mNextInQueue = (mNextInQueue + 1) % kRingSize;
+            elem.hasData = false;
+            return true;
+        }
+
+        return false;
+    }
+
+    virtual void notify(const int64_t* stats) {
+        FrameMetricsNotification& elem = mRingBuffer[mNextFree];
+
+        if (!elem.hasData.load()) {
+            memcpy(elem.buffer, stats, kBufferSize * sizeof(stats[0]));
+
+            elem.dropCount = mDroppedReports;
+            mDroppedReports = 0;
+
+            incStrong(nullptr);
+            mNextFree = (mNextFree + 1) % kRingSize;
+            elem.hasData = true;
+
+            mMessageQueue->getLooper()->sendMessage(mMessageHandler, mMessage);
+        } else {
+            mDroppedReports++;
+        }
+    }
+
+private:
+    static const int kBufferSize = static_cast<int>(FrameInfoIndex::NumIndexes);
+    static constexpr int kRingSize = 3;
+
+    class FrameMetricsNotification {
+    public:
+        FrameMetricsNotification() : hasData(false) {}
+
+        std::atomic_bool hasData;
+        int64_t buffer[kBufferSize];
+        int dropCount = 0;
+    };
+
+    JavaVM* const mVm;
+    jweak mObserverWeak;
+
+    sp<MessageQueue> mMessageQueue;
+    sp<NotifyHandler> mMessageHandler;
+    Message mMessage;
+
+    int mNextFree = 0;
+    int mNextInQueue = 0;
+    FrameMetricsNotification mRingBuffer[kRingSize];
+
+    int mDroppedReports = 0;
+};
+
+void NotifyHandler::handleMessage(const Message& message) {
+    JNIEnv* env = getenv(mVm);
+
+    jobject target = env->NewLocalRef(mObserver->getObserverReference());
+
+    if (target != nullptr) {
+        jlongArray javaBuffer = get_metrics_buffer(env, target);
+        int dropCount = 0;
+        while (mObserver->getNextBuffer(env, javaBuffer, &dropCount)) {
+            env->CallVoidMethod(target, gFrameMetricsObserverClassInfo.callback, dropCount);
+        }
+        env->DeleteLocalRef(target);
+    }
+
+    mObserver->decStrong(nullptr);
+}
+
+static void android_view_ThreadedRenderer_rotateProcessStatsBuffer(JNIEnv* env, jobject clazz) {
+    RenderProxy::rotateProcessStatsBuffer();
 }
 
 static void android_view_ThreadedRenderer_setProcessStatsBuffer(JNIEnv* env, jobject clazz,
-        jlong proxyPtr, jint fd) {
+        jint fd) {
+    RenderProxy::setProcessStatsBuffer(fd);
+}
+
+static jint android_view_ThreadedRenderer_getRenderThreadTid(JNIEnv* env, jobject clazz,
+        jlong proxyPtr) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    proxy->setProcessStatsBuffer(fd);
+    return proxy->getRenderThreadTid();
 }
 
 static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) {
@@ -262,37 +630,43 @@ static void android_view_ThreadedRenderer_setName(JNIEnv* env, jobject clazz,
     env->ReleaseStringUTFChars(jname, name);
 }
 
-static jboolean android_view_ThreadedRenderer_initialize(JNIEnv* env, jobject clazz,
+static void android_view_ThreadedRenderer_initialize(JNIEnv* env, jobject clazz,
         jlong proxyPtr, jobject jsurface) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    sp<ANativeWindow> window = android_view_Surface_getNativeWindow(env, jsurface);
-    return proxy->initialize(window);
+    sp<Surface> surface = android_view_Surface_getSurface(env, jsurface);
+    proxy->initialize(surface);
 }
 
 static void android_view_ThreadedRenderer_updateSurface(JNIEnv* env, jobject clazz,
         jlong proxyPtr, jobject jsurface) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    sp<ANativeWindow> window;
+    sp<Surface> surface;
     if (jsurface) {
-        window = android_view_Surface_getNativeWindow(env, jsurface);
+        surface = android_view_Surface_getSurface(env, jsurface);
     }
-    proxy->updateSurface(window);
+    proxy->updateSurface(surface);
 }
 
 static jboolean android_view_ThreadedRenderer_pauseSurface(JNIEnv* env, jobject clazz,
         jlong proxyPtr, jobject jsurface) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    sp<ANativeWindow> window;
+    sp<Surface> surface;
     if (jsurface) {
-        window = android_view_Surface_getNativeWindow(env, jsurface);
+        surface = android_view_Surface_getSurface(env, jsurface);
     }
-    return proxy->pauseSurface(window);
+    return proxy->pauseSurface(surface);
+}
+
+static void android_view_ThreadedRenderer_setStopped(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jboolean stopped) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->setStopped(stopped);
 }
 
 static void android_view_ThreadedRenderer_setup(JNIEnv* env, jobject clazz, jlong proxyPtr,
-        jint width, jint height, jfloat lightRadius, jint ambientShadowAlpha, jint spotShadowAlpha) {
+        jfloat lightRadius, jint ambientShadowAlpha, jint spotShadowAlpha) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    proxy->setup(width, height, lightRadius, ambientShadowAlpha, spotShadowAlpha);
+    proxy->setup(lightRadius, ambientShadowAlpha, spotShadowAlpha);
 }
 
 static void android_view_ThreadedRenderer_setLightCenter(JNIEnv* env, jobject clazz,
@@ -307,6 +681,12 @@ static void android_view_ThreadedRenderer_setOpaque(JNIEnv* env, jobject clazz,
     proxy->setOpaque(opaque);
 }
 
+static void android_view_ThreadedRenderer_setWideGamut(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jboolean wideGamut) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->setWideGamut(wideGamut);
+}
+
 static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
         jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize) {
     LOG_ALWAYS_FATAL_IF(frameInfoSize != UI_THREAD_FRAME_INFO_SIZE,
@@ -318,7 +698,9 @@ static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject c
 }
 
 static void android_view_ThreadedRenderer_destroy(JNIEnv* env, jobject clazz,
-        jlong proxyPtr) {
+        jlong proxyPtr, jlong rootNodePtr) {
+    RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootNodePtr);
+    rootRenderNode->destroy();
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
     proxy->destroy();
 }
@@ -330,6 +712,13 @@ static void android_view_ThreadedRenderer_registerAnimatingRenderNode(JNIEnv* en
     rootRenderNode->attachAnimatingNode(animatingNode);
 }
 
+static void android_view_ThreadedRenderer_registerVectorDrawableAnimator(JNIEnv* env, jobject clazz,
+        jlong rootNodePtr, jlong animatorPtr) {
+    RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootNodePtr);
+    PropertyValuesAnimatorSet* animator = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorPtr);
+    rootRenderNode->addVectorDrawableAnimator(animator);
+}
+
 static void android_view_ThreadedRenderer_invokeFunctor(JNIEnv* env, jobject clazz,
         jlong functorPtr, jboolean waitForCompletion) {
     Functor* functor = reinterpret_cast<Functor*>(functorPtr);
@@ -418,6 +807,12 @@ static void android_view_ThreadedRenderer_notifyFramePending(JNIEnv* env, jobjec
     proxy->notifyFramePending();
 }
 
+static void android_view_ThreadedRenderer_serializeDisplayListTree(JNIEnv* env, jobject clazz,
+        jlong proxyPtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->serializeDisplayListTree();
+}
+
 static void android_view_ThreadedRenderer_dumpProfileInfo(JNIEnv* env, jobject clazz,
         jlong proxyPtr, jobject javaFileDescriptor, jint dumpFlags) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
@@ -425,15 +820,148 @@ static void android_view_ThreadedRenderer_dumpProfileInfo(JNIEnv* env, jobject c
     proxy->dumpProfileInfo(fd, dumpFlags);
 }
 
-static void android_view_ThreadedRenderer_dumpProfileData(JNIEnv* env, jobject clazz,
-        jbyteArray jdata, jobject javaFileDescriptor) {
-    int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
-    ScopedByteArrayRO buffer(env, jdata);
-    if (buffer.get()) {
-        JankTracker::dumpBuffer(buffer.get(), buffer.size(), fd);
+static void android_view_ThreadedRenderer_addRenderNode(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jlong renderNodePtr, jboolean placeFront) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    proxy->addRenderNode(renderNode, placeFront);
+}
+
+static void android_view_ThreadedRenderer_removeRenderNode(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jlong renderNodePtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    proxy->removeRenderNode(renderNode);
+}
+
+static void android_view_ThreadedRendererd_drawRenderNode(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jlong renderNodePtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    proxy->drawRenderNode(renderNode);
+}
+
+static void android_view_ThreadedRenderer_setContentDrawBounds(JNIEnv* env,
+        jobject clazz, jlong proxyPtr, jint left, jint top, jint right, jint bottom) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->setContentDrawBounds(left, top, right, bottom);
+}
+
+static jint android_view_ThreadedRenderer_copySurfaceInto(JNIEnv* env,
+        jobject clazz, jobject jsurface, jint left, jint top,
+        jint right, jint bottom, jobject jbitmap) {
+    SkBitmap bitmap;
+    GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
+    sp<Surface> surface = android_view_Surface_getSurface(env, jsurface);
+    return RenderProxy::copySurfaceInto(surface, left, top, right, bottom, &bitmap);
+}
+
+class ContextFactory : public IContextFactory {
+public:
+    virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) {
+        return new AnimationContext(clock);
+    }
+};
+
+static jobject android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode(JNIEnv* env,
+        jobject clazz, jlong renderNodePtr, jint jwidth, jint jheight) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    if (jwidth <= 0 || jheight <= 0) {
+        ALOGW("Invalid width %d or height %d", jwidth, jheight);
+        return nullptr;
+    }
+
+    uint32_t width = jwidth;
+    uint32_t height = jheight;
+
+    // Create a Surface wired up to a BufferItemConsumer
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferConsumer> rawConsumer;
+    BufferQueue::createBufferQueue(&producer, &rawConsumer);
+    // We only need 1 buffer but some drivers have bugs so workaround it by setting max count to 2
+    rawConsumer->setMaxBufferCount(2);
+    sp<BufferItemConsumer> consumer = new BufferItemConsumer(rawConsumer,
+            GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_NEVER);
+    consumer->setDefaultBufferSize(width, height);
+    sp<Surface> surface = new Surface(producer);
+
+    // Render into the surface
+    {
+        ContextFactory factory;
+        RenderProxy proxy{true, renderNode, &factory};
+        proxy.loadSystemProperties();
+        proxy.setSwapBehavior(SwapBehavior::kSwap_discardBuffer);
+        proxy.initialize(surface);
+        // Shadows can't be used via this interface, so just set the light source
+        // to all 0s.
+        proxy.setup(0, 0, 0);
+        proxy.setLightCenter((Vector3){0, 0, 0});
+        nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
+        UiFrameInfoBuilder(proxy.frameInfo())
+                .setVsync(vsync, vsync)
+                .addFlag(FrameInfoFlags::SurfaceCanvas);
+        proxy.syncAndDrawFrame();
+    }
+
+    // Yank out the GraphicBuffer
+    BufferItem bufferItem;
+    status_t err;
+    if ((err = consumer->acquireBuffer(&bufferItem, 0, true)) != OK) {
+        ALOGW("Failed to acquireBuffer, error %d (%s)", err, strerror(-err));
+        return nullptr;
+    }
+    sp<GraphicBuffer> buffer = bufferItem.mGraphicBuffer;
+    // We don't really care if this fails or not since we're just going to destroy this anyway
+    consumer->releaseBuffer(bufferItem);
+    if (!buffer.get()) {
+        ALOGW("GraphicBuffer is null?");
+        return nullptr;
+    }
+    if (buffer->getWidth() != width || buffer->getHeight() != height) {
+        ALOGW("GraphicBuffer size mismatch, got %dx%d expected %dx%d",
+                buffer->getWidth(), buffer->getHeight(), width, height);
+        // Continue I guess?
+    }
+    sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer);
+    if (!bitmap.get()) {
+        ALOGW("Failed to create hardware bitmap from graphic buffer");
+        return nullptr;
+    }
+    return createBitmap(env, bitmap.release(), android::bitmap::kBitmapCreateFlag_Mutable);
+}
+
+static void android_view_ThreadedRenderer_disableVsync(JNIEnv*, jclass) {
+    RenderProxy::disableVsync();
+}
+
+// ----------------------------------------------------------------------------
+// FrameMetricsObserver
+// ----------------------------------------------------------------------------
+
+static jlong android_view_ThreadedRenderer_addFrameMetricsObserver(JNIEnv* env,
+        jclass clazz, jlong proxyPtr, jobject fso) {
+    JavaVM* vm = nullptr;
+    if (env->GetJavaVM(&vm) != JNI_OK) {
+        LOG_ALWAYS_FATAL("Unable to get Java VM");
+        return 0;
     }
+
+    renderthread::RenderProxy* renderProxy =
+            reinterpret_cast<renderthread::RenderProxy*>(proxyPtr);
+
+    FrameMetricsObserver* observer = new ObserverProxy(vm, fso);
+    renderProxy->addFrameMetricsObserver(observer);
+    return reinterpret_cast<jlong>(observer);
 }
 
+static void android_view_ThreadedRenderer_removeFrameMetricsObserver(JNIEnv* env, jclass clazz,
+        jlong proxyPtr, jlong observerPtr) {
+    FrameMetricsObserver* observer = reinterpret_cast<FrameMetricsObserver*>(observerPtr);
+    renderthread::RenderProxy* renderProxy =
+            reinterpret_cast<renderthread::RenderProxy*>(proxyPtr);
+
+    renderProxy->removeFrameMetricsObserver(observer);
+}
 
 // ----------------------------------------------------------------------------
 // Shaders
@@ -441,9 +969,8 @@ static void android_view_ThreadedRenderer_dumpProfileData(JNIEnv* env, jobject c
 
 static void android_view_ThreadedRenderer_setupShadersDiskCache(JNIEnv* env, jobject clazz,
         jstring diskCachePath) {
-
     const char* cacheArray = env->GetStringUTFChars(diskCachePath, NULL);
-    egl_cache_t::get()->setCacheFilename(cacheArray);
+    android::egl_set_cache_filename(cacheArray);
     env->ReleaseStringUTFChars(diskCachePath, cacheArray);
 }
 
@@ -453,23 +980,27 @@ static void android_view_ThreadedRenderer_setupShadersDiskCache(JNIEnv* env, job
 
 const char* const kClassPathName = "android/view/ThreadedRenderer";
 
-static JNINativeMethod gMethods[] = {
-    { "nSetAtlas", "(JLandroid/view/GraphicBuffer;[J)V",   (void*) android_view_ThreadedRenderer_setAtlas },
-    { "nSetProcessStatsBuffer", "(JI)V", (void*) android_view_ThreadedRenderer_setProcessStatsBuffer },
+static const JNINativeMethod gMethods[] = {
+    { "nRotateProcessStatsBuffer", "()V", (void*) android_view_ThreadedRenderer_rotateProcessStatsBuffer },
+    { "nSetProcessStatsBuffer", "(I)V", (void*) android_view_ThreadedRenderer_setProcessStatsBuffer },
+    { "nGetRenderThreadTid", "(J)I", (void*) android_view_ThreadedRenderer_getRenderThreadTid },
     { "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode },
     { "nCreateProxy", "(ZJ)J", (void*) android_view_ThreadedRenderer_createProxy },
     { "nDeleteProxy", "(J)V", (void*) android_view_ThreadedRenderer_deleteProxy },
     { "nLoadSystemProperties", "(J)Z", (void*) android_view_ThreadedRenderer_loadSystemProperties },
     { "nSetName", "(JLjava/lang/String;)V", (void*) android_view_ThreadedRenderer_setName },
-    { "nInitialize", "(JLandroid/view/Surface;)Z", (void*) android_view_ThreadedRenderer_initialize },
+    { "nInitialize", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_initialize },
     { "nUpdateSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_updateSurface },
     { "nPauseSurface", "(JLandroid/view/Surface;)Z", (void*) android_view_ThreadedRenderer_pauseSurface },
-    { "nSetup", "(JIIFII)V", (void*) android_view_ThreadedRenderer_setup },
+    { "nSetStopped", "(JZ)V", (void*) android_view_ThreadedRenderer_setStopped },
+    { "nSetup", "(JFII)V", (void*) android_view_ThreadedRenderer_setup },
     { "nSetLightCenter", "(JFFF)V", (void*) android_view_ThreadedRenderer_setLightCenter },
     { "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque },
+    { "nSetWideGamut", "(JZ)V", (void*) android_view_ThreadedRenderer_setWideGamut },
     { "nSyncAndDrawFrame", "(J[JI)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
-    { "nDestroy", "(J)V", (void*) android_view_ThreadedRenderer_destroy },
+    { "nDestroy", "(JJ)V", (void*) android_view_ThreadedRenderer_destroy },
     { "nRegisterAnimatingRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_registerAnimatingRenderNode },
+    { "nRegisterVectorDrawableAnimator", "(JJ)V", (void*) android_view_ThreadedRenderer_registerVectorDrawableAnimator },
     { "nInvokeFunctor", "(JZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor },
     { "nCreateTextureLayer", "(J)J", (void*) android_view_ThreadedRenderer_createTextureLayer },
     { "nBuildLayer", "(JJ)V", (void*) android_view_ThreadedRenderer_buildLayer },
@@ -483,13 +1014,40 @@ static JNINativeMethod gMethods[] = {
     { "nFence", "(J)V", (void*) android_view_ThreadedRenderer_fence },
     { "nStopDrawing", "(J)V", (void*) android_view_ThreadedRenderer_stopDrawing },
     { "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending },
+    { "nSerializeDisplayListTree", "(J)V", (void*) android_view_ThreadedRenderer_serializeDisplayListTree },
     { "nDumpProfileInfo", "(JLjava/io/FileDescriptor;I)V", (void*) android_view_ThreadedRenderer_dumpProfileInfo },
-    { "nDumpProfileData", "([BLjava/io/FileDescriptor;)V", (void*) android_view_ThreadedRenderer_dumpProfileData },
     { "setupShadersDiskCache", "(Ljava/lang/String;)V",
                 (void*) android_view_ThreadedRenderer_setupShadersDiskCache },
+    { "nAddRenderNode", "(JJZ)V", (void*) android_view_ThreadedRenderer_addRenderNode},
+    { "nRemoveRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_removeRenderNode},
+    { "nDrawRenderNode", "(JJ)V", (void*) android_view_ThreadedRendererd_drawRenderNode},
+    { "nSetContentDrawBounds", "(JIIII)V", (void*)android_view_ThreadedRenderer_setContentDrawBounds},
+    { "nAddFrameMetricsObserver",
+            "(JLandroid/view/FrameMetricsObserver;)J",
+            (void*)android_view_ThreadedRenderer_addFrameMetricsObserver },
+    { "nRemoveFrameMetricsObserver",
+            "(JJ)V",
+            (void*)android_view_ThreadedRenderer_removeFrameMetricsObserver },
+    { "nCopySurfaceInto", "(Landroid/view/Surface;IIIILandroid/graphics/Bitmap;)I",
+                (void*)android_view_ThreadedRenderer_copySurfaceInto },
+    { "nCreateHardwareBitmap", "(JII)Landroid/graphics/Bitmap;",
+            (void*)android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode },
+    { "disableVsync", "()V", (void*)android_view_ThreadedRenderer_disableVsync },
 };
 
 int register_android_view_ThreadedRenderer(JNIEnv* env) {
+    jclass observerClass = FindClassOrDie(env, "android/view/FrameMetricsObserver");
+    gFrameMetricsObserverClassInfo.frameMetrics = GetFieldIDOrDie(
+            env, observerClass, "mFrameMetrics", "Landroid/view/FrameMetrics;");
+    gFrameMetricsObserverClassInfo.messageQueue = GetFieldIDOrDie(
+            env, observerClass, "mMessageQueue", "Landroid/os/MessageQueue;");
+    gFrameMetricsObserverClassInfo.callback = GetMethodIDOrDie(
+            env, observerClass, "notifyDataAvailable", "(I)V");
+
+    jclass metricsClass = FindClassOrDie(env, "android/view/FrameMetrics");
+    gFrameMetricsObserverClassInfo.timingDataBuffer = GetFieldIDOrDie(
+            env, metricsClass, "mTimingData", "[J");
+
     return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
 }