Track per-window information in ViewFrameInfo

FrameInfo will now be per-window; that is, per-ViewRootImpl.
Some of the information should remain “global” (it remain in Choreographer),
while some information is going to become ViewRootImpl-specific.
Before the information gets passed to the native layer,
 the ViewRootImpl-specific info will be stitched together
with the general Choreographer info.

This change is useful in order to correctly correlate frames with a specific
input event. In the unlikely scenario of a user touching two windows of the
same app simultaneously, this change will allow us to correctly measure the
latency of both frames produced by the windows.

Design doc: https://docs.google.com/document/d/1KMpMBlOxnl7zkWBCbXZZE6ZlaHEA4efYnN6WYK8n3FE/edit?resourcekey=0-eqooVNP0SskupljlTFvtOQ

Test: atest ViewFrameInfoTest
Bug: 169866723
Change-Id: Ib0bf9cd51cbcc0b9b70460c929c480eb490ec322
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 57ca71a..d839e35 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -21,6 +21,7 @@
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.graphics.FrameInfo;
 import android.graphics.HardwareRenderer;
 import android.graphics.Picture;
 import android.graphics.Point;
@@ -610,8 +611,7 @@
      * @param attachInfo AttachInfo tied to the specified view.
      */
     void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) {
-        final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer;
-        choreographer.mFrameInfo.markDrawStart();
+        attachInfo.mViewRootImpl.mViewFrameInfo.markDrawStart();
 
         updateRootDisplayList(view, callbacks);
 
@@ -629,7 +629,9 @@
             attachInfo.mPendingAnimatingRenderNodes = null;
         }
 
-        int syncResult = syncAndDrawFrame(choreographer.mFrameInfo);
+        final FrameInfo frameInfo = attachInfo.mViewRootImpl.getUpdatedFrameInfo();
+
+        int syncResult = syncAndDrawFrame(frameInfo);
         if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) {
             Log.w("OpenGLRenderer", "Surface lost, forcing relayout");
             // We lost our surface. For a relayout next frame which should give us a new
diff --git a/core/java/android/view/ViewFrameInfo.java b/core/java/android/view/ViewFrameInfo.java
new file mode 100644
index 0000000..890d071
--- /dev/null
+++ b/core/java/android/view/ViewFrameInfo.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+package android.view;
+
+import android.graphics.FrameInfo;
+
+/**
+ * The timing information of events taking place in ViewRootImpl
+ * @hide
+ */
+public class ViewFrameInfo {
+    public long drawStart;
+    public long oldestInputEventTime; // the time of the oldest input event consumed for this frame
+    public long newestInputEventTime; // the time of the newest input event consumed for this frame
+    // Various flags set to provide extra metadata about the current frame. See flag definitions
+    // inside FrameInfo.
+    // @see android.graphics.FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED
+    public long flags;
+
+    /**
+     * Update the oldest event time.
+     * @param eventTime the time of the input event
+     */
+    public void updateOldestInputEvent(long eventTime) {
+        if (oldestInputEventTime == 0 || eventTime < oldestInputEventTime) {
+            oldestInputEventTime = eventTime;
+        }
+    }
+
+    /**
+     * Update the newest event time.
+     * @param eventTime the time of the input event
+     */
+    public void updateNewestInputEvent(long eventTime) {
+        if (newestInputEventTime == 0 || eventTime > newestInputEventTime) {
+            newestInputEventTime = eventTime;
+        }
+    }
+
+    /**
+     * Populate the missing fields using the data from ViewFrameInfo
+     * @param frameInfo : the structure FrameInfo object to populate
+     */
+    public void populateFrameInfo(FrameInfo frameInfo) {
+        frameInfo.frameInfo[FrameInfo.FLAGS] |= flags;
+        frameInfo.frameInfo[FrameInfo.DRAW_START] = drawStart;
+        frameInfo.frameInfo[FrameInfo.OLDEST_INPUT_EVENT] = oldestInputEventTime;
+        frameInfo.frameInfo[FrameInfo.NEWEST_INPUT_EVENT] = newestInputEventTime;
+    }
+
+    /**
+     * Reset this data. Should typically be invoked after calling "populateFrameInfo".
+     */
+    public void reset() {
+        drawStart = 0;
+        oldestInputEventTime = 0;
+        newestInputEventTime = 0;
+        flags = 0;
+    }
+
+    /**
+     * Record the current time, and store it in 'drawStart'
+     */
+    public void markDrawStart() {
+        drawStart = System.nanoTime();
+    }
+}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 2bea0d6..ae162ed 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -423,7 +423,7 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     int mHeight;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    Rect mDirty;
+    private Rect mDirty;
     public boolean mIsAnimating;
 
     private boolean mUseMTRenderer;
@@ -446,6 +446,23 @@
     @UnsupportedAppUsage
     FallbackEventHandler mFallbackEventHandler;
     final Choreographer mChoreographer;
+    protected final ViewFrameInfo mViewFrameInfo = new ViewFrameInfo();
+
+    /**
+     * Update the Choreographer's FrameInfo object with the timing information for the current
+     * ViewRootImpl instance. Erase the data in the current ViewFrameInfo to prepare for the next
+     * frame.
+     * @return the updated FrameInfo object
+     */
+    protected @NonNull FrameInfo getUpdatedFrameInfo() {
+        // Since Choreographer is a thread-local singleton while we can have multiple
+        // ViewRootImpl's, populate the frame information from the current viewRootImpl before
+        // starting the draw
+        FrameInfo frameInfo = mChoreographer.mFrameInfo;
+        mViewFrameInfo.populateFrameInfo(frameInfo);
+        mViewFrameInfo.reset();
+        return frameInfo;
+    }
 
     // used in relayout to get SurfaceControl size
     // for BLAST adapter surface setup
@@ -2675,7 +2692,7 @@
                         // to resume them
                         mDirty.set(0, 0, mWidth, mHeight);
                     }
