Animator stuff

 Bug: 17228458

Change-Id: Id884a429a512f9cd2be0ed16dbd0f10e92b4440d
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index de90899..edb3798 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -485,4 +485,6 @@
      * Called by {@link ViewRootImpl} when a new performTraverals is scheduled.
      */
     abstract void notifyFramePending();
+
+    abstract void registerAnimatingRenderNode(RenderNode animator);
 }
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index 099f153..9dc9766 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -167,10 +167,13 @@
     public static final int STATUS_DREW = 0x4;
 
     private boolean mValid;
-    private final long mNativeRenderNode;
+    // Do not access directly unless you are ThreadedRenderer
+    final long mNativeRenderNode;
+    private final View mOwningView;
 
-    private RenderNode(String name) {
+    private RenderNode(String name, View owningView) {
         mNativeRenderNode = nCreate(name);
+        mOwningView = owningView;
     }
 
     /**
@@ -178,6 +181,7 @@
      */
     private RenderNode(long nativePtr) {
         mNativeRenderNode = nativePtr;
+        mOwningView = null;
     }
 
     /**
@@ -188,8 +192,8 @@
      *
      * @return A new RenderNode.
      */
-    public static RenderNode create(String name) {
-        return new RenderNode(name);
+    public static RenderNode create(String name, @Nullable View owningView) {
+        return new RenderNode(name, owningView);
     }
 
     /**
@@ -805,7 +809,15 @@
     ///////////////////////////////////////////////////////////////////////////
 
     public void addAnimator(RenderNodeAnimator animator) {
+        if (mOwningView == null || mOwningView.mAttachInfo == null) {
+            throw new IllegalStateException("Cannot start this animator on a detached view!");
+        }
         nAddAnimator(mNativeRenderNode, animator.getNativeAnimator());
+        mOwningView.mAttachInfo.mViewRootImpl.registerAnimatingRenderNode(this);
+    }
+
+    public void endAllAnimators() {
+        nEndAllAnimators(mNativeRenderNode);
     }
 
     ///////////////////////////////////////////////////////////////////////////
@@ -891,6 +903,7 @@
     ///////////////////////////////////////////////////////////////////////////
 
     private static native void nAddAnimator(long renderNode, long animatorPtr);
+    private static native void nEndAllAnimators(long renderNode);
 
     ///////////////////////////////////////////////////////////////////////////
     // Finalization
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index 9433237..413ea04 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -19,7 +19,6 @@
 import android.animation.Animator;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
-import android.animation.Animator.AnimatorListener;
 import android.graphics.Canvas;
 import android.graphics.CanvasProperty;
 import android.graphics.Paint;
@@ -30,7 +29,6 @@
 import com.android.internal.view.animation.HasNativeInterpolator;
 import com.android.internal.view.animation.NativeInterpolatorFactory;
 
-import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 
 /**
@@ -111,13 +109,11 @@
         mRenderProperty = property;
         mFinalValue = finalValue;
         mUiThreadHandlesDelay = true;
-        init(nCreateAnimator(new WeakReference<RenderNodeAnimator>(this),
-                property, finalValue));
+        init(nCreateAnimator(property, finalValue));
     }
 
     public RenderNodeAnimator(CanvasProperty<Float> property, float finalValue) {
         init(nCreateCanvasPropertyFloatAnimator(
-                new WeakReference<RenderNodeAnimator>(this),
                 property.getNativeContainer(), finalValue));
         mUiThreadHandlesDelay = false;
     }
@@ -132,14 +128,12 @@
      */
     public RenderNodeAnimator(CanvasProperty<Paint> property, int paintField, float finalValue) {
         init(nCreateCanvasPropertyPaintAnimator(
-                new WeakReference<RenderNodeAnimator>(this),
                 property.getNativeContainer(), paintField, finalValue));
         mUiThreadHandlesDelay = false;
     }
 
     public RenderNodeAnimator(int x, int y, float startRadius, float endRadius) {
-        init(nCreateRevealAnimator(new WeakReference<RenderNodeAnimator>(this),
-                x, y, startRadius, endRadius));
+        init(nCreateRevealAnimator(x, y, startRadius, endRadius));
         mUiThreadHandlesDelay = true;
     }
 
@@ -192,7 +186,7 @@
     }
 
     private void doStart() {
-        nStart(mNativePtr.get());
+        nStart(mNativePtr.get(), this);
 
         // Alpha is a special snowflake that has the canonical value stored
         // in mTransformationInfo instead of in RenderNode, so we need to update
@@ -248,24 +242,21 @@
 
     public void setTarget(View view) {
         mViewTarget = view;
-        mTarget = view.mRenderNode;
-        mTarget.addAnimator(this);
+        setTarget(mViewTarget.mRenderNode);
     }
 
     public void setTarget(Canvas canvas) {
         if (!(canvas instanceof GLES20RecordingCanvas)) {
             throw new IllegalArgumentException("Not a GLES20RecordingCanvas");
         }
-
         final GLES20RecordingCanvas recordingCanvas = (GLES20RecordingCanvas) canvas;
         setTarget(recordingCanvas.mNode);
     }
 
-    public void setTarget(RenderNode node) {
+    private void setTarget(RenderNode node) {
         if (mTarget != null) {
             throw new IllegalStateException("Target already set!");
         }
-        mViewTarget = null;
         mTarget = node;
         mTarget.addAnimator(this);
     }
@@ -335,6 +326,12 @@
         for (int i = 0; i < numListeners; i++) {
             listeners.get(i).onAnimationEnd(this);
         }
+
+        // Release the native object, as it has a global reference to us. This
+        // breaks the cyclic reference chain, and allows this object to be
+        // GC'd
+        mNativePtr.release();
+        mNativePtr = null;
     }
 
     @SuppressWarnings("unchecked")
@@ -427,11 +424,8 @@
     }
 
     // Called by native
-    private static void callOnFinished(WeakReference<RenderNodeAnimator> weakThis) {
-        RenderNodeAnimator animator = weakThis.get();
-        if (animator != null) {
-            animator.onFinished();
-        }
+    private static void callOnFinished(RenderNodeAnimator animator) {
+        animator.onFinished();
     }
 
     @Override
@@ -439,22 +433,20 @@
         throw new IllegalStateException("Cannot clone this animator");
     }
 
-    private static native long nCreateAnimator(WeakReference<RenderNodeAnimator> weakThis,
-            int property, float finalValue);
-    private static native long nCreateCanvasPropertyFloatAnimator(WeakReference<RenderNodeAnimator> weakThis,
+    private static native long nCreateAnimator(int property, float finalValue);
+    private static native long nCreateCanvasPropertyFloatAnimator(
             long canvasProperty, float finalValue);
-    private static native long nCreateCanvasPropertyPaintAnimator(WeakReference<RenderNodeAnimator> weakThis,
+    private static native long nCreateCanvasPropertyPaintAnimator(
             long canvasProperty, int paintField, float finalValue);
-    private static native long nCreateRevealAnimator(WeakReference<RenderNodeAnimator> weakThis,
+    private static native long nCreateRevealAnimator(
             int x, int y, float startRadius, float endRadius);
 
     private static native void nSetStartValue(long nativePtr, float startValue);
     private static native void nSetDuration(long nativePtr, long duration);
     private static native long nGetDuration(long nativePtr);
     private static native void nSetStartDelay(long nativePtr, long startDelay);
-    private static native long nGetStartDelay(long nativePtr);
     private static native void nSetInterpolator(long animPtr, long interpolatorPtr);
 
-    private static native void nStart(long animPtr);
+    private static native void nStart(long animPtr, RenderNodeAnimator finishListener);
     private static native void nEnd(long animPtr);
 }
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 3af214d..764eea7 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -312,6 +312,20 @@
 
         attachInfo.mIgnoreDirtyState = false;
 
+        // register animating rendernodes which started animating prior to renderer
+        // creation, which is typical for animators started prior to first draw
+        if (attachInfo.mPendingAnimatingRenderNodes != null) {
+            final int count = attachInfo.mPendingAnimatingRenderNodes.size();
+            for (int i = 0; i < count; i++) {
+                registerAnimatingRenderNode(
+                        attachInfo.mPendingAnimatingRenderNodes.get(i));
+            }
+            attachInfo.mPendingAnimatingRenderNodes.clear();
+            // We don't need this anymore as subsequent calls to
+            // ViewRootImpl#attachRenderNodeAnimator will go directly to us.
+            attachInfo.mPendingAnimatingRenderNodes = null;
+        }
+
         int syncResult = nSyncAndDrawFrame(mNativeProxy, frameTimeNanos,
                 recordDuration, view.getResources().getDisplayMetrics().density);
         if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) {
@@ -370,6 +384,11 @@
     }
 
     @Override
+    void registerAnimatingRenderNode(RenderNode animator) {
+        nRegisterAnimatingRenderNode(mRootNode.mNativeRenderNode, animator.mNativeRenderNode);
+    }
+
+    @Override
     protected void finalize() throws Throwable {
         try {
             nDeleteProxy(mNativeProxy);
@@ -466,6 +485,7 @@
     private static native int nSyncAndDrawFrame(long nativeProxy,
             long frameTimeNanos, long recordDuration, float density);
     private static native void nDestroyCanvasAndSurface(long nativeProxy);
+    private static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode);
 
     private static native void nInvokeFunctor(long functor, boolean waitForCompletion);
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 2d58ecf..95627e3 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3565,7 +3565,7 @@
         setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS);
         mUserPaddingStart = UNDEFINED_PADDING;
         mUserPaddingEnd = UNDEFINED_PADDING;
-        mRenderNode = RenderNode.create(getClass().getName());
+        mRenderNode = RenderNode.create(getClass().getName(), this);
 
         if (!sCompatibilityDone && context != null) {
             final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
@@ -4161,7 +4161,7 @@
      */
     View() {
         mResources = null;
-        mRenderNode = RenderNode.create(getClass().getName());
+        mRenderNode = RenderNode.create(getClass().getName(), this);
     }
 
     private static SparseArray<String> getAttributeMap() {
@@ -15183,9 +15183,9 @@
      * @param renderNode Existing RenderNode, or {@code null}
      * @return A valid display list for the specified drawable
      */
-    private static RenderNode getDrawableRenderNode(Drawable drawable, RenderNode renderNode) {
+    private RenderNode getDrawableRenderNode(Drawable drawable, RenderNode renderNode) {
         if (renderNode == null) {
-            renderNode = RenderNode.create(drawable.getClass().getName());
+            renderNode = RenderNode.create(drawable.getClass().getName(), this);
         }
 
         final Rect bounds = drawable.getBounds();
@@ -19911,6 +19911,7 @@
         boolean mHardwareAccelerated;
         boolean mHardwareAccelerationRequested;
         HardwareRenderer mHardwareRenderer;
+        List<RenderNode> mPendingAnimatingRenderNodes;
 
         /**
          * The state of the display to which the window is attached, as reported
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index bb469a3..80dbbf5 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -671,6 +671,17 @@
         ThreadedRenderer.invokeFunctor(functor, waitForCompletion);
     }
 
+    public void registerAnimatingRenderNode(RenderNode animator) {
+        if (mAttachInfo.mHardwareRenderer != null) {
+            mAttachInfo.mHardwareRenderer.registerAnimatingRenderNode(animator);
+        } else {
+            if (mAttachInfo.mPendingAnimatingRenderNodes == null) {
+                mAttachInfo.mPendingAnimatingRenderNodes = new ArrayList<RenderNode>();
+            }
+            mAttachInfo.mPendingAnimatingRenderNodes.add(animator);
+        }
+    }
+
     private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
         mAttachInfo.mHardwareAccelerated = false;
         mAttachInfo.mHardwareAccelerationRequested = false;
@@ -2329,6 +2340,16 @@
             Trace.traceEnd(Trace.TRACE_TAG_VIEW);
         }
 
+        // For whatever reason we didn't create a HardwareRenderer, end any
+        // hardware animations that are now dangling
+        if (mAttachInfo.mPendingAnimatingRenderNodes != null) {
+            final int count = mAttachInfo.mPendingAnimatingRenderNodes.size();
+            for (int i = 0; i < count; i++) {
+                mAttachInfo.mPendingAnimatingRenderNodes.get(i).endAllAnimators();
+            }
+            mAttachInfo.mPendingAnimatingRenderNodes.clear();
+        }
+
         if (mReportNextDraw) {
             mReportNextDraw = false;
             if (mAttachInfo.mHardwareRenderer != null) {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 29c8298..46b225d 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -147,7 +147,7 @@
         boolean isDirty;
         public TextDisplayList(String name) {
             isDirty = true;
-            displayList = RenderNode.create(name);
+            displayList = RenderNode.create(name, null);
         }
         boolean needsRecord() { return isDirty || !displayList.isValid(); }
     }
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 1e9d722..1296831 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -455,6 +455,12 @@
     renderNode->addAnimator(animator);
 }
 
+static void android_view_RenderNode_endAllAnimators(JNIEnv* env, jobject clazz,
+        jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    renderNode->animators().endAllAnimators();
+}
+
 #endif // USE_OPENGL_RENDERER
 
 // ----------------------------------------------------------------------------
@@ -534,6 +540,7 @@
     { "nGetPivotY",                "(J)F",  (void*) android_view_RenderNode_getPivotY },
 
     { "nAddAnimator",              "(JJ)V", (void*) android_view_RenderNode_addAnimator },
+    { "nEndAllAnimators",          "(J)V", (void*) android_view_RenderNode_endAllAnimators },
 #endif
 };
 
diff --git a/core/jni/android_view_RenderNodeAnimator.cpp b/core/jni/android_view_RenderNodeAnimator.cpp
index 767534f..85c2a09 100644
--- a/core/jni/android_view_RenderNodeAnimator.cpp
+++ b/core/jni/android_view_RenderNodeAnimator.cpp
@@ -51,28 +51,36 @@
     // cyclic-references-of-doom. If you think "I know, just use NewWeakGlobalRef!"
     // then you end up with basically a PhantomReference, which is totally not
     // what we want.
-    AnimationListenerBridge(JNIEnv* env, jobject weakThis) {
-        mWeakThis = env->NewGlobalRef(weakThis);
+    AnimationListenerBridge(JNIEnv* env, jobject finishListener) {
+        mFinishListener = env->NewGlobalRef(finishListener);
         env->GetJavaVM(&mJvm);
     }
 
     virtual ~AnimationListenerBridge() {
-        JNIEnv* env = getEnv(mJvm);
-        env->DeleteGlobalRef(mWeakThis);
-        mWeakThis = NULL;
+        if (mFinishListener) {
+            onAnimationFinished(NULL);
+        }
     }
 
     virtual void onAnimationFinished(BaseRenderNodeAnimator*) {
+        LOG_ALWAYS_FATAL_IF(!mFinishListener, "Finished listener twice?");
         JNIEnv* env = getEnv(mJvm);
         env->CallStaticVoidMethod(
                 gRenderNodeAnimatorClassInfo.clazz,
                 gRenderNodeAnimatorClassInfo.callOnFinished,
-                mWeakThis);
+                mFinishListener);
+        releaseJavaObject();
     }
 
 private:
+    void releaseJavaObject() {
+        JNIEnv* env = getEnv(mJvm);
+        env->DeleteGlobalRef(mFinishListener);
+        mFinishListener = NULL;
+    }
+
     JavaVM* mJvm;
-    jobject mWeakThis;
+    jobject mFinishListener;
 };
 
 static inline RenderPropertyAnimator::RenderProperty toRenderProperty(jint property) {
@@ -88,38 +96,33 @@
     return static_cast<CanvasPropertyPaintAnimator::PaintField>(field);
 }
 
-static jlong createAnimator(JNIEnv* env, jobject clazz, jobject weakThis,
+static jlong createAnimator(JNIEnv* env, jobject clazz,
         jint propertyRaw, jfloat finalValue) {
     RenderPropertyAnimator::RenderProperty property = toRenderProperty(propertyRaw);
-
     BaseRenderNodeAnimator* animator = new RenderPropertyAnimator(property, finalValue);
-    animator->setListener(new AnimationListenerBridge(env, weakThis));
     return reinterpret_cast<jlong>( animator );
 }
 
 static jlong createCanvasPropertyFloatAnimator(JNIEnv* env, jobject clazz,
-        jobject weakThis, jlong canvasPropertyPtr, jfloat finalValue) {
+        jlong canvasPropertyPtr, jfloat finalValue) {
     CanvasPropertyPrimitive* canvasProperty = reinterpret_cast<CanvasPropertyPrimitive*>(canvasPropertyPtr);
     BaseRenderNodeAnimator* animator = new CanvasPropertyPrimitiveAnimator(canvasProperty, finalValue);
-    animator->setListener(new AnimationListenerBridge(env, weakThis));
     return reinterpret_cast<jlong>( animator );
 }
 
 static jlong createCanvasPropertyPaintAnimator(JNIEnv* env, jobject clazz,
-        jobject weakThis, jlong canvasPropertyPtr, jint paintFieldRaw,
+        jlong canvasPropertyPtr, jint paintFieldRaw,
         jfloat finalValue) {
     CanvasPropertyPaint* canvasProperty = reinterpret_cast<CanvasPropertyPaint*>(canvasPropertyPtr);
     CanvasPropertyPaintAnimator::PaintField paintField = toPaintField(paintFieldRaw);
     BaseRenderNodeAnimator* animator = new CanvasPropertyPaintAnimator(
             canvasProperty, paintField, finalValue);
-    animator->setListener(new AnimationListenerBridge(env, weakThis));
     return reinterpret_cast<jlong>( animator );
 }
 
-static jlong createRevealAnimator(JNIEnv* env, jobject clazz, jobject weakThis,
+static jlong createRevealAnimator(JNIEnv* env, jobject clazz,
         jint centerX, jint centerY, jfloat startRadius, jfloat endRadius) {
     BaseRenderNodeAnimator* animator = new RevealAnimator(centerX, centerY, startRadius, endRadius);
-    animator->setListener(new AnimationListenerBridge(env, weakThis));
     return reinterpret_cast<jlong>( animator );
 }
 
@@ -156,8 +159,11 @@
     animator->setInterpolator(interpolator);
 }
 
-static void start(JNIEnv* env, jobject clazz, jlong animatorPtr) {
+static void start(JNIEnv* env, jobject clazz, jlong animatorPtr, jobject finishListener) {
     BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
+    if (finishListener) {
+        animator->setListener(new AnimationListenerBridge(env, finishListener));
+    }
     animator->start();
 }
 
@@ -176,17 +182,16 @@
 
 static JNINativeMethod gMethods[] = {
 #ifdef USE_OPENGL_RENDERER
-    { "nCreateAnimator", "(Ljava/lang/ref/WeakReference;IF)J", (void*) createAnimator },
-    { "nCreateCanvasPropertyFloatAnimator", "(Ljava/lang/ref/WeakReference;JF)J", (void*) createCanvasPropertyFloatAnimator },
-    { "nCreateCanvasPropertyPaintAnimator", "(Ljava/lang/ref/WeakReference;JIF)J", (void*) createCanvasPropertyPaintAnimator },
-    { "nCreateRevealAnimator", "(Ljava/lang/ref/WeakReference;IIFF)J", (void*) createRevealAnimator },
+    { "nCreateAnimator", "(IF)J", (void*) createAnimator },
+    { "nCreateCanvasPropertyFloatAnimator", "(JF)J", (void*) createCanvasPropertyFloatAnimator },
+    { "nCreateCanvasPropertyPaintAnimator", "(JIF)J", (void*) createCanvasPropertyPaintAnimator },
+    { "nCreateRevealAnimator", "(IIFF)J", (void*) createRevealAnimator },
     { "nSetStartValue", "(JF)V", (void*) setStartValue },
     { "nSetDuration", "(JJ)V", (void*) setDuration },
     { "nGetDuration", "(J)J", (void*) getDuration },
     { "nSetStartDelay", "(JJ)V", (void*) setStartDelay },
-    { "nGetStartDelay", "(J)J", (void*) getStartDelay },
     { "nSetInterpolator", "(JJ)V", (void*) setInterpolator },
-    { "nStart", "(J)V", (void*) start },
+    { "nStart", "(JLandroid/view/RenderNodeAnimator;)V", (void*) start },
     { "nEnd", "(J)V", (void*) end },
 #endif
 };
@@ -204,7 +209,7 @@
     gRenderNodeAnimatorClassInfo.clazz = jclass(env->NewGlobalRef(gRenderNodeAnimatorClassInfo.clazz));
 
     GET_STATIC_METHOD_ID(gRenderNodeAnimatorClassInfo.callOnFinished, gRenderNodeAnimatorClassInfo.clazz,
-            "callOnFinished", "(Ljava/lang/ref/WeakReference;)V");
+            "callOnFinished", "(Landroid/view/RenderNodeAnimator;)V");
 
     return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
 }
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 4e3419a..99babac 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -33,6 +33,8 @@
 #include "android_view_GraphicBuffer.h"
 
 #include <Animator.h>
+#include <AnimationContext.h>
+#include <IContextFactory.h>
 #include <RenderNode.h>
 #include <renderthread/CanvasContext.h>
 #include <renderthread/RenderProxy.h>
@@ -103,7 +105,7 @@
     std::string mMessage;
 };
 
-class RootRenderNode : public RenderNode, AnimationHook, ErrorHandler {
+class RootRenderNode : public RenderNode, ErrorHandler {
 public:
     RootRenderNode(JNIEnv* env) : RenderNode() {
         mLooper = Looper::getForThread();
@@ -114,34 +116,84 @@
 
     virtual ~RootRenderNode() {}
 
-    virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener) {
-        OnFinishedEvent event(animator, listener);
-        mOnFinishedEvents.push_back(event);
-    }
-
     virtual void onError(const std::string& message) {
         mLooper->sendMessage(new RenderingException(mVm, message), 0);
     }
 
     virtual void prepareTree(TreeInfo& info) {
-        info.animationHook = this;
         info.errorHandler = this;
         RenderNode::prepareTree(info);
-        info.animationHook = NULL;
         info.errorHandler = NULL;
+    }
 
-        // post all the finished stuff
-        if (mOnFinishedEvents.size()) {
-            sp<InvokeAnimationListeners> message
-                    = new InvokeAnimationListeners(mOnFinishedEvents);
-            mLooper->sendMessage(message, 0);
+    void sendMessage(const sp<MessageHandler>& handler) {
+        mLooper->sendMessage(handler, 0);
+    }
+
+    void attachAnimatingNode(RenderNode* animatingNode) {
+        mPendingAnimatingRenderNodes.push_back(animatingNode);
+    }
+
+    void doAttachAnimatingNodes(AnimationContext* context) {
+        for (size_t i = 0; i < mPendingAnimatingRenderNodes.size(); i++) {
+            RenderNode* node = mPendingAnimatingRenderNodes[i].get();
+            context->addAnimatingRenderNode(*node);
         }
+        mPendingAnimatingRenderNodes.clear();
     }
 
 private:
     sp<Looper> mLooper;
-    std::vector<OnFinishedEvent> mOnFinishedEvents;
     JavaVM* mVm;
+    std::vector< sp<RenderNode> > mPendingAnimatingRenderNodes;
+};
+
+class AnimationContextBridge : public AnimationContext {
+public:
+    AnimationContextBridge(renderthread::TimeLord& clock, RootRenderNode* rootNode)
+            : AnimationContext(clock), mRootNode(rootNode) {
+    }
+
+    virtual ~AnimationContextBridge() {}
+
+    // Marks the start of a frame, which will update the frame time and move all
+    // next frame animations into the current frame
+    virtual void startFrame() {
+        mRootNode->doAttachAnimatingNodes(this);
+        AnimationContext::startFrame();
+    }
+
+    // Runs any animations still left in mCurrentFrameAnimations
+    virtual void runRemainingAnimations(TreeInfo& info) {
+        AnimationContext::runRemainingAnimations(info);
+        // post all the finished stuff
+        if (mOnFinishedEvents.size()) {
+            sp<InvokeAnimationListeners> message
+                    = new InvokeAnimationListeners(mOnFinishedEvents);
+            mRootNode->sendMessage(message);
+        }
+    }
+
+    virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener) {
+        OnFinishedEvent event(animator, listener);
+        mOnFinishedEvents.push_back(event);
+    }
+
+private:
+    sp<RootRenderNode> mRootNode;
+    std::vector<OnFinishedEvent> mOnFinishedEvents;
+};
+
+class ContextFactoryImpl : public IContextFactory {
+public:
+    ContextFactoryImpl(RootRenderNode* rootNode) : mRootNode(rootNode) {}
+
+    virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) {
+        return new AnimationContextBridge(clock, mRootNode);
+    }
+
+private:
+    RootRenderNode* mRootNode;
 };
 
 static void android_view_ThreadedRenderer_setAtlas(JNIEnv* env, jobject clazz,
@@ -168,8 +220,9 @@
 
 static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz,
         jboolean translucent, jlong rootRenderNodePtr) {
-    RenderNode* rootRenderNode = reinterpret_cast<RenderNode*>(rootRenderNodePtr);
-    return (jlong) new RenderProxy(translucent, rootRenderNode);
+    RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootRenderNodePtr);
+    ContextFactoryImpl factory(rootRenderNode);
+    return (jlong) new RenderProxy(translucent, rootRenderNode, &factory);
 }
 
 static void android_view_ThreadedRenderer_deleteProxy(JNIEnv* env, jobject clazz,
@@ -244,6 +297,13 @@
     proxy->destroyCanvasAndSurface();
 }
 
+static void android_view_ThreadedRenderer_registerAnimatingRenderNode(JNIEnv* env, jobject clazz,
+        jlong rootNodePtr, jlong animatingNodePtr) {
+    RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootNodePtr);
+    RenderNode* animatingNode = reinterpret_cast<RenderNode*>(animatingNodePtr);
+    rootRenderNode->attachAnimatingNode(animatingNode);
+}
+
 static void android_view_ThreadedRenderer_invokeFunctor(JNIEnv* env, jobject clazz,
         jlong functorPtr, jboolean waitForCompletion) {
     Functor* functor = reinterpret_cast<Functor*>(functorPtr);
@@ -371,6 +431,7 @@
     { "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque },
     { "nSyncAndDrawFrame", "(JJJF)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
     { "nDestroyCanvasAndSurface", "(J)V", (void*) android_view_ThreadedRenderer_destroyCanvasAndSurface },
+    { "nRegisterAnimatingRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_registerAnimatingRenderNode },
     { "nInvokeFunctor", "(JZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor },
     { "nCreateDisplayListLayer", "(JII)J", (void*) android_view_ThreadedRenderer_createDisplayListLayer },
     { "nCreateTextureLayer", "(J)J", (void*) android_view_ThreadedRenderer_createTextureLayer },
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index d9f7941..49560ff 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -12,6 +12,7 @@
 		font/CacheTexture.cpp \
 		font/Font.cpp \
 		AmbientShadow.cpp \
+		AnimationContext.cpp \
 		Animator.cpp \
 		AnimatorManager.cpp \
 		AssetAtlas.cpp \
diff --git a/libs/hwui/AnimationContext.cpp b/libs/hwui/AnimationContext.cpp
new file mode 100644
index 0000000..ec44de3
--- /dev/null
+++ b/libs/hwui/AnimationContext.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2014 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 "AnimationContext.h"
+
+#include "Animator.h"
+#include "RenderNode.h"
+#include "TreeInfo.h"
+#include "renderthread/TimeLord.h"
+
+namespace android {
+namespace uirenderer {
+
+AnimationContext::AnimationContext(renderthread::TimeLord& clock)
+        : mClock(clock)
+        , mCurrentFrameAnimations(*this)
+        , mNextFrameAnimations(*this)
+        , mFrameTimeMs(0) {
+}
+
+AnimationContext::~AnimationContext() {
+}
+
+void AnimationContext::addAnimatingRenderNode(RenderNode& node) {
+    if (!node.animators().hasAnimationHandle()) {
+        AnimationHandle* handle = new AnimationHandle(node, *this);
+        addAnimationHandle(handle);
+    }
+}
+
+void AnimationContext::addAnimationHandle(AnimationHandle* handle) {
+    handle->insertAfter(&mNextFrameAnimations);
+}
+
+void AnimationContext::startFrame() {
+    LOG_ALWAYS_FATAL_IF(mCurrentFrameAnimations.mNextHandle,
+            "Missed running animations last frame!");
+    AnimationHandle* head = mNextFrameAnimations.mNextHandle;
+    if (head) {
+        mNextFrameAnimations.mNextHandle = NULL;
+        mCurrentFrameAnimations.mNextHandle = head;
+        head->mPreviousHandle = &mCurrentFrameAnimations;
+    }
+    mFrameTimeMs = mClock.computeFrameTimeMs();
+}
+
+void AnimationContext::runRemainingAnimations(TreeInfo& info) {
+    while (mCurrentFrameAnimations.mNextHandle) {
+        AnimationHandle* current = mCurrentFrameAnimations.mNextHandle;
+        AnimatorManager& animators = current->mRenderNode->animators();
+        animators.pushStaging();
+        animators.animateNoDamage(info);
+        LOG_ALWAYS_FATAL_IF(mCurrentFrameAnimations.mNextHandle == current,
+                "Animate failed to remove from current frame list!");
+    }
+}
+
+void AnimationContext::callOnFinished(BaseRenderNodeAnimator* animator,
+        AnimationListener* listener) {
+    listener->onAnimationFinished(animator);
+}
+
+AnimationHandle::AnimationHandle(AnimationContext& context)
+        : mContext(context)
+        , mPreviousHandle(NULL)
+        , mNextHandle(NULL) {
+}
+
+AnimationHandle::AnimationHandle(RenderNode& animatingNode, AnimationContext& context)
+        : mRenderNode(&animatingNode)
+        , mContext(context)
+        , mPreviousHandle(NULL)
+        , mNextHandle(NULL) {
+    mRenderNode->animators().setAnimationHandle(this);
+}
+
+AnimationHandle::~AnimationHandle() {
+    LOG_ALWAYS_FATAL_IF(mPreviousHandle || mNextHandle,
+            "AnimationHandle destroyed while still animating!");
+}
+
+void AnimationHandle::notifyAnimationsRan() {
+    removeFromList();
+    if (mRenderNode->animators().hasAnimators()) {
+        mContext.addAnimationHandle(this);
+    } else {
+        mRenderNode->animators().setAnimationHandle(NULL);
+        delete this;
+    }
+}
+
+void AnimationHandle::insertAfter(AnimationHandle* prev) {
+    removeFromList();
+    mNextHandle = prev->mNextHandle;
+    if (mNextHandle) {
+        mNextHandle->mPreviousHandle = this;
+    }
+    prev->mNextHandle = this;
+    mPreviousHandle = prev;
+}
+
+void AnimationHandle::removeFromList() {
+    if (mPreviousHandle) {
+        mPreviousHandle->mNextHandle = mNextHandle;
+    }
+    if (mNextHandle) {
+        mNextHandle->mPreviousHandle = mPreviousHandle;
+    }
+    mPreviousHandle = NULL;
+    mNextHandle = NULL;
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/AnimationContext.h b/libs/hwui/AnimationContext.h
new file mode 100644
index 0000000..e32c33d
--- /dev/null
+++ b/libs/hwui/AnimationContext.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#ifndef TREEANIMATIONTRACKER_H_
+#define TREEANIMATIONTRACKER_H_
+
+#include <cutils/compiler.h>
+#include <utils/RefBase.h>
+#include <utils/StrongPointer.h>
+
+#include "renderthread/TimeLord.h"
+#include "utils/Macros.h"
+
+namespace android {
+namespace uirenderer {
+
+class AnimationContext;
+class AnimationListener;
+class BaseRenderNodeAnimator;
+class RenderNode;
+class TreeInfo;
+
+/*
+ * AnimationHandle is several classes merged into one.
+ * 1: It maintains the reference to the AnimationContext required to run animators.
+ * 2: It keeps a strong reference to RenderNodes with animators so that
+ *    we don't lose them if they are no longer in the display tree. This is
+ *    required so that we can keep animating them, and properly notify listeners
+ *    of onAnimationFinished.
+ * 3: It forms a doubly linked list so that we can cheaply move between states.
+ */
+class AnimationHandle {
+    PREVENT_COPY_AND_ASSIGN(AnimationHandle);
+public:
+    AnimationContext& context() { return mContext; }
+
+    void notifyAnimationsRan();
+
+private:
+    friend class AnimationContext;
+    AnimationHandle(AnimationContext& context);
+    AnimationHandle(RenderNode& animatingNode, AnimationContext& context);
+    ~AnimationHandle();
+
+    void insertAfter(AnimationHandle* prev);
+    void removeFromList();
+
+    sp<RenderNode> mRenderNode;
+
+    AnimationContext& mContext;
+
+    AnimationHandle* mPreviousHandle;
+    AnimationHandle* mNextHandle;
+};
+
+class AnimationContext {
+    PREVENT_COPY_AND_ASSIGN(AnimationContext);
+public:
+    ANDROID_API AnimationContext(renderthread::TimeLord& clock);
+    ANDROID_API virtual ~AnimationContext();
+
+    nsecs_t frameTimeMs() { return mFrameTimeMs; }
+    bool hasAnimations() {
+        return mCurrentFrameAnimations.mNextHandle
+                || mNextFrameAnimations.mNextHandle;
+    }
+
+    // Will always add to the next frame list, which is swapped when
+    // startFrame() is called
+    ANDROID_API void addAnimatingRenderNode(RenderNode& node);
+
+    // Marks the start of a frame, which will update the frame time and move all
+    // next frame animations into the current frame
+    ANDROID_API virtual void startFrame();
+
+    // Runs any animations still left in mCurrentFrameAnimations that were not run
+    // as part of the standard RenderNode:prepareTree pass.
+    ANDROID_API virtual void runRemainingAnimations(TreeInfo& info);
+
+    ANDROID_API virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener);
+
+private:
+    friend class AnimationHandle;
+    void addAnimationHandle(AnimationHandle* handle);
+
+    renderthread::TimeLord& mClock;
+
+    // Animations left to run this frame, at the end of the frame this should
+    // be null
+    AnimationHandle mCurrentFrameAnimations;
+    // Animations queued for next frame
+    AnimationHandle mNextFrameAnimations;
+
+    nsecs_t mFrameTimeMs;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* TREEANIMATIONTRACKER_H_ */
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index 78d569d..1c697d5 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -19,6 +19,7 @@
 #include <inttypes.h>
 #include <set>
 
+#include "AnimationContext.h"
 #include "RenderNode.h"
 #include "RenderProperties.h"
 
@@ -85,7 +86,7 @@
     onAttached();
 }
 
-void BaseRenderNodeAnimator::pushStaging(TreeInfo& info) {
+void BaseRenderNodeAnimator::pushStaging(AnimationContext& context) {
     if (!mHasStartValue) {
         doSetStartValue(getValue(mTarget));
     }
@@ -93,21 +94,22 @@
         mPlayState = mStagingPlayState;
         // Oh boy, we're starting! Man the battle stations!
         if (mPlayState == RUNNING) {
-            transitionToRunning(info);
+            transitionToRunning(context);
         }
     }
 }
 
-void BaseRenderNodeAnimator::transitionToRunning(TreeInfo& info) {
-    LOG_ALWAYS_FATAL_IF(info.frameTimeMs <= 0, "%" PRId64 " isn't a real frame time!", info.frameTimeMs);
+void BaseRenderNodeAnimator::transitionToRunning(AnimationContext& context) {
+    nsecs_t frameTimeMs = context.frameTimeMs();
+    LOG_ALWAYS_FATAL_IF(frameTimeMs <= 0, "%" PRId64 " isn't a real frame time!", frameTimeMs);
     if (mStartDelay < 0 || mStartDelay > 50000) {
         ALOGW("Your start delay is strange and confusing: %" PRId64, mStartDelay);
     }
-    mStartTime = info.frameTimeMs + mStartDelay;
+    mStartTime = frameTimeMs + mStartDelay;
     if (mStartTime < 0) {
         ALOGW("Ended up with a really weird start time of %" PRId64
                 " with frame time %" PRId64 " and start delay %" PRId64,
-                mStartTime, info.frameTimeMs, mStartDelay);
+                mStartTime, frameTimeMs, mStartDelay);
         // Set to 0 so that the animate() basically instantly finishes
         mStartTime = 0;
     }
@@ -120,7 +122,7 @@
     }
 }
 
-bool BaseRenderNodeAnimator::animate(TreeInfo& info) {
+bool BaseRenderNodeAnimator::animate(AnimationContext& context) {
     if (mPlayState < RUNNING) {
         return false;
     }
@@ -132,15 +134,14 @@
     // because the staging properties reflect the final value, we always need
     // to call setValue even if the animation isn't yet running or is still
     // being delayed as we need to override the staging value
-    if (mStartTime > info.frameTimeMs) {
-        info.out.hasAnimations |= true;
+    if (mStartTime > context.frameTimeMs()) {
         setValue(mTarget, mFromValue);
         return false;
     }
 
     float fraction = 1.0f;
     if (mPlayState == RUNNING && mDuration > 0) {
-        fraction = (float)(info.frameTimeMs - mStartTime) / mDuration;
+        fraction = (float)(context.frameTimeMs() - mStartTime) / mDuration;
     }
     if (fraction >= 1.0f) {
         fraction = 1.0f;
@@ -151,21 +152,16 @@
     setValue(mTarget, mFromValue + (mDeltaValue * fraction));
 
     if (mPlayState == FINISHED) {
-        callOnFinishedListener(info);
+        callOnFinishedListener(context);
         return true;
     }
 
-    info.out.hasAnimations |= true;
     return false;
 }
 
-void BaseRenderNodeAnimator::callOnFinishedListener(TreeInfo& info) {
+void BaseRenderNodeAnimator::callOnFinishedListener(AnimationContext& context) {
     if (mListener.get()) {
-        if (!info.animationHook) {
-            mListener->onAnimationFinished(this);
-        } else {
-            info.animationHook->callOnFinished(this, mListener.get());
-        }
+        context.callOnFinished(this, mListener.get());
     }
 }
 
diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h
index 6dfe7b4..c52a93f 100644
--- a/libs/hwui/Animator.h
+++ b/libs/hwui/Animator.h
@@ -28,6 +28,8 @@
 namespace android {
 namespace uirenderer {
 
+class AnimationContext;
+class BaseRenderNodeAnimator;
 class RenderNode;
 class RenderProperties;
 
@@ -50,15 +52,17 @@
     ANDROID_API void setListener(AnimationListener* listener) {
         mListener = listener;
     }
+    AnimationListener* listener() { return mListener.get(); }
     ANDROID_API void start() { mStagingPlayState = RUNNING; onStagingPlayStateChanged(); }
     ANDROID_API void end() { mStagingPlayState = FINISHED; onStagingPlayStateChanged(); }
 
     void attach(RenderNode* target);
     virtual void onAttached() {}
     void detach() { mTarget = 0; }
-    void pushStaging(TreeInfo& info);
-    bool animate(TreeInfo& info);
+    void pushStaging(AnimationContext& context);
+    bool animate(AnimationContext& context);
 
+    bool isRunning() { return mPlayState == RUNNING; }
     bool isFinished() { return mPlayState == FINISHED; }
     float finalValue() { return mFinalValue; }
 
@@ -72,7 +76,7 @@
     virtual void setValue(RenderNode* target, float value) = 0;
     RenderNode* target() { return mTarget; }
 
-    void callOnFinishedListener(TreeInfo& info);
+    void callOnFinishedListener(AnimationContext& context);
 
     virtual void onStagingPlayStateChanged() {}
 
@@ -100,7 +104,7 @@
 
 private:
     inline void checkMutable();
-    virtual void transitionToRunning(TreeInfo& info);
+    virtual void transitionToRunning(AnimationContext& context);
     void doSetStartValue(float value);
 };
 
diff --git a/libs/hwui/AnimatorManager.cpp b/libs/hwui/AnimatorManager.cpp
index 7221295a4..3832d42 100644
--- a/libs/hwui/AnimatorManager.cpp
+++ b/libs/hwui/AnimatorManager.cpp
@@ -17,6 +17,7 @@
 
 #include <algorithm>
 
+#include "AnimationContext.h"
 #include "RenderNode.h"
 
 namespace android {
@@ -30,7 +31,8 @@
 }
 
 AnimatorManager::AnimatorManager(RenderNode& parent)
-        : mParent(parent) {
+        : mParent(parent)
+        , mAnimationHandle(NULL) {
 }
 
 AnimatorManager::~AnimatorManager() {
@@ -44,6 +46,11 @@
     mNewAnimators.push_back(animator.get());
 }
 
+void AnimatorManager::setAnimationHandle(AnimationHandle* handle) {
+    LOG_ALWAYS_FATAL_IF(mAnimationHandle && handle, "Already have an AnimationHandle!");
+    mAnimationHandle = handle;
+}
+
 template<typename T>
 static void move_all(T& source, T& dest) {
     dest.reserve(source.size() + dest.size());
@@ -53,26 +60,30 @@
     source.clear();
 }
 
-void AnimatorManager::pushStaging(TreeInfo& info) {
+void AnimatorManager::pushStaging() {
     if (mNewAnimators.size()) {
         // Since this is a straight move, we don't need to inc/dec the ref count
         move_all(mNewAnimators, mAnimators);
     }
     for (vector<BaseRenderNodeAnimator*>::iterator it = mAnimators.begin(); it != mAnimators.end(); it++) {
-        (*it)->pushStaging(info);
+        (*it)->pushStaging(mAnimationHandle->context());
     }
 }
 
 class AnimateFunctor {
 public:
-    AnimateFunctor(RenderNode& target, TreeInfo& info)
-            : dirtyMask(0), mTarget(target), mInfo(info) {}
+    AnimateFunctor(TreeInfo& info, AnimationContext& context)
+            : dirtyMask(0), mInfo(info), mContext(context) {}
 
     bool operator() (BaseRenderNodeAnimator* animator) {
         dirtyMask |= animator->dirtyMask();
-        bool remove = animator->animate(mInfo);
+        bool remove = animator->animate(mContext);
         if (remove) {
             animator->decStrong(0);
+        } else {
+            if (animator->isRunning()) {
+                mInfo.out.hasAnimations = true;
+            }
         }
         return remove;
     }
@@ -80,8 +91,8 @@
     uint32_t dirtyMask;
 
 private:
-    RenderNode& mTarget;
     TreeInfo& mInfo;
+    AnimationContext& mContext;
 };
 
 uint32_t AnimatorManager::animate(TreeInfo& info) {
@@ -93,17 +104,70 @@
     mParent.damageSelf(info);
     info.damageAccumulator->popTransform();
 
-    AnimateFunctor functor(mParent, info);
-    std::vector< BaseRenderNodeAnimator* >::iterator newEnd;
-    newEnd = std::remove_if(mAnimators.begin(), mAnimators.end(), functor);
-    mAnimators.erase(newEnd, mAnimators.end());
+    uint32_t dirty = animateCommon(info);
 
     mParent.mProperties.updateMatrix();
     info.damageAccumulator->pushTransform(&mParent);
     mParent.damageSelf(info);
 
+    return dirty;
+}
+
+void AnimatorManager::animateNoDamage(TreeInfo& info) {
+    if (!mAnimators.size()) return;
+
+    animateCommon(info);
+}
+
+uint32_t AnimatorManager::animateCommon(TreeInfo& info) {
+    AnimateFunctor functor(info, mAnimationHandle->context());
+    std::vector< BaseRenderNodeAnimator* >::iterator newEnd;
+    newEnd = std::remove_if(mAnimators.begin(), mAnimators.end(), functor);
+    mAnimators.erase(newEnd, mAnimators.end());
+    mAnimationHandle->notifyAnimationsRan();
     return functor.dirtyMask;
 }
 
+class EndAnimatorsFunctor {
+public:
+    EndAnimatorsFunctor(AnimationContext& context) : mContext(context) {}
+
+    void operator() (BaseRenderNodeAnimator* animator) {
+        animator->end();
+        animator->pushStaging(mContext);
+        animator->animate(mContext);
+        animator->decStrong(0);
+    }
+
+private:
+    AnimationContext& mContext;
+};
+
+static void endAnimatorsHard(BaseRenderNodeAnimator* animator) {
+    animator->end();
+    if (animator->listener()) {
+        animator->listener()->onAnimationFinished(animator);
+    }
+    animator->decStrong(0);
+}
+
+void AnimatorManager::endAllAnimators() {
+    if (mNewAnimators.size()) {
+        // Since this is a straight move, we don't need to inc/dec the ref count
+        move_all(mNewAnimators, mAnimators);
+    }
+    // First try gracefully ending them
+    if (mAnimationHandle) {
+        EndAnimatorsFunctor functor(mAnimationHandle->context());
+        for_each(mAnimators.begin(), mAnimators.end(), functor);
+    } else {
+        // We have no context, so bust out the sledgehammer
+        // This works because this state can only happen on the UI thread,
+        // which means we're already on the right thread to invoke listeners
+        for_each(mAnimators.begin(), mAnimators.end(), endAnimatorsHard);
+    }
+    mAnimators.clear();
+}
+
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/AnimatorManager.h b/libs/hwui/AnimatorManager.h
index 0d177c5..d5f56ed 100644
--- a/libs/hwui/AnimatorManager.h
+++ b/libs/hwui/AnimatorManager.h
@@ -27,6 +27,7 @@
 namespace android {
 namespace uirenderer {
 
+class AnimationHandle;
 class BaseRenderNodeAnimator;
 class RenderNode;
 
@@ -39,12 +40,26 @@
 
     void addAnimator(const sp<BaseRenderNodeAnimator>& animator);
 
-    void pushStaging(TreeInfo& info);
+    void setAnimationHandle(AnimationHandle* handle);
+    bool hasAnimationHandle() { return mAnimationHandle; }
+
+    void pushStaging();
+
     // Returns the combined dirty mask of all animators run
     uint32_t animate(TreeInfo& info);
 
+    void animateNoDamage(TreeInfo& info);
+
+    // Hard-ends all animators. Used for cleanup if the root is being destroyed.
+    ANDROID_API void endAllAnimators();
+
+    bool hasAnimators() { return mAnimators.size(); }
+
 private:
+    uint32_t animateCommon(TreeInfo& info);
+
     RenderNode& mParent;
+    AnimationHandle* mAnimationHandle;
 
     // To improve the efficiency of resizing & removing from the vector
     // use manual ref counting instead of sp<>.
diff --git a/libs/hwui/IContextFactory.h b/libs/hwui/IContextFactory.h
new file mode 100644
index 0000000..463b55e
--- /dev/null
+++ b/libs/hwui/IContextFactory.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#ifndef CONTEXTFACTORY_H_
+#define CONTEXTFACTORY_H_
+
+namespace android {
+namespace uirenderer {
+
+namespace renderthread {
+class TimeLord;
+}
+
+class AnimationContext;
+
+class IContextFactory {
+public:
+    virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) = 0;
+
+protected:
+    virtual ~IContextFactory() {}
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* CONTEXTFACTORY_H_ */
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 658265d..a79875e 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -235,7 +235,7 @@
     // before properties() is trampled by stagingProperties(), as they are
     // required by some animators.
     if (CC_LIKELY(info.runAnimations)) {
-        mAnimatorManager.pushStaging(info);
+        mAnimatorManager.pushStaging();
     }
     if (mDirtyPropertyFields) {
         mDirtyPropertyFields = 0;
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 18402b2..27b05e2 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -174,6 +174,8 @@
     // UI thread only!
     ANDROID_API void addAnimator(const sp<BaseRenderNodeAnimator>& animator);
 
+    AnimatorManager& animators() { return mAnimatorManager; }
+
     void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false) const;
 
 private:
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index 74d52a3..e78d8bd 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -26,18 +26,9 @@
 namespace android {
 namespace uirenderer {
 
-class BaseRenderNodeAnimator;
-class AnimationListener;
 class OpenGLRenderer;
 class RenderState;
 
-class AnimationHook {
-public:
-    virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener) = 0;
-protected:
-    ~AnimationHook() {}
-};
-
 class ErrorHandler {
 public:
     virtual void onError(const std::string& message) = 0;
@@ -62,8 +53,6 @@
 
     explicit TreeInfo(TraversalMode mode, RenderState& renderState)
         : mode(mode)
-        , frameTimeMs(0)
-        , animationHook(NULL)
         , prepareTextures(mode == MODE_FULL)
         , runAnimations(true)
         , damageAccumulator(NULL)
@@ -74,8 +63,6 @@
 
     explicit TreeInfo(TraversalMode mode, const TreeInfo& clone)
         : mode(mode)
-        , frameTimeMs(clone.frameTimeMs)
-        , animationHook(clone.animationHook)
         , prepareTextures(mode == MODE_FULL)
         , runAnimations(clone.runAnimations)
         , damageAccumulator(clone.damageAccumulator)
@@ -85,8 +72,6 @@
     {}
 
     const TraversalMode mode;
-    nsecs_t frameTimeMs;
-    AnimationHook* animationHook;
     // TODO: Remove this? Currently this is used to signal to stop preparing
     // textures if we run out of cache space.
     bool prepareTextures;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 4bf5a8a..d9fa0bc 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -21,6 +21,7 @@
 
 #include "EglManager.h"
 #include "RenderThread.h"
+#include "../AnimationContext.h"
 #include "../Caches.h"
 #include "../DeferredLayerUpdater.h"
 #include "../RenderState.h"
@@ -35,7 +36,8 @@
 namespace uirenderer {
 namespace renderthread {
 
-CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode)
+CanvasContext::CanvasContext(RenderThread& thread, bool translucent,
+        RenderNode* rootRenderNode, IContextFactory* contextFactory)
         : mRenderThread(thread)
         , mEglManager(thread.eglManager())
         , mEglSurface(EGL_NO_SURFACE)
@@ -44,11 +46,13 @@
         , mCanvas(NULL)
         , mHaveNewSurface(false)
         , mRootRenderNode(rootRenderNode) {
+    mAnimationContext = contextFactory->createAnimationContext(mRenderThread.timeLord());
 }
 
 CanvasContext::~CanvasContext() {
     destroyCanvasAndSurface();
     mRenderThread.removeFrameCallback(this);
+    delete mAnimationContext;
 }
 
 void CanvasContext::destroyCanvasAndSurface() {
@@ -136,10 +140,11 @@
 void CanvasContext::prepareTree(TreeInfo& info) {
     mRenderThread.removeFrameCallback(this);
 
-    info.frameTimeMs = mRenderThread.timeLord().frameTimeMs();
     info.damageAccumulator = &mDamageAccumulator;
     info.renderer = mCanvas;
+    mAnimationContext->startFrame();
     mRootRenderNode->prepareTree(info);
+    mAnimationContext->runRemainingAnimations(info);
 
     int runningBehind = 0;
     // TODO: This query is moderately expensive, investigate adding some sort
@@ -254,7 +259,6 @@
     stopDrawing();
 
     TreeInfo info(TreeInfo::MODE_FULL, mRenderThread.renderState());
-    info.frameTimeMs = mRenderThread.timeLord().frameTimeMs();
     info.damageAccumulator = &mDamageAccumulator;
     info.renderer = mCanvas;
     info.runAnimations = false;
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 0cbed6f..749da1b 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -25,6 +25,7 @@
 
 #include "../DamageAccumulator.h"
 #include "../DrawProfiler.h"
+#include "../IContextFactory.h"
 #include "../RenderNode.h"
 #include "RenderTask.h"
 #include "RenderThread.h"
@@ -34,6 +35,7 @@
 namespace android {
 namespace uirenderer {
 
+class AnimationContext;
 class DeferredLayerUpdater;
 class OpenGLRenderer;
 class Rect;
@@ -45,9 +47,11 @@
 
 // This per-renderer class manages the bridge between the global EGL context
 // and the render surface.
+// TODO: Rename to Renderer or some other per-window, top-level manager
 class CanvasContext : public IFrameCallback {
 public:
-    CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode);
+    CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
+            IContextFactory* contextFactory);
     virtual ~CanvasContext();
 
     bool initialize(ANativeWindow* window);
@@ -105,6 +109,7 @@
     OpenGLRenderer* mCanvas;
     bool mHaveNewSurface;
     DamageAccumulator mDamageAccumulator;
+    AnimationContext* mAnimationContext;
 
     const sp<RenderNode> mRootRenderNode;
 
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 405ce24..3d04316 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -52,17 +52,20 @@
     MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \
     ARGS(method) *args = (ARGS(method) *) task->payload()
 
-CREATE_BRIDGE3(createContext, RenderThread* thread, bool translucent, RenderNode* rootRenderNode) {
-    return new CanvasContext(*args->thread, args->translucent, args->rootRenderNode);
+CREATE_BRIDGE4(createContext, RenderThread* thread, bool translucent,
+        RenderNode* rootRenderNode, IContextFactory* contextFactory) {
+    return new CanvasContext(*args->thread, args->translucent,
+            args->rootRenderNode, args->contextFactory);
 }
 
-RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode)
+RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory)
         : mRenderThread(RenderThread::getInstance())
         , mContext(0) {
     SETUP_TASK(createContext);
     args->translucent = translucent;
     args->rootRenderNode = rootRenderNode;
     args->thread = &mRenderThread;
+    args->contextFactory = contextFactory;
     mContext = (CanvasContext*) postAndWait(task);
     mDrawFrameTask.setContext(&mRenderThread, mContext);
 }
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index eea3674..9e6bcf5 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -30,6 +30,7 @@
 #include <utils/Vector.h>
 
 #include "../Caches.h"
+#include "../IContextFactory.h"
 #include "DrawFrameTask.h"
 
 namespace android {
@@ -58,7 +59,7 @@
  */
 class ANDROID_API RenderProxy {
 public:
-    ANDROID_API RenderProxy(bool translucent, RenderNode* rootNode);
+    ANDROID_API RenderProxy(bool translucent, RenderNode* rootNode, IContextFactory* contextFactory);
     ANDROID_API virtual ~RenderProxy();
 
     ANDROID_API void setFrameInterval(nsecs_t frameIntervalNanos);
diff --git a/libs/hwui/renderthread/TimeLord.cpp b/libs/hwui/renderthread/TimeLord.cpp
index 758d96e..cf3d039 100644
--- a/libs/hwui/renderthread/TimeLord.cpp
+++ b/libs/hwui/renderthread/TimeLord.cpp
@@ -30,7 +30,7 @@
     }
 }
 
-nsecs_t TimeLord::frameTimeMs() {
+nsecs_t TimeLord::computeFrameTimeMs() {
     // Logic copied from Choreographer.java
     nsecs_t now = systemTime(CLOCK_MONOTONIC);
     nsecs_t jitterNanos = now - mFrameTimeNanos;
diff --git a/libs/hwui/renderthread/TimeLord.h b/libs/hwui/renderthread/TimeLord.h
index 52c6d9ef..8b0372c 100644
--- a/libs/hwui/renderthread/TimeLord.h
+++ b/libs/hwui/renderthread/TimeLord.h
@@ -30,7 +30,7 @@
 public:
     void setFrameInterval(nsecs_t intervalNanos) { mFrameIntervalNanos = intervalNanos; }
     void vsyncReceived(nsecs_t vsync);
-    nsecs_t frameTimeMs();
+    nsecs_t computeFrameTimeMs();
 
 private:
     friend class RenderThread;
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java
index a81e063..afd6a8d 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java
@@ -16,7 +16,6 @@
 
 package com.android.test.hwui;
 
-import android.animation.TimeInterpolator;
 import android.app.Activity;
 import android.content.Context;
 import android.graphics.Canvas;
@@ -28,11 +27,6 @@
 import android.view.HardwareCanvas;
 import android.view.RenderNodeAnimator;
 import android.view.View;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.OvershootInterpolator;
-import android.webkit.WebChromeClient;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
 import android.widget.LinearLayout;
 import android.widget.LinearLayout.LayoutParams;
 import android.widget.ProgressBar;