Pass actual present time to ViewRootImpl

To measure end-to-end touch latency, we need to report the actual
present time to ViewRootImpl. ViewRootImpl, in turn, will report this
information to InputDispatcher. Finally, InputDispatcher will combine
all known information for a specific input event, and will report this
data to westworld.

In another patch, we will add a new call, 'reportLatencyInfo', to
InputPublisher. This call will allow the app to send this latency data
to InputDispatcher.

Bug: 169866723
Test: printed the input event present times inside ViewRootImpl
Change-Id: Ibd3a2cfeb1a340eb15cd2165071df1f8589634af
diff --git a/core/java/android/view/FrameMetricsObserver.java b/core/java/android/view/FrameMetricsObserver.java
index 41bc9a7..35d95be 100644
--- a/core/java/android/view/FrameMetricsObserver.java
+++ b/core/java/android/view/FrameMetricsObserver.java
@@ -45,7 +45,8 @@
         mWindow = new WeakReference<>(window);
         mListener = listener;
         mFrameMetrics = new FrameMetrics();
-        mObserver = new HardwareRendererObserver(this,  mFrameMetrics.mTimingData, handler);
+        mObserver = new HardwareRendererObserver(this,  mFrameMetrics.mTimingData, handler,
+                false /*waitForPresentTime*/);
     }
 
     /**
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java
index 7d1adc36..79d8c14 100644
--- a/core/java/android/view/InputEventReceiver.java
+++ b/core/java/android/view/InputEventReceiver.java
@@ -21,6 +21,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.MessageQueue;
+import android.os.Trace;
 import android.util.Log;
 import android.util.SparseIntArray;
 
@@ -198,6 +199,15 @@
     }
 
     /**
+     * Report the latency information for a specific input event.
+     */
+    public final void reportLatencyInfo(int inputEventId, long gpuCompletedTime, long presentTime) {
+        Trace.traceBegin(Trace.TRACE_TAG_INPUT, "reportLatencyInfo");
+        // TODO(b/169866723) : send this data to InputDispatcher via InputChannel
+        Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+    }
+
+    /**
      * Consumes all pending batched input events.
      * Must be called on the same Looper thread to which the receiver is attached.
      *
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 390e3ae..9fc415d 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import static android.os.IInputConstants.INVALID_INPUT_EVENT_ID;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.InputDevice.SOURCE_CLASS_NONE;
@@ -105,6 +106,7 @@
 import android.graphics.FrameInfo;
 import android.graphics.HardwareRenderer;
 import android.graphics.HardwareRenderer.FrameDrawingCallback;
+import android.graphics.HardwareRendererObserver;
 import android.graphics.Insets;
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
@@ -1191,6 +1193,14 @@
                     }
                     mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
                             Looper.myLooper());
+
+                    if (mAttachInfo.mThreadedRenderer != null) {
+                        InputMetricsListener listener =
+                                new InputMetricsListener(mInputEventReceiver);
+                        mHardwareRendererObserver = new HardwareRendererObserver(
+                                listener, listener.data, mHandler, true /*waitForPresentTime*/);
+                        mAttachInfo.mThreadedRenderer.addObserver(mHardwareRendererObserver);
+                    }
                 }
 
                 view.assignParent(this);
@@ -8569,6 +8579,34 @@
     }
     WindowInputEventReceiver mInputEventReceiver;
 
