Merge "systrace: avoid queuing signaled fence in FenceMonitor"
diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml
index 6cbe4ae..5c1cab6 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -40,7 +40,6 @@
     <feature name="android.software.voice_recognizers" notLowRam="true" />
     <feature name="android.software.backup" />
     <feature name="android.software.home_screen" />
-    <feature name="android.software.input_methods" />
     <feature name="android.software.print" />
     <feature name="android.software.companion_device_setup" />
     <feature name="android.software.autofill" />
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index d75de1e..44883cc 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -26,6 +26,7 @@
 #ifndef ANDROID_CHOREOGRAPHER_H
 #define ANDROID_CHOREOGRAPHER_H
 
+#include <stdint.h>
 #include <sys/cdefs.h>
 
 __BEGIN_DECLS
@@ -43,6 +44,16 @@
  */
 typedef void (*AChoreographer_frameCallback)(long frameTimeNanos, void* data);
 
+/**
+ * Prototype of the function that is called when a new frame is being rendered.
+ * It's passed the time that the frame is being rendered as nanoseconds in the
+ * CLOCK_MONOTONIC time base, as well as the data pointer provided by the
+ * application that registered a callback. All callbacks that run as part of
+ * rendering a frame will observe the same frame time, so it should be used
+ * whenever events need to be synchronized (e.g. animations).
+ */
+typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* data);
+
 #if __ANDROID_API__ >= 24
 
 /**
@@ -52,23 +63,39 @@
 AChoreographer* AChoreographer_getInstance() __INTRODUCED_IN(24);
 
 /**
- * Post a callback to be run on the next frame. The data pointer provided will
- * be passed to the callback function when it's called.
+ * Deprecated: Use AChoreographer_postFrameCallback64 instead.
  */
 void AChoreographer_postFrameCallback(AChoreographer* choreographer,
-                AChoreographer_frameCallback callback, void* data) __INTRODUCED_IN(24);
+        AChoreographer_frameCallback callback, void* data) __INTRODUCED_IN(24) __DEPRECATED_IN(29);
 
 /**
- * Post a callback to be run on the frame following the specified delay. The
- * data pointer provided will be passed to the callback function when it's
- * called.
+ * Deprecated: Use AChoreographer_postFrameCallbackDelayed64 instead.
  */
 void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
                 AChoreographer_frameCallback callback, void* data,
-                long delayMillis) __INTRODUCED_IN(24);
+                long delayMillis) __INTRODUCED_IN(24) __DEPRECATED_IN(29);
 
 #endif /* __ANDROID_API__ >= 24 */
 
+#if __ANDROID_API__ >= 29
+
+/**
+ * Power a callback to be run on the next frame.  The data pointer provided will
+ * be passed to the callback function when it's called.
+ */
+void AChoreographer_postFrameCallback64(AChoreographer* chroreographer,
+                AChoreographer_frameCallback64 callback, void* data) __INTRODUCED_IN(29);
+
+/**
+ * Post a callback to be run on the frame following the specified delay.  The
+ * data pointer provided will be passed to the callback function when it's
+ * called.
+ */
+void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer,
+                AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) __INTRODUCED_IN(29);
+
+#endif /* __ANDROID_API__ >= 29 */
+
 __END_DECLS
 
 #endif // ANDROID_CHOREOGRAPHER_H
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index 0e79239..ef2ad99 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -40,9 +40,9 @@
 struct ASurfaceControl;
 
 /**
- * The SurfaceControl API can be used to provide a heirarchy of surfaces for
+ * The SurfaceControl API can be used to provide a hierarchy of surfaces for
  * composition to the system compositor. ASurfaceControl represents a content node in
- * this heirarchy.
+ * this hierarchy.
  */
 typedef struct ASurfaceControl ASurfaceControl;
 
@@ -112,7 +112,7 @@
  * the callback.
  *
  * |stats| is an opaque handle that can be passed to ASurfaceTransactionStats functions to query
- * information about the transaction. The handle is only valid during the the callback.
+ * information about the transaction. The handle is only valid during the callback.
  *
  * THREADING
  * The transaction completed callback can be invoked on any thread.
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 0a4ad46..d6708ab 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1490,6 +1490,19 @@
                                                                      outIsWideColorDisplay);
 }
 
+status_t SurfaceComposerClient::addRegionSamplingListener(
+        const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
+        const sp<IRegionSamplingListener>& listener) {
+    return ComposerService::getComposerService()->addRegionSamplingListener(samplingArea,
+                                                                            stopLayerHandle,
+                                                                            listener);
+}
+
+status_t SurfaceComposerClient::removeRegionSamplingListener(
+        const sp<IRegionSamplingListener>& listener) {
+    return ComposerService::getComposerService()->removeRegionSamplingListener(listener);
+}
+
 // ----------------------------------------------------------------------------
 
 status_t ScreenshotClient::capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace,
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 2c483ee..48c978f 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -50,6 +50,7 @@
 class HdrCapabilities;
 class ISurfaceComposerClient;
 class IGraphicBufferProducer;
+class IRegionSamplingListener;
 class Region;
 
 // ---------------------------------------------------------------------------
@@ -449,6 +450,10 @@
 
     static status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames,
                                               uint64_t timestamp, DisplayedFrameStats* outStats);
+    static status_t addRegionSamplingListener(const Rect& samplingArea,
+                                              const sp<IBinder>& stopLayerHandle,
+                                              const sp<IRegionSamplingListener>& listener);
+    static status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener);
 
 private:
     virtual void onFirstRef();
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index 515209e..a106451 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -66,61 +66,60 @@
     EXPECT_NEAR_BY_FRACTION(actual, target, COEFFICIENT_TOLERANCE);
 }
 
-void failWithMessage(std::string message) {
+static void failWithMessage(std::string message) {
     FAIL() << message; // cannot do this directly from a non-void function
 }
 
 struct Position {
-      nsecs_t time;
-      float x;
-      float y;
+    nsecs_t time;
+    float x;
+    float y;
 };
 
-
-MotionEvent* createSimpleMotionEvent(const Position* positions, size_t numSamples) {
+static std::unique_ptr<MotionEvent> createSimpleMotionEvent(
+        const std::vector<Position>& positions) {
     /**
      * Only populate the basic fields of a MotionEvent, such as time and a single axis
      * Designed for use with manually-defined tests.
-     * Create a new MotionEvent on the heap, caller responsible for destroying the object.
      */
-    if (numSamples < 1) {
-        failWithMessage(StringPrintf("Need at least 1 sample to create a MotionEvent."
-                " Received numSamples=%zu", numSamples));
+    if (positions.empty()) {
+        failWithMessage("Need at least 1 sample to create a MotionEvent. Received empty vector.");
     }
 
-    MotionEvent* event = new MotionEvent();
-    PointerCoords coords;
-    coords.clear();
-    constexpr size_t pointerCount = 1;
-    PointerProperties properties[pointerCount];
+    std::unique_ptr<MotionEvent> event = std::make_unique<MotionEvent>();
 
+    constexpr size_t pointerCount = 1;
+    PointerCoords coords[pointerCount];
+    coords[0].clear();
+
+    PointerProperties properties[pointerCount];
     properties[0].id = DEFAULT_POINTER_ID;
     properties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
 
     // First sample added separately with initialize
-    coords.setAxisValue(AMOTION_EVENT_AXIS_X, positions[0].x);
-    coords.setAxisValue(AMOTION_EVENT_AXIS_Y, positions[0].y);
+    coords[0].setAxisValue(AMOTION_EVENT_AXIS_X, positions[0].x);
+    coords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, positions[0].y);
     event->initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID,
             AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/,
             AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE,
             0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/,
-            0 /*downTime*/, positions[0].time, pointerCount, properties, &coords);
+            0 /*downTime*/, positions[0].time, pointerCount, properties, coords);
 
-    for (size_t i = 1; i < numSamples; i++) {
-        coords.setAxisValue(AMOTION_EVENT_AXIS_X, positions[i].x);
-        coords.setAxisValue(AMOTION_EVENT_AXIS_Y, positions[i].y);
-        event->addSample(positions[i].time, &coords);
+    for (size_t i = 1; i < positions.size(); i++) {
+        coords[0].setAxisValue(AMOTION_EVENT_AXIS_X, positions[i].x);
+        coords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, positions[i].y);
+        event->addSample(positions[i].time, coords);
     }
     return event;
 }
 
-static void computeAndCheckVelocity(const Position* positions, size_t numSamples,
+static void computeAndCheckVelocity(const std::vector<Position>& positions,
             int32_t axis, float targetVelocity) {
     VelocityTracker vt(nullptr);
     float Vx, Vy;
 
-    MotionEvent* event = createSimpleMotionEvent(positions, numSamples);
-    vt.addMovement(event);
+    std::unique_ptr<MotionEvent> event = createSimpleMotionEvent(positions);
+    vt.addMovement(event.get());
 
     vt.getVelocity(DEFAULT_POINTER_ID, &Vx, &Vy);
 
@@ -134,14 +133,13 @@
     default:
         FAIL() << "Axis must be either AMOTION_EVENT_AXIS_X or AMOTION_EVENT_AXIS_Y";
     }
-    delete event;
 }
 
-static void computeAndCheckQuadraticEstimate(const Position* positions, size_t numSamples,
+static void computeAndCheckQuadraticEstimate(const std::vector<Position>& positions,
         const std::array<float, 3>& coefficients) {
     VelocityTracker vt("lsq2");
-    MotionEvent* event = createSimpleMotionEvent(positions, numSamples);
-    vt.addMovement(event);
+    std::unique_ptr<MotionEvent> event = createSimpleMotionEvent(positions);
+    vt.addMovement(event.get());
     VelocityTracker::Estimator estimator;
     EXPECT_TRUE(vt.getEstimator(0, &estimator));
     for (size_t i = 0; i< coefficients.size(); i++) {
@@ -158,35 +156,32 @@
     // Same coordinate is reported 2 times in a row
     // It is difficult to determine the correct answer here, but at least the direction
     // of the reported velocity should be positive.
-    Position values[] = {
+    std::vector<Position> values = {
         { 0, 273, NAN },
         { 12585000, 293, NAN },
         { 14730000, 293, NAN },
     };
-    size_t count = sizeof(values) / sizeof(Position);
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 1600);
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 1600);
 }
 
 TEST_F(VelocityTrackerTest, ThreePointsZeroVelocityTest) {
     // Same coordinate is reported 3 times in a row
-    Position values[] = {
+    std::vector<Position> values = {
         { 0, 293, NAN },
         { 6132000, 293, NAN },
         { 11283000, 293, NAN },
     };
-    size_t count = sizeof(values) / sizeof(Position);
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 0);
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 0);
 }
 
 TEST_F(VelocityTrackerTest, ThreePointsLinearVelocityTest) {
     // Fixed velocity at 5 points per 10 milliseconds
-    Position values[] = {
+    std::vector<Position> values = {
         { 0, 0, NAN },
         { 10000000, 5, NAN },
         { 20000000, 10, NAN },
     };
-    size_t count = sizeof(values) / sizeof(Position);
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 500);
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 500);
 }
 
 
@@ -204,7 +199,7 @@
 // @todo Currently disabled, enable when switching away from lsq2 VelocityTrackerStrategy
 TEST_F(VelocityTrackerTest, DISABLED_SwordfishFlingDown) {
     // Recording of a fling on Swordfish that could cause a fling in the wrong direction
-    Position values[] = {
+    std::vector<Position> values = {
         { 0, 271, 96 },
         { 16071042, 269.786346, 106.922775 },
         { 35648403, 267.983063, 156.660034 },
@@ -213,9 +208,8 @@
         { 85639375, 274.79245, 428.113159 },
         { 96948871, 274.79245, 428.113159 },
     };
-    size_t count = sizeof(values) / sizeof(Position);
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 623.577637);
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 8523.348633);
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 623.577637);
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 8523.348633);
 }
 
 // --------------- Recorded by hand on sailfish, generated by a script -----------------------------
