TIME LORD!

 Bug: 14444180

Change-Id: I68bec3807c4d1c88d5af1aec2fe6907d60b5f2f3
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index f1523ae..0a76075 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -259,6 +259,14 @@
         return delayMillis <= frameDelay ? 0 : delayMillis - frameDelay;
     }
 
+    /**
+     * @return The refresh rate as the nanoseconds between frames
+     * @hide
+     */
+    long getFrameIntervalNanos() {
+        return mFrameIntervalNanos;
+    }
+
     void dump(String prefix, PrintWriter writer) {
         String innerPrefix = prefix + "  ";
         writer.print(prefix); writer.println("Choreographer:");
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index eaec8ab..89a2ea2 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -19,7 +19,6 @@
 import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
-import android.os.SystemClock;
 import android.os.Trace;
 import android.view.Surface.OutOfResourcesException;
 import android.view.View.AttachInfo;
@@ -52,16 +51,23 @@
 
     private static final Rect NULL_RECT = new Rect();
 
+    private static final long NANOS_PER_MS = 1000000;
+
     private int mWidth, mHeight;
     private long mNativeProxy;
     private boolean mInitialized = false;
     private RenderNode mRootNode;
+    private Choreographer mChoreographer;
 
     ThreadedRenderer(boolean translucent) {
         long rootNodePtr = nCreateRootRenderNode();
         mRootNode = RenderNode.adopt(rootNodePtr);
         mRootNode.setClipToBounds(false);
         mNativeProxy = nCreateProxy(translucent, rootNodePtr);
+
+        // Setup timing
+        mChoreographer = Choreographer.getInstance();
+        nSetFrameInterval(mNativeProxy, mChoreographer.getFrameIntervalNanos());
     }
 
     @Override
@@ -161,15 +167,6 @@
         return false;
     }
 
-    /**
-     * TODO: Remove
-     * Temporary hack to allow RenderThreadTest prototype app to trigger
-     * replaying a DisplayList after modifying the displaylist properties
-     *
-     *  @hide */
-    public void repeatLastDraw() {
-    }
-
     private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {
         view.mPrivateFlags |= View.PFLAG_DRAWN;
 
@@ -194,7 +191,8 @@
     @Override
     void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty) {
         attachInfo.mIgnoreDirtyState = true;
-        attachInfo.mDrawingTime = SystemClock.uptimeMillis();
+        long frameTimeNanos = mChoreographer.getFrameTimeNanos();
+        attachInfo.mDrawingTime = frameTimeNanos / NANOS_PER_MS;
 
         updateRootDisplayList(view, callbacks);
 
@@ -203,7 +201,8 @@
         if (dirty == null) {
             dirty = NULL_RECT;
         }
-        nSyncAndDrawFrame(mNativeProxy, dirty.left, dirty.top, dirty.right, dirty.bottom);
+        nSyncAndDrawFrame(mNativeProxy, frameTimeNanos,
+                dirty.left, dirty.top, dirty.right, dirty.bottom);
     }
 
     @Override
@@ -297,13 +296,15 @@
     private static native long nCreateProxy(boolean translucent, long rootRenderNode);
     private static native void nDeleteProxy(long nativeProxy);
 
+    private static native void nSetFrameInterval(long nativeProxy, long frameIntervalNanos);
+
     private static native boolean nInitialize(long nativeProxy, Surface window);
     private static native void nUpdateSurface(long nativeProxy, Surface window);
     private static native void nPauseSurface(long nativeProxy, Surface window);
     private static native void nSetup(long nativeProxy, int width, int height);
     private static native void nSetDisplayListData(long nativeProxy, long displayList,
             long newData);
-    private static native void nSyncAndDrawFrame(long nativeProxy,
+    private static native void nSyncAndDrawFrame(long nativeProxy, long frameTimeNanos,
             int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
     private static native void nRunWithGlContext(long nativeProxy, Runnable runnable);
     private static native void nDestroyCanvasAndSurface(long nativeProxy);
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 564c9a6..6a048672 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -152,6 +152,12 @@
     delete proxy;
 }
 
+static void android_view_ThreadedRenderer_setFrameInterval(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jlong frameIntervalNanos) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->setFrameInterval(frameIntervalNanos);
+}
+
 static jboolean android_view_ThreadedRenderer_initialize(JNIEnv* env, jobject clazz,
         jlong proxyPtr, jobject jsurface) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
@@ -186,10 +192,10 @@
 }
 
 static void android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