+    final class InputMetricsListener
+            implements HardwareRendererObserver.OnFrameMetricsAvailableListener {
+        public long[] data = new long[FrameMetrics.Index.FRAME_STATS_COUNT];
+
+        private InputEventReceiver mReceiver;
+
+        InputMetricsListener(InputEventReceiver receiver) {
+            mReceiver = receiver;
+        }
+
+        @Override
+        public void onFrameMetricsAvailable(int dropCountSinceLastInvocation) {
+            final int inputEventId = (int) data[FrameMetrics.Index.INPUT_EVENT_ID];
+            if (inputEventId == INVALID_INPUT_EVENT_ID) {
+                return;
+            }
+            final long presentTime = data[FrameMetrics.Index.DISPLAY_PRESENT_TIME];
+            if (presentTime <= 0) {
+                // Present time is not available for this frame. If the present time is not
+                // available, we cannot compute end-to-end input latency metrics.
+                return;
+            }
+            final long gpuCompletedTime = data[FrameMetrics.Index.GPU_COMPLETED];
+            mReceiver.reportLatencyInfo(inputEventId, gpuCompletedTime, presentTime);
+        }
+    }
+    HardwareRendererObserver mHardwareRendererObserver;
+
     final class ConsumeBatchedInputRunnable implements Runnable {
         @Override
         public void run() {
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 33ee8f0..48e5455 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -121,7 +121,8 @@
         mChoreographer = choreographer;
         mSurfaceControlWrapper = surfaceControlWrapper;
         mHandler = handler;
-        mObserver = new HardwareRendererObserver(this, mMetricsWrapper.getTiming(), handler);
+        mObserver = new HardwareRendererObserver(
+                this, mMetricsWrapper.getTiming(), handler, false /*waitForPresentTime*/);
         mTraceThresholdMissedFrames = traceThresholdMissedFrames;
         mTraceThresholdFrameTimeMillis = traceThresholdFrameTimeMillis;
         mListener = listener;
diff --git a/graphics/java/android/graphics/HardwareRendererObserver.java b/graphics/java/android/graphics/HardwareRendererObserver.java
index da9d03c..e2a0572 100644
--- a/graphics/java/android/graphics/HardwareRendererObserver.java
+++ b/graphics/java/android/graphics/HardwareRendererObserver.java
@@ -62,7 +62,7 @@
      * @param handler the Handler to use when invoking callbacks
      */
     public HardwareRendererObserver(@NonNull OnFrameMetricsAvailableListener listener,
-            @NonNull long[] frameMetrics, @NonNull Handler handler) {
+            @NonNull long[] frameMetrics, @NonNull Handler handler, boolean waitForPresentTime) {
         if (handler == null || handler.getLooper() == null) {
             throw new NullPointerException("handler and its looper cannot be null");
         }
@@ -74,7 +74,7 @@
         mFrameMetrics = frameMetrics;
         mHandler = handler;
         mListener = listener;
-        mNativePtr = new VirtualRefBasePtr(nCreateObserver());
+        mNativePtr = new VirtualRefBasePtr(nCreateObserver(waitForPresentTime));
     }
 
     /*package*/ long getNativeInstance() {
@@ -98,6 +98,6 @@
         });
     }
 
-    private native long nCreateObserver();
+    private native long nCreateObserver(boolean waitForPresentTime);
     private static native int nGetNextBuffer(long nativePtr, long[] data);
 }
diff --git a/libs/hwui/FrameMetricsObserver.h b/libs/hwui/FrameMetricsObserver.h
index b93f078..ef1f5aa 100644
--- a/libs/hwui/FrameMetricsObserver.h
+++ b/libs/hwui/FrameMetricsObserver.h
@@ -24,6 +24,24 @@
 class FrameMetricsObserver : public VirtualLightRefBase {
 public:
     virtual void notify(const int64_t* buffer) = 0;
+    bool waitForPresentTime() const { return mWaitForPresentTime; };
+
+    /**
+     * Create a new metrics observer. An observer that watches present time gets notified at a
+     * different time than the observer that doesn't.
+     *
+     * The observer that doesn't want present time is notified about metrics just after the frame
+     * is completed. This is the default behaviour that's used by public API's.
+     *
+     * An observer that watches present time is notified about metrics after the actual display
+     * present time is known.
+     * WARNING! This observer may not receive metrics for the last several frames that the app
+     * produces.
+     */
+    FrameMetricsObserver(bool waitForPresentTime) : mWaitForPresentTime(waitForPresentTime) {}
+
+private:
+    const bool mWaitForPresentTime;
 };
 
 }  // namespace uirenderer