-                    mChoreographer.mFrameInfo.addFlags(FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED);
+                    mViewFrameInfo.flags |= FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED;
                 }
                 relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
 
@@ -8138,7 +8155,8 @@
                     oldestEventTime = me.getHistoricalEventTimeNano(0);
                 }
             }
-            mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);
+            mViewFrameInfo.updateOldestInputEvent(oldestEventTime);
+            mViewFrameInfo.updateNewestInputEvent(eventTime);
 
             deliverInputEvent(q);
         }
diff --git a/graphics/java/android/graphics/FrameInfo.java b/graphics/java/android/graphics/FrameInfo.java
index 0061ea1..d59abb5 100644
--- a/graphics/java/android/graphics/FrameInfo.java
+++ b/graphics/java/android/graphics/FrameInfo.java
@@ -43,7 +43,7 @@
     public long[] frameInfo = new long[FRAME_INFO_SIZE];
 
     // Various flags set to provide extra metadata about the current frame
-    private static final int FLAGS = 0;
+    public static final int FLAGS = 0;
 
     // Is this the first-draw following a window layout?
     public static final long FLAG_WINDOW_LAYOUT_CHANGED = 1;
@@ -60,35 +60,35 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface FrameInfoFlags {}
 
-    private static final int FRAME_TIMELINE_VSYNC_ID = 1;
+    public static final int FRAME_TIMELINE_VSYNC_ID = 1;
 
     // The intended vsync time, unadjusted by jitter
-    private static final int INTENDED_VSYNC = 2;
+    public static final int INTENDED_VSYNC = 2;
 
     // Jitter-adjusted vsync time, this is what was used as input into the
     // animation & drawing system
-    private static final int VSYNC = 3;
+    public static final int VSYNC = 3;
 
     // The time of the oldest input event
-    private static final int OLDEST_INPUT_EVENT = 4;
+    public static final int OLDEST_INPUT_EVENT = 4;
 
     // The time of the newest input event
-    private static final int NEWEST_INPUT_EVENT = 5;
+    public static final int NEWEST_INPUT_EVENT = 5;
 
     // When input event handling started
-    private static final int HANDLE_INPUT_START = 6;
+    public static final int HANDLE_INPUT_START = 6;
 
     // When animation evaluations started
-    private static final int ANIMATION_START = 7;
+    public static final int ANIMATION_START = 7;
 
     // When ViewRootImpl#performTraversals() started