-        jlong proxyPtr, jint dirtyLeft, jint dirtyTop,
+        jlong proxyPtr, jlong frameTimeNanos, jint dirtyLeft, jint dirtyTop,
         jint dirtyRight, jint dirtyBottom) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    proxy->syncAndDrawFrame(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
+    proxy->syncAndDrawFrame(frameTimeNanos, dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
 }
 
 static void android_view_ThreadedRenderer_destroyCanvasAndSurface(JNIEnv* env, jobject clazz,
@@ -261,11 +267,12 @@
     { "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode },
     { "nCreateProxy", "(ZJ)J", (void*) android_view_ThreadedRenderer_createProxy },
     { "nDeleteProxy", "(J)V", (void*) android_view_ThreadedRenderer_deleteProxy },
+    { "nSetFrameInterval", "(JJ)V", (void*) android_view_ThreadedRenderer_setFrameInterval },
     { "nInitialize", "(JLandroid/view/Surface;)Z", (void*) android_view_ThreadedRenderer_initialize },
     { "nUpdateSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_updateSurface },
     { "nPauseSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_pauseSurface },
     { "nSetup", "(JII)V", (void*) android_view_ThreadedRenderer_setup },
-    { "nSyncAndDrawFrame", "(JIIII)V", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
+    { "nSyncAndDrawFrame", "(JJIIII)V", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
     { "nDestroyCanvasAndSurface", "(J)V", (void*) android_view_ThreadedRenderer_destroyCanvasAndSurface },
     { "nInvokeFunctor", "(JJZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor },
     { "nRunWithGlContext", "(JLjava/lang/Runnable;)V", (void*) android_view_ThreadedRenderer_runWithGlContext },
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index d324439..eb0cac8 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -59,7 +59,8 @@
 		renderthread/DrawFrameTask.cpp \
 		renderthread/RenderProxy.cpp \
 		renderthread/RenderTask.cpp \
-		renderthread/RenderThread.cpp
+		renderthread/RenderThread.cpp \
+		renderthread/TimeLord.cpp
 
 	intermediates := $(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,)
 
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 63f4b95..f199236 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -406,6 +406,8 @@
 }
 
 void CanvasContext::prepareTree(TreeInfo& info) {
+    info.frameTimeMs = mRenderThread.timeLord().frameTimeMs();
+
     mRootRenderNode->prepareTree(info);
 
     if (info.hasAnimations && !info.hasFunctors) {
@@ -449,12 +451,11 @@
 }
 
 // Called by choreographer to do an RT-driven animation
-void CanvasContext::doFrame(nsecs_t frameTimeNanos) {
+void CanvasContext::doFrame() {
     ATRACE_CALL();
 
     TreeInfo info;
     info.evaluateAnimations = true;
-    info.frameTimeMs = nanoseconds_to_milliseconds(frameTimeNanos);
     info.performStagingPush = false;
     info.prepareTextures = false;
 
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 0873ad4..8022c9e 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -59,7 +59,7 @@
     void destroyCanvasAndSurface();
 
     // IFrameCallback, Chroreographer-driven frame callback entry point
-    virtual void doFrame(nsecs_t frameTimeNanos);
+    virtual void doFrame();
 
     bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
 
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 45f5cb0..a7b781a 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -30,13 +30,17 @@
 namespace uirenderer {
 namespace renderthread {
 
-DrawFrameTask::DrawFrameTask() : mContext(0) {
+DrawFrameTask::DrawFrameTask()
+        : mRenderThread(NULL)
+        , mContext(NULL)
+        , mFrameTimeNanos(NULL) {
 }
 
 DrawFrameTask::~DrawFrameTask() {
 }
 
-void DrawFrameTask::setContext(CanvasContext* context) {
+void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context) {
+    mRenderThread = thread;
     mContext = context;
 }
 
@@ -59,18 +63,20 @@
     mDirty.set(left, top, right, bottom);
 }
 
-void DrawFrameTask::drawFrame(RenderThread* renderThread) {
+void DrawFrameTask::drawFrame(nsecs_t frameTimeNanos) {
     LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!");
 
-    postAndWait(renderThread);
+    mFrameTimeNanos = frameTimeNanos;
+    postAndWait();
 
     // Reset the single-frame data
+    mFrameTimeNanos = 0;
     mDirty.setEmpty();
 }
 
-void DrawFrameTask::postAndWait(RenderThread* renderThread) {
+void DrawFrameTask::postAndWait() {
     AutoMutex _lock(mLock);
-    renderThread->queue(this);
+    mRenderThread->queue(this);
     mSignal.wait(mLock);
 }
 
@@ -99,13 +105,11 @@
     info.prepareTextures = true;
     info.performStagingPush = true;
     info.evaluateAnimations = true;
-    // TODO: Get this from Choreographer
-    nsecs_t frameTimeNs = systemTime(CLOCK_MONOTONIC);
-    info.frameTimeMs = nanoseconds_to_milliseconds(frameTimeNs);
 }
 
 bool DrawFrameTask::syncFrameState() {
     ATRACE_CALL();
+    mRenderThread->timeLord().vsyncReceived(mFrameTimeNanos);
     mContext->makeCurrent();
     Caches::getInstance().textureCache.resetMarkInUse();
     TreeInfo info;
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index c280868..ea00900 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -48,30 +48,32 @@
     DrawFrameTask();
     virtual ~DrawFrameTask();
 
-    void setContext(CanvasContext* context);
+    void setContext(RenderThread* thread, CanvasContext* context);
 
     void addLayer(DeferredLayerUpdater* layer);
     void removeLayer(DeferredLayerUpdater* layer);
 
     void setDirty(int left, int top, int right, int bottom);
-    void drawFrame(RenderThread* renderThread);
+    void drawFrame(nsecs_t frameTimeNanos);
 
     virtual void run();
 
 private:
-    void postAndWait(RenderThread* renderThread);
+    void postAndWait();
     bool syncFrameState();
     void unblockUiThread();
 
     Mutex mLock;
     Condition mSignal;
 
+    RenderThread* mRenderThread;
     CanvasContext* mContext;
 
     /*********************************************
      *  Single frame data
      *********************************************/
     Rect mDirty;
+    nsecs_t mFrameTimeNanos;
 
     /*********************************************
      *  Multi frame data
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 87886e6..7b7c019 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -62,7 +62,7 @@
     args->translucent = translucent;
     args->rootRenderNode = rootRenderNode;
     mContext = (CanvasContext*) postAndWait(task);
-    mDrawFrameTask.setContext(mContext);
+    mDrawFrameTask.setContext(&mRenderThread, mContext);
 }
 
 RenderProxy::~RenderProxy() {
@@ -79,13 +79,25 @@
         SETUP_TASK(destroyContext);
         args->context = mContext;
         mContext = 0;
-        mDrawFrameTask.setContext(0);
+        mDrawFrameTask.setContext(NULL, NULL);
         // This is also a fence as we need to be certain that there are no
         // outstanding mDrawFrame tasks posted before it is destroyed
         postAndWait(task);
     }
 }
 
+CREATE_BRIDGE2(setFrameInterval, RenderThread* thread, nsecs_t frameIntervalNanos) {
+    args->thread->timeLord().setFrameInterval(args->frameIntervalNanos);
+    return NULL;
+}
+
+void RenderProxy::setFrameInterval(nsecs_t frameIntervalNanos) {
+    SETUP_TASK(setFrameInterval);
+    args->thread = &mRenderThread;
+    args->frameIntervalNanos = frameIntervalNanos;
+    post(task);
+}
+
 CREATE_BRIDGE2(initialize, CanvasContext* context, EGLNativeWindowType window) {
     return (void*) args->context->initialize(args->window);
 }
@@ -134,10 +146,10 @@
     post(task);
 }
 
-void RenderProxy::syncAndDrawFrame(
+void RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos,
         int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) {
     mDrawFrameTask.setDirty(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
-    mDrawFrameTask.drawFrame(&mRenderThread);
+    mDrawFrameTask.drawFrame(frameTimeNanos);
 }
 
 CREATE_BRIDGE1(destroyCanvasAndSurface, CanvasContext* context) {
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index eab1395..bfa2b8d 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -25,6 +25,7 @@
 #include <utils/Condition.h>
 #include <utils/Functor.h>
 #include <utils/Mutex.h>
+#include <utils/Timers.h>
 #include <utils/StrongPointer.h>
 #include <utils/Vector.h>
 
@@ -59,11 +60,13 @@
     ANDROID_API RenderProxy(bool translucent, RenderNode* rootNode);
     ANDROID_API virtual ~RenderProxy();
 
+    ANDROID_API void setFrameInterval(nsecs_t frameIntervalNanos);
+
     ANDROID_API bool initialize(const sp<ANativeWindow>& window);
     ANDROID_API void updateSurface(const sp<ANativeWindow>& window);
     ANDROID_API void pauseSurface(const sp<ANativeWindow>& window);
     ANDROID_API void setup(int width, int height);
-    ANDROID_API void syncAndDrawFrame(
+    ANDROID_API void syncAndDrawFrame(nsecs_t frameTimeNanos,
             int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
     ANDROID_API void destroyCanvasAndSurface();
 
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index e95707a..35a3eab 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -129,8 +129,7 @@
         , mDisplayEventReceiver(0)
         , mVsyncRequested(false)
         , mFrameCallbackTaskPending(false)
-        , mFrameCallbackTask(0)
-        , mFrameTime(0) {
+        , mFrameCallbackTask(0) {
     mFrameCallbackTask = new DispatchFrameCallbacks(this);
     mLooper = new Looper(false);
     run("RenderThread");
@@ -193,7 +192,7 @@
     nsecs_t vsyncEvent = latestVsyncEvent(mDisplayEventReceiver);
     if (vsyncEvent > 0) {
         mVsyncRequested = false;
-        mFrameTime = vsyncEvent;
+        mTimeLord.vsyncReceived(vsyncEvent);
         if (!mFrameCallbackTaskPending) {
             mFrameCallbackTaskPending = true;
             //queueDelayed(mFrameCallbackTask, DISPATCH_FRAME_CALLBACKS_DELAY);
@@ -209,7 +208,7 @@
     mFrameCallbacks.swap(callbacks);
 
     for (std::set<IFrameCallback*>::iterator it = callbacks.begin(); it != callbacks.end(); it++) {
-        (*it)->doFrame(mFrameTime);
+        (*it)->doFrame();
     }
 }
 
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index b93dfd6..215d294 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -28,6 +28,8 @@
 #include <utils/Singleton.h>
 #include <utils/Thread.h>
 
+#include "TimeLord.h"
+
 namespace android {
 class DisplayEventReceiver;
 
@@ -53,7 +55,7 @@
 // Mimics android.view.Choreographer.FrameCallback
 class IFrameCallback {
 public:
-    virtual void doFrame(nsecs_t frameTimeNanos) = 0;
+    virtual void doFrame() = 0;
 
 protected:
     ~IFrameCallback() {}
@@ -71,6 +73,8 @@
     void postFrameCallback(IFrameCallback* callback);
     void removeFrameCallback(IFrameCallback* callback);
 
+    TimeLord& timeLord() { return mTimeLord; }
+
 protected:
     virtual bool threadLoop();
 
@@ -102,7 +106,8 @@
     std::set<IFrameCallback*> mFrameCallbacks;
     bool mFrameCallbackTaskPending;
     DispatchFrameCallbacks* mFrameCallbackTask;
-    nsecs_t mFrameTime;
+
+    TimeLord mTimeLord;
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/TimeLord.cpp b/libs/hwui/renderthread/TimeLord.cpp
new file mode 100644
index 0000000..758d96e
--- /dev/null
+++ b/libs/hwui/renderthread/TimeLord.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 "TimeLord.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+TimeLord::TimeLord()
+        : mFrameIntervalNanos(0)
+        , mFrameTimeNanos(0) {
+}
+
+void TimeLord::vsyncReceived(nsecs_t vsync) {
+    if (vsync > mFrameTimeNanos) {
+        mFrameTimeNanos = vsync;
+    }
+}
+
+nsecs_t TimeLord::frameTimeMs() {
+    // Logic copied from Choreographer.java
+    nsecs_t now = systemTime(CLOCK_MONOTONIC);
+    nsecs_t jitterNanos = now - mFrameTimeNanos;
+    if (jitterNanos >= mFrameIntervalNanos) {
+        nsecs_t lastFrameOffset = jitterNanos % mFrameIntervalNanos;
+        mFrameTimeNanos = now - lastFrameOffset;
+    }
+    return nanoseconds_to_milliseconds(mFrameTimeNanos);
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/TimeLord.h b/libs/hwui/renderthread/TimeLord.h
new file mode 100644
index 0000000..52c6d9ef
--- /dev/null
+++ b/libs/hwui/renderthread/TimeLord.h
@@ -0,0 +1,49 @@
+/*
+ * 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 TIMELORD_H
+#define TIMELORD_H
+
+#include <utils/Timers.h>
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+class RenderThread;
+
+// This class serves as a helper to filter & manage frame times from multiple sources
+// ensuring that time flows linearly and smoothly
+class TimeLord {
+public:
+    void setFrameInterval(nsecs_t intervalNanos) { mFrameIntervalNanos = intervalNanos; }
+    void vsyncReceived(nsecs_t vsync);
+    nsecs_t frameTimeMs();
+
+private:
+    friend class RenderThread;
+
+    TimeLord();
+    ~TimeLord() {}
+
+    nsecs_t mFrameIntervalNanos;
+    nsecs_t mFrameTimeNanos;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* TIMELORD_H */