diff --git a/libs/hwui/FrameMetricsReporter.h b/libs/hwui/FrameMetricsReporter.h
index 0643e79..3f2dc12 100644
--- a/libs/hwui/FrameMetricsReporter.h
+++ b/libs/hwui/FrameMetricsReporter.h
@@ -55,13 +55,24 @@
         return mObservers.size() > 0;
     }
 
-    void reportFrameMetrics(const int64_t* stats) {
+    /**
+     * Notify observers about the metrics contained in 'stats'.
+     * If an observer is waiting for present time, notify when 'stats' has present time.
+     *
+     * If an observer does not want present time, only notify when 'hasPresentTime' is false.
+     * Never notify both types of observers from the same callback, because the callback with
+     * 'hasPresentTime' is sent at a different time than the one without.
+     */
+    void reportFrameMetrics(const int64_t* stats, bool hasPresentTime) {
         FatVector<sp<FrameMetricsObserver>, 10> copy;
         {
             std::lock_guard lock(mObserversLock);
             copy.reserve(mObservers.size());
             for (size_t i = 0; i < mObservers.size(); i++) {
-                copy.push_back(mObservers[i]);
+                const bool wantsPresentTime = mObservers[i]->waitForPresentTime();
+                if (hasPresentTime == wantsPresentTime) {
+                    copy.push_back(mObservers[i]);
+                }
             }
         }
         for (size_t i = 0; i < copy.size(); i++) {
diff --git a/libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp b/libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp
index 5b3e656..e5d5e75 100644
--- a/libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp
@@ -35,7 +35,9 @@
     return env;
 }
 
-HardwareRendererObserver::HardwareRendererObserver(JavaVM *vm, jobject observer) : mVm(vm) {
+HardwareRendererObserver::HardwareRendererObserver(JavaVM* vm, jobject observer,
+                                                   bool waitForPresentTime)
+        : uirenderer::FrameMetricsObserver(waitForPresentTime), mVm(vm) {
     mObserverWeak = getenv(mVm)->NewWeakGlobalRef(observer);
     LOG_ALWAYS_FATAL_IF(mObserverWeak == nullptr,
             "unable to create frame stats observer reference");
@@ -86,14 +88,16 @@
 }
 
 static jlong android_graphics_HardwareRendererObserver_createObserver(JNIEnv* env,
-                                                                      jobject observerObj) {
+                                                                      jobject observerObj,
+                                                                      jboolean waitForPresentTime) {
     JavaVM* vm = nullptr;
     if (env->GetJavaVM(&vm) != JNI_OK) {
         LOG_ALWAYS_FATAL("Unable to get Java VM");
         return 0;
     }
 
-    HardwareRendererObserver* observer = new HardwareRendererObserver(vm, observerObj);
+    HardwareRendererObserver* observer =
+            new HardwareRendererObserver(vm, observerObj, waitForPresentTime);
     return reinterpret_cast<jlong>(observer);
 }
 
@@ -110,10 +114,10 @@
 }
 
 static const std::array gMethods = {
-    MAKE_JNI_NATIVE_METHOD("nCreateObserver", "()J",
-                           android_graphics_HardwareRendererObserver_createObserver),
-    MAKE_JNI_NATIVE_METHOD("nGetNextBuffer", "(J[J)I",
-                           android_graphics_HardwareRendererObserver_getNextBuffer),
+        MAKE_JNI_NATIVE_METHOD("nCreateObserver", "(Z)J",
+                               android_graphics_HardwareRendererObserver_createObserver),
+        MAKE_JNI_NATIVE_METHOD("nGetNextBuffer", "(J[J)I",
+                               android_graphics_HardwareRendererObserver_getNextBuffer),
 };
 
 int register_android_graphics_HardwareRendererObserver(JNIEnv* env) {
diff --git a/libs/hwui/jni/android_graphics_HardwareRendererObserver.h b/libs/hwui/jni/android_graphics_HardwareRendererObserver.h
index 62111fd..d307614 100644
--- a/libs/hwui/jni/android_graphics_HardwareRendererObserver.h
+++ b/libs/hwui/jni/android_graphics_HardwareRendererObserver.h
@@ -26,7 +26,7 @@
  */
 class HardwareRendererObserver : public uirenderer::FrameMetricsObserver {
 public:
-    HardwareRendererObserver(JavaVM *vm, jobject observer);
+    HardwareRendererObserver(JavaVM* vm, jobject observer, bool waitForPresentTime);
     ~HardwareRendererObserver();
 
     /**
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index f69ddac..9793300 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -599,10 +599,41 @@
     // TODO (b/169858044): Move this into JankTracker to adjust deadline when queue is
     // double-stuffed.
     if (CC_UNLIKELY(mFrameMetricsReporter.get() != nullptr)) {
-        mFrameMetricsReporter->reportFrameMetrics(frameInfo->data());
+        mFrameMetricsReporter->reportFrameMetrics(frameInfo->data(), false /*hasPresentTime*/);
     }
 }
 
+void CanvasContext::reportMetricsWithPresentTime() {
+    if (mFrameMetricsReporter == nullptr) {
+        return;
+    }
+    if (mNativeSurface == nullptr) {
+        return;
+    }
+    FrameInfo* forthBehind;
+    int64_t frameNumber;
+    {  // acquire lock
+        std::scoped_lock lock(mLast4FrameInfosMutex);
+        if (mLast4FrameInfos.size() != mLast4FrameInfos.capacity()) {
+            // Not enough frames yet
+            return;
+        }
+        // Surface object keeps stats for the last 8 frames.
+        std::tie(forthBehind, frameNumber) = mLast4FrameInfos.front();
+    }  // release lock
+
+    nsecs_t presentTime = 0;
+    native_window_get_frame_timestamps(
+            mNativeSurface->getNativeWindow(), frameNumber, nullptr /*outRequestedPresentTime*/,
+            nullptr /*outAcquireTime*/, nullptr /*outLatchTime*/,
+            nullptr /*outFirstRefreshStartTime*/, nullptr /*outLastRefreshStartTime*/,
+            nullptr /*outGpuCompositionDoneTime*/, &presentTime, nullptr /*outDequeueReadyTime*/,
+            nullptr /*outReleaseTime*/);
+
+    forthBehind->set(FrameInfoIndex::DisplayPresentTime) = presentTime;
+    mFrameMetricsReporter->reportFrameMetrics(forthBehind->data(), true /*hasPresentTime*/);
+}
+
 void CanvasContext::onSurfaceStatsAvailable(void* context, ASurfaceControl* control,
             ASurfaceControlStats* stats) {
 
@@ -624,6 +655,9 @@
             }
         }
     }
+
+    instance->reportMetricsWithPresentTime();
+
     if (frameInfo != nullptr) {
         if (gpuCompleteTime == -1) {
             gpuCompleteTime = frameInfo->get(FrameInfoIndex::SwapBuffersCompleted);
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 2e7b2f6..74f426e 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -219,6 +219,12 @@
     SkRect computeDirtyRect(const Frame& frame, SkRect* dirty);
     void finishFrame(FrameInfo* frameInfo);
 
+    /**
+     * Invoke 'reportFrameMetrics' on the last frame stored in 'mLast4FrameInfos'.
+     * Populate the 'presentTime' field before calling.
+     */
+    void reportMetricsWithPresentTime();
+
     // The same type as Frame.mWidth and Frame.mHeight
     int32_t mLastFrameWidth = 0;
     int32_t mLastFrameHeight = 0;