-    private static final int PERFORM_TRAVERSALS_START = 8;
+    public static final int PERFORM_TRAVERSALS_START = 8;
 
     // When View:draw() started
-    private static final int DRAW_START = 9;
+    public static final int DRAW_START = 9;
 
     // When the frame needs to be ready by
-    private static final int FRAME_DEADLINE = 10;
+    public static final int FRAME_DEADLINE = 10;
 
     // Must be the last one
     private static final int FRAME_INFO_SIZE = FRAME_DEADLINE + 1;
@@ -99,23 +99,11 @@
         frameInfo[FRAME_TIMELINE_VSYNC_ID] = frameTimelineVsyncId;
         frameInfo[INTENDED_VSYNC] = intendedVsync;
         frameInfo[VSYNC] = usedVsync;
-        frameInfo[OLDEST_INPUT_EVENT] = Long.MAX_VALUE;
-        frameInfo[NEWEST_INPUT_EVENT] = 0;
         frameInfo[FLAGS] = 0;
         frameInfo[FRAME_DEADLINE] = frameDeadline;
     }
 
     /** checkstyle */
-    public void updateInputEventTime(long inputEventTime, long inputEventOldestTime) {
-        if (inputEventOldestTime < frameInfo[OLDEST_INPUT_EVENT]) {
-            frameInfo[OLDEST_INPUT_EVENT] = inputEventOldestTime;
-        }
-        if (inputEventTime > frameInfo[NEWEST_INPUT_EVENT]) {
-            frameInfo[NEWEST_INPUT_EVENT] = inputEventTime;
-        }
-    }
-
-    /** checkstyle */
     public void markInputHandlingStart() {
         frameInfo[HANDLE_INPUT_START] = System.nanoTime();
     }
@@ -131,13 +119,7 @@
     }
 
     /** checkstyle */
-    public void markDrawStart() {
-        frameInfo[DRAW_START] = System.nanoTime();
-    }
-
-    /** checkstyle */
     public void addFlags(@FrameInfoFlags long flags) {
         frameInfo[FLAGS] |= flags;
     }
-
 }
diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp
index fd18d2f..8b20492 100644
--- a/libs/hwui/FrameInfo.cpp
+++ b/libs/hwui/FrameInfo.cpp
@@ -20,7 +20,7 @@
 namespace android {
 namespace uirenderer {
 
-const std::string FrameInfoNames[] = {
+const std::array<std::string, static_cast<int>(FrameInfoIndex::NumIndexes)> FrameInfoNames = {
         "Flags",
         "FrameTimelineVsyncId",
         "IntendedVsync",
@@ -42,10 +42,6 @@
         "GpuCompleted",
 };
 
-static_assert((sizeof(FrameInfoNames) / sizeof(FrameInfoNames[0])) ==
-                      static_cast<int>(FrameInfoIndex::NumIndexes),
-              "size mismatch: FrameInfoNames doesn't match the enum!");
-
 static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 19,
               "Must update value in FrameMetrics.java#FRAME_STATS_COUNT (and here)");
 
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index bb875e3..738246d 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -21,6 +21,7 @@
 #include <cutils/compiler.h>
 #include <utils/Timers.h>
 
+#include <array>
 #include <memory.h>
 #include <string>
 
@@ -60,7 +61,7 @@
     NumIndexes
 };
 
-extern const std::string FrameInfoNames[];
+extern const std::array<std::string, static_cast<int>(FrameInfoIndex::NumIndexes)> FrameInfoNames;
 
 namespace FrameInfoFlags {
 enum {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 9e0fb56..13450be 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -238,16 +238,16 @@
 
     /* --- InputReaderPolicyInterface implementation --- */
 
-    virtual void getReaderConfiguration(InputReaderConfiguration* outConfig);
-    virtual std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId);
-    virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices);
-    virtual std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
-            const InputDeviceIdentifier& identifier);
-    virtual std::string getDeviceAlias(const InputDeviceIdentifier& identifier);
-    virtual TouchAffineTransformation getTouchAffineTransformation(JNIEnv *env,
-            jfloatArray matrixArr);
-    virtual TouchAffineTransformation getTouchAffineTransformation(
-            const std::string& inputDeviceDescriptor, int32_t surfaceRotation);
+    void getReaderConfiguration(InputReaderConfiguration* outConfig) override;
+    std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId) override;
+    void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override;
+    std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
+            const InputDeviceIdentifier& identifier) override;
+    std::string getDeviceAlias(const InputDeviceIdentifier& identifier) override;
+    TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor,
+                                                           int32_t surfaceRotation) override;
+
+    TouchAffineTransformation getTouchAffineTransformation(JNIEnv* env, jfloatArray matrixArr);
 
     /* --- InputDispatcherPolicyInterface implementation --- */
 
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index a8aab2a..a72b07c 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -6,6 +6,7 @@
     static_libs: [
             "androidx.test.ext.junit",
             "androidx.test.rules",
+            "truth-prebuilt",
             "ub-uiautomator",
         ],
     test_suites: ["device-tests"],