@@ -237,7 +231,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingUpSlow1) {
     // Sailfish - fling up - slow - 1
-    Position values[] = {
+    std::vector<Position> values = {
         { 235089067457000, 528.00, 983.00 },
         { 235089084684000, 527.00, 981.00 },
         { 235089093349000, 527.00, 977.00 },
@@ -255,17 +249,16 @@
         { 235089160784000, 559.00, 851.00 },
         { 235089162955851, 560.66, 843.82 },
     };
-    size_t count = sizeof(values) / sizeof(Position);
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 872.794617); // impulse
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 951.698181); // lsq2
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3604.819336); // impulse
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3044.966064); // lsq2
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 872.794617); // impulse
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 951.698181); // lsq2
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -3604.819336); // impulse
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -3044.966064); // lsq2
 }
 
 
 TEST_F(VelocityTrackerTest, SailfishFlingUpSlow2) {
     // Sailfish - fling up - slow - 2
-    Position values[] = {
+    std::vector<Position> values = {
         { 235110560704000, 522.00, 1107.00 },
         { 235110575764000, 522.00, 1107.00 },
         { 235110584385000, 522.00, 1107.00 },
@@ -284,15 +277,14 @@
         { 235110655089581, 525.54, 1000.19 },
         { 235110660368000, 530.00, 980.00 },
     };
-    size_t count = sizeof(values) / sizeof(Position);
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -4096.583008); // impulse
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3455.094238); // lsq2
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -4096.583008); // impulse
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -3455.094238); // lsq2
 }
 
 
 TEST_F(VelocityTrackerTest, SailfishFlingUpSlow3) {
     // Sailfish - fling up - slow - 3
-    Position values[] = {
+    std::vector<Position> values = {
         { 792536237000, 580.00, 1317.00 },
         { 792541538987, 580.63, 1311.94 },
         { 792544613000, 581.00, 1309.00 },
@@ -312,17 +304,16 @@
         { 792625653873, 617.32, 1121.73 },
         { 792629200000, 619.00, 1115.00 },
     };
-    size_t count = sizeof(values) / sizeof(Position);
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 574.33429); // impulse
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 617.40564); // lsq2
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -2361.982666); // impulse
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -2500.055664); // lsq2
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 574.33429); // impulse
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 617.40564); // lsq2
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -2361.982666); // impulse
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -2500.055664); // lsq2
 }
 
 
 TEST_F(VelocityTrackerTest, SailfishFlingUpFaster1) {
     // Sailfish - fling up - faster - 1
-    Position values[] = {
+    std::vector<Position> values = {
         { 235160420675000, 610.00, 1042.00 },
         { 235160428220000, 609.00, 1026.00 },
         { 235160436544000, 609.00, 1024.00 },
@@ -342,17 +333,16 @@
         { 235160512603000, 670.00, 837.00 },
         { 235160520366000, 679.00, 814.00 },
     };
-    size_t count = sizeof(values) / sizeof(Position);
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 1274.141724); // impulse
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 1438.53186); // lsq2
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3877.35498); // impulse
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3695.859619); // lsq2
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 1274.141724); // impulse
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 1438.53186); // lsq2
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -3877.35498); // impulse
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -3695.859619); // lsq2
 }
 
 
 TEST_F(VelocityTrackerTest, SailfishFlingUpFaster2) {
     // Sailfish - fling up - faster - 2
-    Position values[] = {
+    std::vector<Position> values = {
         { 847153808000, 576.00, 1264.00 },
         { 847171174000, 576.00, 1262.00 },
         { 847179640000, 576.00, 1257.00 },
@@ -368,15 +358,14 @@
         { 847235701400, 607.56, 1103.83 },
         { 847237986000, 610.00, 1095.00 },
     };
-    size_t count = sizeof(values) / sizeof(Position);
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -4280.07959); // impulse
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -4241.004395); // lsq2
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -4280.07959); // impulse
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -4241.004395); // lsq2
 }
 
 
 TEST_F(VelocityTrackerTest, SailfishFlingUpFaster3) {
     // Sailfish - fling up - faster - 3
-    Position values[] = {
+    std::vector<Position> values = {
         { 235200532789000, 507.00, 1084.00 },
         { 235200549221000, 507.00, 1083.00 },
         { 235200557841000, 507.00, 1081.00 },
@@ -392,15 +381,14 @@
         { 235200608527086, 563.02, 910.94 },
         { 235200616933000, 590.00, 844.00 },
     };
-    size_t count = sizeof(values) / sizeof(Position);
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -8715.686523); // impulse
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -7639.026367); // lsq2
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -8715.686523); // impulse
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -7639.026367); // lsq2
 }
 
 
 TEST_F(VelocityTrackerTest, SailfishFlingUpFast1) {
     // Sailfish - fling up - fast - 1
-    Position values[] = {
+    std::vector<Position> values = {
         { 920922149000, 561.00, 1412.00 },
         { 920930185000, 559.00, 1377.00 },
         { 920930262463, 558.98, 1376.66 },
@@ -415,17 +403,16 @@
         { 920980906000, 672.00, 993.00 },
         { 920989261000, 715.00, 903.00 },
     };
-    size_t count = sizeof(values) / sizeof(Position);
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 5670.329102); // impulse
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 5991.866699); // lsq2
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -13021.101562); // impulse
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -15093.995117); // lsq2
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 5670.329102); // impulse
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 5991.866699); // lsq2
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -13021.101562); // impulse
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -15093.995117); // lsq2
 }
 
 
 TEST_F(VelocityTrackerTest, SailfishFlingUpFast2) {
     // Sailfish - fling up - fast - 2
-    Position values[] = {
+    std::vector<Position> values = {
         { 235247153233000, 518.00, 1168.00 },
         { 235247170452000, 517.00, 1167.00 },
         { 235247178908000, 515.00, 1159.00 },
@@ -438,15 +425,14 @@
         { 235247213222491, 574.72, 778.45 },
         { 235247220736000, 620.00, 641.00 },
     };
-    size_t count = sizeof(values) / sizeof(Position);
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -20286.958984); // impulse
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -20494.587891); // lsq2
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -20286.958984); // impulse
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -20494.587891); // lsq2
 }
 
 
 TEST_F(VelocityTrackerTest, SailfishFlingUpFast3) {
     // Sailfish - fling up - fast - 3
-    Position values[] = {
+    std::vector<Position> values = {
         { 235302568736000, 529.00, 1167.00 },
         { 235302576644000, 523.00, 1140.00 },
         { 235302579395063, 520.91, 1130.61 },
@@ -457,15 +443,14 @@
         { 235302610545000, 652.00, 605.00 },
         { 235302613019881, 679.26, 526.73 },
     };
-    size_t count = sizeof(values) / sizeof(Position);
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -39295.941406); // impulse
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -36461.421875); // lsq2
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -39295.941406); // impulse
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -36461.421875); // lsq2
 }
 
 
 TEST_F(VelocityTrackerTest, SailfishFlingDownSlow1) {
     // Sailfish - fling down - slow - 1
-    Position values[] = {
+    std::vector<Position> values = {
         { 235655749552755, 582.00, 432.49 },
         { 235655750638000, 582.00, 433.00 },
         { 235655758865000, 582.00, 440.00 },
@@ -485,17 +470,16 @@
         { 235655834541000, 566.00, 623.00 },
         { 235655842893000, 563.00, 649.00 },
     };
-    size_t count = sizeof(values) / sizeof(Position);
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -419.749695); // impulse
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -398.303894); // lsq2
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 3309.016357); // impulse
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 3969.099854); // lsq2
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -419.749695); // impulse
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -398.303894); // lsq2
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 3309.016357); // impulse
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 3969.099854); // lsq2
 }
 
 
 TEST_F(VelocityTrackerTest, SailfishFlingDownSlow2) {
     // Sailfish - fling down - slow - 2
-    Position values[] = {
+    std::vector<Position> values = {
         { 235671152083370, 485.24, 558.28 },
         { 235671154126000, 485.00, 559.00 },
         { 235671162497000, 484.00, 566.00 },
@@ -515,17 +499,16 @@
         { 235671238098000, 472.00, 765.00 },
         { 235671246532000, 470.00, 799.00 },
     };
-    size_t count = sizeof(values) / sizeof(Position);
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -262.80426); // impulse
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -243.665344); // lsq2
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4215.682129); // impulse
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4587.986816); // lsq2
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -262.80426); // impulse
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -243.665344); // lsq2
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 4215.682129); // impulse
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 4587.986816); // lsq2
 }
 
 
 TEST_F(VelocityTrackerTest, SailfishFlingDownSlow3) {
     // Sailfish - fling down - slow - 3
-    Position values[] = {
+    std::vector<Position> values = {
         { 170983201000, 557.00, 533.00 },
         { 171000668000, 556.00, 534.00 },
         { 171007359750, 554.73, 535.27 },
@@ -538,17 +521,16 @@
         { 171043147000, 541.00, 571.00 },
         { 171051052000, 536.00, 586.00 },
     };
-    size_t count = sizeof(values) / sizeof(Position);
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -723.413513); // impulse
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -651.038452); // lsq2
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 2091.502441); // impulse
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 1934.517456); // lsq2
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -723.413513); // impulse
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -651.038452); // lsq2
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 2091.502441); // impulse
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 1934.517456); // lsq2
 }
 
 
 TEST_F(VelocityTrackerTest, SailfishFlingDownFaster1) {
     // Sailfish - fling down - faster - 1
-    Position values[] = {
+    std::vector<Position> values = {
         { 235695280333000, 558.00, 451.00 },
         { 235695283971237, 558.43, 454.45 },
         { 235695289038000, 559.00, 462.00 },
@@ -568,15 +550,14 @@
         { 235695368118682, 562.24, 722.52 },
         { 235695373403000, 564.00, 744.00 },
     };
-    size_t count = sizeof(values) / sizeof(Position);
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4254.639648); // impulse
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4698.415039); // lsq2
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 4254.639648); // impulse
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 4698.415039); // lsq2
 }
 
 
 TEST_F(VelocityTrackerTest, SailfishFlingDownFaster2) {
     // Sailfish - fling down - faster - 2
-    Position values[] = {
+    std::vector<Position> values = {
         { 235709624766000, 535.00, 579.00 },
         { 235709642256000, 534.00, 580.00 },
         { 235709643350278, 533.94, 580.06 },
@@ -593,17 +574,16 @@
         { 235709709830000, 512.00, 739.00 },
         { 235709710626776, 511.72, 741.85 },
     };
-    size_t count = sizeof(values) / sizeof(Position);
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -430.440247); // impulse
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -447.600311); // lsq2
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 3953.859375); // impulse
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4316.155273); // lsq2
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -430.440247); // impulse
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -447.600311); // lsq2
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 3953.859375); // impulse
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 4316.155273); // lsq2
 }
 
 
 TEST_F(VelocityTrackerTest, SailfishFlingDownFaster3) {
     // Sailfish - fling down - faster - 3
-    Position values[] = {
+    std::vector<Position> values = {
         { 235727628927000, 540.00, 440.00 },
         { 235727636810000, 537.00, 454.00 },
         { 235727646176000, 536.00, 454.00 },
@@ -622,15 +602,14 @@
         { 235727720880776, 516.33, 655.36 },
         { 235727721580000, 516.00, 658.00 },
     };
-    size_t count = sizeof(values) / sizeof(Position);
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4484.617676); // impulse
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4927.92627); // lsq2
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 4484.617676); // impulse
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 4927.92627); // lsq2
 }
 
 
 TEST_F(VelocityTrackerTest, SailfishFlingDownFast1) {
     // Sailfish - fling down - fast - 1
-    Position values[] = {
+    std::vector<Position> values = {
         { 235762352849000, 467.00, 286.00 },
         { 235762360250000, 443.00, 344.00 },
         { 235762362787412, 434.77, 363.89 },
@@ -641,15 +620,14 @@
         { 235762394133000, 406.00, 648.00 },
         { 235762396429369, 404.37, 680.67 },
     };
-    size_t count = sizeof(values) / sizeof(Position);
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 19084.931641); // impulse
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 16064.685547); // lsq2
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 19084.931641); // impulse
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 16064.685547); // lsq2
 }
 
 
 TEST_F(VelocityTrackerTest, SailfishFlingDownFast2) {
     // Sailfish - fling down - fast - 2
-    Position values[] = {
+    std::vector<Position> values = {
         { 235772487188000, 576.00, 204.00 },
         { 235772495159000, 553.00, 236.00 },
         { 235772503568000, 551.00, 240.00 },
@@ -660,15 +638,14 @@
         { 235772529174000, 498.00, 451.00 },
         { 235772537635000, 484.00, 589.00 },
     };
-    size_t count = sizeof(values) / sizeof(Position);
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 18660.048828); // impulse
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 16918.439453); // lsq2
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 18660.048828); // impulse
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 16918.439453); // lsq2
 }
 
 
 TEST_F(VelocityTrackerTest, SailfishFlingDownFast3) {
     // Sailfish - fling down - fast - 3
-    Position values[] = {
+    std::vector<Position> values = {
         { 507650295000, 628.00, 233.00 },
         { 507658234000, 605.00, 269.00 },
         { 507666784000, 601.00, 274.00 },
@@ -680,11 +657,10 @@
         { 507700707000, 454.00, 792.00 },
         { 507703352649, 443.71, 857.77 },
     };
-    size_t count = sizeof(values) / sizeof(Position);
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -6772.508301); // impulse
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -6388.48877); // lsq2
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 29765.908203); // impulse
-    computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 28354.796875); // lsq2
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -6772.508301); // impulse
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -6388.48877); // lsq2
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 29765.908203); // impulse
+    computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 28354.796875); // lsq2
 }
 
 /*
@@ -711,7 +687,7 @@
  * In the test, we would convert these coefficients to (0*(1E3)^0, 0*(1E3)^1, 1*(1E3)^2).
  */
 TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Constant) {
-    Position values[] = {
+    std::vector<Position> values = {
         { 0000000, 1, 1 }, // 0 s
         { 1000000, 1, 1 }, // 0.001 s
         { 2000000, 1, 1 }, // 0.002 s
@@ -721,15 +697,14 @@
     // -0.002, 1
     // -0.001, 1
     // -0.000, 1
-    size_t count = sizeof(values) / sizeof(Position);
-    computeAndCheckQuadraticEstimate(values, count, std::array<float, 3>({1, 0, 0}));
+    computeAndCheckQuadraticEstimate(values, std::array<float, 3>({1, 0, 0}));
 }
 
 /*
  * Straight line y = x :: the constant and quadratic coefficients are zero.
  */
 TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Linear) {
-    Position values[] = {
+    std::vector<Position> values = {
         { 0000000, -2, -2 },
         { 1000000, -1, -1 },
         { 2000000, -0, -0 },
@@ -739,15 +714,14 @@
     // -0.002, -2
     // -0.001, -1
     // -0.000,  0
-    size_t count = sizeof(values) / sizeof(Position);
-    computeAndCheckQuadraticEstimate(values, count, std::array<float, 3>({0, 1E3, 0}));
+    computeAndCheckQuadraticEstimate(values, std::array<float, 3>({0, 1E3, 0}));
 }
 
 /*
  * Parabola
  */
 TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic) {
-    Position values[] = {
+    std::vector<Position> values = {
         { 0000000, 1, 1 },
         { 1000000, 4, 4 },
         { 2000000, 8, 8 },
@@ -757,15 +731,14 @@
     // -0.002, 1
     // -0.001, 4
     // -0.000, 8
-    size_t count = sizeof(values) / sizeof(Position);
-    computeAndCheckQuadraticEstimate(values, count, std::array<float, 3>({8, 4.5E3, 0.5E6}));
+    computeAndCheckQuadraticEstimate(values, std::array<float, 3>({8, 4.5E3, 0.5E6}));
 }
 
 /*
  * Parabola
  */
 TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic2) {
-    Position values[] = {
+    std::vector<Position> values = {
         { 0000000, 1, 1 },
         { 1000000, 4, 4 },
         { 2000000, 9, 9 },
@@ -775,15 +748,14 @@
     // -0.002, 1
     // -0.001, 4
     // -0.000, 9
-    size_t count = sizeof(values) / sizeof(Position);
-    computeAndCheckQuadraticEstimate(values, count, std::array<float, 3>({9, 6E3, 1E6}));
+    computeAndCheckQuadraticEstimate(values, std::array<float, 3>({9, 6E3, 1E6}));
 }
 
 /*
  * Parabola :: y = x^2 :: the constant and linear coefficients are zero.
  */
 TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic3) {
-    Position values[] = {
+    std::vector<Position> values = {
         { 0000000, 4, 4 },
         { 1000000, 1, 1 },
         { 2000000, 0, 0 },
@@ -793,8 +765,7 @@
     // -0.002, 4
     // -0.001, 1
     // -0.000, 0
-    size_t count = sizeof(values) / sizeof(Position);
-    computeAndCheckQuadraticEstimate(values, count, std::array<float, 3>({0, 0E3, 1E6}));
+    computeAndCheckQuadraticEstimate(values, std::array<float, 3>({0, 0E3, 1E6}));
 }
 
 } // namespace android
diff --git a/services/bufferhub/tests/Android.bp b/services/bufferhub/tests/Android.bp
index 8d29923..3033e70 100644
--- a/services/bufferhub/tests/Android.bp
+++ b/services/bufferhub/tests/Android.bp
@@ -11,6 +11,7 @@
         "-Wall",
         "-Werror",
     ],
+    compile_multilib: "first",
     header_libs: [
         "libdvr_headers",
         "libnativewindow_headers",
diff --git a/services/inputflinger/BlockingQueue.h b/services/inputflinger/BlockingQueue.h
index 4b16027..db9f26e 100644
--- a/services/inputflinger/BlockingQueue.h
+++ b/services/inputflinger/BlockingQueue.h
@@ -43,7 +43,7 @@
      * Blocks execution while queue is empty.
      */
     T pop() {
-        std::unique_lock<std::mutex> lock(mLock);
+        std::unique_lock lock(mLock);
         android::base::ScopedLockAssertion assumeLock(mLock);
         mHasElements.wait(lock, [this]{
                 android::base::ScopedLockAssertion assumeLock(mLock);
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index d288736..bcb1ec5 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -46,6 +46,7 @@
 #include "InputDispatcher.h"
 
 #include <errno.h>
+#include <inttypes.h>
 #include <limits.h>
 #include <sstream>
 #include <stddef.h>
@@ -262,7 +263,7 @@
 
 InputDispatcher::~InputDispatcher() {
     { // acquire lock
-        AutoMutex _l(mLock);
+        std::scoped_lock _l(mLock);
 
         resetKeyRepeatLocked();
         releasePendingEventLocked();
@@ -277,8 +278,8 @@
 void InputDispatcher::dispatchOnce() {
     nsecs_t nextWakeupTime = LONG_LONG_MAX;
     { // acquire lock
-        AutoMutex _l(mLock);
-        mDispatcherIsAliveCondition.broadcast();
+        std::scoped_lock _l(mLock);
+        mDispatcherIsAlive.notify_all();
 
         // Run a dispatch loop if there are no pending commands.
         // The dispatch loop might enqueue commands to run afterwards.
@@ -697,7 +698,7 @@
 #if DEBUG_DISPATCH_CYCLE
         ALOGD("Injected inbound event was dropped.");
 #endif
-        setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
+        setInjectionResult(entry, INPUT_EVENT_INJECTION_FAILED);
     }
     if (entry == mNextUnblockedEvent) {
         mNextUnblockedEvent = nullptr;
@@ -851,7 +852,7 @@
 
     // Clean up if dropping the event.
     if (*dropReason != DROP_REASON_NOT_DROPPED) {
-        setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY
+        setInjectionResult(entry, *dropReason == DROP_REASON_POLICY
                 ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
         mReporter->reportDroppedKey(entry->sequenceNum);
         return true;
@@ -865,7 +866,7 @@
         return false;
     }
 
-    setInjectionResultLocked(entry, injectionResult);
+    setInjectionResult(entry, injectionResult);
     if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
         return true;
     }
@@ -901,7 +902,7 @@
 
     // Clean up if dropping the event.
     if (*dropReason != DROP_REASON_NOT_DROPPED) {
-        setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY
+        setInjectionResult(entry, *dropReason == DROP_REASON_POLICY
                 ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
         return true;
     }
@@ -926,7 +927,7 @@
         return false;
     }
 
-    setInjectionResultLocked(entry, injectionResult);
+    setInjectionResult(entry, injectionResult);
     if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
         if (injectionResult != INPUT_EVENT_INJECTION_PERMISSION_DENIED) {
             CancelationOptions::Mode mode(isPointerEvent ?
@@ -1972,17 +1973,17 @@
     bool wasEmpty = connection->outboundQueue.isEmpty();
 
     // Enqueue dispatch entries for the requested modes.
-    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
+    enqueueDispatchEntry(connection, eventEntry, inputTarget,
             InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
-    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
+    enqueueDispatchEntry(connection, eventEntry, inputTarget,
             InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
-    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
+    enqueueDispatchEntry(connection, eventEntry, inputTarget,
             InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
-    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
+    enqueueDispatchEntry(connection, eventEntry, inputTarget,
             InputTarget::FLAG_DISPATCH_AS_IS);
-    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
+    enqueueDispatchEntry(connection, eventEntry, inputTarget,
             InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
-    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
+    enqueueDispatchEntry(connection, eventEntry, inputTarget,
             InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);
 
     // If the outbound queue was previously empty, start the dispatch cycle going.
@@ -1991,7 +1992,7 @@
     }
 }
 
-void InputDispatcher::enqueueDispatchEntryLocked(
+void InputDispatcher::enqueueDispatchEntry(
         const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
         int32_t dispatchMode) {
     int32_t inputTargetFlags = inputTarget->flags;
@@ -2226,9 +2227,9 @@
 #endif
 
     // Clear the dispatch queues.
-    drainDispatchQueueLocked(&connection->outboundQueue);
+    drainDispatchQueue(&connection->outboundQueue);
     traceOutboundQueueLength(connection);
-    drainDispatchQueueLocked(&connection->waitQueue);
+    drainDispatchQueue(&connection->waitQueue);
     traceWaitQueueLength(connection);
 
     // The connection appears to be unrecoverably broken.
@@ -2243,16 +2244,16 @@
     }
 }
 
-void InputDispatcher::drainDispatchQueueLocked(Queue<DispatchEntry>* queue) {
+void InputDispatcher::drainDispatchQueue(Queue<DispatchEntry>* queue) {
     while (!queue->isEmpty()) {
         DispatchEntry* dispatchEntry = queue->dequeueAtHead();
-        releaseDispatchEntryLocked(dispatchEntry);
+        releaseDispatchEntry(dispatchEntry);
     }
 }
 
-void InputDispatcher::releaseDispatchEntryLocked(DispatchEntry* dispatchEntry) {
+void InputDispatcher::releaseDispatchEntry(DispatchEntry* dispatchEntry) {
     if (dispatchEntry->hasForegroundTarget()) {
-        decrementPendingForegroundDispatchesLocked(dispatchEntry->eventEntry);
+        decrementPendingForegroundDispatches(dispatchEntry->eventEntry);
     }
     delete dispatchEntry;
 }
@@ -2261,7 +2262,7 @@
     InputDispatcher* d = static_cast<InputDispatcher*>(data);
 
     { // acquire lock
-        AutoMutex _l(d->mLock);
+        std::scoped_lock _l(d->mLock);
 
         ssize_t connectionIndex = d->mConnectionsByFd.indexOfKey(fd);
         if (connectionIndex < 0) {
@@ -2399,7 +2400,7 @@
             target.inputChannel = connection->inputChannel;
             target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
 
-            enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
+            enqueueDispatchEntry(connection, cancelationEventEntry, // increments ref
                     &target, InputTarget::FLAG_DISPATCH_AS_IS);
 
             cancelationEventEntry->release();
@@ -2509,7 +2510,7 @@
 
     bool needWake;
     { // acquire lock
-        AutoMutex _l(mLock);
+        std::scoped_lock _l(mLock);
 
         ConfigurationChangedEntry* newEntry =
                 new ConfigurationChangedEntry(args->sequenceNum, args->eventTime);
@@ -2537,7 +2538,7 @@
             newKeyCode = AKEYCODE_HOME;
         }
         if (newKeyCode != AKEYCODE_UNKNOWN) {
-            AutoMutex _l(mLock);
+            std::scoped_lock _l(mLock);
             struct KeyReplacement replacement = {keyCode, deviceId};
             mReplacedKeys.add(replacement, newKeyCode);
             keyCode = newKeyCode;
@@ -2547,7 +2548,7 @@
         // In order to maintain a consistent stream of up and down events, check to see if the key
         // going up is one we've replaced in a down event and haven't yet replaced in an up event,
         // even if the modifier was released between the down and the up events.
-        AutoMutex _l(mLock);
+        std::scoped_lock _l(mLock);
         struct KeyReplacement replacement = {keyCode, deviceId};
         ssize_t index = mReplacedKeys.indexOfKey(replacement);
         if (index >= 0) {
@@ -2742,7 +2743,7 @@
 
     bool needWake;
     { // acquire lock
-        AutoMutex _l(mLock);
+        std::scoped_lock _l(mLock);
 
         DeviceResetEntry* newEntry =
                 new DeviceResetEntry(args->sequenceNum, args->eventTime, args->deviceId);
@@ -2896,7 +2897,7 @@
 
     int32_t injectionResult;
     { // acquire lock
-        AutoMutex _l(mLock);
+        std::unique_lock _l(mLock);
 
         if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
             injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
@@ -2917,7 +2918,7 @@
                     break;
                 }
 
-                mInjectionResultAvailableCondition.waitRelative(mLock, remainingTimeout);
+                mInjectionResultAvailable.wait_for(_l, std::chrono::nanoseconds(remainingTimeout));
             }
 
             if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED
@@ -2937,7 +2938,7 @@
                         break;
                     }
 
-                    mInjectionSyncFinishedCondition.waitRelative(mLock, remainingTimeout);
+                    mInjectionSyncFinished.wait_for(_l, std::chrono::nanoseconds(remainingTimeout));
                 }
             }
         }
@@ -2959,7 +2960,7 @@
             || mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid);
 }
 
-void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t injectionResult) {
+void InputDispatcher::setInjectionResult(EventEntry* entry, int32_t injectionResult) {
     InjectionState* injectionState = entry->injectionState;
     if (injectionState) {
 #if DEBUG_INJECTION
@@ -2988,7 +2989,7 @@
         }
 
         injectionState->injectionResult = injectionResult;
-        mInjectionResultAvailableCondition.broadcast();
+        mInjectionResultAvailable.notify_all();
     }
 }
 
@@ -2999,13 +3000,13 @@
     }
 }
 
-void InputDispatcher::decrementPendingForegroundDispatchesLocked(EventEntry* entry) {
+void InputDispatcher::decrementPendingForegroundDispatches(EventEntry* entry) {
     InjectionState* injectionState = entry->injectionState;
     if (injectionState) {
         injectionState->pendingForegroundDispatches -= 1;
 
         if (injectionState->pendingForegroundDispatches == 0) {
-            mInjectionSyncFinishedCondition.broadcast();
+            mInjectionSyncFinished.notify_all();
         }
     }
 }
@@ -3077,7 +3078,7 @@
     ALOGD("setInputWindows displayId=%" PRId32, displayId);
 #endif
     { // acquire lock
-        AutoMutex _l(mLock);
+        std::scoped_lock _l(mLock);
 
         // Copy old handles for release if they are no longer present.
         const Vector<sp<InputWindowHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);
@@ -3234,7 +3235,7 @@
     ALOGD("setFocusedApplication displayId=%" PRId32, displayId);
 #endif
     { // acquire lock
-        AutoMutex _l(mLock);
+        std::scoped_lock _l(mLock);
 
         sp<InputApplicationHandle> oldFocusedApplicationHandle =
                 getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
@@ -3276,7 +3277,7 @@
     ALOGD("setFocusedDisplay displayId=%" PRId32, displayId);
 #endif
     { // acquire lock
-        AutoMutex _l(mLock);
+        std::scoped_lock _l(mLock);
 
         if (mFocusedDisplayId != displayId) {
             sp<InputWindowHandle> oldFocusedWindowHandle =
@@ -3328,7 +3329,7 @@
 
     bool changed;
     { // acquire lock
-        AutoMutex _l(mLock);
+        std::scoped_lock _l(mLock);
 
         if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) {
             if (mDispatchFrozen && !frozen) {
@@ -3363,7 +3364,7 @@
 #endif
 
     { // acquire lock
-        AutoMutex _l(mLock);
+        std::scoped_lock _l(mLock);
 
         if (mInputFilterEnabled == enabled) {
             return;
@@ -3386,7 +3387,7 @@
     }
 
     { // acquire lock
-        AutoMutex _l(mLock);
+        std::scoped_lock _l(mLock);
 
         sp<InputWindowHandle> fromWindowHandle = getWindowHandleLocked(fromToken);
         sp<InputWindowHandle> toWindowHandle = getWindowHandleLocked(toToken);
@@ -3730,7 +3731,7 @@
 #endif
 
     { // acquire lock
-        AutoMutex _l(mLock);
+        std::scoped_lock _l(mLock);
 
         // If InputWindowHandle is null and displayId is not ADISPLAY_ID_NONE,
         // treat inputChannel as monitor channel for displayId.
@@ -3772,7 +3773,7 @@
 #endif
 
     { // acquire lock
-        AutoMutex _l(mLock);
+        std::scoped_lock _l(mLock);
 
         status_t status = unregisterInputChannelLocked(inputChannel, false /*notify*/);
         if (status) {
@@ -4033,7 +4034,7 @@
                 connection->outboundQueue.enqueueAtHead(dispatchEntry);
                 traceOutboundQueueLength(connection);
             } else {
-                releaseDispatchEntryLocked(dispatchEntry);
+                releaseDispatchEntry(dispatchEntry);
             }
         }
 
@@ -4271,7 +4272,7 @@
 }
 
 void InputDispatcher::dump(std::string& dump) {
-    AutoMutex _l(mLock);
+    std::scoped_lock _l(mLock);
 
     dump += "Input Dispatcher State:\n";
     dumpDispatchStateLocked(dump);
@@ -4284,10 +4285,9 @@
 
 void InputDispatcher::monitor() {
     // Acquire and release the lock to ensure that the dispatcher has not deadlocked.
-    mLock.lock();
+    std::unique_lock _l(mLock);
     mLooper->wake();
-    mDispatcherIsAliveCondition.wait(mLock);
-    mLock.unlock();
+    mDispatcherIsAlive.wait(_l);
 }
 
 
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
index 9d8919b..24ae32f 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -17,6 +17,7 @@
 #ifndef _UI_INPUT_DISPATCHER_H
 #define _UI_INPUT_DISPATCHER_H
 
+#include <condition_variable>
 #include <input/Input.h>
 #include <input/InputApplication.h>
 #include <input/InputTransport.h>
@@ -884,9 +885,9 @@
     sp<InputDispatcherPolicyInterface> mPolicy;
     InputDispatcherConfiguration mConfig;
 
-    Mutex mLock;
+    std::mutex mLock;
 
-    Condition mDispatcherIsAliveCondition;
+    std::condition_variable mDispatcherIsAlive;
 
     sp<Looper> mLooper;
 
@@ -944,13 +945,13 @@
             GUARDED_BY(mLock);
 
     // Event injection and synchronization.
-    Condition mInjectionResultAvailableCondition;
+    std::condition_variable mInjectionResultAvailable;
     bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid);
-    void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult);
+    void setInjectionResult(EventEntry* entry, int32_t injectionResult);
 
-    Condition mInjectionSyncFinishedCondition;
+    std::condition_variable mInjectionSyncFinished;
     void incrementPendingForegroundDispatches(EventEntry* entry);
-    void decrementPendingForegroundDispatchesLocked(EventEntry* entry);
+    void decrementPendingForegroundDispatches(EventEntry* entry);
 
     // Key repeat tracking.
     struct KeyRepeatState {
@@ -1136,7 +1137,7 @@
             EventEntry* eventEntry, const InputTarget* inputTarget) REQUIRES(mLock);
     void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection,
             EventEntry* eventEntry, const InputTarget* inputTarget) REQUIRES(mLock);
-    void enqueueDispatchEntryLocked(const sp<Connection>& connection,
+    void enqueueDispatchEntry(const sp<Connection>& connection,
             EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode);
     void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection)
             REQUIRES(mLock);
@@ -1144,8 +1145,8 @@
             uint32_t seq, bool handled) REQUIRES(mLock);
     void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
             bool notify) REQUIRES(mLock);
-    void drainDispatchQueueLocked(Queue<DispatchEntry>* queue);
-    void releaseDispatchEntryLocked(DispatchEntry* dispatchEntry);
+    void drainDispatchQueue(Queue<DispatchEntry>* queue);
+    void releaseDispatchEntry(DispatchEntry* dispatchEntry);
     static int handleReceiveCallback(int fd, int events, void* data);
 
     void synthesizeCancelationEventsForAllConnectionsLocked(
@@ -1197,7 +1198,7 @@
     bool afterKeyEventLockedInterruptible(const sp<Connection>& connection,
             DispatchEntry* dispatchEntry, KeyEntry* keyEntry, bool handled) REQUIRES(mLock);
     bool afterMotionEventLockedInterruptible(const sp<Connection>& connection,
-            DispatchEntry* dispatchEntry, MotionEntry* motionEntry, bool handled);
+            DispatchEntry* dispatchEntry, MotionEntry* motionEntry, bool handled) REQUIRES(mLock);
     void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
     void initializeKeyEvent(KeyEvent* event, const KeyEntry* entry);
 
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 7d2dcba..cc78ece 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -496,7 +496,7 @@
 
 BufferLayerConsumer::Image::~Image() {
     if (mGraphicBuffer != nullptr) {
-        ALOGE("Destroying buffer: %" PRId64, mGraphicBuffer->getId());
+        ALOGV("Destroying buffer: %" PRId64, mGraphicBuffer->getId());
         mRE.unbindExternalTextureBuffer(mGraphicBuffer->getId());
     }
 }
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index da34083..e4179ee 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -393,9 +393,8 @@
     // Add this buffer from our internal queue tracker
     { // Autolock scope
         if (mFlinger->mUseSmart90ForVideo) {
-            // Report the requested present time to the Scheduler, if the feature is turned on.
-            mFlinger->mScheduler->addFramePresentTimeForLayer(item.mTimestamp,
-                                                              item.mIsAutoTimestamp, mName.c_str());
+            // Report mApi ID for each layer.
+            mFlinger->mScheduler->addNativeWindowApi(item.mApi);
         }
 
         Mutex::Autolock lock(mQueueItemLock);
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 2d6d33c..04e8796 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -92,12 +92,10 @@
 
 void LayerProtoHelper::writeToProto(const ui::Transform& transform,
                                     TransformProto* transformProto) {
-    const uint32_t type = transform.getType();
+    const uint32_t type = transform.getType() | (transform.getOrientation() << 8);
     transformProto->set_type(type);
 
-    if (type &
-        (ui::Transform::SCALE | ui::Transform::ROTATE | ui::Transform::TRANSLATE |
-         ui::Transform::UNKNOWN)) {
+    if (type & (ui::Transform::SCALE | ui::Transform::UNKNOWN)) {
         transformProto->set_dsdx(transform[0][0]);
         transformProto->set_dtdx(transform[0][1]);
         transformProto->set_dsdy(transform[1][0]);
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index bd8548c..4f0b3bb 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -21,27 +21,168 @@
 
 #include "RegionSamplingThread.h"
 
+#include <cutils/properties.h>
 #include <gui/IRegionSamplingListener.h>
 #include <utils/Trace.h>
+#include <string>
 
 #include "DisplayDevice.h"
 #include "Layer.h"
 #include "SurfaceFlinger.h"
 
 namespace android {
+using namespace std::chrono_literals;
 
 template <typename T>
 struct SpHash {
     size_t operator()(const sp<T>& p) const { return std::hash<T*>()(p.get()); }
 };
 
-RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger) : mFlinger(flinger) {
-    std::lock_guard threadLock(mThreadMutex);
-    mThread = std::thread([this]() { threadMain(); });
-    pthread_setname_np(mThread.native_handle(), "RegionSamplingThread");
+constexpr auto lumaSamplingStepTag = "LumaSamplingStep";
+enum class samplingStep {
+    noWorkNeeded,
+    idleTimerWaiting,
+    waitForZeroPhase,
+    waitForSamplePhase,
+    sample
+};
+
+constexpr auto defaultRegionSamplingOffset = -3ms;
+constexpr auto defaultRegionSamplingPeriod = 100ms;
+constexpr auto defaultRegionSamplingTimerTimeout = 100ms;
+// TODO: (b/127403193) duration to string conversion could probably be constexpr
+template <typename Rep, typename Per>
+inline std::string toNsString(std::chrono::duration<Rep, Per> t) {
+    return std::to_string(std::chrono::duration_cast<std::chrono::nanoseconds>(t).count());
 }
 
+RegionSamplingThread::EnvironmentTimingTunables::EnvironmentTimingTunables() {
+    char value[PROPERTY_VALUE_MAX] = {};
+
+    property_get("debug.sf.region_sampling_offset_ns", value,
+                 toNsString(defaultRegionSamplingOffset).c_str());
+    int const samplingOffsetNsRaw = atoi(value);
+
+    property_get("debug.sf.region_sampling_period_ns", value,
+                 toNsString(defaultRegionSamplingPeriod).c_str());
+    int const samplingPeriodNsRaw = atoi(value);
+
+    property_get("debug.sf.region_sampling_timer_timeout_ns", value,
+                 toNsString(defaultRegionSamplingTimerTimeout).c_str());
+    int const samplingTimerTimeoutNsRaw = atoi(value);
+
+    if ((samplingPeriodNsRaw < 0) || (samplingTimerTimeoutNsRaw < 0)) {
+        ALOGW("User-specified sampling tuning options nonsensical. Using defaults");
+        mSamplingOffset = defaultRegionSamplingOffset;
+        mSamplingPeriod = defaultRegionSamplingPeriod;
+        mSamplingTimerTimeout = defaultRegionSamplingTimerTimeout;
+    } else {
+        mSamplingOffset = std::chrono::nanoseconds(samplingOffsetNsRaw);
+        mSamplingPeriod = std::chrono::nanoseconds(samplingPeriodNsRaw);
+        mSamplingTimerTimeout = std::chrono::nanoseconds(samplingTimerTimeoutNsRaw);
+    }
+}
+
+struct SamplingOffsetCallback : DispSync::Callback {
+    SamplingOffsetCallback(RegionSamplingThread& samplingThread, Scheduler& scheduler,
+                           std::chrono::nanoseconds targetSamplingOffset)
+          : mRegionSamplingThread(samplingThread),
+            mScheduler(scheduler),
+            mTargetSamplingOffset(targetSamplingOffset) {}
+
+    ~SamplingOffsetCallback() { stopVsyncListener(); }
+
+    SamplingOffsetCallback(const SamplingOffsetCallback&) = delete;
+    SamplingOffsetCallback& operator=(const SamplingOffsetCallback&) = delete;
+
+    void startVsyncListener() {
+        std::lock_guard lock(mMutex);
+        if (mVsyncListening) return;
+
+        mPhaseIntervalSetting = Phase::ZERO;
+        mScheduler.withPrimaryDispSync([this](android::DispSync& sync) {
+            sync.addEventListener("SamplingThreadDispSyncListener", 0, this);
+        });
+        mVsyncListening = true;
+    }
+
+    void stopVsyncListener() {
+        std::lock_guard lock(mMutex);
+        stopVsyncListenerLocked();
+    }
+
+private:
+    void stopVsyncListenerLocked() /*REQUIRES(mMutex)*/ {
+        if (!mVsyncListening) return;
+
+        mScheduler.withPrimaryDispSync(
+                [this](android::DispSync& sync) { sync.removeEventListener(this); });
+        mVsyncListening = false;
+    }
+
+    void onDispSyncEvent(nsecs_t /* when */) final {
+        std::unique_lock<decltype(mMutex)> lock(mMutex);
+
+        if (mPhaseIntervalSetting == Phase::ZERO) {
+            ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase));
+            mPhaseIntervalSetting = Phase::SAMPLING;
+            mScheduler.withPrimaryDispSync([this](android::DispSync& sync) {
+                sync.changePhaseOffset(this, mTargetSamplingOffset.count());
+            });
+            return;
+        }
+
+        if (mPhaseIntervalSetting == Phase::SAMPLING) {
+            mPhaseIntervalSetting = Phase::ZERO;
+            mScheduler.withPrimaryDispSync(
+                    [this](android::DispSync& sync) { sync.changePhaseOffset(this, 0); });
+            stopVsyncListenerLocked();
+            lock.unlock();
+            mRegionSamplingThread.notifySamplingOffset();
+            return;
+        }
+    }
+
+    RegionSamplingThread& mRegionSamplingThread;
+    Scheduler& mScheduler;
+    const std::chrono::nanoseconds mTargetSamplingOffset;
+    mutable std::mutex mMutex;
+    enum class Phase {
+        ZERO,
+        SAMPLING
+    } mPhaseIntervalSetting /*GUARDED_BY(mMutex) macro doesnt work with unique_lock?*/
+            = Phase::ZERO;
+    bool mVsyncListening /*GUARDED_BY(mMutex)*/ = false;
+};
+
+RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler,
+                                           const TimingTunables& tunables)
+      : mFlinger(flinger),
+        mScheduler(scheduler),
+        mTunables(tunables),
+        mIdleTimer(std::chrono::duration_cast<std::chrono::milliseconds>(
+                           mTunables.mSamplingTimerTimeout),
+                   [] {}, [this] { checkForStaleLuma(); }),
+        mPhaseCallback(std::make_unique<SamplingOffsetCallback>(*this, mScheduler,
+                                                                tunables.mSamplingOffset)),
+        lastSampleTime(0ns) {
+    {
+        std::lock_guard threadLock(mThreadMutex);
+        mThread = std::thread([this]() { threadMain(); });
+        pthread_setname_np(mThread.native_handle(), "RegionSamplingThread");
+    }
+    mIdleTimer.start();
+}
+
+RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler)
+      : RegionSamplingThread(flinger, scheduler,
+                             TimingTunables{defaultRegionSamplingOffset,
+                                            defaultRegionSamplingPeriod,
+                                            defaultRegionSamplingTimerTimeout}) {}
+
 RegionSamplingThread::~RegionSamplingThread() {
+    mIdleTimer.stop();
+
     {
         std::lock_guard lock(mMutex);
         mRunning = false;
@@ -71,8 +212,41 @@
     mDescriptors.erase(wp<IBinder>(IInterface::asBinder(listener)));
 }
 
-void RegionSamplingThread::sampleNow() {
+void RegionSamplingThread::checkForStaleLuma() {
     std::lock_guard lock(mMutex);
+
+    if (mDiscardedFrames) {
+        ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForZeroPhase));
+        mDiscardedFrames = false;
+        mPhaseCallback->startVsyncListener();
+    }
+}
+
+void RegionSamplingThread::notifyNewContent() {
+    doSample();
+}
+
+void RegionSamplingThread::notifySamplingOffset() {
+    doSample();
+}
+
+void RegionSamplingThread::doSample() {
+    std::lock_guard lock(mMutex);
+    auto now = std::chrono::nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC));
+    if (lastSampleTime + mTunables.mSamplingPeriod > now) {
+        ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::idleTimerWaiting));
+        mDiscardedFrames = true;
+        return;
+    }
+
+    ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::sample));
+
+    mDiscardedFrames = false;
+    lastSampleTime = now;
+
+    mIdleTimer.reset();
+    mPhaseCallback->stopVsyncListener();
+
     mSampleRequested = true;
     mCondition.notify_one();
 }
@@ -238,6 +412,7 @@
     for (size_t d = 0; d < activeDescriptors.size(); ++d) {
         activeDescriptors[d].listener->onSampleCollected(lumas[d]);
     }
+    ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::noWorkNeeded));
 }
 
 void RegionSamplingThread::threadMain() {
diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h
index ab06513..d4e57bf 100644
--- a/services/surfaceflinger/RegionSamplingThread.h
+++ b/services/surfaceflinger/RegionSamplingThread.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <chrono>
 #include <condition_variable>
 #include <mutex>
 #include <thread>
@@ -25,17 +26,42 @@
 #include <binder/IBinder.h>
 #include <ui/Rect.h>
 #include <utils/StrongPointer.h>
+#include "Scheduler/IdleTimer.h"
 
 namespace android {
 
 class GraphicBuffer;
 class IRegionSamplingListener;
 class Layer;
+class Scheduler;
 class SurfaceFlinger;
+struct SamplingOffsetCallback;
 
 class RegionSamplingThread : public IBinder::DeathRecipient {
 public:
-    explicit RegionSamplingThread(SurfaceFlinger& flinger);
+    struct TimingTunables {
+        // debug.sf.sampling_offset_ns
+        // When asynchronously collecting sample, the offset, from zero phase in the vsync timeline
+        // at which the sampling should start.
+        std::chrono::nanoseconds mSamplingOffset;
+        // debug.sf.sampling_period_ns
+        // This is the maximum amount of time the luma recieving client
+        // should have to wait for a new luma value after a frame is updated. The inverse of this is
+        // roughly the sampling rate. Sampling system rounds up sub-vsync sampling period to vsync
+        // period.
+        std::chrono::nanoseconds mSamplingPeriod;
+        // debug.sf.sampling_timer_timeout_ns
+        // This is the interval at which the luma sampling system will check that the luma clients
+        // have up to date information. It defaults to the mSamplingPeriod.
+        std::chrono::nanoseconds mSamplingTimerTimeout;
+    };
+    struct EnvironmentTimingTunables : TimingTunables {
+        EnvironmentTimingTunables();
+    };
+    explicit RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler,
+                                  const TimingTunables& tunables);
+    explicit RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler);
+
     ~RegionSamplingThread();
 
     // Add a listener to receive luma notifications. The luma reported via listener will
@@ -44,8 +70,13 @@
                      const sp<IRegionSamplingListener>& listener);
     // Remove the listener to stop receiving median luma notifications.
     void removeListener(const sp<IRegionSamplingListener>& listener);
-    // Instruct the thread to perform a median luma sampling on the layers.
-    void sampleNow();
+
+    // Notifies sampling engine that new content is available. This will trigger a sampling
+    // pass at some point in the future.
+    void notifyNewContent();
+
+    // Notifies the sampling engine that it has a good timing window in which to sample.
+    void notifySamplingOffset();
 
 private:
     struct Descriptor {
@@ -63,12 +94,19 @@
             const sp<GraphicBuffer>& buffer, const Point& leftTop,
             const std::vector<RegionSamplingThread::Descriptor>& descriptors);
 
+    void doSample();
     void binderDied(const wp<IBinder>& who) override;
+    void checkForStaleLuma();
 
     void captureSample() REQUIRES(mMutex);
     void threadMain();
 
     SurfaceFlinger& mFlinger;
+    Scheduler& mScheduler;
+    const TimingTunables mTunables;
+    scheduler::IdleTimer mIdleTimer;
+
+    std::unique_ptr<SamplingOffsetCallback> const mPhaseCallback;
 
     std::mutex mThreadMutex;
     std::thread mThread GUARDED_BY(mThreadMutex);
@@ -79,6 +117,8 @@
     bool mSampleRequested GUARDED_BY(mMutex) = false;
 
     std::unordered_map<wp<IBinder>, Descriptor, WpHash> mDescriptors GUARDED_BY(mMutex);
+    std::chrono::nanoseconds lastSampleTime GUARDED_BY(mMutex);
+    bool mDiscardedFrames GUARDED_BY(mMutex) = false;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
index 1eccf9a..5178836 100644
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -76,9 +76,18 @@
     void updateModel(nsecs_t period, nsecs_t phase, nsecs_t referenceTime) {
         if (mTraceDetailedInfo) ATRACE_CALL();
         Mutex::Autolock lock(mMutex);
-        mPeriod = period;
+
         mPhase = phase;
         mReferenceTime = referenceTime;
+        if (mPeriod != period && mReferenceTime != 0) {
+            // Inflate the reference time to be the most recent predicted
+            // vsync before the current time.
+            const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+            const nsecs_t baseTime = now - mReferenceTime;
+            const nsecs_t numOldPeriods = baseTime / mPeriod;
+            mReferenceTime = mReferenceTime + (numOldPeriods)*mPeriod;
+        }
+        mPeriod = period;
         if (mTraceDetailedInfo) {
             ATRACE_INT64("DispSync:Period", mPeriod);
             ATRACE_INT64("DispSync:Phase", mPhase + mPeriod / 2);
@@ -429,8 +438,16 @@
 
 void DispSync::resetLocked() {
     mPhase = 0;
-    mReferenceTime = 0;
+    const size_t lastSampleIdx = (mFirstResyncSample + mNumResyncSamples - 1) % MAX_RESYNC_SAMPLES;
+    // Keep the most recent sample, when we resync to hardware we'll overwrite this
+    // with a more accurate signal
+    if (mResyncSamples[lastSampleIdx] != 0) {
+        mReferenceTime = mResyncSamples[lastSampleIdx];
+    }
     mModelUpdated = false;
+    for (size_t i = 0; i < MAX_RESYNC_SAMPLES; i++) {
+        mResyncSamples[i] = 0;
+    }
     mNumResyncSamples = 0;
     mFirstResyncSample = 0;
     mNumResyncSamplesSincePresent = 0;
@@ -530,7 +547,6 @@
     Mutex::Autolock lock(mMutex);
     mPeriod = period;
     mPhase = 0;
-    mReferenceTime = 0;
     mThread->updateModel(mPeriod, mPhase, mReferenceTime);
 }
 
diff --git a/services/surfaceflinger/Scheduler/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h
index f629697..6f3bd00 100644
--- a/services/surfaceflinger/Scheduler/DispSync.h
+++ b/services/surfaceflinger/Scheduler/DispSync.h
@@ -213,7 +213,7 @@
     // These member variables are the state used during the resynchronization
     // process to store information about the hardware vsync event times used
     // to compute the model.
-    nsecs_t mResyncSamples[MAX_RESYNC_SAMPLES];
+    nsecs_t mResyncSamples[MAX_RESYNC_SAMPLES] = {0};
     size_t mFirstResyncSample;
     size_t mNumResyncSamples;
     int mNumResyncSamplesSincePresent;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index f3a7f87..0063c8a 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -28,6 +28,7 @@
 #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
 #include <configstore/Utils.h>
 #include <cutils/properties.h>
+#include <system/window.h>
 #include <ui/DisplayStatInfo.h>
 #include <utils/Timers.h>
 #include <utils/Trace.h>
@@ -296,21 +297,33 @@
     mPrimaryDispSync->dump(result);
 }
 
-void Scheduler::addFramePresentTimeForLayer(const nsecs_t framePresentTime, bool isAutoTimestamp,
-                                            const std::string layerName) {
-    // This is V1 logic. It calculates the average FPS based on the timestamp frequency
-    // regardless of which layer the timestamp came from.
-    // For now, the averages and FPS are recorded in the systrace.
-    determineTimestampAverage(isAutoTimestamp, framePresentTime);
-
-    // This is V2 logic. It calculates the average and median timestamp difference based on the
-    // individual layer history. The results are recorded in the systrace.
-    determineLayerTimestampStats(layerName, framePresentTime);
+void Scheduler::addNativeWindowApi(int apiId) {
+    std::lock_guard<std::mutex> lock(mWindowApiHistoryLock);
+    mWindowApiHistory[mApiHistoryCounter] = apiId;
+    mApiHistoryCounter++;
+    mApiHistoryCounter = mApiHistoryCounter % scheduler::ARRAY_SIZE;
 }
 
-void Scheduler::incrementFrameCounter() {
-    std::lock_guard<std::mutex> lock(mLayerHistoryLock);
-    mLayerHistory.incrementCounter();
+void Scheduler::withPrimaryDispSync(std::function<void(DispSync&)> const& fn) {
+    fn(*mPrimaryDispSync);
+}
+
+void Scheduler::updateFpsBasedOnNativeWindowApi() {
+    int mode;
+    {
+        std::lock_guard<std::mutex> lock(mWindowApiHistoryLock);
+        mode = scheduler::calculate_mode(mWindowApiHistory);
+    }
+    ATRACE_INT("NativeWindowApiMode", mode);
+
+    if (mode == NATIVE_WINDOW_API_MEDIA) {
+        // TODO(b/127365162): These callback names are not accurate anymore. Update.
+        mediaChangeRefreshRate(MediaFeatureState::MEDIA_PLAYING);
+        ATRACE_INT("DetectedVideo", 1);
+    } else {
+        mediaChangeRefreshRate(MediaFeatureState::MEDIA_OFF);
+        ATRACE_INT("DetectedVideo", 0);
+    }
 }
 
 void Scheduler::setChangeRefreshRateCallback(
@@ -328,85 +341,6 @@
     }
 }
 
-void Scheduler::determineLayerTimestampStats(const std::string layerName,
-                                             const nsecs_t framePresentTime) {
-    std::vector<int64_t> differencesMs;
-    std::string differencesText = "";
-    {
-        std::lock_guard<std::mutex> lock(mLayerHistoryLock);
-        mLayerHistory.insert(layerName, framePresentTime);
-
-        // Traverse through the layer history, and determine the differences in present times.
-        nsecs_t newestPresentTime = framePresentTime;
-        for (int i = 1; i < mLayerHistory.getSize(); i++) {
-            std::unordered_map<std::string, nsecs_t> layers = mLayerHistory.get(i);
-            for (auto layer : layers) {
-                if (layer.first != layerName) {
-                    continue;
-                }
-                int64_t differenceMs = (newestPresentTime - layer.second) / 1000000;
-                // Dismiss noise.
-                if (differenceMs > 10 && differenceMs < 60) {
-                    differencesMs.push_back(differenceMs);
-                }
-                IF_ALOGV() { differencesText += (std::to_string(differenceMs) + " "); }
-                newestPresentTime = layer.second;
-            }
-        }
-    }
-    ALOGV("Layer %s timestamp intervals: %s", layerName.c_str(), differencesText.c_str());
-
-    if (!differencesMs.empty()) {
-        // Mean/Average is a good indicator for when 24fps videos are playing, because the frames
-        // come in 33, and 49 ms intervals with occasional 41ms.
-        const int64_t meanMs = scheduler::calculate_mean(differencesMs);
-        const auto tagMean = "TimestampMean_" + layerName;
-        ATRACE_INT(tagMean.c_str(), meanMs);
-
-        // Mode and median are good indicators for 30 and 60 fps videos, because the majority of
-        // frames come in 16, or 33 ms intervals.
-        const auto tagMedian = "TimestampMedian_" + layerName;
-        ATRACE_INT(tagMedian.c_str(), scheduler::calculate_median(&differencesMs));
-
-        const auto tagMode = "TimestampMode_" + layerName;
-        ATRACE_INT(tagMode.c_str(), scheduler::calculate_mode(differencesMs));
-    }
-}
-
-void Scheduler::determineTimestampAverage(bool isAutoTimestamp, const nsecs_t framePresentTime) {
-    ATRACE_INT("AutoTimestamp", isAutoTimestamp);
-
-    // Video does not have timestamp automatically set, so we discard timestamps that are
-    // coming in from other sources for now.
-    if (isAutoTimestamp) {
-        return;
-    }
-    int64_t differenceMs = (framePresentTime - mPreviousFrameTimestamp) / 1000000;
-    mPreviousFrameTimestamp = framePresentTime;
-
-    if (differenceMs < 10 || differenceMs > 100) {
-        // Dismiss noise.
-        return;
-    }
-    ATRACE_INT("TimestampDiff", differenceMs);
-
-    mTimeDifferences[mCounter % scheduler::ARRAY_SIZE] = differenceMs;
-    mCounter++;
-    int64_t mean = scheduler::calculate_mean(mTimeDifferences);
-    ATRACE_INT("AutoTimestampMean", mean);
-
-    // TODO(b/113612090): This are current numbers from trial and error while running videos
-    // from YouTube at 24, 30, and 60 fps.
-    if (mean > 14 && mean < 18) {
-        ATRACE_INT("MediaFPS", 60);
-    } else if (mean > 31 && mean < 34) {
-        ATRACE_INT("MediaFPS", 30);
-        return;
-    } else if (mean > 39 && mean < 42) {
-        ATRACE_INT("MediaFPS", 24);
-    }
-}
-
 void Scheduler::resetIdleTimer() {
     if (mIdleTimer) {
         mIdleTimer->reset();
@@ -414,21 +348,15 @@
 }
 
 void Scheduler::resetTimerCallback() {
-    std::lock_guard<std::mutex> lock(mCallbackLock);
-    if (mChangeRefreshRateCallback) {
-        // We do not notify the applications about config changes when idle timer is reset.
-        mChangeRefreshRateCallback(RefreshRateType::PERFORMANCE, ConfigEvent::None);
-        ATRACE_INT("ExpiredIdleTimer", 0);
-    }
+    // We do not notify the applications about config changes when idle timer is reset.
+    timerChangeRefreshRate(IdleTimerState::RESET);
+    ATRACE_INT("ExpiredIdleTimer", 0);
 }
 
 void Scheduler::expiredTimerCallback() {
-    std::lock_guard<std::mutex> lock(mCallbackLock);
-    if (mChangeRefreshRateCallback) {
-        // We do not notify the applications about config changes when idle timer expires.
-        mChangeRefreshRateCallback(RefreshRateType::DEFAULT, ConfigEvent::None);
-        ATRACE_INT("ExpiredIdleTimer", 1);
-    }
+    // We do not notify the applications about config changes when idle timer expires.
+    timerChangeRefreshRate(IdleTimerState::EXPIRED);
+    ATRACE_INT("ExpiredIdleTimer", 1);
 }
 
 std::string Scheduler::doDump() {
@@ -437,4 +365,46 @@
     return stream.str();
 }
 
+void Scheduler::mediaChangeRefreshRate(MediaFeatureState mediaFeatureState) {
+    // Default playback for media is DEFAULT when media is on.
+    RefreshRateType refreshRateType = RefreshRateType::DEFAULT;
+    ConfigEvent configEvent = ConfigEvent::None;
+
+    {
+        std::lock_guard<std::mutex> lock(mFeatureStateLock);
+        mCurrentMediaFeatureState = mediaFeatureState;
+        // Only switch to PERFORMANCE if idle timer was reset, when turning
+        // media off. If the timer is IDLE, stay at DEFAULT.
+        if (mediaFeatureState == MediaFeatureState::MEDIA_OFF &&
+            mCurrentIdleTimerState == IdleTimerState::RESET) {
+            refreshRateType = RefreshRateType::PERFORMANCE;
+        }
+    }
+    changeRefreshRate(refreshRateType, configEvent);
+}
+
+void Scheduler::timerChangeRefreshRate(IdleTimerState idleTimerState) {
+    RefreshRateType refreshRateType = RefreshRateType::DEFAULT;
+    ConfigEvent configEvent = ConfigEvent::None;
+
+    {
+        std::lock_guard<std::mutex> lock(mFeatureStateLock);
+        mCurrentIdleTimerState = idleTimerState;
+        // Only switch to PERFOMANCE if the idle timer was reset, and media is
+        // not playing. Otherwise, stay at DEFAULT.
+        if (idleTimerState == IdleTimerState::RESET &&
+            mCurrentMediaFeatureState == MediaFeatureState::MEDIA_OFF) {
+            refreshRateType = RefreshRateType::PERFORMANCE;
+        }
+    }
+    changeRefreshRate(refreshRateType, configEvent);
+}
+
+void Scheduler::changeRefreshRate(RefreshRateType refreshRateType, ConfigEvent configEvent) {
+    std::lock_guard<std::mutex> lock(mCallbackLock);
+    if (mChangeRefreshRateCallback) {
+        mChangeRefreshRateCallback(refreshRateType, configEvent);
+    }
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 5b9759b..73896d5 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <cstdint>
+#include <functional>
 #include <memory>
 
 #include <ui/DisplayStatInfo.h>
@@ -104,6 +105,9 @@
     // Getter methods.
     EventThread* getEventThread(const sp<ConnectionHandle>& handle);
 
+    // Provides access to the DispSync object for the primary display.
+    void withPrimaryDispSync(std::function<void(DispSync&)> const& fn);
+
     sp<EventThreadConnection> getEventConnection(const sp<ConnectionHandle>& handle);
 
     // Should be called when receiving a hotplug event.
@@ -141,11 +145,11 @@
     void addPresentFence(const std::shared_ptr<FenceTime>& fenceTime);
     void setIgnorePresentFences(bool ignore);
     nsecs_t expectedPresentTime();
-    // Adds the present time for given layer to the history of present times.
-    void addFramePresentTimeForLayer(const nsecs_t framePresentTime, bool isAutoTimestamp,
-                                     const std::string layerName);
-    // Increments counter in the layer history to indicate that SF has started a new frame.
-    void incrementFrameCounter();
+    // apiId indicates the API (NATIVE_WINDOW_API_xxx) that queues the buffer.
+    // TODO(b/123956502): Remove this call with V1 go/content-fps-detection-in-scheduler.
+    void addNativeWindowApi(int apiId);
+    // Updates FPS based on the most occured request for Native Window API.
+    void updateFpsBasedOnNativeWindowApi();
     // Callback that gets invoked when Scheduler wants to change the refresh rate.
     void setChangeRefreshRateCallback(const ChangeRefreshRateCallback& changeRefreshRateCallback);
 
@@ -165,17 +169,16 @@
 private:
     friend class TestableScheduler;
 
+    // In order to make sure that the features don't override themselves, we need a state machine
+    // to keep track which feature requested the config change.
+    enum class MediaFeatureState { MEDIA_PLAYING, MEDIA_OFF };
+    enum class IdleTimerState { EXPIRED, RESET };
+
     // Creates a connection on the given EventThread and forwards the given callbacks.
     sp<EventThreadConnection> createConnectionInternal(EventThread*, ResyncCallback&&);
 
     nsecs_t calculateAverage() const;
     void updateFrameSkipping(const int64_t skipCount);
-    // Collects the statistical mean (average) and median between timestamp
-    // intervals for each frame for each layer.
-    void determineLayerTimestampStats(const std::string layerName, const nsecs_t framePresentTime);
-    // Collects the average difference between timestamps for each frame regardless
-    // of which layer the timestamp came from.
-    void determineTimestampAverage(bool isAutoTimestamp, const nsecs_t framePresentTime);
     // Function that resets the idle timer.
     void resetIdleTimer();
     // Function that is called when the timer resets.
@@ -184,6 +187,12 @@
     void expiredTimerCallback();
     // Sets vsync period.
     void setVsyncPeriod(const nsecs_t period);
+    // Media feature's function to change the refresh rate.
+    void mediaChangeRefreshRate(MediaFeatureState mediaFeatureState);
+    // Idle timer feature's function to change the refresh rate.
+    void timerChangeRefreshRate(IdleTimerState idleTimerState);
+    // Acquires a lock and calls the ChangeRefreshRateCallback() with given parameters.
+    void changeRefreshRate(RefreshRateType refreshRateType, ConfigEvent configEvent);
 
     // If fences from sync Framework are supported.
     const bool mHasSyncFramework;
@@ -217,10 +226,13 @@
     std::array<int64_t, scheduler::ARRAY_SIZE> mTimeDifferences{};
     size_t mCounter = 0;
 
-    // DetermineLayerTimestampStats is called from BufferQueueLayer::onFrameAvailable which
-    // can run on any thread, and cause failure.
-    std::mutex mLayerHistoryLock;
-    LayerHistory mLayerHistory GUARDED_BY(mLayerHistoryLock);
+    // The following few fields follow native window api bits that come with buffers. If there are
+    // more buffers with NATIVE_WINDOW_API_MEDIA we render at 60Hz, otherwise we render at 90Hz.
+    // There is not dependency on timestamp for V0.
+    // TODO(b/123956502): Remove this when more robust logic for content fps detection is developed.
+    std::mutex mWindowApiHistoryLock;
+    std::array<int, scheduler::ARRAY_SIZE> mWindowApiHistory GUARDED_BY(mWindowApiHistoryLock);
+    int64_t mApiHistoryCounter = 0;
 
     // Timer that records time between requests for next vsync. If the time is higher than a given
     // interval, a callback is fired. Set this variable to >0 to use this feature.
@@ -229,6 +241,13 @@
 
     std::mutex mCallbackLock;
     ChangeRefreshRateCallback mChangeRefreshRateCallback GUARDED_BY(mCallbackLock);
+
+    // In order to make sure that the features don't override themselves, we need a state machine
+    // to keep track which feature requested the config change.
+    std::mutex mFeatureStateLock;
+    MediaFeatureState mCurrentMediaFeatureState GUARDED_BY(mFeatureStateLock) =
+            MediaFeatureState::MEDIA_OFF;
+    IdleTimerState mCurrentIdleTimerState GUARDED_BY(mFeatureStateLock) = IdleTimerState::RESET;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.cpp b/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
index 191022d..fb5414f 100644
--- a/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
@@ -34,23 +34,5 @@
     return v->at(n);
 }
 
-int64_t calculate_mode(const std::vector<int64_t>& v) {
-    if (v.empty()) {
-        return 0;
-    }
-
-    // Create a map with all the counts for the indivicual values in the vector.
-    std::unordered_map<int64_t, int64_t> counts;
-    for (int64_t value : v) {
-        counts[value]++;
-    }
-
-    // Sort the map, and return the number with the highest count. If two numbers have
-    // the same count, first one is returned.
-    using ValueType = const decltype(counts)::value_type&;
-    const auto compareCounts = [](ValueType l, ValueType r) { return l.second <= r.second; };
-    return std::max_element(counts.begin(), counts.end(), compareCounts)->first;
-}
-
 } // namespace scheduler
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h
index edd23de..9e6e8c7 100644
--- a/services/surfaceflinger/Scheduler/SchedulerUtils.h
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.h
@@ -18,6 +18,7 @@
 
 #include <cinttypes>
 #include <numeric>
+#include <unordered_map>
 #include <vector>
 
 namespace android {
@@ -45,7 +46,24 @@
 int64_t calculate_median(std::vector<int64_t>* v);
 
 // Calculates the statistical mode in the vector. Return 0 if the vector is empty.
-int64_t calculate_mode(const std::vector<int64_t>& v);
+template <typename T>
+auto calculate_mode(const T& v) {
+    if (v.empty()) {
+        return 0;
+    }
+
+    // Create a map with all the counts for the indivicual values in the vector.
+    std::unordered_map<int64_t, int> counts;
+    for (int64_t value : v) {
+        counts[value]++;
+    }
+
+    // Sort the map, and return the number with the highest count. If two numbers have
+    // the same count, first one is returned.
+    using ValueType = const decltype(counts)::value_type&;
+    const auto compareCounts = [](ValueType l, ValueType r) { return l.second <= r.second; };
+    return static_cast<int>(std::max_element(counts.begin(), counts.end(), compareCounts)->first);
+}
 
 } // namespace scheduler
 } // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 9b0e8c1..96292a8 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -370,8 +370,13 @@
     auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
     mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize;
 
+    mUseSmart90ForVideo = use_smart_90_for_video(false);
     property_get("debug.sf.use_smart_90_for_video", value, "0");
-    mUseSmart90ForVideo = atoi(value);
+
+    int int_value = atoi(value);
+    if (int_value) {
+        mUseSmart90ForVideo = true;
+    }
 
     property_get("debug.sf.luma_sampling", value, "1");
     mLumaSampling = atoi(value);
@@ -568,12 +573,8 @@
                 mRefreshRateConfigs[*displayId]->getRefreshRate(RefreshRateType::PERFORMANCE);
 
         if (isConfigAllowed(*displayId, performanceRefreshRate.configId)) {
-            mPhaseOffsets->setRefreshRateType(
-                    scheduler::RefreshRateConfigs::RefreshRateType::PERFORMANCE);
             setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::None);
         } else {
-            mPhaseOffsets->setRefreshRateType(
-                    scheduler::RefreshRateConfigs::RefreshRateType::DEFAULT);
             setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::None);
         }
     }));
@@ -632,6 +633,10 @@
     mVsyncModulator.setSchedulerAndHandles(mScheduler.get(), mAppConnectionHandle.get(),
                                            mSfConnectionHandle.get());
 
+    mRegionSamplingThread =
+            new RegionSamplingThread(*this, *mScheduler,
+                                     RegionSamplingThread::EnvironmentTimingTunables());
+
     // Get a RenderEngine for the given display / config (can't fail)
     int32_t renderEngineFeature = 0;
     renderEngineFeature |= (useColorManagement ?
@@ -970,6 +975,8 @@
     display->setActiveConfig(mUpcomingActiveConfig.configId);
 
     mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
+    const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
+    mVsyncModulator.setPhaseOffsets(early, gl, late);
     ATRACE_INT("ActiveConfigMode", mUpcomingActiveConfig.configId);
     if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) {
         mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
@@ -979,15 +986,6 @@
 
 bool SurfaceFlinger::performSetActiveConfig() NO_THREAD_SAFETY_ANALYSIS {
     ATRACE_CALL();
-    // we may be in the process of changing the active state
-    if (mWaitForNextInvalidate) {
-        mWaitForNextInvalidate = false;
-
-        repaintEverythingForHWC();
-        // We do not want to give another frame to HWC while we are transitioning.
-        return true;
-    }
-
     if (mCheckPendingFence) {
         if (mPreviousPresentFence != Fence::NO_FENCE &&
             (mPreviousPresentFence->getStatus() == Fence::Status::Unsignaled)) {
@@ -1039,7 +1037,6 @@
     getHwComposer().setActiveConfig(*displayId, mUpcomingActiveConfig.configId);
 
     // we need to submit an empty frame to HWC to start the process
-    mWaitForNextInvalidate = true;
     mCheckPendingFence = true;
 
     return false;
@@ -1413,15 +1410,10 @@
 }
 
 void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate, Scheduler::ConfigEvent event) {
-    ATRACE_CALL();
-    mPhaseOffsets->setRefreshRateType(refreshRate);
-
-    const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
-    mVsyncModulator.setPhaseOffsets(early, gl, late);
-
     if (mBootStage != BootStage::FINISHED) {
         return;
     }
+    ATRACE_CALL();
 
     // Don't do any updating if the current fps is the same as the new one.
     const auto displayId = getInternalDisplayIdLocked();
@@ -1439,6 +1431,7 @@
         return;
     }
 
+    mPhaseOffsets->setRefreshRateType(refreshRate);
     setDesiredActiveConfig(getInternalDisplayTokenLocked(), desiredConfigId, event);
 }
 
@@ -1583,23 +1576,28 @@
     ATRACE_CALL();
     switch (what) {
         case MessageQueue::INVALIDATE: {
-            if (performSetActiveConfig()) {
-                break;
+            bool frameMissed = mPreviousPresentFence != Fence::NO_FENCE &&
+                    (mPreviousPresentFence->getStatus() == Fence::Status::Unsignaled);
+            bool hwcFrameMissed = mHadDeviceComposition && frameMissed;
+            bool gpuFrameMissed = mHadClientComposition && frameMissed;
+            ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
+            ATRACE_INT("HwcFrameMissed", static_cast<int>(hwcFrameMissed));
+            ATRACE_INT("GpuFrameMissed", static_cast<int>(gpuFrameMissed));
+            if (frameMissed) {
+                mFrameMissedCount++;
+                mTimeStats->incrementMissedFrames();
             }
 
             if (mUseSmart90ForVideo) {
                 // This call is made each time SF wakes up and creates a new frame. It is part
                 // of video detection feature.
-                mScheduler->incrementFrameCounter();
+                mScheduler->updateFpsBasedOnNativeWindowApi();
             }
-            bool frameMissed = mPreviousPresentFence != Fence::NO_FENCE &&
-                    (mPreviousPresentFence->getStatus() == Fence::Status::Unsignaled);
-            bool hwcFrameMissed = !mHadClientComposition && frameMissed;
-            if (frameMissed) {
-                ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
-                mFrameMissedCount++;
-                mTimeStats->incrementMissedFrames();
+
+            if (performSetActiveConfig()) {
+                break;
             }
+
             // For now, only propagate backpressure when missing a hwc frame.
             if (hwcFrameMissed) {
                 if (mPropagateBackpressure) {
@@ -1680,11 +1678,14 @@
     postComposition();
 
     mHadClientComposition = false;
+    mHadDeviceComposition = false;
     for (const auto& [token, displayDevice] : mDisplays) {
         auto display = displayDevice->getCompositionDisplay();
         const auto displayId = display->getId();
         mHadClientComposition =
                 mHadClientComposition || getHwComposer().hasClientComposition(displayId);
+        mHadDeviceComposition =
+                mHadDeviceComposition || getHwComposer().hasDeviceComposition(displayId);
     }
 
     mVsyncModulator.onRefreshed(mHadClientComposition);
@@ -2057,8 +2058,8 @@
     mTransactionCompletedThread.addPresentFence(mPreviousPresentFence);
     mTransactionCompletedThread.sendCallbacks();
 
-    if (mLumaSampling) {
-        mRegionSamplingThread->sampleNow();
+    if (mLumaSampling && mRegionSamplingThread) {
+        mRegionSamplingThread->notifyNewContent();
     }
 }
 
@@ -3872,7 +3873,7 @@
         if (layer->setSidebandStream(s.sidebandStream)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eInputInfoChanged) {
-        if (callingThreadHasUnscopedSurfaceFlingerAccess()) {
+        if (privileged) {
             layer->setInputInfo(s.inputInfo);
             flags |= eTraversalNeeded;
         } else {
@@ -4744,6 +4745,7 @@
      */
     result.append("\nScheduler state:\n");
     result.append(mScheduler->doDump() + "\n");
+    StringAppendF(&result, "+  Smart video mode: %s\n\n", mUseSmart90ForVideo ? "on" : "off");
     result.append(mRefreshRateStats->doDump() + "\n");
 }
 
@@ -5664,8 +5666,6 @@
         const auto performanceRefreshRate =
                 mRefreshRateConfigs[*displayId]->getRefreshRate(RefreshRateType::PERFORMANCE);
         if (isConfigAllowed(*displayId, performanceRefreshRate.configId)) {
-            mPhaseOffsets->setRefreshRateType(
-                    scheduler::RefreshRateConfigs::RefreshRateType::PERFORMANCE);
             setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::Changed);
         }
     }
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8de1e97..96a21cc 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -584,8 +584,9 @@
     bool containsAnyInvalidClientState(const Vector<ComposerState>& states);
     bool transactionIsReadyToBeApplied(int64_t desiredPresentTime,
                                        const Vector<ComposerState>& states);
-    uint32_t setClientStateLocked(const ComposerState& composerState, bool privileged);
-    uint32_t setDisplayStateLocked(const DisplayState& s);
+    uint32_t setClientStateLocked(const ComposerState& composerState, bool privileged)
+            REQUIRES(mStateLock);
+    uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
     uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
             REQUIRES(mStateLock);
 
@@ -970,7 +971,12 @@
     // Tracks layers that need to update a display's dirty region.
     std::vector<sp<Layer>> mLayersPendingRefresh;
     sp<Fence> mPreviousPresentFence = Fence::NO_FENCE;
+    // True if in the previous frame at least one layer was composed via the GPU.
     bool mHadClientComposition = false;
+    // True if in the previous frame at least one layer was composed via HW Composer.
+    // Note that it is possible for a frame to be composed via both client and device
+    // composition, for example in the case of overlays.
+    bool mHadDeviceComposition = false;
 
     enum class BootStage {
         BOOTLOADER,
@@ -1011,9 +1017,6 @@
 
     TransactionCompletedThread mTransactionCompletedThread;
 
-    bool mLumaSampling = true;
-    sp<RegionSamplingThread> mRegionSamplingThread = new RegionSamplingThread(*this);
-
     // Restrict layers to use two buffers in their bufferqueues.
     bool mLayerTripleBufferingDisabled = false;
 
@@ -1135,10 +1138,12 @@
 
     // below flags are set by main thread only
     bool mDesiredActiveConfigChanged GUARDED_BY(mActiveConfigLock) = false;
-    bool mWaitForNextInvalidate = false;
     bool mCheckPendingFence = false;
 
     /* ------------------------------------------------------------------------ */
+    bool mLumaSampling = true;
+    sp<RegionSamplingThread> mRegionSamplingThread;
+
     sp<IInputFlinger> mInputFlinger;
 
     InputWindowCommands mPendingInputWindowCommands GUARDED_BY(mStateLock);
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 875fca8..e130511 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -217,6 +217,14 @@
     return defaultValue;
 }
 
+bool use_smart_90_for_video(bool defaultValue) {
+    auto temp = SurfaceFlingerProperties::use_smart_90_for_video();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return defaultValue;
+}
+
 #define DISPLAY_PRIMARY_SIZE 3
 
 constexpr float kSrgbRedX = 0.4123f;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 0199d79..6f90117 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -57,6 +57,8 @@
 
 int32_t set_idle_timer_ms(int32_t defaultValue);
 
+bool use_smart_90_for_video(bool defaultValue);
+
 android::ui::DisplayPrimaries getDisplayNativePrimaries();
 } // namespace sysprop
 } // namespace android
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 949c23c..fe6dc93 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -297,3 +297,13 @@
     access: Readonly
     prop_name: "ro.surface_flinger.set_idle_timer_ms"
 }
+
+# useSmart90ForVideo indicates whether Scheduler should detect content FPS, and try to adjust the
+# screen refresh rate based on that.
+prop {
+    api_name: "use_smart_90_for_video"
+    type: Boolean
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.use_smart_90_for_video"
+}
diff --git a/services/surfaceflinger/tests/SurfaceFlinger_test.filter b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
index 91999ae..be862c9 100644
--- a/services/surfaceflinger/tests/SurfaceFlinger_test.filter
+++ b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
@@ -1,5 +1,5 @@
 {
         "presubmit": {
-            "filter": "CredentialsTest.*:SurfaceFlingerStress.*:SurfaceInterceptorTest.*:LayerTransactionTest.*:LayerTypeTransactionTest.*:LayerUpdateTest.*:GeometryLatchingTest.*:CropLatchingTest.*:ChildLayerTest.*:ScreenCaptureTest.*:ScreenCaptureChildOnlyTest.*:DereferenceSurfaceControlTest.*:BoundlessLayerTest.*"
+            "filter": "CredentialsTest.*:SurfaceFlingerStress.*:SurfaceInterceptorTest.*:LayerTransactionTest.*:LayerTypeTransactionTest.*:LayerUpdateTest.*:GeometryLatchingTest.*:CropLatchingTest.*:ChildLayerTest.*:ScreenCaptureTest.*:ScreenCaptureChildOnlyTest.*:DereferenceSurfaceControlTest.*:BoundlessLayerTest.*:MultiDisplayLayerBoundsTest.*"
         }
 }
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index 34cdff7..319e01c 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -198,12 +198,15 @@
 class ScreenCapture : public RefBase {
 public:
     static void captureScreen(std::unique_ptr<ScreenCapture>* sc) {
+        captureScreen(sc, SurfaceComposerClient::getInternalDisplayToken());
+    }
+
+    static void captureScreen(std::unique_ptr<ScreenCapture>* sc, sp<IBinder> displayToken) {
         const auto sf = ComposerService::getComposerService();
-        const auto token = sf->getInternalDisplayToken();
         SurfaceComposerClient::Transaction().apply(true);
 
         sp<GraphicBuffer> outBuffer;
-        ASSERT_EQ(NO_ERROR, sf->captureScreen(token, &outBuffer, Rect(), 0, 0, false));
+        ASSERT_EQ(NO_ERROR, sf->captureScreen(displayToken, &outBuffer, Rect(), 0, 0, false));
         *sc = std::make_unique<ScreenCapture>(outBuffer);
     }
 
@@ -482,6 +485,12 @@
         return screenshot;
     }
 
+    void asTransaction(const std::function<void(Transaction&)>& exec) {
+        Transaction t;
+        exec(t);
+        t.apply(true);
+    }
+
     static status_t getBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
         static BufferGenerator bufferGenerator;
         return bufferGenerator.get(outBuffer, outFence);
@@ -4086,11 +4095,6 @@
         fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
     }
 
-    void asTransaction(const std::function<void(Transaction&)>& exec) {
-        Transaction t;
-        exec(t);
-        t.apply(true);
-    }
 
     sp<SurfaceControl> mBGSurfaceControl;
     sp<SurfaceControl> mFGSurfaceControl;
@@ -5421,4 +5425,102 @@
     }
 }
 
+class MultiDisplayLayerBoundsTest : public LayerTransactionTest {
+protected:
+    virtual void SetUp() {
+        LayerTransactionTest::SetUp();
+        ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+        mMainDisplay = SurfaceComposerClient::getInternalDisplayToken();
+        SurfaceComposerClient::getDisplayInfo(mMainDisplay, &mMainDisplayInfo);
+
+        sp<IGraphicBufferConsumer> consumer;
+        BufferQueue::createBufferQueue(&mProducer, &consumer);
+        consumer->setConsumerName(String8("Virtual disp consumer"));
+        consumer->setDefaultBufferSize(mMainDisplayInfo.w, mMainDisplayInfo.h);
+    }
+
+    virtual void TearDown() {
+        SurfaceComposerClient::destroyDisplay(mVirtualDisplay);
+        LayerTransactionTest::TearDown();
+        mColorLayer = 0;
+    }
+
+    void createDisplay(const Rect& layerStackRect, uint32_t layerStack) {
+        mVirtualDisplay =
+                SurfaceComposerClient::createDisplay(String8("VirtualDisplay"), false /*secure*/);
+        asTransaction([&](Transaction& t) {
+            t.setDisplaySurface(mVirtualDisplay, mProducer);
+            t.setDisplayLayerStack(mVirtualDisplay, layerStack);
+            t.setDisplayProjection(mVirtualDisplay, mMainDisplayInfo.orientation, layerStackRect,
+                                   Rect(mMainDisplayInfo.w, mMainDisplayInfo.h));
+        });
+    }
+
+    void createColorLayer(uint32_t layerStack) {
+        mColorLayer =
+                createSurface(mClient, "ColorLayer", 0 /* buffer width */, 0 /* buffer height */,
+                              PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor);
+        ASSERT_TRUE(mColorLayer != nullptr);
+        ASSERT_TRUE(mColorLayer->isValid());
+        asTransaction([&](Transaction& t) {
+            t.setLayerStack(mColorLayer, layerStack);
+            t.setCrop_legacy(mColorLayer, Rect(0, 0, 30, 40));
+            t.setLayer(mColorLayer, INT32_MAX - 2);
+            t.setColor(mColorLayer,
+                       half3{mExpectedColor.r / 255.0f, mExpectedColor.g / 255.0f,
+                             mExpectedColor.b / 255.0f});
+            t.show(mColorLayer);
+        });
+    }
+
+    DisplayInfo mMainDisplayInfo;
+    sp<IBinder> mMainDisplay;
+    sp<IBinder> mVirtualDisplay;
+    sp<IGraphicBufferProducer> mProducer;
+    sp<SurfaceControl> mColorLayer;
+    Color mExpectedColor = {63, 63, 195, 255};
+};
+
+TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInVirtualDisplay) {
+    createDisplay({mMainDisplayInfo.viewportW, mMainDisplayInfo.viewportH}, 1 /* layerStack */);
+    createColorLayer(1 /* layerStack */);
+
+    asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
+
+    // Verify color layer does not render on main display.
+    std::unique_ptr<ScreenCapture> sc;
+    ScreenCapture::captureScreen(&sc, mMainDisplay);
+    sc->expectColor(Rect(10, 10, 40, 50), {0, 0, 0, 255});
+    sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
+
+    // Verify color layer renders correctly on virtual display.
+    ScreenCapture::captureScreen(&sc, mVirtualDisplay);
+    sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
+    sc->expectColor(Rect(1, 1, 9, 9), {0, 0, 0, 0});
+}
+
+TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInMirroredVirtualDisplay) {
+    // Create a display and set its layer stack to the main display's layer stack so
+    // the contents of the main display are mirrored on to the virtual display.
+
+    // Assumption here is that the new mirrored display has the same viewport as the
+    // primary display that it is mirroring.
+    createDisplay({mMainDisplayInfo.viewportW, mMainDisplayInfo.viewportH}, 0 /* layerStack */);
+    createColorLayer(0 /* layerStack */);
+
+    asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
+
+    // Verify color layer renders correctly on main display and it is mirrored on the
+    // virtual display.
+    std::unique_ptr<ScreenCapture> sc;
+    ScreenCapture::captureScreen(&sc, mMainDisplay);
+    sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
+    sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
+
+    ScreenCapture::captureScreen(&sc, mVirtualDisplay);
+    sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
+    sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 79b5ca0..46fd964 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -257,7 +257,10 @@
         return mFlinger->onHotplugReceived(sequenceId, display, connection);
     }
 
-    auto setDisplayStateLocked(const DisplayState& s) { return mFlinger->setDisplayStateLocked(s); }
+    auto setDisplayStateLocked(const DisplayState& s) {
+        Mutex::Autolock _l(mFlinger->mStateLock);
+        return mFlinger->setDisplayStateLocked(s);
+    }
 
     // Allow reading display state without locking, as if called on the SF main thread.
     auto onInitializeDisplays() NO_THREAD_SAFETY_ANALYSIS {
@@ -341,6 +344,8 @@
         // still be referenced by something despite our best efforts to destroy
         // it after each test is done.
         mutableDisplays().clear();
+        mutableCurrentState().displays.clear();
+        mutableDrawingState().displays.clear();
         mutableEventQueue().reset();
         mutableInterceptor().reset();
         mutableScheduler().reset();