diff --git a/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt b/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt
new file mode 100644
index 0000000..f919a3e
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+package com.android.test.input
+
+import android.graphics.FrameInfo
+import android.os.SystemClock
+import android.view.ViewFrameInfo
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+
+class ViewFrameInfoTest {
+    companion object {
+        private const val TAG = "ViewFrameInfoTest"
+    }
+    private val mViewFrameInfo = ViewFrameInfo()
+    private var mTimeStarted: Long = 0
+
+    @Before
+    fun setUp() {
+        mViewFrameInfo.reset()
+        mViewFrameInfo.updateOldestInputEvent(10)
+        mViewFrameInfo.updateNewestInputEvent(20)
+        mViewFrameInfo.flags = mViewFrameInfo.flags or FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED
+        mTimeStarted = SystemClock.uptimeNanos()
+        mViewFrameInfo.markDrawStart()
+    }
+
+    @Test
+    fun testPopulateFields() {
+        assertThat(mViewFrameInfo.drawStart).isGreaterThan(mTimeStarted)
+        assertThat(mViewFrameInfo.oldestInputEventTime).isEqualTo(10)
+        assertThat(mViewFrameInfo.newestInputEventTime).isEqualTo(20)
+        assertThat(mViewFrameInfo.flags).isEqualTo(FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED)
+    }
+
+    @Test
+    fun testReset() {
+        mViewFrameInfo.reset()
+        // Ensure that the original object is reset correctly
+        assertThat(mViewFrameInfo.drawStart).isEqualTo(0)
+        assertThat(mViewFrameInfo.oldestInputEventTime).isEqualTo(0)
+        assertThat(mViewFrameInfo.newestInputEventTime).isEqualTo(0)
+        assertThat(mViewFrameInfo.flags).isEqualTo(0)
+    }
+
+    @Test
+    fun testUpdateFrameInfoFromViewFrameInfo() {
+        val frameInfo = FrameInfo()
+        // By default, all values should be zero
+        assertThat(frameInfo.frameInfo[FrameInfo.OLDEST_INPUT_EVENT]).isEqualTo(0)
+        assertThat(frameInfo.frameInfo[FrameInfo.NEWEST_INPUT_EVENT]).isEqualTo(0)
+        assertThat(frameInfo.frameInfo[FrameInfo.FLAGS]).isEqualTo(0)
+        assertThat(frameInfo.frameInfo[FrameInfo.DRAW_START]).isEqualTo(0)
+
+        // The values inside FrameInfo should match those from ViewFrameInfo after we update them
+        mViewFrameInfo.populateFrameInfo(frameInfo)
+        assertThat(frameInfo.frameInfo[FrameInfo.OLDEST_INPUT_EVENT]).isEqualTo(10)
+        assertThat(frameInfo.frameInfo[FrameInfo.NEWEST_INPUT_EVENT]).isEqualTo(20)
+        assertThat(frameInfo.frameInfo[FrameInfo.FLAGS]).isEqualTo(
+                FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED)
+        assertThat(frameInfo.frameInfo[FrameInfo.DRAW_START]).isGreaterThan(mTimeStarted)
+    }
+}
\ No newline at end of file