Merge "CE: Remove compositionengine::Layer"
diff --git a/cmds/dumpstate/DumpstateInternal.h b/cmds/dumpstate/DumpstateInternal.h
index 10db5d6..c1ec55e 100644
--- a/cmds/dumpstate/DumpstateInternal.h
+++ b/cmds/dumpstate/DumpstateInternal.h
@@ -49,6 +49,7 @@
 
 // TODO: use functions from <chrono> instead
 const uint64_t NANOS_PER_SEC = 1000000000;
+const uint64_t NANOS_PER_MILLI = 1000000;
 uint64_t Nanotime();
 
 // Switches to non-root user and group.
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index fc94f23..a20db24 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -649,7 +649,7 @@
 }
 
 uint64_t Dumpstate::ConsentCallback::getElapsedTimeMs() const {
-    return Nanotime() - start_time_;
+    return (Nanotime() - start_time_) / NANOS_PER_MILLI;
 }
 
 void Dumpstate::PrintHeader() const {
diff --git a/cmds/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp
index d398559..3a3df08 100644
--- a/cmds/flatland/GLHelper.cpp
+++ b/cmds/flatland/GLHelper.cpp
@@ -14,15 +14,14 @@
  * limitations under the License.
  */
 
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
-#include <ui/DisplayInfo.h>
-#include <gui/SurfaceComposerClient.h>
-
 #include "GLHelper.h"
 
- namespace android {
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <gui/SurfaceComposerClient.h>
+#include <ui/DisplayConfig.h>
+
+namespace android {
 
 GLHelper::GLHelper() :
     mDisplay(EGL_NO_DISPLAY),
@@ -228,15 +227,15 @@
         return false;
     }
 
-    DisplayInfo info;
-    status_t err = mSurfaceComposerClient->getDisplayInfo(dpy, &info);
+    DisplayConfig config;
+    status_t err = mSurfaceComposerClient->getActiveDisplayConfig(dpy, &config);
     if (err != NO_ERROR) {
-        fprintf(stderr, "SurfaceComposer::getDisplayInfo failed: %#x\n", err);
+        fprintf(stderr, "SurfaceComposer::getActiveDisplayConfig failed: %#x\n", err);
         return false;
     }
 
-    float scaleX = float(info.w) / float(w);
-    float scaleY = float(info.h) / float(h);
+    float scaleX = static_cast<float>(config.resolution.getWidth()) / w;
+    float scaleY = static_cast<float>(config.resolution.getHeight()) / h;
     *scale = scaleX < scaleY ? scaleX : scaleY;
 
     return true;
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index e7b0d5d..cfd6a3e 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -2250,26 +2250,6 @@
     return *_aidl_return ? ok() : error("viewcompiler failed");
 }
 
-binder::Status InstalldNativeService::markBootComplete(const std::string& instructionSet) {
-    ENFORCE_UID(AID_SYSTEM);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
-
-    const char* instruction_set = instructionSet.c_str();
-
-    char boot_marker_path[PKG_PATH_MAX];
-    sprintf(boot_marker_path,
-          "%s/%s/%s/.booting",
-          android_data_dir.c_str(),
-          DALVIK_CACHE,
-          instruction_set);
-
-    ALOGV("mark_boot_complete : %s", boot_marker_path);
-    if (unlink(boot_marker_path) != 0) {
-        return error(StringPrintf("Failed to unlink %s", boot_marker_path));
-    }
-    return ok();
-}
-
 binder::Status InstalldNativeService::linkNativeLibraryDirectory(
         const std::unique_ptr<std::string>& uuid, const std::string& packageName,
         const std::string& nativeLibPath32, int32_t userId) {
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index bf11002..dd56de6 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -130,7 +130,6 @@
             const std::string& profileName);
 
     binder::Status rmPackageDir(const std::string& packageDir);
-    binder::Status markBootComplete(const std::string& instructionSet);
     binder::Status freeCache(const std::unique_ptr<std::string>& uuid, int64_t targetFreeBytes,
             int64_t cacheReservedBytes, int32_t flags);
     binder::Status linkNativeLibraryDirectory(const std::unique_ptr<std::string>& uuid,
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 891b26d..07ced0d 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -81,7 +81,6 @@
     void destroyProfileSnapshot(@utf8InCpp String packageName, @utf8InCpp String profileName);
 
     void rmPackageDir(@utf8InCpp String packageDir);
-    void markBootComplete(@utf8InCpp String instructionSet);
     void freeCache(@nullable @utf8InCpp String uuid, long targetFreeBytes,
             long cacheReservedBytes, int flags);
     void linkNativeLibraryDirectory(@nullable @utf8InCpp String uuid,
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index e61eb6e..d236f76 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -104,12 +104,12 @@
     EXPECT_EQ(-1, validate_apk_path(badint2))
             << badint2 << " should be rejected as a invalid path";
 
-    // Only one subdir should be allowed.
-    const char *bad_path3 = TEST_APP_DIR "example.com/subdir/pkg.apk";
+    // Should not have more than two sub directories
+    const char *bad_path3 = TEST_APP_DIR "random/example.com/subdir/pkg.apk";
     EXPECT_EQ(-1, validate_apk_path(bad_path3))
             << bad_path3 << " should be rejected as a invalid path";
 
-    const char *bad_path4 = TEST_APP_DIR "example.com/subdir/../pkg.apk";
+    const char *bad_path4 = TEST_APP_DIR "random/example.com/subdir/pkg.apk";
     EXPECT_EQ(-1, validate_apk_path(bad_path4))
             << bad_path4 << " should be rejected as a invalid path";
 
@@ -120,6 +120,7 @@
 
 TEST_F(UtilsTest, IsValidApkPath_TopDir) {
     EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/com.example"));
+    EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/random/com.example"));
     EXPECT_EQ(0, validate_apk_path(TEST_EXPAND_DIR "app/com.example"));
     EXPECT_EQ(-1, validate_apk_path(TEST_DATA_DIR "data/com.example"));
     EXPECT_EQ(-1, validate_apk_path(TEST_EXPAND_DIR "data/com.example"));
@@ -127,6 +128,7 @@
 
 TEST_F(UtilsTest, IsValidApkPath_TopFile) {
     EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/com.example/base.apk"));
+    EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/random/com.example/base.apk"));
     EXPECT_EQ(0, validate_apk_path(TEST_EXPAND_DIR "app/com.example/base.apk"));
     EXPECT_EQ(-1, validate_apk_path(TEST_DATA_DIR "data/com.example/base.apk"));
     EXPECT_EQ(-1, validate_apk_path(TEST_EXPAND_DIR "data/com.example/base.apk"));
@@ -134,6 +136,7 @@
 
 TEST_F(UtilsTest, IsValidApkPath_OatDir) {
     EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat"));
+    EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/random/com.example/oat"));
     EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat"));
     EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat"));
     EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat"));
@@ -141,6 +144,7 @@
 
 TEST_F(UtilsTest, IsValidApkPath_OatDirDir) {
     EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat/arm64"));
+    EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/random/com.example/oat/arm64"));
     EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat/arm64"));
     EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat/arm64"));
     EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat/arm64"));
@@ -148,6 +152,7 @@
 
 TEST_F(UtilsTest, IsValidApkPath_OatDirDirFile) {
     EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat/arm64/base.odex"));
+    EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/random/com.example/oat/arm64/base.odex"));
     EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat/arm64/base.odex"));
     EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat/arm64/base.odex"));
     EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat/arm64/base.odex"));
@@ -164,6 +169,10 @@
     EXPECT_EQ(0, validate_apk_path(path2))
             << path2 << " should be allowed as a valid path";
 
+    const char *path3 = TEST_APP_DIR "random/example.com/example.apk";
+    EXPECT_EQ(0, validate_apk_path(path3))
+            << path3 << " should be allowed as a valid path";
+
     const char *badpriv1 = TEST_APP_PRIVATE_DIR "../example.apk";
     EXPECT_EQ(-1, validate_apk_path(badpriv1))
             << badpriv1 << " should be rejected as a invalid path";
@@ -172,16 +181,16 @@
     EXPECT_EQ(-1, validate_apk_path(badpriv2))
             << badpriv2 << " should be rejected as a invalid path";
 
-    // Only one subdir should be allowed.
-    const char *bad_path3 = TEST_APP_PRIVATE_DIR "example.com/subdir/pkg.apk";
+    // Only one or two subdir should be allowed.
+    const char *bad_path3 = TEST_APP_PRIVATE_DIR "random/example.com/subdir/pkg.apk";
     EXPECT_EQ(-1, validate_apk_path(bad_path3))
             << bad_path3 << " should be rejected as a invalid path";
 
-    const char *bad_path4 = TEST_APP_PRIVATE_DIR "example.com/subdir/../pkg.apk";
+    const char *bad_path4 = TEST_APP_PRIVATE_DIR "random/example.com/subdir/../pkg.apk";
     EXPECT_EQ(-1, validate_apk_path(bad_path4))
             << bad_path4 << " should be rejected as a invalid path";
 
-    const char *bad_path5 = TEST_APP_PRIVATE_DIR "example.com1/../example.com2/pkg.apk";
+    const char *bad_path5 = TEST_APP_PRIVATE_DIR "random/example.com1/../example.com2/pkg.apk";
     EXPECT_EQ(-1, validate_apk_path(bad_path5))
             << bad_path5 << " should be rejected as a invalid path";
 }
@@ -229,10 +238,16 @@
             << badasec6 << " should be rejected as a invalid path";
 }
 
-TEST_F(UtilsTest, IsValidApkPath_TwoSubdirFail) {
-    const char *badasec7 = TEST_ASEC_DIR "com.example.asec/subdir1/pkg.apk";
-    EXPECT_EQ(-1, validate_apk_path(badasec7))
-            << badasec7 << " should be rejected as a invalid path";
+TEST_F(UtilsTest, IsValidApkPath_TwoSubdir) {
+    const char *badasec7 = TEST_ASEC_DIR "random/com.example.asec/pkg.apk";
+    EXPECT_EQ(0, validate_apk_path(badasec7))
+            << badasec7 << " should be allowed as a valid path";
+}
+
+TEST_F(UtilsTest, IsValidApkPath_ThreeSubdirFail) {
+    const char *badasec8 = TEST_ASEC_DIR "random/com.example.asec/subdir/pkg.apk";
+    EXPECT_EQ(-1, validate_apk_path(badasec8))
+            << badasec8 << " should be rejcted as an invalid path";
 }
 
 TEST_F(UtilsTest, CheckSystemApp_Dir1) {
@@ -511,8 +526,8 @@
     EXPECT_EQ(0, validate_apk_path("/data/app/com.example"));
     EXPECT_EQ(0, validate_apk_path("/data/app/com.example/file"));
     EXPECT_EQ(0, validate_apk_path("/data/app/com.example//file"));
-    EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/"));
-    EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/file"));
+    EXPECT_EQ(0, validate_apk_path("/data/app/random/com.example/"));
+    EXPECT_EQ(0, validate_apk_path("/data/app/random/com.example/file"));
     EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir/file"));
     EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir//file"));
     EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir/dir/file"));
@@ -527,8 +542,10 @@
     EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/file"));
     EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/file"));
     EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir//file"));
-    EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/file"));
-    EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir//file"));
+    EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/file"));
+    EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir//file"));
+    EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/dir/file"));
+    EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/dir//file"));
 }
 
 TEST_F(UtilsTest, MatchExtension_Valid) {
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 6012822..939cf90 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -954,11 +954,11 @@
 }
 
 int validate_apk_path(const char* path) {
-    return validate_apk_path_internal(path, 1 /* maxSubdirs */);
+    return validate_apk_path_internal(path, 2 /* maxSubdirs */);
 }
 
 int validate_apk_path_subdirs(const char* path) {
-    return validate_apk_path_internal(path, 3 /* maxSubdirs */);
+    return validate_apk_path_internal(path, 4 /* maxSubdirs */);
 }
 
 int ensure_config_user_dirs(userid_t userid) {
diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h
index 31efa65..0d943b7 100644
--- a/include/android/imagedecoder.h
+++ b/include/android/imagedecoder.h
@@ -27,7 +27,7 @@
 #define ANDROID_IMAGE_DECODER_H
 
 #include "bitmap.h"
-
+#include <android/rect.h>
 #include <stdint.h>
 
 #ifdef __cplusplus
@@ -35,7 +35,6 @@
 #endif
 
 struct AAsset;
-struct ARect;
 
 #if __ANDROID_API__ >= 30
 
@@ -92,7 +91,8 @@
  * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
  *         indicating reason for the failure.
  */
-int AImageDecoder_createFromAAsset(AAsset* asset, AImageDecoder** outDecoder) __INTRODUCED_IN(30);
+int AImageDecoder_createFromAAsset(struct AAsset* asset, AImageDecoder** outDecoder)
+        __INTRODUCED_IN(30);
 
 /**
  * Create a new AImageDecoder from a file descriptor.
@@ -331,7 +331,8 @@
  *               {@link AImageDecoder_getMinimumStride}.
  * @param size Size of the pixel buffer in bytes. Must be at least
  *             stride * (height - 1) +
- *             {@link AImageDecoder_getMinimumStride}.
+ *             {@link AImageDecoder_getMinimumStride}. Must also be a multiple
+ *             of the bytes per pixel of the {@link AndroidBitmapFormat}.
  * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success, or an error code
  *         from the same enum describing the failure.
  */
diff --git a/include/input/Input.h b/include/input/Input.h
index f871847..cf0814c 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -31,6 +31,7 @@
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
 #include <utils/Vector.h>
+#include <array>
 #include <limits>
 #include <queue>
 
@@ -258,6 +259,11 @@
  */
 constexpr float AMOTION_EVENT_INVALID_CURSOR_POSITION = std::numeric_limits<float>::quiet_NaN();
 
+/**
+ * Invalid value of HMAC - SHA256. Any events with this HMAC value will be marked as not verified.
+ */
+constexpr std::array<uint8_t, 32> INVALID_HMAC = {0};
+
 /*
  * Pointer coordinate data.
  */
@@ -348,22 +354,25 @@
 
     inline int32_t getDeviceId() const { return mDeviceId; }
 
-    inline int32_t getSource() const { return mSource; }
+    inline uint32_t getSource() const { return mSource; }
 
-    inline void setSource(int32_t source) { mSource = source; }
+    inline void setSource(uint32_t source) { mSource = source; }
 
     inline int32_t getDisplayId() const { return mDisplayId; }
 
     inline void setDisplayId(int32_t displayId) { mDisplayId = displayId; }
 
+    inline std::array<uint8_t, 32> getHmac() const { return mHmac; }
 
 protected:
-    void initialize(int32_t deviceId, int32_t source, int32_t displayId);
+    void initialize(int32_t deviceId, uint32_t source, int32_t displayId,
+                    std::array<uint8_t, 32> hmac);
     void initialize(const InputEvent& from);
 
     int32_t mDeviceId;
-    int32_t mSource;
+    uint32_t mSource;
     int32_t mDisplayId;
+    std::array<uint8_t, 32> mHmac;
 };
 
 /*
@@ -396,18 +405,10 @@
     static const char* getLabel(int32_t keyCode);
     static int32_t getKeyCodeFromLabel(const char* label);
 
-    void initialize(
-            int32_t deviceId,
-            int32_t source,
-            int32_t displayId,
-            int32_t action,
-            int32_t flags,
-            int32_t keyCode,
-            int32_t scanCode,
-            int32_t metaState,
-            int32_t repeatCount,
-            nsecs_t downTime,
-            nsecs_t eventTime);
+    void initialize(int32_t deviceId, uint32_t source, int32_t displayId,
+                    std::array<uint8_t, 32> hmac, int32_t action, int32_t flags, int32_t keyCode,
+                    int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime,
+                    nsecs_t eventTime);
     void initialize(const KeyEvent& from);
 
 protected:
@@ -463,6 +464,10 @@
 
     inline void setActionButton(int32_t button) { mActionButton = button; }
 
+    inline float getXScale() const { return mXScale; }
+
+    inline float getYScale() const { return mYScale; }
+
     inline float getXOffset() const { return mXOffset; }
 
     inline float getYOffset() const { return mYOffset; }
@@ -624,9 +629,10 @@
 
     ssize_t findPointerIndex(int32_t pointerId) const;
 
-    void initialize(int32_t deviceId, int32_t source, int32_t displayId, int32_t action,
-                    int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState,
-                    int32_t buttonState, MotionClassification classification, float xOffset,
+    void initialize(int32_t deviceId, uint32_t source, int32_t displayId,
+                    std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton,
+                    int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState,
+                    MotionClassification classification, float xScale, float yScale, float xOffset,
                     float yOffset, float xPrecision, float yPrecision, float rawXCursorPosition,
                     float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime,
                     size_t pointerCount, const PointerProperties* pointerProperties,
@@ -651,7 +657,7 @@
     status_t writeToParcel(Parcel* parcel) const;
 #endif
 
-    static bool isTouchEvent(int32_t source, int32_t action);
+    static bool isTouchEvent(uint32_t source, int32_t action);
     inline bool isTouchEvent() const {
         return isTouchEvent(mSource, mAction);
     }
@@ -676,6 +682,8 @@
     int32_t mMetaState;
     int32_t mButtonState;
     MotionClassification mClassification;
+    float mXScale;
+    float mYScale;
     float mXOffset;
     float mYOffset;
     float mXPrecision;
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index ae47438..06fd3bb 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -76,6 +76,9 @@
     } header;
 
     // Body *must* be 8 byte aligned.
+    // For keys and motions, rely on the fact that std::array takes up exactly as much space
+    // as the underlying data. This is not guaranteed by C++, but it simplifies the conversions.
+    static_assert(sizeof(std::array<uint8_t, 32>) == 32);
     union Body {
         struct Key {
             uint32_t seq;
@@ -84,6 +87,7 @@
             int32_t deviceId;
             int32_t source;
             int32_t displayId;
+            std::array<uint8_t, 32> hmac;
             int32_t action;
             int32_t flags;
             int32_t keyCode;
@@ -103,6 +107,7 @@
             int32_t deviceId;
             int32_t source;
             int32_t displayId;
+            std::array<uint8_t, 32> hmac;
             int32_t action;
             int32_t actionButton;
             int32_t flags;
@@ -112,6 +117,8 @@
             uint8_t empty2[3];                   // 3 bytes to fill gap created by classification
             int32_t edgeFlags;
             nsecs_t downTime __attribute__((aligned(8)));
+            float xScale;
+            float yScale;
             float xOffset;
             float yOffset;
             float xPrecision;
@@ -269,19 +276,10 @@
      * Returns BAD_VALUE if seq is 0.
      * Other errors probably indicate that the channel is broken.
      */
-    status_t publishKeyEvent(
-            uint32_t seq,
-            int32_t deviceId,
-            int32_t source,
-            int32_t displayId,
-            int32_t action,
-            int32_t flags,
-            int32_t keyCode,
-            int32_t scanCode,
-            int32_t metaState,
-            int32_t repeatCount,
-            nsecs_t downTime,
-            nsecs_t eventTime);
+    status_t publishKeyEvent(uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId,
+                             std::array<uint8_t, 32> hmac, int32_t action, int32_t flags,
+                             int32_t keyCode, int32_t scanCode, int32_t metaState,
+                             int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime);
 
     /* Publishes a motion event to the input channel.
      *
@@ -292,9 +290,10 @@
      * Other errors probably indicate that the channel is broken.
      */
     status_t publishMotionEvent(uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId,
-                                int32_t action, int32_t actionButton, int32_t flags,
-                                int32_t edgeFlags, int32_t metaState, int32_t buttonState,
-                                MotionClassification classification, float xOffset, float yOffset,
+                                std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton,
+                                int32_t flags, int32_t edgeFlags, int32_t metaState,
+                                int32_t buttonState, MotionClassification classification,
+                                float xScale, float yScale, float xOffset, float yOffset,
                                 float xPrecision, float yPrecision, float xCursorPosition,
                                 float yCursorPosition, nsecs_t downTime, nsecs_t eventTime,
                                 uint32_t pointerCount, const PointerProperties* pointerProperties,
diff --git a/libs/adbd_auth/Android.bp b/libs/adbd_auth/Android.bp
index 9cf0143..8ac044c 100644
--- a/libs/adbd_auth/Android.bp
+++ b/libs/adbd_auth/Android.bp
@@ -20,11 +20,14 @@
         "-Wthread-safety",
         "-Werror",
     ],
+    stl: "libc++_static",
+
     srcs: ["adbd_auth.cpp"],
     export_include_dirs: ["include"],
 
     version_script: "libadbd_auth.map.txt",
     stubs: {
+        versions: ["1"],
         symbol_file: "libadbd_auth.map.txt",
     },
 
@@ -36,7 +39,7 @@
         }
     },
 
-    shared_libs: [
+    static_libs: [
         "libbase",
         "libcutils",
         "liblog",
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 079dd82..1ee3853 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -73,7 +73,6 @@
     // or dessert updates. Instead, apex users should use libbinder_ndk.
     apex_available: [
         "//apex_available:platform",
-        "com.android.vndk.current",
         // TODO(b/139016109) remove these three
         "com.android.media.swcodec",
         "test_com.android.media.swcodec",
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 238c9dc..f16c39c 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -435,7 +435,8 @@
     Vector<Obituary>* obits = mObituaries;
     if(obits != nullptr) {
         if (!obits->isEmpty()) {
-            ALOGI("onLastStrongRef automatically unlinking death recipients");
+            ALOGI("onLastStrongRef automatically unlinking death recipients: %s",
+                  mDescriptorCache.size() ? String8(mDescriptorCache).c_str() : "<uncached descriptor>");
         }
 
         if (ipc) ipc->clearDeathNotification(mHandle, this);
diff --git a/libs/binder/BufferedTextOutput.cpp b/libs/binder/BufferedTextOutput.cpp
index 824b56b..8cf6097 100644
--- a/libs/binder/BufferedTextOutput.cpp
+++ b/libs/binder/BufferedTextOutput.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <binder/BufferedTextOutput.h>
+#include "BufferedTextOutput.h"
 #include <binder/Debug.h>
 
 #include <cutils/atomic.h>
diff --git a/libs/binder/include/binder/BufferedTextOutput.h b/libs/binder/BufferedTextOutput.h
similarity index 100%
rename from libs/binder/include/binder/BufferedTextOutput.h
rename to libs/binder/BufferedTextOutput.h
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 5ca9156..328653a 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -85,10 +85,11 @@
     sp<AidlServiceManager> mTheRealServiceManager;
 };
 
+static Mutex gDefaultServiceManagerLock;
+static sp<IServiceManager> gDefaultServiceManager;
+
 sp<IServiceManager> defaultServiceManager()
 {
-    static Mutex gDefaultServiceManagerLock;
-    static sp<IServiceManager> gDefaultServiceManager;
 
     if (gDefaultServiceManager != nullptr) return gDefaultServiceManager;
 
@@ -106,6 +107,11 @@
     return gDefaultServiceManager;
 }
 
+void setDefaultServiceManager(const sp<IServiceManager>& sm) {
+  AutoMutex _l(gDefaultServiceManagerLock);
+  gDefaultServiceManager = sm;
+}
+
 #if !defined(__ANDROID_VNDK__) && defined(__ANDROID__)
 // IPermissionController is not accessible to vendors
 
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 994e3b9..9f8d752 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -507,7 +507,7 @@
     }
 }
 
-#if defined(__ANDROID_APEX_COM_ANDROID_VNDK_CURRENT__) || (defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__))
+#if defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
 constexpr int32_t kHeader = B_PACK_CHARS('V', 'N', 'D', 'R');
 #else
 constexpr int32_t kHeader = B_PACK_CHARS('S', 'Y', 'S', 'T');
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 37c0d77..bdc2e40 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -60,14 +60,14 @@
         : mIsMain(isMain)
     {
     }
-    
+
 protected:
     virtual bool threadLoop()
     {
         IPCThreadState::self()->joinThreadPool(mIsMain);
         return false;
     }
-    
+
     const bool mIsMain;
 };
 
@@ -296,7 +296,7 @@
 void ProcessState::expungeHandle(int32_t handle, IBinder* binder)
 {
     AutoMutex _l(mLock);
-    
+
     handle_entry* e = lookupHandleLocked(handle);
 
     // This handle may have already been replaced with a new BpBinder
@@ -387,7 +387,7 @@
 {
 
 // TODO(b/139016109): enforce in build system
-#if defined(__ANDROID_APEX__) && !defined(__ANDROID_APEX_COM_ANDROID_VNDK_CURRENT__)
+#if defined(__ANDROID_APEX__)
     LOG_ALWAYS_FATAL("Cannot use libbinder in APEX (only system.img libbinder) since it is not stable.");
 #endif
 
@@ -418,5 +418,5 @@
     }
     mDriverFD = -1;
 }
-        
+
 } // namespace android
diff --git a/libs/binder/Static.cpp b/libs/binder/Static.cpp
index bd40536..7a77f6d 100644
--- a/libs/binder/Static.cpp
+++ b/libs/binder/Static.cpp
@@ -19,7 +19,7 @@
 
 #include "Static.h"
 
-#include <binder/BufferedTextOutput.h>
+#include "BufferedTextOutput.h"
 #include <binder/IPCThreadState.h>
 #include <utils/Log.h>
 
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 922504e..7489afa 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -19,6 +19,9 @@
       "name": "binderStabilityTest"
     },
     {
+      "name": "libbinder_ndk_unit_test"
+    },
+    {
       "name": "CtsNdkBinderTestCases"
     },
     {
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 2c43263..31f022d 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -100,6 +100,11 @@
 
 sp<IServiceManager> defaultServiceManager();
 
+/**
+ * Directly set the default service manager. Only used for testing.
+ */
+void setDefaultServiceManager(const sp<IServiceManager>& sm);
+
 template<typename INTERFACE>
 sp<INTERFACE> waitForService(const String16& name) {
     const sp<IServiceManager> sm = defaultServiceManager();
diff --git a/libs/binder/include/binder/Stability.h b/libs/binder/include/binder/Stability.h
index b2f51d3..2894482 100644
--- a/libs/binder/include/binder/Stability.h
+++ b/libs/binder/include/binder/Stability.h
@@ -81,7 +81,7 @@
         VINTF = 0b111111,
     };
 
-#if defined(__ANDROID_APEX_COM_ANDROID_VNDK_CURRENT__) || (defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__))
+#if defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
     static constexpr Level kLocalStability = Level::VENDOR;
 #else
     static constexpr Level kLocalStability = Level::SYSTEM;
diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h
index 56d95a7..f5e8bf6 100644
--- a/libs/binder/ndk/include_platform/android/binder_stability.h
+++ b/libs/binder/ndk/include_platform/android/binder_stability.h
@@ -30,8 +30,7 @@
     FLAG_PRIVATE_VENDOR = 0x10000000,
 };
 
-#if defined(__ANDROID_APEX_COM_ANDROID_VNDK_CURRENT__) || \
-        (defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__))
+#if defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
 
 enum {
     FLAG_PRIVATE_LOCAL = FLAG_PRIVATE_VENDOR,
@@ -46,8 +45,7 @@
     AIBinder_markVendorStability(binder);
 }
 
-#else  // defined(__ANDROID_APEX_COM_ANDROID_VNDK_CURRENT__) || (defined(__ANDROID_VNDK__) &&
-       // !defined(__ANDROID_APEX__))
+#else  // defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
 
 enum {
     FLAG_PRIVATE_LOCAL = 0,
@@ -64,8 +62,7 @@
     AIBinder_markSystemStability(binder);
 }
 
-#endif  // defined(__ANDROID_APEX_COM_ANDROID_VNDK_CURRENT__) || (defined(__ANDROID_VNDK__) &&
-        // !defined(__ANDROID_APEX__))
+#endif  // defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
 
 /**
  * This interface has system<->vendor stability
diff --git a/libs/binder/ndk/runtests.sh b/libs/binder/ndk/runtests.sh
deleted file mode 100755
index a0c49fb..0000000
--- a/libs/binder/ndk/runtests.sh
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/usr/bin/env bash
-
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-if [ -z $ANDROID_BUILD_TOP ]; then
-  echo "You need to source and lunch before you can use this script"
-  exit 1
-fi
-
-set -ex
-
-function run_libbinder_ndk_test() {
-    adb shell /data/nativetest64/libbinder_ndk_test_server/libbinder_ndk_test_server &
-
-    # avoid getService 1s delay for most runs, non-critical
-    sleep 0.1
-
-    adb shell /data/nativetest64/libbinder_ndk_test_client/libbinder_ndk_test_client; \
-        adb shell killall libbinder_ndk_test_server
-}
-
-[ "$1" != "--skip-build" ] && $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode \
-    MODULES-IN-frameworks-native-libs-binder-ndk
-
-adb root
-adb wait-for-device
-adb sync data
-
-# very simple unit tests, tests things outside of the NDK as well
-run_libbinder_ndk_test
-
-# CTS tests (much more comprehensive, new tests should ideally go here)
-atest android.binder.cts
diff --git a/libs/binder/ndk/test/Android.bp b/libs/binder/ndk/test/Android.bp
index daaaa5a..513d8c2 100644
--- a/libs/binder/ndk/test/Android.bp
+++ b/libs/binder/ndk/test/Android.bp
@@ -56,16 +56,17 @@
 // specifically the parts which are outside of the NDK. Actual users should
 // also instead use AIDL to generate these stubs. See android.binder.cts.
 cc_test {
-    name: "libbinder_ndk_test_client",
+    name: "libbinder_ndk_unit_test",
     defaults: ["test_libbinder_ndk_test_defaults"],
-    srcs: ["main_client.cpp"],
-}
+    srcs: ["libbinder_ndk_unit_test.cpp"],
+    static_libs: [
+        "IBinderNdkUnitTest-ndk_platform",
+    ],
+    test_suites: ["general-tests"],
+    require_root: true,
 
-cc_test {
-    name: "libbinder_ndk_test_server",
-    defaults: ["test_libbinder_ndk_test_defaults"],
-    srcs: ["main_server.cpp"],
-    gtest: false,
+    // force since binderVendorDoubleLoadTest has its own
+    auto_gen_config: true,
 }
 
 cc_test {
@@ -85,7 +86,7 @@
         "libbinder_ndk",
         "libutils",
     ],
-    test_suites: ["device-tests"],
+    test_suites: ["general-tests"],
 }
 
 aidl_interface {
@@ -95,3 +96,11 @@
         "IBinderVendorDoubleLoadTest.aidl",
     ],
 }
+
+aidl_interface {
+    name: "IBinderNdkUnitTest",
+    srcs: [
+        "IBinderNdkUnitTest.aidl",
+        "IEmpty.aidl",
+    ],
+}
diff --git a/libs/binder/ndk/test/IBinderNdkUnitTest.aidl b/libs/binder/ndk/test/IBinderNdkUnitTest.aidl
new file mode 100644
index 0000000..6e8e463
--- /dev/null
+++ b/libs/binder/ndk/test/IBinderNdkUnitTest.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This AIDL is to test things that can't be tested in CtsNdkBinderTestCases
+// because it requires libbinder_ndk implementation details or APIs not
+// available to apps. Please prefer adding tests to CtsNdkBinderTestCases
+// over here.
+
+import IEmpty;
+
+interface IBinderNdkUnitTest {
+    void takeInterface(IEmpty test);
+    void forceFlushCommands();
+}
diff --git a/libs/binder/ndk/test/IEmpty.aidl b/libs/binder/ndk/test/IEmpty.aidl
new file mode 100644
index 0000000..95e4341
--- /dev/null
+++ b/libs/binder/ndk/test/IEmpty.aidl
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+interface IEmpty { }
diff --git a/libs/binder/ndk/test/main_client.cpp b/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp
similarity index 66%
rename from libs/binder/ndk/test/main_client.cpp
rename to libs/binder/ndk/test/libbinder_ndk_unit_test.cpp
index 8467734..51dd169 100644
--- a/libs/binder/ndk/test/main_client.cpp
+++ b/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <aidl/BnBinderNdkUnitTest.h>
+#include <aidl/BnEmpty.h>
 #include <android-base/logging.h>
 #include <android/binder_ibinder_jni.h>
 #include <android/binder_manager.h>
@@ -21,6 +23,11 @@
 #include <gtest/gtest.h>
 #include <iface/iface.h>
 
+// warning: this is assuming that libbinder_ndk is using the same copy
+// of libbinder that we are.
+#include <binder/IPCThreadState.h>
+
+#include <sys/prctl.h>
 #include <chrono>
 #include <condition_variable>
 #include <mutex>
@@ -28,6 +35,65 @@
 using ::android::sp;
 
 constexpr char kExistingNonNdkService[] = "SurfaceFlinger";
+constexpr char kBinderNdkUnitTestService[] = "BinderNdkUnitTest";
+
+class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest {
+    ndk::ScopedAStatus takeInterface(const std::shared_ptr<aidl::IEmpty>& empty) {
+        (void)empty;
+        return ndk::ScopedAStatus::ok();
+    }
+    ndk::ScopedAStatus forceFlushCommands() {
+        // warning: this is assuming that libbinder_ndk is using the same copy
+        // of libbinder that we are.
+        android::IPCThreadState::self()->flushCommands();
+        return ndk::ScopedAStatus::ok();
+    }
+};
+
+int generatedService() {
+    ABinderProcess_setThreadPoolMaxThreadCount(0);
+
+    auto service = ndk::SharedRefBase::make<MyBinderNdkUnitTest>();
+    binder_status_t status =
+            AServiceManager_addService(service->asBinder().get(), kBinderNdkUnitTestService);
+
+    if (status != STATUS_OK) {
+        LOG(FATAL) << "Could not register: " << status << " " << kBinderNdkUnitTestService;
+    }
+
+    ABinderProcess_joinThreadPool();
+
+    return 1;  // should not return
+}
+
+// manually-written parceling class considered bad practice
+class MyFoo : public IFoo {
+    binder_status_t doubleNumber(int32_t in, int32_t* out) override {
+        *out = 2 * in;
+        LOG(INFO) << "doubleNumber (" << in << ") => " << *out;
+        return STATUS_OK;
+    }
+
+    binder_status_t die() override {
+        LOG(FATAL) << "IFoo::die called!";
+        return STATUS_UNKNOWN_ERROR;
+    }
+};
+
+int manualService(const char* instance) {
+    ABinderProcess_setThreadPoolMaxThreadCount(0);
+
+    // Strong reference to MyFoo kept by service manager.
+    binder_status_t status = (new MyFoo)->addService(instance);
+
+    if (status != STATUS_OK) {
+        LOG(FATAL) << "Could not register: " << status << " " << instance;
+    }
+
+    ABinderProcess_joinThreadPool();
+
+    return 1;  // should not return
+}
 
 // This is too slow
 // TEST(NdkBinder, GetServiceThatDoesntExist) {
@@ -87,14 +153,14 @@
     EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die());
 
     foo = nullptr;
-    AIBinder_decStrong(binder);
-    binder = nullptr;
 
     std::unique_lock<std::mutex> lock(deathMutex);
     EXPECT_TRUE(deathCv.wait_for(lock, 1s, [&] { return deathRecieved; }));
     EXPECT_TRUE(deathRecieved);
 
     AIBinder_DeathRecipient_delete(recipient);
+    AIBinder_decStrong(binder);
+    binder = nullptr;
 }
 
 TEST(NdkBinder, RetrieveNonNdkService) {
@@ -196,9 +262,56 @@
     EXPECT_EQ(IFoo::getService(kInstanceName1), IFoo::getService(kInstanceName2));
 }
 
+TEST(NdkBinder, SentAidlBinderCanBeDestroyed) {
+    static volatile bool destroyed = false;
+    static std::mutex dMutex;
+    static std::condition_variable cv;
+
+    class MyEmpty : public aidl::BnEmpty {
+        virtual ~MyEmpty() {
+            destroyed = true;
+            cv.notify_one();
+        }
+    };
+
+    std::shared_ptr<MyEmpty> empty = ndk::SharedRefBase::make<MyEmpty>();
+
+    ndk::SpAIBinder binder(AServiceManager_getService(kBinderNdkUnitTestService));
+    std::shared_ptr<aidl::IBinderNdkUnitTest> service =
+            aidl::IBinderNdkUnitTest::fromBinder(binder);
+
+    EXPECT_FALSE(destroyed);
+
+    service->takeInterface(empty);
+    service->forceFlushCommands();
+    empty = nullptr;
+
+    // give other binder thread time to process commands
+    {
+        using namespace std::chrono_literals;
+        std::unique_lock<std::mutex> lk(dMutex);
+        cv.wait_for(lk, 1s, [] { return destroyed; });
+    }
+
+    EXPECT_TRUE(destroyed);
+}
+
 int main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
 
+    if (fork() == 0) {
+        prctl(PR_SET_PDEATHSIG, SIGHUP);
+        return manualService(IFoo::kInstanceNameToDieFor);
+    }
+    if (fork() == 0) {
+        prctl(PR_SET_PDEATHSIG, SIGHUP);
+        return manualService(IFoo::kSomeInstanceName);
+    }
+    if (fork() == 0) {
+        prctl(PR_SET_PDEATHSIG, SIGHUP);
+        return generatedService();
+    }
+
     ABinderProcess_setThreadPoolMaxThreadCount(1);  // to recieve death notifications/callbacks
     ABinderProcess_startThreadPool();
 
diff --git a/libs/binder/ndk/test/main_server.cpp b/libs/binder/ndk/test/main_server.cpp
deleted file mode 100644
index a6e17e8..0000000
--- a/libs/binder/ndk/test/main_server.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android-base/logging.h>
-#include <android/binder_process.h>
-#include <iface/iface.h>
-
-using ::android::sp;
-
-class MyFoo : public IFoo {
-    binder_status_t doubleNumber(int32_t in, int32_t* out) override {
-        *out = 2 * in;
-        LOG(INFO) << "doubleNumber (" << in << ") => " << *out;
-        return STATUS_OK;
-    }
-
-    binder_status_t die() override {
-        LOG(FATAL) << "IFoo::die called!";
-        return STATUS_UNKNOWN_ERROR;
-    }
-};
-
-int service(const char* instance) {
-    ABinderProcess_setThreadPoolMaxThreadCount(0);
-
-    // Strong reference to MyFoo kept by service manager.
-    binder_status_t status = (new MyFoo)->addService(instance);
-
-    if (status != STATUS_OK) {
-        LOG(FATAL) << "Could not register: " << status << " " << instance;
-    }
-
-    ABinderProcess_joinThreadPool();
-
-    return 1;  // should not return
-}
-
-int main() {
-    if (fork() == 0) {
-        return service(IFoo::kInstanceNameToDieFor);
-    }
-
-    return service(IFoo::kSomeInstanceName);
-}
diff --git a/libs/bufferqueueconverter/Android.bp b/libs/bufferqueueconverter/Android.bp
new file mode 100644
index 0000000..bab2674
--- /dev/null
+++ b/libs/bufferqueueconverter/Android.bp
@@ -0,0 +1,28 @@
+cc_library_headers {
+    name: "libbufferqueueconverter_headers",
+    vendor_available: true,
+    export_include_dirs: ["include"],
+}
+
+cc_library_shared {
+    name: "libbufferqueueconverter",
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
+    double_loadable: true,
+
+    srcs: [
+        "BufferQueueConverter.cpp",
+    ],
+
+    shared_libs: [
+        "libgui",
+        "libui",
+        "libutils",
+        "libbinder",
+        "libbase",
+        "liblog",
+    ],
+    export_include_dirs: ["include"],
+}
diff --git a/libs/bufferqueueconverter/BufferQueueConverter.cpp b/libs/bufferqueueconverter/BufferQueueConverter.cpp
new file mode 100644
index 0000000..b1896fa
--- /dev/null
+++ b/libs/bufferqueueconverter/BufferQueueConverter.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/Surface.h>
+#include <gui/bufferqueue/2.0/H2BGraphicBufferProducer.h>
+
+#include "include/bufferqueueconverter/BufferQueueConverter.h"
+
+
+using ::android::Surface;
+using ::android::IGraphicBufferProducer;
+using ::android::hardware::graphics::bufferqueue::V2_0::utils::H2BGraphicBufferProducer;
+
+
+namespace android {
+
+struct SurfaceHolder {
+    sp<Surface> surface;
+    SurfaceHolder(const sp<Surface>& s) : surface(s) {}
+};
+
+/**
+ * Custom deleter for SurfaceHolder unique pointer
+ */
+void destroySurfaceHolder(SurfaceHolder* surfaceHolder) {
+    delete surfaceHolder;
+}
+
+
+SurfaceHolderUniquePtr getSurfaceFromHGBP(const sp<HGraphicBufferProducer>& token) {
+    if (token == nullptr) {
+        ALOGE("Passed IGraphicBufferProducer handle is invalid.");
+        return SurfaceHolderUniquePtr(nullptr, nullptr);
+    }
+
+    sp<IGraphicBufferProducer> bufferProducer = new H2BGraphicBufferProducer(token);
+    if (bufferProducer == nullptr) {
+        ALOGE("Failed to get IGraphicBufferProducer.");
+        return SurfaceHolderUniquePtr(nullptr, nullptr);
+    }
+
+    sp<Surface> newSurface(new Surface(bufferProducer, true));
+    if (newSurface == nullptr) {
+        ALOGE("Failed to create Surface from HGBP.");
+        return SurfaceHolderUniquePtr(nullptr, nullptr);
+    }
+
+    return SurfaceHolderUniquePtr(new SurfaceHolder(newSurface), destroySurfaceHolder);
+}
+
+
+ANativeWindow* getNativeWindow(SurfaceHolder* handle) {
+    if (handle == nullptr) {
+        ALOGE("SurfaceHolder is invalid.");
+        return nullptr;
+    }
+
+    return static_cast<ANativeWindow*>(handle->surface.get());
+}
+
+} // namespace android
diff --git a/libs/bufferqueueconverter/include/bufferqueueconverter/BufferQueueConverter.h b/libs/bufferqueueconverter/include/bufferqueueconverter/BufferQueueConverter.h
new file mode 100644
index 0000000..84bceba
--- /dev/null
+++ b/libs/bufferqueueconverter/include/bufferqueueconverter/BufferQueueConverter.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_BUFFER_QUEUE_CONVERTER_H
+#define ANDROID_BUFFER_QUEUE_CONVERTER_H
+
+#include <gui/IGraphicBufferProducer.h>
+#include <android/native_window.h>
+
+using ::android::sp;
+using HGraphicBufferProducer =
+      ::android::hardware::graphics::bufferqueue::V2_0::IGraphicBufferProducer;
+
+namespace android {
+    /**
+     * Opaque handle for a data structure holding Surface.
+     */
+    typedef struct SurfaceHolder SurfaceHolder;
+
+    /**
+     * SurfaceHolder unique pointer type
+     */
+    using SurfaceHolderUniquePtr = std::unique_ptr<SurfaceHolder, void(*)(SurfaceHolder*)>;
+
+    /**
+     * Returns a SurfaceHolder that wraps a Surface generated from a given HGBP.
+     *
+     * @param  token         Hardware IGraphicBufferProducer to create a
+     *                       Surface.
+     * @return SurfaceHolder Unique pointer to created SurfaceHolder object.
+     */
+    SurfaceHolderUniquePtr getSurfaceFromHGBP(const sp<HGraphicBufferProducer>& token);
+
+    /**
+     * Returns ANativeWindow pointer from a given SurfaceHolder.  Returned
+     * pointer is valid only while the containing SurfaceHolder is alive.
+     *
+     * @param  surfaceHolder  SurfaceHolder to generate a native window.
+     * @return ANativeWindow* a pointer to a generated native window.
+     */
+    ANativeWindow* getNativeWindow(SurfaceHolder* surfaceHolder);
+
+} // namespace android
+
+#endif // ANDROID_BUFFER_QUEUE_CONVERTER_H
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
index ee44cf5..31a399b 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -49,6 +49,8 @@
 
 static std::mutex gInitializedMutex;
 static bool gInitialized = false;
+static std::mutex gTrackingMutex;
+static bool gTracking = false;
 static uint32_t gNPolicies = 0;
 static uint32_t gNCpus = 0;
 static std::vector<std::vector<uint32_t>> gPolicyFreqs;
@@ -161,7 +163,9 @@
 // This function should *not* be called while tracking is already active; doing so is unnecessary
 // and can lead to accounting errors.
 bool startTrackingUidTimes() {
+    std::lock_guard<std::mutex> guard(gTrackingMutex);
     if (!initGlobals()) return false;
+    if (gTracking) return true;
 
     unique_fd cpuPolicyFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_cpu_policy_map"));
     if (cpuPolicyFd < 0) return false;
@@ -209,8 +213,9 @@
         if (writeToMapEntry(policyFreqIdxFd, &i, &zero, BPF_ANY)) return false;
     }
 
-    return attachTracepointProgram("sched", "sched_switch") &&
+    gTracking = attachTracepointProgram("sched", "sched_switch") &&
             attachTracepointProgram("power", "cpu_frequency");
+    return gTracking;
 }
 
 std::optional<std::vector<std::vector<uint32_t>>> getCpuFreqs() {
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 875059c..90fded0 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -55,6 +55,7 @@
         "android.hardware.audio@2.0::IDevicesFactory",
         "android.hardware.audio@4.0::IDevicesFactory",
         "android.hardware.audio@5.0::IDevicesFactory",
+        "android.hardware.audio@6.0::IDevicesFactory",
         "android.hardware.biometrics.face@1.0::IBiometricsFace",
         "android.hardware.bluetooth@1.0::IBluetoothHci",
         "android.hardware.camera.provider@2.4::ICameraProvider",
diff --git a/libs/fakeservicemanager/Android.bp b/libs/fakeservicemanager/Android.bp
new file mode 100644
index 0000000..de32ff4
--- /dev/null
+++ b/libs/fakeservicemanager/Android.bp
@@ -0,0 +1,25 @@
+cc_defaults {
+    name: "fakeservicemanager_defaults",
+    srcs: [
+        "ServiceManager.cpp",
+    ],
+
+    shared_libs: [
+        "libbinder",
+        "libutils",
+    ],
+}
+
+cc_library {
+    name: "libfakeservicemanager",
+    defaults: ["fakeservicemanager_defaults"],
+}
+
+cc_test_host {
+    name: "fakeservicemanager_test",
+    defaults: ["fakeservicemanager_defaults"],
+    srcs: [
+        "test_sm.cpp",
+    ],
+    static_libs: ["libgmock"],
+}
diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/ServiceManager.cpp
new file mode 100644
index 0000000..6964324
--- /dev/null
+++ b/libs/fakeservicemanager/ServiceManager.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ServiceManager.h"
+
+namespace android {
+
+ServiceManager::ServiceManager() {}
+
+sp<IBinder> ServiceManager::getService( const String16& name) const {
+    // Servicemanager is single-threaded and cannot block. This method exists for legacy reasons.
+    return checkService(name);
+}
+
+sp<IBinder> ServiceManager::checkService( const String16& name) const {
+    auto it = mNameToService.find(name);
+    if (it == mNameToService.end()) {
+        return nullptr;
+    }
+    return it->second;
+}
+
+status_t ServiceManager::addService(const String16& name, const sp<IBinder>& service,
+                                bool /*allowIsolated*/,
+                                int /*dumpsysFlags*/) {
+    mNameToService[name] = service;
+    return NO_ERROR;
+}
+
+Vector<String16> ServiceManager::listServices(int /*dumpsysFlags*/) {
+    Vector<String16> services;
+    for (auto const& [name, service] : mNameToService) {
+        (void) service;
+         services.push_back(name);
+    }
+  return services;
+}
+
+IBinder* ServiceManager::onAsBinder() {
+    return nullptr;
+}
+
+sp<IBinder> ServiceManager::waitForService(const String16& name) {
+    return checkService(name);
+}
+
+bool ServiceManager::isDeclared(const String16& name) {
+    return mNameToService.find(name) != mNameToService.end();
+}
+
+}  // namespace android
diff --git a/libs/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/ServiceManager.h
new file mode 100644
index 0000000..62311d4
--- /dev/null
+++ b/libs/fakeservicemanager/ServiceManager.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/IServiceManager.h>
+
+#include <map>
+
+namespace android {
+
+/**
+ * A local host simple implementation of IServiceManager, that does not
+ * communicate over binder.
+*/
+class ServiceManager : public IServiceManager {
+public:
+    ServiceManager();
+
+    /**
+     * Equivalent of checkService.
+     */
+    sp<IBinder> getService( const String16& name) const override;
+
+    /**
+     * Retrieve an existing service, non-blocking.
+     */
+    sp<IBinder> checkService( const String16& name) const override;
+
+    /**
+     * Register a service.
+     */
+    status_t addService(const String16& name, const sp<IBinder>& service,
+                        bool allowIsolated = false,
+                        int dumpsysFlags = DUMP_FLAG_PRIORITY_DEFAULT) override;
+
+    /**
+     * Return list of all existing services.
+     */
+    Vector<String16> listServices(int dumpsysFlags = 0) override;
+
+    IBinder* onAsBinder() override;
+
+    /**
+     * Effectively no-oped in this implementation - equivalent to checkService.
+     */
+    sp<IBinder> waitForService(const String16& name) override;
+
+    /**
+     * Check if a service is declared (e.g. VINTF manifest).
+     *
+     * If this returns true, waitForService should always be able to return the
+     * service.
+     */
+     bool isDeclared(const String16& name) override;
+
+private:
+    std::map<String16, sp<IBinder>> mNameToService;
+};
+
+}  // namespace android
diff --git a/libs/fakeservicemanager/test_sm.cpp b/libs/fakeservicemanager/test_sm.cpp
new file mode 100644
index 0000000..71e5abe
--- /dev/null
+++ b/libs/fakeservicemanager/test_sm.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include <binder/Binder.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+
+#include "ServiceManager.h"
+
+using android::sp;
+using android::BBinder;
+using android::IBinder;
+using android::OK;
+using android::status_t;
+using android::ServiceManager;
+using android::String16;
+using android::IServiceManager;
+using testing::ElementsAre;
+
+static sp<IBinder> getBinder() {
+    class LinkableBinder : public BBinder {
+        status_t linkToDeath(const sp<DeathRecipient>&, void*, uint32_t) override {
+            // let SM linkToDeath
+            return OK;
+        }
+    };
+
+    return new LinkableBinder;
+}
+
+TEST(AddService, HappyHappy) {
+    auto sm = new ServiceManager();
+    EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
+        IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
+}
+
+TEST(AddService, HappyOverExistingService) {
+    auto sm = new ServiceManager();
+    EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
+        IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
+    EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
+        IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
+}
+
+TEST(GetService, HappyHappy) {
+    auto sm = new ServiceManager();
+    sp<IBinder> service = getBinder();
+
+    EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/,
+        IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
+
+    EXPECT_EQ(sm->getService(String16("foo")), service);
+}
+
+TEST(GetService, NonExistant) {
+    auto sm = new ServiceManager();
+
+    EXPECT_EQ(sm->getService(String16("foo")), nullptr);
+}
+
+TEST(ListServices, AllServices) {
+    auto sm = new ServiceManager();
+
+    EXPECT_EQ(sm->addService(String16("sd"), getBinder(), false /*allowIsolated*/,
+        IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
+    EXPECT_EQ(sm->addService(String16("sc"), getBinder(), false /*allowIsolated*/,
+        IServiceManager::DUMP_FLAG_PRIORITY_NORMAL), OK);
+    EXPECT_EQ(sm->addService(String16("sb"), getBinder(), false /*allowIsolated*/,
+        IServiceManager::DUMP_FLAG_PRIORITY_HIGH), OK);
+    EXPECT_EQ(sm->addService(String16("sa"), getBinder(), false /*allowIsolated*/,
+        IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL), OK);
+
+    android::Vector<String16> out = sm->listServices(IServiceManager::DUMP_FLAG_PRIORITY_ALL);
+
+    // all there and in the right order
+    EXPECT_THAT(out, ElementsAre(String16("sa"), String16("sb"), String16("sc"),
+        String16("sd")));
+}
+
+TEST(WaitForService, NonExistant) {
+    auto sm = new ServiceManager();
+
+    EXPECT_EQ(sm->waitForService(String16("foo")), nullptr);
+}
+
+TEST(WaitForService, HappyHappy) {
+    auto sm = new ServiceManager();
+    sp<IBinder> service = getBinder();
+
+    EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/,
+        IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
+
+    EXPECT_EQ(sm->waitForService(String16("foo")), service);
+}
+
+TEST(IsDeclared, NonExistant) {
+    auto sm = new ServiceManager();
+
+    EXPECT_FALSE(sm->isDeclared(String16("foo")));
+}
+
+TEST(IsDeclared, HappyHappy) {
+    auto sm = new ServiceManager();
+    sp<IBinder> service = getBinder();
+
+    EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/,
+        IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
+
+    EXPECT_TRUE(sm->isDeclared(String16("foo")));
+}
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index d2b25a2..4e62da7 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -83,6 +83,7 @@
         if (stats.size() > 0) {
             mPendingReleaseItem.releaseFence = stats[0].previousReleaseFence;
             mTransformHint = stats[0].transformHint;
+            mBufferItemConsumer->setTransformHint(mTransformHint);
         } else {
             ALOGE("Warning: no SurfaceControlStats returned in BLASTBufferQueue callback");
             mPendingReleaseItem.releaseFence = nullptr;
@@ -151,10 +152,11 @@
                        bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE);
     t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
 
-    t->setFrame(mSurfaceControl, {0, 0, (int32_t)buffer->getWidth(), (int32_t)buffer->getHeight()});
+    t->setFrame(mSurfaceControl, {0, 0, mWidth, mHeight});
     t->setCrop(mSurfaceControl, computeCrop(bufferItem));
     t->setTransform(mSurfaceControl, bufferItem.mTransform);
     t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse);
+    t->setDesiredPresentTime(bufferItem.mTimestamp);
 
     if (applyTransaction) {
         t->apply();
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 3b0120b..9e5d681 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -84,54 +84,54 @@
     return INVALID_OPERATION;
 }
 
-BufferQueueCore::BufferQueueCore() :
-    mMutex(),
-    mIsAbandoned(false),
-    mConsumerControlledByApp(false),
-    mConsumerName(getUniqueName()),
-    mConsumerListener(),
-    mConsumerUsageBits(0),
-    mConsumerIsProtected(false),
-    mConnectedApi(NO_CONNECTED_API),
-    mLinkedToDeath(),
-    mConnectedProducerListener(),
-    mBufferReleasedCbEnabled(false),
-    mSlots(),
-    mQueue(),
-    mFreeSlots(),
-    mFreeBuffers(),
-    mUnusedSlots(),
-    mActiveBuffers(),
-    mDequeueCondition(),
-    mDequeueBufferCannotBlock(false),
-    mQueueBufferCanDrop(false),
-    mLegacyBufferDrop(true),
-    mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),
-    mDefaultWidth(1),
-    mDefaultHeight(1),
-    mDefaultBufferDataSpace(HAL_DATASPACE_UNKNOWN),
-    mMaxBufferCount(BufferQueueDefs::NUM_BUFFER_SLOTS),
-    mMaxAcquiredBufferCount(1),
-    mMaxDequeuedBufferCount(1),
-    mBufferHasBeenQueued(false),
-    mFrameCounter(0),
-    mTransformHint(0),
-    mIsAllocating(false),
-    mIsAllocatingCondition(),
-    mAllowAllocation(true),
-    mBufferAge(0),
-    mGenerationNumber(0),
-    mAsyncMode(false),
-    mSharedBufferMode(false),
-    mAutoRefresh(false),
-    mSharedBufferSlot(INVALID_BUFFER_SLOT),
-    mSharedBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,
-            HAL_DATASPACE_UNKNOWN),
-    mLastQueuedSlot(INVALID_BUFFER_SLOT),
-    mUniqueId(getUniqueId()),
-    mAutoPrerotation(false),
-    mTransformHintInUse(0)
-{
+BufferQueueCore::BufferQueueCore()
+      : mMutex(),
+        mIsAbandoned(false),
+        mConsumerControlledByApp(false),
+        mConsumerName(getUniqueName()),
+        mConsumerListener(),
+        mConsumerUsageBits(0),
+        mConsumerIsProtected(false),
+        mConnectedApi(NO_CONNECTED_API),
+        mLinkedToDeath(),
+        mConnectedProducerListener(),
+        mBufferReleasedCbEnabled(false),
+        mSlots(),
+        mQueue(),
+        mFreeSlots(),
+        mFreeBuffers(),
+        mUnusedSlots(),
+        mActiveBuffers(),
+        mDequeueCondition(),
+        mDequeueBufferCannotBlock(false),
+        mQueueBufferCanDrop(false),
+        mLegacyBufferDrop(true),
+        mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),
+        mDefaultWidth(1),
+        mDefaultHeight(1),
+        mDefaultBufferDataSpace(HAL_DATASPACE_UNKNOWN),
+        mMaxBufferCount(BufferQueueDefs::NUM_BUFFER_SLOTS),
+        mMaxAcquiredBufferCount(1),
+        mMaxDequeuedBufferCount(1),
+        mBufferHasBeenQueued(false),
+        mFrameCounter(0),
+        mTransformHint(0),
+        mIsAllocating(false),
+        mIsAllocatingCondition(),
+        mAllowAllocation(true),
+        mBufferAge(0),
+        mGenerationNumber(0),
+        mAsyncMode(false),
+        mSharedBufferMode(false),
+        mAutoRefresh(false),
+        mSharedBufferSlot(INVALID_BUFFER_SLOT),
+        mSharedBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,
+                           HAL_DATASPACE_UNKNOWN),
+        mLastQueuedSlot(INVALID_BUFFER_SLOT),
+        mUniqueId(getUniqueId()),
+        mAutoPrerotation(false),
+        mTransformHintInUse(0),
+        mFrameRate(0) {
     int numStartingBuffers = getMaxBufferCountLocked();
     for (int s = 0; s < numStartingBuffers; s++) {
         mFreeSlots.insert(s);
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 073543c..2f27fd2 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -34,8 +34,10 @@
 
 #include <system/graphics.h>
 
+#include <ui/DisplayConfig.h>
 #include <ui/DisplayInfo.h>
 #include <ui/DisplayStatInfo.h>
+#include <ui/DisplayState.h>
 #include <ui/HdrCapabilities.h>
 
 #include <utils/Log.h>
@@ -351,22 +353,43 @@
         remote()->transact(BnSurfaceComposer::SET_POWER_MODE, data, &reply);
     }
 
-    virtual status_t getDisplayConfigs(const sp<IBinder>& display,
-            Vector<DisplayInfo>* configs)
-    {
+    virtual status_t getDisplayState(const sp<IBinder>& display, ui::DisplayState* state) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        data.writeStrongBinder(display);
+        remote()->transact(BnSurfaceComposer::GET_DISPLAY_STATE, data, &reply);
+        const status_t result = reply.readInt32();
+        if (result == NO_ERROR) {
+            memcpy(state, reply.readInplace(sizeof(ui::DisplayState)), sizeof(ui::DisplayState));
+        }
+        return result;
+    }
+
+    virtual status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo* info) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        data.writeStrongBinder(display);
+        remote()->transact(BnSurfaceComposer::GET_DISPLAY_INFO, data, &reply);
+        const status_t result = reply.readInt32();
+        if (result == NO_ERROR) {
+            memcpy(info, reply.readInplace(sizeof(DisplayInfo)), sizeof(DisplayInfo));
+        }
+        return result;
+    }
+
+    virtual status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayConfig>* configs) {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
         data.writeStrongBinder(display);
         remote()->transact(BnSurfaceComposer::GET_DISPLAY_CONFIGS, data, &reply);
-        status_t result = reply.readInt32();
+        const status_t result = reply.readInt32();
         if (result == NO_ERROR) {
-            size_t numConfigs = reply.readUint32();
+            const size_t numConfigs = reply.readUint32();
             configs->clear();
             configs->resize(numConfigs);
             for (size_t c = 0; c < numConfigs; ++c) {
-                memcpy(&(configs->editItemAt(c)),
-                        reply.readInplace(sizeof(DisplayInfo)),
-                        sizeof(DisplayInfo));
+                memcpy(&(configs->editItemAt(c)), reply.readInplace(sizeof(DisplayConfig)),
+                       sizeof(DisplayConfig));
             }
         }
         return result;
@@ -1297,17 +1320,40 @@
             reply->writeStrongBinder(display);
             return NO_ERROR;
         }
+        case GET_DISPLAY_STATE: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            ui::DisplayState state;
+            const sp<IBinder> display = data.readStrongBinder();
+            const status_t result = getDisplayState(display, &state);
+            reply->writeInt32(result);
+            if (result == NO_ERROR) {
+                memcpy(reply->writeInplace(sizeof(ui::DisplayState)), &state,
+                       sizeof(ui::DisplayState));
+            }
+            return NO_ERROR;
+        }
+        case GET_DISPLAY_INFO: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            DisplayInfo info;
+            const sp<IBinder> display = data.readStrongBinder();
+            const status_t result = getDisplayInfo(display, &info);
+            reply->writeInt32(result);
+            if (result == NO_ERROR) {
+                memcpy(reply->writeInplace(sizeof(DisplayInfo)), &info, sizeof(DisplayInfo));
+            }
+            return NO_ERROR;
+        }
         case GET_DISPLAY_CONFIGS: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            Vector<DisplayInfo> configs;
-            sp<IBinder> display = data.readStrongBinder();
-            status_t result = getDisplayConfigs(display, &configs);
+            Vector<DisplayConfig> configs;
+            const sp<IBinder> display = data.readStrongBinder();
+            const status_t result = getDisplayConfigs(display, &configs);
             reply->writeInt32(result);
             if (result == NO_ERROR) {
                 reply->writeUint32(static_cast<uint32_t>(configs.size()));
                 for (size_t c = 0; c < configs.size(); ++c) {
-                    memcpy(reply->writeInplace(sizeof(DisplayInfo)),
-                            &configs[c], sizeof(DisplayInfo));
+                    memcpy(reply->writeInplace(sizeof(DisplayConfig)), &configs[c],
+                           sizeof(DisplayConfig));
                 }
             }
             return NO_ERROR;
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index d5cf11d..23532e7 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -50,6 +50,17 @@
 using ui::ColorMode;
 using ui::Dataspace;
 
+namespace {
+
+bool isInterceptorRegistrationOp(int op) {
+    return op == NATIVE_WINDOW_SET_CANCEL_INTERCEPTOR ||
+            op == NATIVE_WINDOW_SET_DEQUEUE_INTERCEPTOR ||
+            op == NATIVE_WINDOW_SET_PERFORM_INTERCEPTOR ||
+            op == NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR;
+}
+
+} // namespace
+
 Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp)
       : mGraphicBufferProducer(bufferProducer),
         mCrop(Rect::EMPTY_RECT),
@@ -366,18 +377,58 @@
 int Surface::hook_dequeueBuffer(ANativeWindow* window,
         ANativeWindowBuffer** buffer, int* fenceFd) {
     Surface* c = getSelf(window);
+    {
+        std::shared_lock<std::shared_mutex> lock(c->mInterceptorMutex);
+        if (c->mDequeueInterceptor != nullptr) {
+            auto interceptor = c->mDequeueInterceptor;
+            auto data = c->mDequeueInterceptorData;
+            return interceptor(window, Surface::dequeueBufferInternal, data, buffer, fenceFd);
+        }
+    }
+    return c->dequeueBuffer(buffer, fenceFd);
+}
+
+int Surface::dequeueBufferInternal(ANativeWindow* window, ANativeWindowBuffer** buffer,
+                                   int* fenceFd) {
+    Surface* c = getSelf(window);
     return c->dequeueBuffer(buffer, fenceFd);
 }
 
 int Surface::hook_cancelBuffer(ANativeWindow* window,
         ANativeWindowBuffer* buffer, int fenceFd) {
     Surface* c = getSelf(window);
+    {
+        std::shared_lock<std::shared_mutex> lock(c->mInterceptorMutex);
+        if (c->mCancelInterceptor != nullptr) {
+            auto interceptor = c->mCancelInterceptor;
+            auto data = c->mCancelInterceptorData;
+            return interceptor(window, Surface::cancelBufferInternal, data, buffer, fenceFd);
+        }
+    }
+    return c->cancelBuffer(buffer, fenceFd);
+}
+
+int Surface::cancelBufferInternal(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) {
+    Surface* c = getSelf(window);
     return c->cancelBuffer(buffer, fenceFd);
 }
 
 int Surface::hook_queueBuffer(ANativeWindow* window,
         ANativeWindowBuffer* buffer, int fenceFd) {
     Surface* c = getSelf(window);
+    {
+        std::shared_lock<std::shared_mutex> lock(c->mInterceptorMutex);
+        if (c->mQueueInterceptor != nullptr) {
+            auto interceptor = c->mQueueInterceptor;
+            auto data = c->mQueueInterceptorData;
+            return interceptor(window, Surface::queueBufferInternal, data, buffer, fenceFd);
+        }
+    }
+    return c->queueBuffer(buffer, fenceFd);
+}
+
+int Surface::queueBufferInternal(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) {
+    Surface* c = getSelf(window);
     return c->queueBuffer(buffer, fenceFd);
 }
 
@@ -420,21 +471,38 @@
     return c->queueBuffer(buffer, -1);
 }
 
-int Surface::hook_query(const ANativeWindow* window,
-                                int what, int* value) {
-    const Surface* c = getSelf(window);
-    return c->query(what, value);
-}
-
 int Surface::hook_perform(ANativeWindow* window, int operation, ...) {
     va_list args;
     va_start(args, operation);
     Surface* c = getSelf(window);
-    int result = c->perform(operation, args);
+    int result;
+    // Don't acquire shared ownership of the interceptor mutex if we're going to
+    // do interceptor registration, as otherwise we'll deadlock on acquiring
+    // exclusive ownership.
+    if (!isInterceptorRegistrationOp(operation)) {
+        std::shared_lock<std::shared_mutex> lock(c->mInterceptorMutex);
+        if (c->mPerformInterceptor != nullptr) {
+            result = c->mPerformInterceptor(window, Surface::performInternal,
+                                            c->mPerformInterceptorData, operation, args);
+            va_end(args);
+            return result;
+        }
+    }
+    result = c->perform(operation, args);
     va_end(args);
     return result;
 }
 
+int Surface::performInternal(ANativeWindow* window, int operation, va_list args) {
+    Surface* c = getSelf(window);
+    return c->perform(operation, args);
+}
+
+int Surface::hook_query(const ANativeWindow* window, int what, int* value) {
+    const Surface* c = getSelf(window);
+    return c->query(what, value);
+}
+
 int Surface::setSwapInterval(int interval) {
     ATRACE_CALL();
     // EGL specification states:
@@ -1096,6 +1164,22 @@
     case NATIVE_WINDOW_SET_FRAME_RATE:
         res = dispatchSetFrameRate(args);
         break;
+    case NATIVE_WINDOW_SET_CANCEL_INTERCEPTOR:
+        res = dispatchAddCancelInterceptor(args);
+        break;
+    case NATIVE_WINDOW_SET_DEQUEUE_INTERCEPTOR:
+        res = dispatchAddDequeueInterceptor(args);
+        break;
+    case NATIVE_WINDOW_SET_PERFORM_INTERCEPTOR:
+        res = dispatchAddPerformInterceptor(args);
+        break;
+    case NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR:
+        res = dispatchAddQueueInterceptor(args);
+        break;
+    case NATIVE_WINDOW_ALLOCATE_BUFFERS:
+        allocateBuffers();
+        res = NO_ERROR;
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -1329,6 +1413,45 @@
     return setFrameRate(frameRate);
 }
 
+int Surface::dispatchAddCancelInterceptor(va_list args) {
+    ANativeWindow_cancelBufferInterceptor interceptor =
+            va_arg(args, ANativeWindow_cancelBufferInterceptor);
+    void* data = va_arg(args, void*);
+    std::lock_guard<std::shared_mutex> lock(mInterceptorMutex);
+    mCancelInterceptor = interceptor;
+    mCancelInterceptorData = data;
+    return NO_ERROR;
+}
+
+int Surface::dispatchAddDequeueInterceptor(va_list args) {
+    ANativeWindow_dequeueBufferInterceptor interceptor =
+            va_arg(args, ANativeWindow_dequeueBufferInterceptor);
+    void* data = va_arg(args, void*);
+    std::lock_guard<std::shared_mutex> lock(mInterceptorMutex);
+    mDequeueInterceptor = interceptor;
+    mDequeueInterceptorData = data;
+    return NO_ERROR;
+}
+
+int Surface::dispatchAddPerformInterceptor(va_list args) {
+    ANativeWindow_performInterceptor interceptor = va_arg(args, ANativeWindow_performInterceptor);
+    void* data = va_arg(args, void*);
+    std::lock_guard<std::shared_mutex> lock(mInterceptorMutex);
+    mPerformInterceptor = interceptor;
+    mPerformInterceptorData = data;
+    return NO_ERROR;
+}
+
+int Surface::dispatchAddQueueInterceptor(va_list args) {
+    ANativeWindow_queueBufferInterceptor interceptor =
+            va_arg(args, ANativeWindow_queueBufferInterceptor);
+    void* data = va_arg(args, void*);
+    std::lock_guard<std::shared_mutex> lock(mInterceptorMutex);
+    mQueueInterceptor = interceptor;
+    mQueueInterceptorData = data;
+    return NO_ERROR;
+}
+
 bool Surface::transformToDisplayInverse() {
     return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) ==
             NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 63dc333..43bccf6 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -31,8 +31,6 @@
 
 #include <system/graphics.h>
 
-#include <ui/DisplayInfo.h>
-
 #include <gui/BufferItemConsumer.h>
 #include <gui/CpuConsumer.h>
 #include <gui/IGraphicBufferProducer.h>
@@ -41,6 +39,7 @@
 #include <gui/LayerState.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
+#include <ui/DisplayConfig.h>
 
 #ifndef NO_INPUT
 #include <input/InputWindow.h>
@@ -1623,15 +1622,23 @@
     return sf->injectVSync(when);
 }
 
-status_t SurfaceComposerClient::getDisplayConfigs(
-        const sp<IBinder>& display, Vector<DisplayInfo>* configs)
-{
+status_t SurfaceComposerClient::getDisplayState(const sp<IBinder>& display,
+                                                ui::DisplayState* state) {
+    return ComposerService::getComposerService()->getDisplayState(display, state);
+}
+
+status_t SurfaceComposerClient::getDisplayInfo(const sp<IBinder>& display, DisplayInfo* info) {
+    return ComposerService::getComposerService()->getDisplayInfo(display, info);
+}
+
+status_t SurfaceComposerClient::getDisplayConfigs(const sp<IBinder>& display,
+                                                  Vector<DisplayConfig>* configs) {
     return ComposerService::getComposerService()->getDisplayConfigs(display, configs);
 }
 
-status_t SurfaceComposerClient::getDisplayInfo(const sp<IBinder>& display,
-        DisplayInfo* info) {
-    Vector<DisplayInfo> configs;
+status_t SurfaceComposerClient::getActiveDisplayConfig(const sp<IBinder>& display,
+                                                       DisplayConfig* config) {
+    Vector<DisplayConfig> configs;
     status_t result = getDisplayConfigs(display, &configs);
     if (result != NO_ERROR) {
         return result;
@@ -1643,7 +1650,7 @@
         return NAME_NOT_FOUND;
     }
 
-    *info = configs[static_cast<size_t>(activeId)];
+    *config = configs[static_cast<size_t>(activeId)];
     return NO_ERROR;
 }
 
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 46c9f3a..e860f61 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_GUI_ISURFACE_COMPOSER_H
-#define ANDROID_GUI_ISURFACE_COMPOSER_H
+#pragma once
 
 #include <stdint.h>
 #include <sys/types.h>
@@ -46,13 +45,13 @@
 #include <vector>
 
 namespace android {
-// ----------------------------------------------------------------------------
 
 struct client_cache_t;
 struct ComposerState;
-struct DisplayState;
+struct DisplayConfig;
 struct DisplayInfo;
 struct DisplayStatInfo;
+struct DisplayState;
 struct InputWindowCommands;
 class LayerDebugInfo;
 class HdrCapabilities;
@@ -63,6 +62,12 @@
 class Rect;
 enum class FrameEvent;
 
+namespace ui {
+
+struct DisplayState;
+
+} // namespace ui
+
 /*
  * This class defines the Binder IPC interface for accessing various
  * SurfaceFlinger features.
@@ -161,10 +166,6 @@
      */
     virtual void setPowerMode(const sp<IBinder>& display, int mode) = 0;
 
-    /* returns information for each configuration of the given display
-     * intended to be used to get information about built-in displays */
-    virtual status_t getDisplayConfigs(const sp<IBinder>& display,
-            Vector<DisplayInfo>* configs) = 0;
 
     /* returns display statistics for a given display
      * intended to be used by the media framework to properly schedule
@@ -172,8 +173,25 @@
     virtual status_t getDisplayStats(const sp<IBinder>& display,
             DisplayStatInfo* stats) = 0;
 
-    /* indicates which of the configurations returned by getDisplayInfo is
-     * currently active */
+    /**
+     * Get transactional state of given display.
+     */
+    virtual status_t getDisplayState(const sp<IBinder>& display, ui::DisplayState*) = 0;
+
+    /**
+     * Get immutable information about given physical display.
+     */
+    virtual status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo*) = 0;
+
+    /**
+     * Get configurations supported by given physical display.
+     */
+    virtual status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayConfig>*) = 0;
+
+    /**
+     * Get the index into configurations returned by getDisplayConfigs,
+     * corresponding to the active configuration.
+     */
     virtual int getActiveConfig(const sp<IBinder>& display) = 0;
 
     virtual status_t getDisplayColorModes(const sp<IBinder>& display,
@@ -493,7 +511,7 @@
         // Java by ActivityManagerService.
         BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION,
         CREATE_CONNECTION,
-        CREATE_GRAPHIC_BUFFER_ALLOC_UNUSED, // unused, fails permissions check
+        GET_DISPLAY_INFO,
         CREATE_DISPLAY_EVENT_CONNECTION,
         CREATE_DISPLAY,
         DESTROY_DISPLAY,
@@ -503,7 +521,7 @@
         GET_SUPPORTED_FRAME_TIMESTAMPS,
         GET_DISPLAY_CONFIGS,
         GET_ACTIVE_CONFIG,
-        CONNECT_DISPLAY_UNUSED, // unused, fails permissions check
+        GET_DISPLAY_STATE,
         CAPTURE_SCREEN,
         CAPTURE_LAYERS,
         CLEAR_ANIMATION_FRAME_STATS,
@@ -546,8 +564,4 @@
             Parcel* reply, uint32_t flags = 0);
 };
 
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_GUI_ISURFACE_COMPOSER_H
+} // namespace android
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 86cc61f..0139507 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -21,16 +21,15 @@
 #include <gui/HdrMetadata.h>
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/IProducerListener.h>
-
+#include <system/window.h>
 #include <ui/ANativeObjectBase.h>
 #include <ui/GraphicTypes.h>
 #include <ui/Region.h>
-
 #include <utils/Condition.h>
 #include <utils/Mutex.h>
 #include <utils/RefBase.h>
 
-#include <system/window.h>
+#include <shared_mutex>
 
 namespace android {
 
@@ -205,6 +204,13 @@
             ANativeWindowBuffer* buffer, int fenceFd);
     static int hook_setSwapInterval(ANativeWindow* window, int interval);
 
+    static int cancelBufferInternal(ANativeWindow* window, ANativeWindowBuffer* buffer,
+                                    int fenceFd);
+    static int dequeueBufferInternal(ANativeWindow* window, ANativeWindowBuffer** buffer,
+                                     int* fenceFd);
+    static int performInternal(ANativeWindow* window, int operation, va_list args);
+    static int queueBufferInternal(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd);
+
     static int hook_cancelBuffer_DEPRECATED(ANativeWindow* window,
             ANativeWindowBuffer* buffer);
     static int hook_dequeueBuffer_DEPRECATED(ANativeWindow* window,
@@ -252,6 +258,10 @@
     int dispatchGetLastDequeueDuration(va_list args);
     int dispatchGetLastQueueDuration(va_list args);
     int dispatchSetFrameRate(va_list args);
+    int dispatchAddCancelInterceptor(va_list args);
+    int dispatchAddDequeueInterceptor(va_list args);
+    int dispatchAddPerformInterceptor(va_list args);
+    int dispatchAddQueueInterceptor(va_list args);
     bool transformToDisplayInverse();
 
 protected:
@@ -457,6 +467,18 @@
     // member variables are accessed.
     mutable Mutex mMutex;
 
+    // mInterceptorMutex is the mutex guarding interceptors.
+    std::shared_mutex mInterceptorMutex;
+
+    ANativeWindow_cancelBufferInterceptor mCancelInterceptor = nullptr;
+    void* mCancelInterceptorData = nullptr;
+    ANativeWindow_dequeueBufferInterceptor mDequeueInterceptor = nullptr;
+    void* mDequeueInterceptorData = nullptr;
+    ANativeWindow_performInterceptor mPerformInterceptor = nullptr;
+    void* mPerformInterceptorData = nullptr;
+    ANativeWindow_queueBufferInterceptor mQueueInterceptor = nullptr;
+    void* mQueueInterceptorData = nullptr;
+
     // must be used from the lock/unlock thread
     sp<GraphicBuffer>           mLockedBuffer;
     sp<GraphicBuffer>           mPostedBuffer;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 08e6a5a..6eec2b7 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_GUI_SURFACE_COMPOSER_CLIENT_H
-#define ANDROID_GUI_SURFACE_COMPOSER_CLIENT_H
+#pragma once
 
 #include <stdint.h>
 #include <sys/types.h>
@@ -46,17 +45,12 @@
 
 namespace android {
 
-// ---------------------------------------------------------------------------
-
-struct DisplayInfo;
 class HdrCapabilities;
 class ISurfaceComposerClient;
 class IGraphicBufferProducer;
 class IRegionSamplingListener;
 class Region;
 
-// ---------------------------------------------------------------------------
-
 struct SurfaceControlStats {
     SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t time,
                         const sp<Fence>& prevReleaseFence, uint32_t hint)
@@ -102,18 +96,21 @@
     status_t linkToComposerDeath(const sp<IBinder::DeathRecipient>& recipient,
             void* cookie = nullptr, uint32_t flags = 0);
 
-    // Get a list of supported configurations for a given display
-    static status_t getDisplayConfigs(const sp<IBinder>& display,
-            Vector<DisplayInfo>* configs);
+    // Get transactional state of given display.
+    static status_t getDisplayState(const sp<IBinder>& display, ui::DisplayState*);
 
-    // Get the DisplayInfo for the currently-active configuration
-    static status_t getDisplayInfo(const sp<IBinder>& display,
-            DisplayInfo* info);
+    // Get immutable information about given physical display.
+    static status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo*);
 
-    // Get the index of the current active configuration (relative to the list
-    // returned by getDisplayInfo)
+    // Get configurations supported by given physical display.
+    static status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayConfig>*);
+
+    // Get the ID of the active DisplayConfig, as getDisplayConfigs index.
     static int getActiveConfig(const sp<IBinder>& display);
 
+    // Shorthand for getDisplayConfigs element at getActiveConfig index.
+    static status_t getActiveDisplayConfig(const sp<IBinder>& display, DisplayConfig*);
+
     // Sets the refresh rate boundaries for display configuration.
     // For all other parameters, default configuration is used. The index for the default is
     // corresponting to the configs returned from getDisplayConfigs().
@@ -644,8 +641,4 @@
     void onTransactionCompleted(ListenerStats stats) override;
 };
 
-// ---------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_GUI_SURFACE_COMPOSER_CLIENT_H
+} // namespace android
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 8fca883..a273914 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -25,7 +25,7 @@
 #include <gui/IProducerListener.h>
 #include <gui/SurfaceComposerClient.h>
 #include <private/gui/ComposerService.h>
-#include <ui/DisplayInfo.h>
+#include <ui/DisplayConfig.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/GraphicTypes.h>
 #include <ui/Transform.h>
@@ -103,10 +103,11 @@
         t.apply();
         t.clear();
 
-        DisplayInfo info;
-        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(mDisplayToken, &info));
-        mDisplayWidth = info.w;
-        mDisplayHeight = info.h;
+        DisplayConfig config;
+        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(mDisplayToken, &config));
+        const ui::Size& resolution = config.resolution;
+        mDisplayWidth = resolution.getWidth();
+        mDisplayHeight = resolution.getHeight();
 
         mSurfaceControl = mClient->createSurface(String8("TestSurface"), mDisplayWidth,
                                                  mDisplayHeight, PIXEL_FORMAT_RGBA_8888,
@@ -114,7 +115,7 @@
                                                  /*parent*/ nullptr);
         t.setLayerStack(mSurfaceControl, 0)
                 .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
-                .setFrame(mSurfaceControl, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+                .setFrame(mSurfaceControl, Rect(resolution))
                 .show(mSurfaceControl)
                 .setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB)
                 .apply();
@@ -237,6 +238,33 @@
     ASSERT_EQ(&next, adapter.getNextTransaction());
 }
 
+TEST_F(BLASTBufferQueueTest, onFrameAvailable_ApplyDesiredPresentTime) {
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    sp<IGraphicBufferProducer> igbProducer;
+    setUpProducer(adapter, igbProducer);
+
+    int slot;
+    sp<Fence> fence;
+    sp<GraphicBuffer> buf;
+    auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+                                          PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                          nullptr, nullptr);
+    ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+    ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+    nsecs_t desiredPresentTime = systemTime() + nsecs_t(5 * 1e8);
+    IGraphicBufferProducer::QueueBufferOutput qbOutput;
+    IGraphicBufferProducer::QueueBufferInput input(desiredPresentTime, false, HAL_DATASPACE_UNKNOWN,
+                                                   Rect(mDisplayWidth, mDisplayHeight),
+                                                   NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+                                                   Fence::NO_FENCE);
+    igbProducer->queueBuffer(slot, input, &qbOutput);
+    ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+
+    adapter.waitForCallbacks();
+    ASSERT_GE(systemTime(), desiredPresentTime);
+}
+
 TEST_F(BLASTBufferQueueTest, onFrameAvailable_Apply) {
     uint8_t r = 255;
     uint8_t g = 0;
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 04749e6..1a623e2 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -41,7 +41,7 @@
 #include <input/InputTransport.h>
 #include <input/Input.h>
 
-#include <ui/DisplayInfo.h>
+#include <ui/DisplayConfig.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
@@ -223,13 +223,13 @@
         const auto display = mComposerClient->getInternalDisplayToken();
         ASSERT_NE(display, nullptr);
 
-        DisplayInfo info;
-        ASSERT_EQ(NO_ERROR, mComposerClient->getDisplayInfo(display, &info));
+        DisplayConfig config;
+        ASSERT_EQ(NO_ERROR, mComposerClient->getActiveDisplayConfig(display, &config));
 
         // After a new buffer is queued, SurfaceFlinger is notified and will
         // latch the new buffer on next vsync.  Let's heuristically wait for 3
         // vsyncs.
-        mBufferPostDelay = int32_t(1e6 / info.fps) * 3;
+        mBufferPostDelay = static_cast<int32_t>(1e6 / config.refreshRate) * 3;
     }
 
     void TearDown() {
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 25c032f..70fd888 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -718,8 +718,15 @@
     }
 
     void setPowerMode(const sp<IBinder>& /*display*/, int /*mode*/) override {}
-    status_t getDisplayConfigs(const sp<IBinder>& /*display*/,
-            Vector<DisplayInfo>* /*configs*/) override { return NO_ERROR; }
+    status_t getDisplayInfo(const sp<IBinder>& /*display*/, DisplayInfo*) override {
+        return NO_ERROR;
+    }
+    status_t getDisplayConfigs(const sp<IBinder>& /*display*/, Vector<DisplayConfig>*) override {
+        return NO_ERROR;
+    }
+    status_t getDisplayState(const sp<IBinder>& /*display*/, ui::DisplayState*) override {
+        return NO_ERROR;
+    }
     status_t getDisplayStats(const sp<IBinder>& /*display*/,
             DisplayStatInfo* /*stats*/) override { return NO_ERROR; }
     int getActiveConfig(const sp<IBinder>& /*display*/) override { return 0; }
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 8ccbc7f..85b0fd0 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -57,16 +57,19 @@
     return "UNKNOWN";
 }
 
-void InputEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId) {
+void InputEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId,
+                            std::array<uint8_t, 32> hmac) {
     mDeviceId = deviceId;
     mSource = source;
     mDisplayId = displayId;
+    mHmac = hmac;
 }
 
 void InputEvent::initialize(const InputEvent& from) {
     mDeviceId = from.mDeviceId;
     mSource = from.mSource;
     mDisplayId = from.mDisplayId;
+    mHmac = from.mHmac;
 }
 
 // --- KeyEvent ---
@@ -79,19 +82,11 @@
     return getKeyCodeByLabel(label);
 }
 
-void KeyEvent::initialize(
-        int32_t deviceId,
-        int32_t source,
-        int32_t displayId,
-        int32_t action,
-        int32_t flags,
-        int32_t keyCode,
-        int32_t scanCode,
-        int32_t metaState,
-        int32_t repeatCount,
-        nsecs_t downTime,
-        nsecs_t eventTime) {
-    InputEvent::initialize(deviceId, source, displayId);
+void KeyEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId,
+                          std::array<uint8_t, 32> hmac, int32_t action, int32_t flags,
+                          int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount,
+                          nsecs_t downTime, nsecs_t eventTime) {
+    InputEvent::initialize(deviceId, source, displayId, hmac);
     mAction = action;
     mFlags = flags;
     mKeyCode = keyCode;
@@ -250,15 +245,16 @@
 
 // --- MotionEvent ---
 
-void MotionEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId, int32_t action,
-                             int32_t actionButton, int32_t flags, int32_t edgeFlags,
-                             int32_t metaState, int32_t buttonState,
-                             MotionClassification classification, float xOffset, float yOffset,
-                             float xPrecision, float yPrecision, float rawXCursorPosition,
-                             float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime,
-                             size_t pointerCount, const PointerProperties* pointerProperties,
+void MotionEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId,
+                             std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton,
+                             int32_t flags, int32_t edgeFlags, int32_t metaState,
+                             int32_t buttonState, MotionClassification classification, float xScale,
+                             float yScale, float xOffset, float yOffset, float xPrecision,
+                             float yPrecision, float rawXCursorPosition, float rawYCursorPosition,
+                             nsecs_t downTime, nsecs_t eventTime, size_t pointerCount,
+                             const PointerProperties* pointerProperties,
                              const PointerCoords* pointerCoords) {
-    InputEvent::initialize(deviceId, source, displayId);
+    InputEvent::initialize(deviceId, source, displayId, hmac);
     mAction = action;
     mActionButton = actionButton;
     mFlags = flags;
@@ -266,6 +262,8 @@
     mMetaState = metaState;
     mButtonState = buttonState;
     mClassification = classification;
+    mXScale = xScale;
+    mYScale = yScale;
     mXOffset = xOffset;
     mYOffset = yOffset;
     mXPrecision = xPrecision;
@@ -281,7 +279,7 @@
 }
 
 void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) {
-    InputEvent::initialize(other->mDeviceId, other->mSource, other->mDisplayId);
+    InputEvent::initialize(other->mDeviceId, other->mSource, other->mDisplayId, other->mHmac);
     mAction = other->mAction;
     mActionButton = other->mActionButton;
     mFlags = other->mFlags;
@@ -289,6 +287,8 @@
     mMetaState = other->mMetaState;
     mButtonState = other->mButtonState;
     mClassification = other->mClassification;
+    mXScale = other->mXScale;
+    mYScale = other->mYScale;
     mXOffset = other->mXOffset;
     mYOffset = other->mYOffset;
     mXPrecision = other->mXPrecision;
@@ -321,17 +321,17 @@
 
 float MotionEvent::getXCursorPosition() const {
     const float rawX = getRawXCursorPosition();
-    return rawX + mXOffset;
+    return rawX * mXScale + mXOffset;
 }
 
 float MotionEvent::getYCursorPosition() const {
     const float rawY = getRawYCursorPosition();
-    return rawY + mYOffset;
+    return rawY * mYScale + mYOffset;
 }
 
 void MotionEvent::setCursorPosition(float x, float y) {
-    mRawXCursorPosition = x - mXOffset;
-    mRawYCursorPosition = y - mYOffset;
+    mRawXCursorPosition = (x - mXOffset) / mXScale;
+    mRawYCursorPosition = (y - mYOffset) / mYScale;
 }
 
 const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const {
@@ -346,9 +346,9 @@
     float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis);
     switch (axis) {
     case AMOTION_EVENT_AXIS_X:
-        return value + mXOffset;
+        return value * mXScale + mXOffset;
     case AMOTION_EVENT_AXIS_Y:
-        return value + mYOffset;
+        return value * mYScale + mYOffset;
     }
     return value;
 }
@@ -368,9 +368,9 @@
     float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
     switch (axis) {
     case AMOTION_EVENT_AXIS_X:
-        return value + mXOffset;
+        return value * mXScale + mXOffset;
     case AMOTION_EVENT_AXIS_Y:
-        return value + mYOffset;
+        return value * mYScale + mYOffset;
     }
     return value;
 }
@@ -442,11 +442,11 @@
     float oldXOffset = mXOffset;
     float oldYOffset = mYOffset;
     float newX, newY;
-    float rawX = getRawX(0);
-    float rawY = getRawY(0);
-    transformPoint(matrix, rawX + oldXOffset, rawY + oldYOffset, &newX, &newY);
-    mXOffset = newX - rawX;
-    mYOffset = newY - rawY;
+    float scaledRawX = getRawX(0) * mXScale;
+    float scaledRawY = getRawY(0) * mYScale;
+    transformPoint(matrix, scaledRawX + oldXOffset, scaledRawY + oldYOffset, &newX, &newY);
+    mXOffset = newX - scaledRawX;
+    mYOffset = newY - scaledRawY;
 
     // Determine how the origin is transformed by the matrix so that we
     // can transform orientation vectors.
@@ -455,22 +455,22 @@
 
     // Apply the transformation to cursor position.
     if (isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) {
-        float x = mRawXCursorPosition + oldXOffset;
-        float y = mRawYCursorPosition + oldYOffset;
+        float x = mRawXCursorPosition * mXScale + oldXOffset;
+        float y = mRawYCursorPosition * mYScale + oldYOffset;
         transformPoint(matrix, x, y, &x, &y);
-        mRawXCursorPosition = x - mXOffset;
-        mRawYCursorPosition = y - mYOffset;
+        mRawXCursorPosition = (x - mXOffset) / mXScale;
+        mRawYCursorPosition = (y - mYOffset) / mYScale;
     }
 
     // Apply the transformation to all samples.
     size_t numSamples = mSamplePointerCoords.size();
     for (size_t i = 0; i < numSamples; i++) {
         PointerCoords& c = mSamplePointerCoords.editItemAt(i);
-        float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) + oldXOffset;
-        float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) + oldYOffset;
+        float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) * mXScale + oldXOffset;
+        float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) * mYScale + oldYOffset;
         transformPoint(matrix, x, y, &x, &y);
-        c.setAxisValue(AMOTION_EVENT_AXIS_X, x - mXOffset);
-        c.setAxisValue(AMOTION_EVENT_AXIS_Y, y - mYOffset);
+        c.setAxisValue(AMOTION_EVENT_AXIS_X, (x - mXOffset) / mXScale);
+        c.setAxisValue(AMOTION_EVENT_AXIS_Y, (y - mYOffset) / mYScale);
 
         float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
         c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
@@ -488,8 +488,14 @@
     }
 
     mDeviceId = parcel->readInt32();
-    mSource = parcel->readInt32();
+    mSource = parcel->readUint32();
     mDisplayId = parcel->readInt32();
+    std::vector<uint8_t> hmac;
+    status_t result = parcel->readByteVector(&hmac);
+    if (result != OK || hmac.size() != 32) {
+        return BAD_VALUE;
+    }
+    std::move(hmac.begin(), hmac.begin() + hmac.size(), mHmac.begin());
     mAction = parcel->readInt32();
     mActionButton = parcel->readInt32();
     mFlags = parcel->readInt32();
@@ -497,6 +503,8 @@
     mMetaState = parcel->readInt32();
     mButtonState = parcel->readInt32();
     mClassification = static_cast<MotionClassification>(parcel->readByte());
+    mXScale = parcel->readFloat();
+    mYScale = parcel->readFloat();
     mXOffset = parcel->readFloat();
     mYOffset = parcel->readFloat();
     mXPrecision = parcel->readFloat();
@@ -541,8 +549,10 @@
     parcel->writeInt32(sampleCount);
 
     parcel->writeInt32(mDeviceId);
-    parcel->writeInt32(mSource);
+    parcel->writeUint32(mSource);
     parcel->writeInt32(mDisplayId);
+    std::vector<uint8_t> hmac(mHmac.begin(), mHmac.end());
+    parcel->writeByteVector(hmac);
     parcel->writeInt32(mAction);
     parcel->writeInt32(mActionButton);
     parcel->writeInt32(mFlags);
@@ -550,6 +560,8 @@
     parcel->writeInt32(mMetaState);
     parcel->writeInt32(mButtonState);
     parcel->writeByte(static_cast<int8_t>(mClassification));
+    parcel->writeFloat(mXScale);
+    parcel->writeFloat(mYScale);
     parcel->writeFloat(mXOffset);
     parcel->writeFloat(mYOffset);
     parcel->writeFloat(mXPrecision);
@@ -578,7 +590,7 @@
 }
 #endif
 
-bool MotionEvent::isTouchEvent(int32_t source, int32_t action) {
+bool MotionEvent::isTouchEvent(uint32_t source, int32_t action) {
     if (source & AINPUT_SOURCE_CLASS_POINTER) {
         // Specifically excludes HOVER_MOVE and SCROLL.
         switch (action & AMOTION_EVENT_ACTION_MASK) {
@@ -607,7 +619,7 @@
 
 void FocusEvent::initialize(bool hasFocus, bool inTouchMode) {
     InputEvent::initialize(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN,
-                           ADISPLAY_ID_NONE);
+                           ADISPLAY_ID_NONE, INVALID_HMAC);
     mHasFocus = hasFocus;
     mInTouchMode = inTouchMode;
 }
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index d53a557..d25a5cc 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -147,6 +147,8 @@
             msg->body.key.source = body.key.source;
             // int32_t displayId
             msg->body.key.displayId = body.key.displayId;
+            // std::array<uint8_t, 32> hmac
+            msg->body.key.hmac = body.key.hmac;
             // int32_t action
             msg->body.key.action = body.key.action;
             // int32_t flags
@@ -174,6 +176,8 @@
             msg->body.motion.source = body.motion.source;
             // int32_t displayId
             msg->body.motion.displayId = body.motion.displayId;
+            // std::array<uint8_t, 32> hmac
+            msg->body.motion.hmac = body.motion.hmac;
             // int32_t action
             msg->body.motion.action = body.motion.action;
             // int32_t actionButton
@@ -190,6 +194,10 @@
             msg->body.motion.edgeFlags = body.motion.edgeFlags;
             // nsecs_t downTime
             msg->body.motion.downTime = body.motion.downTime;
+            // float xScale
+            msg->body.motion.xScale = body.motion.xScale;
+            // float yScale
+            msg->body.motion.yScale = body.motion.yScale;
             // float xOffset
             msg->body.motion.xOffset = body.motion.xOffset;
             // float yOffset
@@ -424,19 +432,11 @@
 InputPublisher::~InputPublisher() {
 }
 
-status_t InputPublisher::publishKeyEvent(
-        uint32_t seq,
-        int32_t deviceId,
-        int32_t source,
-        int32_t displayId,
-        int32_t action,
-        int32_t flags,
-        int32_t keyCode,
-        int32_t scanCode,
-        int32_t metaState,
-        int32_t repeatCount,
-        nsecs_t downTime,
-        nsecs_t eventTime) {
+status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t deviceId, int32_t source,
+                                         int32_t displayId, std::array<uint8_t, 32> hmac,
+                                         int32_t action, int32_t flags, int32_t keyCode,
+                                         int32_t scanCode, int32_t metaState, int32_t repeatCount,
+                                         nsecs_t downTime, nsecs_t eventTime) {
     if (ATRACE_ENABLED()) {
         std::string message = StringPrintf("publishKeyEvent(inputChannel=%s, keyCode=%" PRId32 ")",
                 mChannel->getName().c_str(), keyCode);
@@ -461,6 +461,7 @@
     msg.body.key.deviceId = deviceId;
     msg.body.key.source = source;
     msg.body.key.displayId = displayId;
+    msg.body.key.hmac = hmac;
     msg.body.key.action = action;
     msg.body.key.flags = flags;
     msg.body.key.keyCode = keyCode;
@@ -473,11 +474,12 @@
 }
 
 status_t InputPublisher::publishMotionEvent(
-        uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, int32_t action,
-        int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState,
-        int32_t buttonState, MotionClassification classification, float xOffset, float yOffset,
-        float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition,
-        nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
+        uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId,
+        std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags,
+        int32_t edgeFlags, int32_t metaState, int32_t buttonState,
+        MotionClassification classification, float xScale, float yScale, float xOffset,
+        float yOffset, float xPrecision, float yPrecision, float xCursorPosition,
+        float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
         const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) {
     if (ATRACE_ENABLED()) {
         std::string message = StringPrintf(
@@ -489,13 +491,14 @@
         ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
               "displayId=%" PRId32 ", "
               "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
-              "metaState=0x%x, buttonState=0x%x, classification=%s, xOffset=%f, yOffset=%f, "
+              "metaState=0x%x, buttonState=0x%x, classification=%s, xScale=%.1f, yScale=%.1f, "
+              "xOffset=%.1f, yOffset=%.1f, "
               "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
               "pointerCount=%" PRIu32,
               mChannel->getName().c_str(), seq, deviceId, source, displayId, action, actionButton,
               flags, edgeFlags, metaState, buttonState,
-              motionClassificationToString(classification), xOffset, yOffset, xPrecision,
-              yPrecision, downTime, eventTime, pointerCount);
+              motionClassificationToString(classification), xScale, yScale, xOffset, yOffset,
+              xPrecision, yPrecision, downTime, eventTime, pointerCount);
     }
 
     if (!seq) {
@@ -515,6 +518,7 @@
     msg.body.motion.deviceId = deviceId;
     msg.body.motion.source = source;
     msg.body.motion.displayId = displayId;
+    msg.body.motion.hmac = hmac;
     msg.body.motion.action = action;
     msg.body.motion.actionButton = actionButton;
     msg.body.motion.flags = flags;
@@ -522,6 +526,8 @@
     msg.body.motion.metaState = metaState;
     msg.body.motion.buttonState = buttonState;
     msg.body.motion.classification = classification;
+    msg.body.motion.xScale = xScale;
+    msg.body.motion.yScale = yScale;
     msg.body.motion.xOffset = xOffset;
     msg.body.motion.yOffset = yOffset;
     msg.body.motion.xPrecision = xPrecision;
@@ -1136,18 +1142,10 @@
 }
 
 void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) {
-    event->initialize(
-            msg->body.key.deviceId,
-            msg->body.key.source,
-            msg->body.key.displayId,
-            msg->body.key.action,
-            msg->body.key.flags,
-            msg->body.key.keyCode,
-            msg->body.key.scanCode,
-            msg->body.key.metaState,
-            msg->body.key.repeatCount,
-            msg->body.key.downTime,
-            msg->body.key.eventTime);
+    event->initialize(msg->body.key.deviceId, msg->body.key.source, msg->body.key.displayId,
+                      msg->body.key.hmac, msg->body.key.action, msg->body.key.flags,
+                      msg->body.key.keyCode, msg->body.key.scanCode, msg->body.key.metaState,
+                      msg->body.key.repeatCount, msg->body.key.downTime, msg->body.key.eventTime);
 }
 
 void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) {
@@ -1164,15 +1162,15 @@
     }
 
     event->initialize(msg->body.motion.deviceId, msg->body.motion.source,
-                      msg->body.motion.displayId, msg->body.motion.action,
+                      msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action,
                       msg->body.motion.actionButton, msg->body.motion.flags,
                       msg->body.motion.edgeFlags, msg->body.motion.metaState,
                       msg->body.motion.buttonState, msg->body.motion.classification,
-                      msg->body.motion.xOffset, msg->body.motion.yOffset,
-                      msg->body.motion.xPrecision, msg->body.motion.yPrecision,
-                      msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition,
-                      msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount,
-                      pointerProperties, pointerCoords);
+                      msg->body.motion.xScale, msg->body.motion.yScale, msg->body.motion.xOffset,
+                      msg->body.motion.yOffset, msg->body.motion.xPrecision,
+                      msg->body.motion.yPrecision, msg->body.motion.xCursorPosition,
+                      msg->body.motion.yCursorPosition, msg->body.motion.downTime,
+                      msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords);
 }
 
 void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) {
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index e189d20..6f9b162 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -487,9 +487,9 @@
         int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) {
     outEvents.push();
     KeyEvent& event = outEvents.editTop();
-    event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
-            down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
-            0, keyCode, 0, metaState, 0, time, time);
+    event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC,
+                     down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, 0, keyCode, 0, metaState,
+                     0, time, time);
 }
 
 void KeyCharacterMap::addMetaKeys(Vector<KeyEvent>& outEvents,
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index b90857c..dce1f29 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -26,7 +26,12 @@
 // Default display id.
 static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
 
-class BaseTest : public testing::Test { };
+class BaseTest : public testing::Test {
+protected:
+    static constexpr std::array<uint8_t, 32> HMAC = {0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10,
+                                                     11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+                                                     22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
+};
 
 // --- PointerCoordsTest ---
 
@@ -176,16 +181,17 @@
     KeyEvent event;
 
     // Initialize and get properties.
-    const nsecs_t ARBITRARY_DOWN_TIME = 1;
-    const nsecs_t ARBITRARY_EVENT_TIME = 2;
-    event.initialize(2, AINPUT_SOURCE_GAMEPAD, DISPLAY_ID, AKEY_EVENT_ACTION_DOWN,
-            AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121,
-            AMETA_ALT_ON, 1, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME);
+    constexpr nsecs_t ARBITRARY_DOWN_TIME = 1;
+    constexpr nsecs_t ARBITRARY_EVENT_TIME = 2;
+    event.initialize(2, AINPUT_SOURCE_GAMEPAD, DISPLAY_ID, HMAC, AKEY_EVENT_ACTION_DOWN,
+                     AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121, AMETA_ALT_ON, 1,
+                     ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME);
 
     ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event.getType());
     ASSERT_EQ(2, event.getDeviceId());
-    ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_GAMEPAD), event.getSource());
+    ASSERT_EQ(AINPUT_SOURCE_GAMEPAD, event.getSource());
     ASSERT_EQ(DISPLAY_ID, event.getDisplayId());
+    EXPECT_EQ(HMAC, event.getHmac());
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, event.getAction());
     ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, event.getFlags());
     ASSERT_EQ(AKEYCODE_BUTTON_X, event.getKeyCode());
@@ -197,7 +203,7 @@
 
     // Set source.
     event.setSource(AINPUT_SOURCE_JOYSTICK);
-    ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_JOYSTICK), event.getSource());
+    ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource());
 
     // Set display id.
     constexpr int32_t newDisplayId = 2;
@@ -210,19 +216,17 @@
 
 class MotionEventTest : public BaseTest {
 protected:
-    static const nsecs_t ARBITRARY_DOWN_TIME;
-    static const nsecs_t ARBITRARY_EVENT_TIME;
-    static const float X_OFFSET;
-    static const float Y_OFFSET;
+    static constexpr nsecs_t ARBITRARY_DOWN_TIME = 1;
+    static constexpr nsecs_t ARBITRARY_EVENT_TIME = 2;
+    static constexpr float X_SCALE = 2.0;
+    static constexpr float Y_SCALE = 3.0;
+    static constexpr float X_OFFSET = 1;
+    static constexpr float Y_OFFSET = 1.1;
 
     void initializeEventWithHistory(MotionEvent* event);
     void assertEqualsEventWithHistory(const MotionEvent* event);
 };
 
-const nsecs_t MotionEventTest::ARBITRARY_DOWN_TIME = 1;
-const nsecs_t MotionEventTest::ARBITRARY_EVENT_TIME = 2;
-const float MotionEventTest::X_OFFSET = 1.0f;
-const float MotionEventTest::Y_OFFSET = 1.1f;
 
 void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
     PointerProperties pointerProperties[2];
@@ -254,12 +258,13 @@
     pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26);
     pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27);
     pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28);
-    event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, 0,
+    event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC, AMOTION_EVENT_ACTION_MOVE, 0,
                       AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, AMOTION_EVENT_EDGE_FLAG_TOP,
                       AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, MotionClassification::NONE,
-                      X_OFFSET, Y_OFFSET, 2.0f, 2.1f, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                      AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_DOWN_TIME,
-                      ARBITRARY_EVENT_TIME, 2, pointerProperties, pointerCoords);
+                      X_SCALE, Y_SCALE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f,
+                      AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                      ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties,
+                      pointerCoords);
 
     pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110);
     pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111);
@@ -306,14 +311,17 @@
     // Check properties.
     ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType());
     ASSERT_EQ(2, event->getDeviceId());
-    ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_TOUCHSCREEN), event->getSource());
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, event->getSource());
     ASSERT_EQ(DISPLAY_ID, event->getDisplayId());
+    EXPECT_EQ(HMAC, event->getHmac());
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event->getAction());
     ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event->getFlags());
     ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event->getEdgeFlags());
     ASSERT_EQ(AMETA_ALT_ON, event->getMetaState());
     ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState());
     ASSERT_EQ(MotionClassification::NONE, event->getClassification());
+    EXPECT_EQ(X_SCALE, event->getXScale());
+    EXPECT_EQ(Y_SCALE, event->getYScale());
     ASSERT_EQ(X_OFFSET, event->getXOffset());
     ASSERT_EQ(Y_OFFSET, event->getYOffset());
     ASSERT_EQ(2.0f, event->getXPrecision());
@@ -367,19 +375,19 @@
     ASSERT_EQ(211, event->getRawY(0));
     ASSERT_EQ(221, event->getRawY(1));
 
-    ASSERT_EQ(X_OFFSET + 10, event->getHistoricalX(0, 0));
-    ASSERT_EQ(X_OFFSET + 20, event->getHistoricalX(1, 0));
-    ASSERT_EQ(X_OFFSET + 110, event->getHistoricalX(0, 1));
-    ASSERT_EQ(X_OFFSET + 120, event->getHistoricalX(1, 1));
-    ASSERT_EQ(X_OFFSET + 210, event->getX(0));
-    ASSERT_EQ(X_OFFSET + 220, event->getX(1));
+    ASSERT_EQ(X_OFFSET + 10 * X_SCALE, event->getHistoricalX(0, 0));
+    ASSERT_EQ(X_OFFSET + 20 * X_SCALE, event->getHistoricalX(1, 0));
+    ASSERT_EQ(X_OFFSET + 110 * X_SCALE, event->getHistoricalX(0, 1));
+    ASSERT_EQ(X_OFFSET + 120 * X_SCALE, event->getHistoricalX(1, 1));
+    ASSERT_EQ(X_OFFSET + 210 * X_SCALE, event->getX(0));
+    ASSERT_EQ(X_OFFSET + 220 * X_SCALE, event->getX(1));
 
-    ASSERT_EQ(Y_OFFSET + 11, event->getHistoricalY(0, 0));
-    ASSERT_EQ(Y_OFFSET + 21, event->getHistoricalY(1, 0));
-    ASSERT_EQ(Y_OFFSET + 111, event->getHistoricalY(0, 1));
-    ASSERT_EQ(Y_OFFSET + 121, event->getHistoricalY(1, 1));
-    ASSERT_EQ(Y_OFFSET + 211, event->getY(0));
-    ASSERT_EQ(Y_OFFSET + 221, event->getY(1));
+    ASSERT_EQ(Y_OFFSET + 11 * Y_SCALE, event->getHistoricalY(0, 0));
+    ASSERT_EQ(Y_OFFSET + 21 * Y_SCALE, event->getHistoricalY(1, 0));
+    ASSERT_EQ(Y_OFFSET + 111 * Y_SCALE, event->getHistoricalY(0, 1));
+    ASSERT_EQ(Y_OFFSET + 121 * Y_SCALE, event->getHistoricalY(1, 1));
+    ASSERT_EQ(Y_OFFSET + 211 * Y_SCALE, event->getY(0));
+    ASSERT_EQ(Y_OFFSET + 221 * Y_SCALE, event->getY(1));
 
     ASSERT_EQ(12, event->getHistoricalPressure(0, 0));
     ASSERT_EQ(22, event->getHistoricalPressure(1, 0));
@@ -440,7 +448,7 @@
 
     // Set source.
     event.setSource(AINPUT_SOURCE_JOYSTICK);
-    ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_JOYSTICK), event.getSource());
+    ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource());
 
     // Set displayId.
     constexpr int32_t newDisplayId = 2;
@@ -505,8 +513,8 @@
 
     ASSERT_EQ(210 * 2, event.getRawX(0));
     ASSERT_EQ(211 * 2, event.getRawY(0));
-    ASSERT_EQ((X_OFFSET + 210) * 2, event.getX(0));
-    ASSERT_EQ((Y_OFFSET + 211) * 2, event.getY(0));
+    ASSERT_EQ((X_OFFSET + 210 * X_SCALE) * 2, event.getX(0));
+    ASSERT_EQ((Y_OFFSET + 211 * Y_SCALE) * 2, event.getY(0));
     ASSERT_EQ(212, event.getPressure(0));
     ASSERT_EQ(213, event.getSize(0));
     ASSERT_EQ(214 * 2, event.getTouchMajor(0));
@@ -570,12 +578,13 @@
         pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle);
     }
     MotionEvent event;
-    event.initialize(0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, 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*/, 3 + RADIUS /*xCursorPosition*/,
-                     2 /*yCursorPosition*/, 0 /*downTime*/, 0 /*eventTime*/, pointerCount,
-                     pointerProperties, pointerCoords);
+    event.initialize(0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, INVALID_HMAC,
+                     AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/,
+                     AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
+                     MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0 /*xOffset*/,
+                     0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/,
+                     3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/, 0 /*downTime*/,
+                     0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
     float originalRawX = 0 + 3;
     float originalRawY = -RADIUS + 2;
 
@@ -634,9 +643,10 @@
     }
 
     for (MotionClassification classification : classifications) {
-        event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID,
+        event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, INVALID_HMAC,
                          AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE,
-                         0, classification, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                         0, classification, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0, 0,
+                         AMOTION_EVENT_INVALID_CURSOR_POSITION,
                          AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, 0 /*eventTime*/,
                          pointerCount, pointerProperties, pointerCoords);
         ASSERT_EQ(classification, event.getClassification());
@@ -654,9 +664,10 @@
         pointerCoords[i].clear();
     }
 
-    event.initialize(0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0,
-                     0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, MotionClassification::NONE, 0,
-                     0, 0, 0, 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/,
+    event.initialize(0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, INVALID_HMAC,
+                     AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0,
+                     MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0, 0,
+                     280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/,
                      0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
     event.offsetLocation(20, 60);
     ASSERT_EQ(280, event.getRawXCursorPosition());
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 2fc77e9..d4bbf6c 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -73,8 +73,11 @@
 
     constexpr uint32_t seq = 15;
     constexpr int32_t deviceId = 1;
-    constexpr int32_t source = AINPUT_SOURCE_KEYBOARD;
+    constexpr uint32_t source = AINPUT_SOURCE_KEYBOARD;
     constexpr int32_t displayId = ADISPLAY_ID_DEFAULT;
+    constexpr std::array<uint8_t, 32> hmac = {31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21,
+                                              20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10,
+                                              9,  8,  7,  6,  5,  4,  3,  2,  1,  0};
     constexpr int32_t action = AKEY_EVENT_ACTION_DOWN;
     constexpr int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM;
     constexpr int32_t keyCode = AKEYCODE_ENTER;
@@ -84,8 +87,9 @@
     constexpr nsecs_t downTime = 3;
     constexpr nsecs_t eventTime = 4;
 
-    status = mPublisher->publishKeyEvent(seq, deviceId, source, displayId, action, flags,
-            keyCode, scanCode, metaState, repeatCount, downTime, eventTime);
+    status = mPublisher->publishKeyEvent(seq, deviceId, source, displayId, hmac, action, flags,
+                                         keyCode, scanCode, metaState, repeatCount, downTime,
+                                         eventTime);
     ASSERT_EQ(OK, status)
             << "publisher publishKeyEvent should return OK";
 
@@ -105,6 +109,7 @@
     EXPECT_EQ(deviceId, keyEvent->getDeviceId());
     EXPECT_EQ(source, keyEvent->getSource());
     EXPECT_EQ(displayId, keyEvent->getDisplayId());
+    EXPECT_EQ(hmac, keyEvent->getHmac());
     EXPECT_EQ(action, keyEvent->getAction());
     EXPECT_EQ(flags, keyEvent->getFlags());
     EXPECT_EQ(keyCode, keyEvent->getKeyCode());
@@ -134,8 +139,11 @@
 
     constexpr uint32_t seq = 15;
     constexpr int32_t deviceId = 1;
-    constexpr int32_t source = AINPUT_SOURCE_TOUCHSCREEN;
+    constexpr uint32_t source = AINPUT_SOURCE_TOUCHSCREEN;
     constexpr int32_t displayId = ADISPLAY_ID_DEFAULT;
+    constexpr std::array<uint8_t, 32> hmac = {0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10,
+                                              11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+                                              22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
     constexpr int32_t action = AMOTION_EVENT_ACTION_MOVE;
     constexpr int32_t actionButton = 0;
     constexpr int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
@@ -143,6 +151,8 @@
     constexpr int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
     constexpr int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY;
     constexpr MotionClassification classification = MotionClassification::AMBIGUOUS_GESTURE;
+    constexpr float xScale = 2;
+    constexpr float yScale = 3;
     constexpr float xOffset = -10;
     constexpr float yOffset = -20;
     constexpr float xPrecision = 0.25;
@@ -171,12 +181,12 @@
         pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i);
     }
 
-    status =
-            mPublisher->publishMotionEvent(seq, deviceId, source, displayId, action, actionButton,
-                                           flags, edgeFlags, metaState, buttonState, classification,
-                                           xOffset, yOffset, xPrecision, yPrecision,
-                                           xCursorPosition, yCursorPosition, downTime, eventTime,
-                                           pointerCount, pointerProperties, pointerCoords);
+    status = mPublisher->publishMotionEvent(seq, deviceId, source, displayId, hmac, action,
+                                            actionButton, flags, edgeFlags, metaState, buttonState,
+                                            classification, xScale, yScale, xOffset, yOffset,
+                                            xPrecision, yPrecision, xCursorPosition,
+                                            yCursorPosition, downTime, eventTime, pointerCount,
+                                            pointerProperties, pointerCoords);
     ASSERT_EQ(OK, status)
             << "publisher publishMotionEvent should return OK";
 
@@ -196,18 +206,23 @@
     EXPECT_EQ(deviceId, motionEvent->getDeviceId());
     EXPECT_EQ(source, motionEvent->getSource());
     EXPECT_EQ(displayId, motionEvent->getDisplayId());
+    EXPECT_EQ(hmac, motionEvent->getHmac());
     EXPECT_EQ(action, motionEvent->getAction());
     EXPECT_EQ(flags, motionEvent->getFlags());
     EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags());
     EXPECT_EQ(metaState, motionEvent->getMetaState());
     EXPECT_EQ(buttonState, motionEvent->getButtonState());
     EXPECT_EQ(classification, motionEvent->getClassification());
+    EXPECT_EQ(xScale, motionEvent->getXScale());
+    EXPECT_EQ(yScale, motionEvent->getYScale());
+    EXPECT_EQ(xOffset, motionEvent->getXOffset());
+    EXPECT_EQ(yOffset, motionEvent->getYOffset());
     EXPECT_EQ(xPrecision, motionEvent->getXPrecision());
     EXPECT_EQ(yPrecision, motionEvent->getYPrecision());
     EXPECT_EQ(xCursorPosition, motionEvent->getRawXCursorPosition());
     EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition());
-    EXPECT_EQ(xCursorPosition + xOffset, motionEvent->getXCursorPosition());
-    EXPECT_EQ(yCursorPosition + yOffset, motionEvent->getYCursorPosition());
+    EXPECT_EQ(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition());
+    EXPECT_EQ(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition());
     EXPECT_EQ(downTime, motionEvent->getDownTime());
     EXPECT_EQ(eventTime, motionEvent->getEventTime());
     EXPECT_EQ(pointerCount, motionEvent->getPointerCount());
@@ -222,10 +237,10 @@
                 motionEvent->getRawX(i));
         EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
                 motionEvent->getRawY(i));
-        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset,
-                motionEvent->getX(i));
-        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset,
-                motionEvent->getY(i));
+        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) * xScale + xOffset,
+                  motionEvent->getX(i));
+        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) * yScale + yOffset,
+                  motionEvent->getY(i));
         EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
                 motionEvent->getPressure(i));
         EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
@@ -316,11 +331,12 @@
         pointerCoords[i].clear();
     }
 
-    status =
-            mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE,
-                                           0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                                           AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
-                                           pointerCount, pointerProperties, pointerCoords);
+    status = mPublisher->publishMotionEvent(0, 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0,
+                                            MotionClassification::NONE, 1 /* xScale */,
+                                            1 /* yScale */, 0, 0, 0, 0,
+                                            AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                            AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
+                                            pointerCount, pointerProperties, pointerCoords);
     ASSERT_EQ(BAD_VALUE, status)
             << "publisher publishMotionEvent should return BAD_VALUE";
 }
@@ -331,11 +347,12 @@
     PointerProperties pointerProperties[pointerCount];
     PointerCoords pointerCoords[pointerCount];
 
-    status =
-            mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE,
-                                           0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                                           AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
-                                           pointerCount, pointerProperties, pointerCoords);
+    status = mPublisher->publishMotionEvent(1, 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0,
+                                            MotionClassification::NONE, 1 /* xScale */,
+                                            1 /* yScale */, 0, 0, 0, 0,
+                                            AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                            AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
+                                            pointerCount, pointerProperties, pointerCoords);
     ASSERT_EQ(BAD_VALUE, status)
             << "publisher publishMotionEvent should return BAD_VALUE";
 }
@@ -351,11 +368,12 @@
         pointerCoords[i].clear();
     }
 
-    status =
-            mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE,
-                                           0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                                           AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
-                                           pointerCount, pointerProperties, pointerCoords);
+    status = mPublisher->publishMotionEvent(1, 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0,
+                                            MotionClassification::NONE, 1 /* xScale */,
+                                            1 /* yScale */, 0, 0, 0, 0,
+                                            AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                            AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
+                                            pointerCount, pointerProperties, pointerCoords);
     ASSERT_EQ(BAD_VALUE, status)
             << "publisher publishMotionEvent should return BAD_VALUE";
 }
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 9ab0dba..aa8a2d4 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -39,35 +39,39 @@
   CHECK_OFFSET(InputMessage::Body::Key, deviceId, 16);
   CHECK_OFFSET(InputMessage::Body::Key, source, 20);
   CHECK_OFFSET(InputMessage::Body::Key, displayId, 24);
-  CHECK_OFFSET(InputMessage::Body::Key, action, 28);
-  CHECK_OFFSET(InputMessage::Body::Key, flags, 32);
-  CHECK_OFFSET(InputMessage::Body::Key, keyCode, 36);
-  CHECK_OFFSET(InputMessage::Body::Key, scanCode, 40);
-  CHECK_OFFSET(InputMessage::Body::Key, metaState, 44);
-  CHECK_OFFSET(InputMessage::Body::Key, repeatCount, 48);
-  CHECK_OFFSET(InputMessage::Body::Key, downTime, 56);
+  CHECK_OFFSET(InputMessage::Body::Key, hmac, 28);
+  CHECK_OFFSET(InputMessage::Body::Key, action, 60);
+  CHECK_OFFSET(InputMessage::Body::Key, flags, 64);
+  CHECK_OFFSET(InputMessage::Body::Key, keyCode, 68);
+  CHECK_OFFSET(InputMessage::Body::Key, scanCode, 72);
+  CHECK_OFFSET(InputMessage::Body::Key, metaState, 76);
+  CHECK_OFFSET(InputMessage::Body::Key, repeatCount, 80);
+  CHECK_OFFSET(InputMessage::Body::Key, downTime, 88);
 
   CHECK_OFFSET(InputMessage::Body::Motion, seq, 0);
   CHECK_OFFSET(InputMessage::Body::Motion, eventTime, 8);
   CHECK_OFFSET(InputMessage::Body::Motion, deviceId, 16);
   CHECK_OFFSET(InputMessage::Body::Motion, source, 20);
   CHECK_OFFSET(InputMessage::Body::Motion, displayId, 24);
-  CHECK_OFFSET(InputMessage::Body::Motion, action, 28);
-  CHECK_OFFSET(InputMessage::Body::Motion, actionButton, 32);
-  CHECK_OFFSET(InputMessage::Body::Motion, flags, 36);
-  CHECK_OFFSET(InputMessage::Body::Motion, metaState, 40);
-  CHECK_OFFSET(InputMessage::Body::Motion, buttonState, 44);
-  CHECK_OFFSET(InputMessage::Body::Motion, classification, 48);
-  CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 52);
-  CHECK_OFFSET(InputMessage::Body::Motion, downTime, 56);
-  CHECK_OFFSET(InputMessage::Body::Motion, xOffset, 64);
-  CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 68);
-  CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 72);
-  CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 76);
-  CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 80);
-  CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 84);
-  CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 88);
-  CHECK_OFFSET(InputMessage::Body::Motion, pointers, 96);
+  CHECK_OFFSET(InputMessage::Body::Motion, hmac, 28);
+  CHECK_OFFSET(InputMessage::Body::Motion, action, 60);
+  CHECK_OFFSET(InputMessage::Body::Motion, actionButton, 64);
+  CHECK_OFFSET(InputMessage::Body::Motion, flags, 68);
+  CHECK_OFFSET(InputMessage::Body::Motion, metaState, 72);
+  CHECK_OFFSET(InputMessage::Body::Motion, buttonState, 76);
+  CHECK_OFFSET(InputMessage::Body::Motion, classification, 80);
+  CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 84);
+  CHECK_OFFSET(InputMessage::Body::Motion, downTime, 88);
+  CHECK_OFFSET(InputMessage::Body::Motion, xScale, 96);
+  CHECK_OFFSET(InputMessage::Body::Motion, yScale, 100);
+  CHECK_OFFSET(InputMessage::Body::Motion, xOffset, 104);
+  CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 108);
+  CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 112);
+  CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 116);
+  CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 120);
+  CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 124);
+  CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 128);
+  CHECK_OFFSET(InputMessage::Body::Motion, pointers, 136);
 
   CHECK_OFFSET(InputMessage::Body::Focus, seq, 0);
   CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4);
@@ -86,7 +90,7 @@
  * the Motion type, where "pointerCount" variable affects the size and can change at runtime.
  */
 void TestBodySize() {
-    static_assert(sizeof(InputMessage::Body::Key) == 64);
+    static_assert(sizeof(InputMessage::Body::Key) == 96);
     static_assert(sizeof(InputMessage::Body::Motion) ==
                   offsetof(InputMessage::Body::Motion, pointers) +
                           sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS);
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index 968e2fa..731eb6a 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -176,11 +176,11 @@
         EXPECT_EQ(pointerIndex, pointerCount);
 
         MotionEvent event;
-        event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, action,
-                         0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE,
-                         0 /*buttonState*/, MotionClassification::NONE, 0 /*xOffset*/,
-                         0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/,
-                         AMOTION_EVENT_INVALID_CURSOR_POSITION,
+        event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, INVALID_HMAC,
+                         action, 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE,
+                         AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, 1 /*xScale*/,
+                         1 /*yScale*/, 0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/,
+                         0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION,
                          AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/,
                          entry.eventTime.count(), pointerCount, properties, coords);
 
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 15d937e..58fff8f 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -60,7 +60,7 @@
     void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
                                   AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay);
     void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
-    void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb);
+    void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
 
     enum {
         MSG_SCHEDULE_CALLBACKS = 0,
@@ -152,21 +152,34 @@
 void Choreographer::registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) {
     {
         AutoMutex _l{mLock};
+        for (const auto& callback : mRefreshRateCallbacks) {
+            // Don't re-add callbacks.
+            if (cb == callback.callback && data == callback.data) {
+                return;
+            }
+        }
         mRefreshRateCallbacks.emplace_back(RefreshRateCallback{cb, data});
         toggleConfigEvents(ISurfaceComposer::ConfigChanged::eConfigChangedDispatch);
     }
 }
 
-void Choreographer::unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb) {
+void Choreographer::unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb,
+                                                  void* data) {
     {
         AutoMutex _l{mLock};
         mRefreshRateCallbacks.erase(std::remove_if(mRefreshRateCallbacks.begin(),
                                                    mRefreshRateCallbacks.end(),
                                                    [&](const RefreshRateCallback& callback) {
-                                                       return cb == callback.callback;
-                                                   }));
+                                                       return cb == callback.callback &&
+                                                               data == callback.data;
+                                                   }),
+                                    mRefreshRateCallbacks.end());
         if (mRefreshRateCallbacks.empty()) {
             toggleConfigEvents(ISurfaceComposer::ConfigChanged::eConfigChangedSuppress);
+            // If callbacks are empty then clear out the most recently seen
+            // vsync period so that when another callback is registered then the
+            // up-to-date refresh rate can be communicated to the app again.
+            mVsyncPeriod = 0;
         }
     }
 }
@@ -224,9 +237,9 @@
             // on every single configuration change.
             if (mVsyncPeriod != vsyncPeriod) {
                 cb.callback(vsyncPeriod, cb.data);
-                mVsyncPeriod = vsyncPeriod;
             }
         }
+        mVsyncPeriod = vsyncPeriod;
     }
 }
 
@@ -285,8 +298,9 @@
     AChoreographer_to_Choreographer(choreographer)->registerRefreshRateCallback(callback, data);
 }
 void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer,
-                                                  AChoreographer_refreshRateCallback callback) {
-    AChoreographer_to_Choreographer(choreographer)->unregisterRefreshRateCallback(callback);
+                                                  AChoreographer_refreshRateCallback callback,
+                                                  void* data) {
+    AChoreographer_to_Choreographer(choreographer)->unregisterRefreshRateCallback(callback, data);
 }
 
 AChoreographer* AChoreographer_create() {
diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp
index 1e25049..277635c 100644
--- a/libs/nativedisplay/ADisplay.cpp
+++ b/libs/nativedisplay/ADisplay.cpp
@@ -16,6 +16,7 @@
 
 #include <apex/display.h>
 #include <gui/SurfaceComposerClient.h>
+#include <ui/DisplayConfig.h>
 #include <ui/DisplayInfo.h>
 #include <ui/GraphicTypes.h>
 #include <ui/PixelFormat.h>
@@ -116,17 +117,12 @@
     LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument");
 
 namespace {
+
 sp<IBinder> getToken(ADisplay* display) {
     DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
     return SurfaceComposerClient::getPhysicalDisplayToken(impl->id);
 }
 
-int64_t computeSfOffset(const DisplayInfo& info) {
-    // This should probably be part of the config instead of extrapolated from
-    // the presentation deadline and fudged here, but the way the math works out
-    // here we do get the right offset.
-    return static_cast<int64_t>((1000000000 / info.fps) - info.presentationDeadline + 1000000);
-}
 } // namespace
 
 namespace android {
@@ -142,9 +138,16 @@
     int numConfigs = 0;
     for (int i = 0; i < size; ++i) {
         const sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(ids[i]);
-        Vector<DisplayInfo> configs;
-        const status_t status = SurfaceComposerClient::getDisplayConfigs(token, &configs);
-        if (status != OK) {
+
+        DisplayInfo info;
+        if (const status_t status = SurfaceComposerClient::getDisplayInfo(token, &info);
+            status != OK) {
+            return status;
+        }
+
+        Vector<DisplayConfig> configs;
+        if (const status_t status = SurfaceComposerClient::getDisplayConfigs(token, &configs);
+            status != OK) {
             return status;
         }
         if (configs.empty()) {
@@ -154,11 +157,11 @@
         numConfigs += configs.size();
         configsPerDisplay[i].reserve(configs.size());
         for (int j = 0; j < configs.size(); ++j) {
-            const DisplayInfo config = configs[j];
+            const DisplayConfig& config = configs[j];
             configsPerDisplay[i].emplace_back(
-                    DisplayConfigImpl{static_cast<int32_t>(config.w),
-                                      static_cast<int32_t>(config.h), config.density, config.fps,
-                                      computeSfOffset(config), config.appVsyncOffset});
+                    DisplayConfigImpl{config.resolution.getWidth(), config.resolution.getHeight(),
+                                      info.density, config.refreshRate, config.sfVsyncOffset,
+                                      config.appVsyncOffset});
         }
     }
 
diff --git a/libs/nativedisplay/include/android/choreographer.h b/libs/nativedisplay/include/android/choreographer.h
index 0d97e8c..5fd3de9 100644
--- a/libs/nativedisplay/include/android/choreographer.h
+++ b/libs/nativedisplay/include/android/choreographer.h
@@ -54,6 +54,13 @@
  */
 typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* data);
 
+/**
+ * Prototype of the function that is called when the display refresh rate
+ * changes. It's passed the new vsync period in nanoseconds, as well as the data
+ * pointer provided by the application that registered a callback.
+ */
+typedef void (*AChoreographer_refreshRateCallback)(int64_t vsyncPeriodNanos, void* data);
+
 #if __ANDROID_API__ >= 24
 
 /**
@@ -102,6 +109,42 @@
 
 #endif /* __ANDROID_API__ >= 29 */
 
+#if __ANDROID_API__ >= 30
+
+/**
+ * Registers a callback to be run when the display refresh rate changes. The
+ * data pointer provided will be passed to the callback function when it's
+ * called. The same callback may be registered multiple times, provided that a
+ * different data pointer is provided each time.
+ *
+ * If an application registers a callback for this choreographer instance when
+ * no new callbacks were previously registered, that callback is guaranteed to
+ * be dispatched. However, if the callback and associated data pointer are
+ * unregistered prior to running the callback, then the callback may be silently
+ * dropped.
+ *
+ * This api is thread-safe. Any thread is allowed to register a new refresh
+ * rate callback for the choreographer instance.
+ */
+void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
+                                                AChoreographer_refreshRateCallback, void* data);
+
+/**
+ * Unregisters a callback to be run when the display refresh rate changes, along
+ * with the data pointer previously provided when registering the callback. The
+ * callback is only unregistered when the data pointer matches one that was
+ * previously registered.
+ *
+ * This api is thread-safe. Any thread is allowed to unregister an existing
+ * refresh rate callback for the choreographer instance. When a refresh rate
+ * callback and associated data pointer are unregistered, then there is a
+ * guarantee that when the unregistration completes that that callback will not
+ * be run with the data pointer passed.
+ */
+void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer,
+                                                  AChoreographer_refreshRateCallback, void* data);
+#endif /* __ANDROID_API__ >= 30 */
+
 __END_DECLS
 
 #endif // ANDROID_CHOREOGRAPHER_H
diff --git a/libs/nativedisplay/include/apex/choreographer.h b/libs/nativedisplay/include/apex/choreographer.h
index 5251fd3..b17b497 100644
--- a/libs/nativedisplay/include/apex/choreographer.h
+++ b/libs/nativedisplay/include/apex/choreographer.h
@@ -22,25 +22,6 @@
 __BEGIN_DECLS
 
 /**
- * Prototype of the function that is called when the display refresh rate
- * changes. It's passed the new vsync period in nanoseconds, as well as the data
- * pointer provided by the application that registered a callback.
- */
-typedef void (*AChoreographer_refreshRateCallback)(int64_t vsyncPeriodNanos, void* data);
-
-/**
- * Registers a callback to be run when the display refresh rate changes.
- */
-void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
-                                                AChoreographer_refreshRateCallback, void* data);
-
-/**
- * Unregisters a callback to be run when the display refresh rate changes.
- */
-void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer,
-                                                  AChoreographer_refreshRateCallback);
-
-/**
  * Creates an instance of AChoreographer.
  *
  * The key differences between this method and AChoreographer_getInstance are:
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index a60bc4d..a1c9eb8 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -304,3 +304,34 @@
 int ANativeWindow_setDequeueTimeout(ANativeWindow* window, int64_t timeout) {
     return window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT, timeout);
 }
+
+int ANativeWindow_setCancelBufferInterceptor(ANativeWindow* window,
+                                             ANativeWindow_cancelBufferInterceptor interceptor,
+                                             void* data) {
+    return window->perform(window, NATIVE_WINDOW_SET_CANCEL_INTERCEPTOR, interceptor, data);
+}
+
+int ANativeWindow_setDequeueBufferInterceptor(ANativeWindow* window,
+                                              ANativeWindow_dequeueBufferInterceptor interceptor,
+                                              void* data) {
+    return window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_INTERCEPTOR, interceptor, data);
+}
+
+int ANativeWindow_setPerformInterceptor(ANativeWindow* window,
+                                        ANativeWindow_performInterceptor interceptor, void* data) {
+    return window->perform(window, NATIVE_WINDOW_SET_PERFORM_INTERCEPTOR, interceptor, data);
+}
+
+int ANativeWindow_setQueueBufferInterceptor(ANativeWindow* window,
+                                            ANativeWindow_queueBufferInterceptor interceptor,
+                                            void* data) {
+    return window->perform(window, NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR, interceptor, data);
+}
+
+void ANativeWindow_allocateBuffers(ANativeWindow* window) {
+    window->perform(window, NATIVE_WINDOW_ALLOCATE_BUFFERS);
+}
+
+int64_t ANativeWindow_getNextFrameId(ANativeWindow* window) {
+    return query64(window, NATIVE_WINDOW_GET_NEXT_FRAME_ID);
+}
diff --git a/libs/nativewindow/include/apex/window.h b/libs/nativewindow/include/apex/window.h
index 869b22e..02b886c 100644
--- a/libs/nativewindow/include/apex/window.h
+++ b/libs/nativewindow/include/apex/window.h
@@ -17,12 +17,159 @@
 #pragma once
 
 #include <nativebase/nativebase.h>
+#include <stdarg.h>
 
 // apex is a superset of the NDK
 #include <android/native_window.h>
 
 __BEGIN_DECLS
 
+/*
+ * perform bits that can be used with ANativeWindow_perform()
+ *
+ * This is only to support the intercepting methods below - these should notbe
+ * used directly otherwise.
+ */
+enum ANativeWindowPerform {
+    // clang-format off
+    ANATIVEWINDOW_PERFORM_SET_USAGE            = 0,
+    ANATIVEWINDOW_PERFORM_SET_BUFFERS_GEOMETRY = 5,
+    ANATIVEWINDOW_PERFORM_SET_BUFFERS_FORMAT   = 9,
+    ANATIVEWINDOW_PERFORM_SET_USAGE64          = 30,
+    // clang-format on
+};
+
+/**
+ * Prototype of the function that an ANativeWindow implementation would call
+ * when ANativeWindow_cancelBuffer is called.
+ */
+typedef int (*ANativeWindow_cancelBufferFn)(ANativeWindow* window, ANativeWindowBuffer* buffer,
+                                            int fenceFd);
+
+/**
+ * Prototype of the function that intercepts an invocation of
+ * ANativeWindow_cancelBufferFn, along with a data pointer that's passed by the
+ * caller who set the interceptor, as well as arguments that would be
+ * passed to ANativeWindow_cancelBufferFn if it were to be called.
+ */
+typedef int (*ANativeWindow_cancelBufferInterceptor)(ANativeWindow* window,
+                                                     ANativeWindow_cancelBufferFn cancelBuffer,
+                                                     void* data, ANativeWindowBuffer* buffer,
+                                                     int fenceFd);
+
+/**
+ * Prototype of the function that an ANativeWindow implementation would call
+ * when ANativeWindow_dequeueBuffer is called.
+ */
+typedef int (*ANativeWindow_dequeueBufferFn)(ANativeWindow* window, ANativeWindowBuffer** buffer,
+                                             int* fenceFd);
+
+/**
+ * Prototype of the function that intercepts an invocation of
+ * ANativeWindow_dequeueBufferFn, along with a data pointer that's passed by the
+ * caller who set the interceptor, as well as arguments that would be
+ * passed to ANativeWindow_dequeueBufferFn if it were to be called.
+ */
+typedef int (*ANativeWindow_dequeueBufferInterceptor)(ANativeWindow* window,
+                                                      ANativeWindow_dequeueBufferFn dequeueBuffer,
+                                                      void* data, ANativeWindowBuffer** buffer,
+                                                      int* fenceFd);
+
+/**
+ * Prototype of the function that an ANativeWindow implementation would call
+ * when ANativeWindow_perform is called.
+ */
+typedef int (*ANativeWindow_performFn)(ANativeWindow* window, int operation, va_list args);
+
+/**
+ * Prototype of the function that intercepts an invocation of
+ * ANativeWindow_performFn, along with a data pointer that's passed by the
+ * caller who set the interceptor, as well as arguments that would be
+ * passed to ANativeWindow_performFn if it were to be called.
+ */
+typedef int (*ANativeWindow_performInterceptor)(ANativeWindow* window,
+                                                ANativeWindow_performFn perform, void* data,
+                                                int operation, va_list args);
+
+/**
+ * Prototype of the function that an ANativeWindow implementation would call
+ * when ANativeWindow_queueBuffer is called.
+ */
+typedef int (*ANativeWindow_queueBufferFn)(ANativeWindow* window, ANativeWindowBuffer* buffer,
+                                           int fenceFd);
+
+/**
+ * Prototype of the function that intercepts an invocation of
+ * ANativeWindow_queueBufferFn, along with a data pointer that's passed by the
+ * caller who set the interceptor, as well as arguments that would be
+ * passed to ANativeWindow_queueBufferFn if it were to be called.
+ */
+typedef int (*ANativeWindow_queueBufferInterceptor)(ANativeWindow* window,
+                                                    ANativeWindow_queueBufferFn queueBuffer,
+                                                    void* data, ANativeWindowBuffer* buffer,
+                                                    int fenceFd);
+
+/**
+ * Registers an interceptor for ANativeWindow_cancelBuffer. Instead of calling
+ * the underlying cancelBuffer function, instead the provided interceptor is
+ * called, which may optionally call the underlying cancelBuffer function. An
+ * optional data pointer is also provided to side-channel additional arguments.
+ *
+ * Note that usage of this should only be used for specialized use-cases by
+ * either the system partition or to Mainline modules. This should never be
+ * exposed to NDK or LL-NDK.
+ *
+ * Returns NO_ERROR on success, -errno if registration failed.
+ */
+int ANativeWindow_setCancelBufferInterceptor(ANativeWindow* window,
+                                             ANativeWindow_cancelBufferInterceptor interceptor,
+                                             void* data);
+
+/**
+ * Registers an interceptor for ANativeWindow_dequeueBuffer. Instead of calling
+ * the underlying dequeueBuffer function, instead the provided interceptor is
+ * called, which may optionally call the underlying dequeueBuffer function. An
+ * optional data pointer is also provided to side-channel additional arguments.
+ *
+ * Note that usage of this should only be used for specialized use-cases by
+ * either the system partition or to Mainline modules. This should never be
+ * exposed to NDK or LL-NDK.
+ *
+ * Returns NO_ERROR on success, -errno if registration failed.
+ */
+int ANativeWindow_setDequeueBufferInterceptor(ANativeWindow* window,
+                                              ANativeWindow_dequeueBufferInterceptor interceptor,
+                                              void* data);
+/**
+ * Registers an interceptor for ANativeWindow_perform. Instead of calling
+ * the underlying perform function, instead the provided interceptor is
+ * called, which may optionally call the underlying perform function. An
+ * optional data pointer is also provided to side-channel additional arguments.
+ *
+ * Note that usage of this should only be used for specialized use-cases by
+ * either the system partition or to Mainline modules. This should never be
+ * exposed to NDK or LL-NDK.
+ *
+ * Returns NO_ERROR on success, -errno if registration failed.
+ */
+int ANativeWindow_setPerformInterceptor(ANativeWindow* window,
+                                        ANativeWindow_performInterceptor interceptor, void* data);
+/**
+ * Registers an interceptor for ANativeWindow_queueBuffer. Instead of calling
+ * the underlying queueBuffer function, instead the provided interceptor is
+ * called, which may optionally call the underlying queueBuffer function. An
+ * optional data pointer is also provided to side-channel additional arguments.
+ *
+ * Note that usage of this should only be used for specialized use-cases by
+ * either the system partition or to Mainline modules. This should never be
+ * exposed to NDK or LL-NDK.
+ *
+ * Returns NO_ERROR on success, -errno if registration failed.
+ */
+int ANativeWindow_setQueueBufferInterceptor(ANativeWindow* window,
+                                            ANativeWindow_queueBufferInterceptor interceptor,
+                                            void* data);
+
 /**
  * Retrieves how long it took for the last time a buffer was dequeued.
  *
@@ -53,8 +200,23 @@
  * made by the window will return -ETIMEDOUT after the timeout if the dequeue
  * takes too long.
  *
- * \return NO_ERROR on succes, -errno on error.
+ * \return NO_ERROR on success, -errno on error.
  */
 int ANativeWindow_setDequeueTimeout(ANativeWindow* window, int64_t timeout);
 
+/**
+ * Provides a hint to the window that buffers should be preallocated ahead of
+ * time. Note that the window implementation is not guaranteed to preallocate
+ * any buffers, for instance if a private API disallows allocation of new
+ * buffers. As such no success/error status is returned.
+ */
+void ANativeWindow_allocateBuffers(ANativeWindow* window);
+
+/**
+ * Retrieves an identifier for the next frame to be queued by this window.
+ *
+ * \return -errno on error, otherwise returns the next frame id.
+ */
+int64_t ANativeWindow_getNextFrameId(ANativeWindow* window);
+
 __END_DECLS
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 14f7214..121374b 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -207,16 +207,16 @@
  */
 enum {
     // clang-format off
-    NATIVE_WINDOW_SET_USAGE                       =  0,   /* deprecated */
+    NATIVE_WINDOW_SET_USAGE                       =  ANATIVEWINDOW_PERFORM_SET_USAGE,   /* deprecated */
     NATIVE_WINDOW_CONNECT                         =  1,   /* deprecated */
     NATIVE_WINDOW_DISCONNECT                      =  2,   /* deprecated */
     NATIVE_WINDOW_SET_CROP                        =  3,   /* private */
     NATIVE_WINDOW_SET_BUFFER_COUNT                =  4,
-    NATIVE_WINDOW_SET_BUFFERS_GEOMETRY            =  5,   /* deprecated */
+    NATIVE_WINDOW_SET_BUFFERS_GEOMETRY            =  ANATIVEWINDOW_PERFORM_SET_BUFFERS_GEOMETRY,   /* deprecated */
     NATIVE_WINDOW_SET_BUFFERS_TRANSFORM           =  6,
     NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP           =  7,
     NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS          =  8,
-    NATIVE_WINDOW_SET_BUFFERS_FORMAT              =  9,
+    NATIVE_WINDOW_SET_BUFFERS_FORMAT              =  ANATIVEWINDOW_PERFORM_SET_BUFFERS_FORMAT,
     NATIVE_WINDOW_SET_SCALING_MODE                = 10,   /* private */
     NATIVE_WINDOW_LOCK                            = 11,   /* private */
     NATIVE_WINDOW_UNLOCK_AND_POST                 = 12,   /* private */
@@ -237,7 +237,7 @@
     NATIVE_WINDOW_GET_FRAME_TIMESTAMPS            = 27,
     NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT          = 28,
     NATIVE_WINDOW_GET_HDR_SUPPORT                 = 29,
-    NATIVE_WINDOW_SET_USAGE64                     = 30,
+    NATIVE_WINDOW_SET_USAGE64                     = ANATIVEWINDOW_PERFORM_SET_USAGE64,
     NATIVE_WINDOW_GET_CONSUMER_USAGE64            = 31,
     NATIVE_WINDOW_SET_BUFFERS_SMPTE2086_METADATA  = 32,
     NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA   = 33,
@@ -248,6 +248,11 @@
     NATIVE_WINDOW_GET_LAST_DEQUEUE_DURATION       = 38,    /* private */
     NATIVE_WINDOW_GET_LAST_QUEUE_DURATION         = 39,    /* private */
     NATIVE_WINDOW_SET_FRAME_RATE                  = 40,
+    NATIVE_WINDOW_SET_CANCEL_INTERCEPTOR          = 41,    /* private */
+    NATIVE_WINDOW_SET_DEQUEUE_INTERCEPTOR         = 42,    /* private */
+    NATIVE_WINDOW_SET_PERFORM_INTERCEPTOR         = 43,    /* private */
+    NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR           = 44,    /* private */
+    NATIVE_WINDOW_ALLOCATE_BUFFERS                = 45,    /* private */
     // clang-format on
 };
 
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index 3002da2..e0e20c3 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -17,6 +17,7 @@
     ANativeWindow_OemStorageGet; # llndk
     ANativeWindow_OemStorageSet; # llndk
     ANativeWindow_acquire;
+    ANativeWindow_allocateBuffers; # apex # introduced=30
     ANativeWindow_cancelBuffer; # llndk
     ANativeWindow_dequeueBuffer; # llndk
     ANativeWindow_getBuffersDataSpace; # introduced=28
@@ -25,11 +26,16 @@
     ANativeWindow_getLastDequeueDuration; # apex # introduced=30
     ANativeWindow_getLastDequeueStartTime; # apex # introduced=30
     ANativeWindow_getLastQueueDuration; # apex # introduced=30
+    ANativeWindow_getNextFrameId; # apex # introduced=30
     ANativeWindow_getWidth;
     ANativeWindow_lock;
     ANativeWindow_query; # llndk
     ANativeWindow_queryf; # llndk
     ANativeWindow_queueBuffer; # llndk
+    ANativeWindow_setCancelBufferInterceptor; # apex # introduced=30
+    ANativeWindow_setDequeueBufferInterceptor; # apex # introduced=30
+    ANativeWindow_setPerformInterceptor; # apex # introduced=30
+    ANativeWindow_setQueueBufferInterceptor; # apex # introduced=30
     ANativeWindow_release;
     ANativeWindow_setAutoPrerotation; # llndk
     ANativeWindow_setAutoRefresh; # llndk
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 2e3ab4c..4c7b629 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -52,6 +52,7 @@
         "gl/GLExtensions.cpp",
         "gl/GLFramebuffer.cpp",
         "gl/GLImage.cpp",
+        "gl/GLShadowTexture.cpp",
         "gl/GLShadowVertexGenerator.cpp",
         "gl/GLSkiaShadowPort.cpp",
         "gl/ImageManager.cpp",
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index e257704..69003fb 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -1003,7 +1003,7 @@
         setViewportAndProjection(display.physicalDisplay, display.clip);
     } else {
         setViewportAndProjection(display.physicalDisplay, display.clip);
-        auto status = mBlurFilter->setAsDrawTarget(display);
+        auto status = mBlurFilter->setAsDrawTarget(display, blurLayer->backgroundBlurRadius);
         if (status != NO_ERROR) {
             ALOGE("Failed to prepare blur filter! Aborting GPU composition for buffer (%p).",
                   buffer->handle);
@@ -1037,7 +1037,7 @@
                         .build();
     for (auto const layer : layers) {
         if (blurLayer == layer) {
-            auto status = mBlurFilter->prepare(layer->backgroundBlurRadius);
+            auto status = mBlurFilter->prepare();
             if (status != NO_ERROR) {
                 ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
                       buffer->handle);
@@ -1661,6 +1661,7 @@
 
     mState.cornerRadius = 0.0f;
     mState.drawShadows = true;
+    setupLayerTexturing(mShadowTexture.getTexture());
     drawMesh(mesh);
     mState.drawShadows = false;
 }
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 45c85de..4fc457f 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -32,6 +32,7 @@
 #include <renderengine/RenderEngine.h>
 #include <renderengine/private/Description.h>
 #include <sys/types.h>
+#include "GLShadowTexture.h"
 #include "ImageManager.h"
 
 #define EGL_NO_CONFIG ((EGLConfig)0)
@@ -183,6 +184,7 @@
     GLuint mVpWidth;
     GLuint mVpHeight;
     Description mState;
+    GLShadowTexture mShadowTexture;
 
     mat4 mSrgbToXyz;
     mat4 mDisplayP3ToXyz;
diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp
index 091eac9..153935b 100644
--- a/libs/renderengine/gl/GLFramebuffer.cpp
+++ b/libs/renderengine/gl/GLFramebuffer.cpp
@@ -122,6 +122,14 @@
     glBindFramebuffer(GL_FRAMEBUFFER, mFramebufferName);
 }
 
+void GLFramebuffer::bindAsReadBuffer() const {
+    glBindFramebuffer(GL_READ_FRAMEBUFFER, mFramebufferName);
+}
+
+void GLFramebuffer::bindAsDrawBuffer() const {
+    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mFramebufferName);
+}
+
 void GLFramebuffer::unbind() const {
     glBindFramebuffer(GL_FRAMEBUFFER, 0);
 }
diff --git a/libs/renderengine/gl/GLFramebuffer.h b/libs/renderengine/gl/GLFramebuffer.h
index 668685a..69102d6 100644
--- a/libs/renderengine/gl/GLFramebuffer.h
+++ b/libs/renderengine/gl/GLFramebuffer.h
@@ -48,6 +48,8 @@
     int32_t getBufferWidth() const { return mBufferWidth; }
     GLenum getStatus() const { return mStatus; }
     void bind() const;
+    void bindAsReadBuffer() const;
+    void bindAsDrawBuffer() const;
     void unbind() const;
 
 private:
diff --git a/libs/renderengine/gl/GLShadowTexture.cpp b/libs/renderengine/gl/GLShadowTexture.cpp
new file mode 100644
index 0000000..2423a34
--- /dev/null
+++ b/libs/renderengine/gl/GLShadowTexture.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+
+#include "GLShadowTexture.h"
+#include "GLSkiaShadowPort.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GLShadowTexture::GLShadowTexture() {
+    fillShadowTextureData(mTextureData, SHADOW_TEXTURE_WIDTH);
+
+    glGenTextures(1, &mName);
+    glBindTexture(GL_TEXTURE_2D, mName);
+    glTexImage2D(GL_TEXTURE_2D, 0 /* base image level */, GL_ALPHA, SHADOW_TEXTURE_WIDTH,
+                 SHADOW_TEXTURE_HEIGHT, 0 /* border */, GL_ALPHA, GL_UNSIGNED_BYTE, mTextureData);
+    mTexture.init(Texture::TEXTURE_2D, mName);
+    mTexture.setFiltering(true);
+    mTexture.setDimensions(SHADOW_TEXTURE_WIDTH, 1);
+}
+
+GLShadowTexture::~GLShadowTexture() {
+    glDeleteTextures(1, &mName);
+}
+
+const Texture& GLShadowTexture::getTexture() {
+    return mTexture;
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLShadowTexture.h b/libs/renderengine/gl/GLShadowTexture.h
new file mode 100644
index 0000000..250a9d7
--- /dev/null
+++ b/libs/renderengine/gl/GLShadowTexture.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <renderengine/Texture.h>
+#include <cstdint>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GLShadowTexture {
+public:
+    GLShadowTexture();
+    ~GLShadowTexture();
+
+    const Texture& getTexture();
+
+private:
+    static constexpr int SHADOW_TEXTURE_WIDTH = 128;
+    static constexpr int SHADOW_TEXTURE_HEIGHT = 1;
+
+    GLuint mName;
+    Texture mTexture;
+    uint8_t mTextureData[SHADOW_TEXTURE_WIDTH];
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLSkiaShadowPort.cpp b/libs/renderengine/gl/GLSkiaShadowPort.cpp
index 224ce6c..da8b435 100644
--- a/libs/renderengine/gl/GLSkiaShadowPort.cpp
+++ b/libs/renderengine/gl/GLSkiaShadowPort.cpp
@@ -644,6 +644,13 @@
                              2.0f * devSpaceSpotBlur, std::abs(insetWidth));
 }
 
+void fillShadowTextureData(uint8_t* data, size_t shadowTextureWidth) {
+    for (int i = 0; i < shadowTextureWidth; i++) {
+        const float d = 1 - i / ((shadowTextureWidth * 1.0f) - 1.0f);
+        data[i] = static_cast<uint8_t>((exp(-4.0f * d * d) - 0.018f) * 255);
+    }
+}
+
 } // namespace gl
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/gl/GLSkiaShadowPort.h b/libs/renderengine/gl/GLSkiaShadowPort.h
index e7d1861..912c8bb 100644
--- a/libs/renderengine/gl/GLSkiaShadowPort.h
+++ b/libs/renderengine/gl/GLSkiaShadowPort.h
@@ -17,13 +17,11 @@
 #pragma once
 
 #include <math/vec4.h>
+#include <renderengine/Mesh.h>
 #include <ui/Rect.h>
 
 namespace android {
 namespace renderengine {
-
-class Mesh;
-
 namespace gl {
 
 /**
@@ -79,6 +77,20 @@
 void fillIndicesForGeometry(const Geometry& shadowGeometry, int indexCount,
                             int startingVertexOffset, uint16_t* indices);
 
+/**
+ * Maps shadow geometry 'alpha' varying (1 for darkest, 0 for transparent) to
+ * darkness at that spot. Values are determined by an exponential falloff
+ * function provided by UX.
+ *
+ * The texture is used for quick lookup in theshadow shader.
+ *
+ * textureData - filled with shadow texture data that needs to be at least of
+ *               size textureWidth
+ *
+ * textureWidth - width of the texture, height is always 1
+ */
+void fillShadowTextureData(uint8_t* textureData, size_t textureWidth);
+
 } // namespace gl
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
index ba0e4ad..3ae35ec 100644
--- a/libs/renderengine/gl/ProgramCache.cpp
+++ b/libs/renderengine/gl/ProgramCache.cpp
@@ -550,7 +550,7 @@
 
 String8 ProgramCache::generateVertexShader(const Key& needs) {
     Formatter vs;
-    if (needs.isTexturing()) {
+    if (needs.hasTextureCoords()) {
         vs << "attribute vec4 texCoords;"
            << "varying vec2 outTexCoords;";
     }
@@ -559,16 +559,16 @@
         vs << "varying lowp vec2 outCropCoords;";
     }
     if (needs.drawShadows()) {
-        vs << "attribute vec4 shadowColor;";
-        vs << "varying vec4 outShadowColor;";
-        vs << "attribute vec4 shadowParams;";
-        vs << "varying vec3 outShadowParams;";
+        vs << "attribute lowp vec4 shadowColor;";
+        vs << "varying lowp vec4 outShadowColor;";
+        vs << "attribute lowp vec4 shadowParams;";
+        vs << "varying lowp vec3 outShadowParams;";
     }
     vs << "attribute vec4 position;"
        << "uniform mat4 projection;"
        << "uniform mat4 texture;"
        << "void main(void) {" << indent << "gl_Position = projection * position;";
-    if (needs.isTexturing()) {
+    if (needs.hasTextureCoords()) {
         vs << "outTexCoords = (texture * texCoords).st;";
     }
     if (needs.hasRoundedCorners()) {
@@ -592,11 +592,13 @@
     fs << "precision mediump float;";
 
     if (needs.getTextureTarget() == Key::TEXTURE_EXT) {
-        fs << "uniform samplerExternalOES sampler;"
-           << "varying vec2 outTexCoords;";
+        fs << "uniform samplerExternalOES sampler;";
     } else if (needs.getTextureTarget() == Key::TEXTURE_2D) {
-        fs << "uniform sampler2D sampler;"
-           << "varying vec2 outTexCoords;";
+        fs << "uniform sampler2D sampler;";
+    }
+
+    if (needs.hasTextureCoords()) {
+        fs << "varying vec2 outTexCoords;";
     }
 
     if (needs.hasRoundedCorners()) {
@@ -625,19 +627,17 @@
 
     if (needs.drawShadows()) {
         fs << R"__SHADER__(
-            varying vec4 outShadowColor;
-            varying vec3 outShadowParams;
+            varying lowp vec4 outShadowColor;
+            varying lowp vec3 outShadowParams;
 
             /**
              * Returns the shadow color.
              */
             vec4 getShadowColor()
             {
-                // exponential falloff function provided by UX
-                float d = length(outShadowParams.xy);
-                float distance = outShadowParams.z * (1.0 - d);
-                float factor = 1.0 - clamp(distance, 0.0, 1.0);
-                factor = exp(-factor * factor * 4.0) - 0.018;
+                lowp float d = length(outShadowParams.xy);
+                vec2 uv = vec2(outShadowParams.z * (1.0 - d), 0.5);
+                lowp float factor = texture2D(sampler, uv).a;
                 return outShadowColor * factor;
             }
             )__SHADER__";
diff --git a/libs/renderengine/gl/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h
index c8b6da7..901e631 100644
--- a/libs/renderengine/gl/ProgramCache.h
+++ b/libs/renderengine/gl/ProgramCache.h
@@ -128,6 +128,7 @@
         }
 
         inline bool isTexturing() const { return (mKey & TEXTURE_MASK) != TEXTURE_OFF; }
+        inline bool hasTextureCoords() const { return isTexturing() && !drawShadows(); }
         inline int getTextureTarget() const { return (mKey & TEXTURE_MASK); }
         inline bool isPremultiplied() const { return (mKey & BLEND_MASK) == BLEND_PREMULT; }
         inline bool isOpaque() const { return (mKey & OPACITY_MASK) == OPACITY_OPAQUE; }
diff --git a/libs/renderengine/gl/filters/BlurFilter.cpp b/libs/renderengine/gl/filters/BlurFilter.cpp
index a18a999..48c2560 100644
--- a/libs/renderengine/gl/filters/BlurFilter.cpp
+++ b/libs/renderengine/gl/filters/BlurFilter.cpp
@@ -31,29 +31,24 @@
 namespace gl {
 
 BlurFilter::BlurFilter(GLESRenderEngine& engine)
-      : mEngine(engine), mCompositionFbo(engine), mBlurredFbo(engine), mSimpleProgram(engine) {
-    mSimpleProgram.compile(getVertexShader(), getSimpleFragShader());
-    mSPosLoc = mSimpleProgram.getAttributeLocation("aPosition");
-    mSUvLoc = mSimpleProgram.getAttributeLocation("aUV");
-    mSTextureLoc = mSimpleProgram.getUniformLocation("uTexture");
+      : mEngine(engine), mCompositionFbo(engine), mBlurredFbo(engine), mMixProgram(engine) {
+    mMixProgram.compile(getVertexShader(), getMixFragShader());
+    mMPosLoc = mMixProgram.getAttributeLocation("aPosition");
+    mMUvLoc = mMixProgram.getAttributeLocation("aUV");
+    mMTextureLoc = mMixProgram.getUniformLocation("uTexture");
+    mMCompositionTextureLoc = mMixProgram.getUniformLocation("uCompositionTexture");
+    mMMixLoc = mMixProgram.getUniformLocation("uMix");
 }
 
-status_t BlurFilter::setAsDrawTarget(const DisplaySettings& display) {
+status_t BlurFilter::setAsDrawTarget(const DisplaySettings& display, uint32_t radius) {
     ATRACE_NAME("BlurFilter::setAsDrawTarget");
+    mRadius = radius;
 
     if (!mTexturesAllocated) {
         mDisplayWidth = display.physicalDisplay.width();
         mDisplayHeight = display.physicalDisplay.height();
         mCompositionFbo.allocateBuffers(mDisplayWidth, mDisplayHeight);
 
-        // Let's use mimap filtering on the offscreen composition texture,
-        // this will drastically improve overall shader quality.
-        glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 3);
-        glBindTexture(GL_TEXTURE_2D, 0);
-
         const uint32_t fboWidth = floorf(mDisplayWidth * kFboScale);
         const uint32_t fboHeight = floorf(mDisplayHeight * kFboScale);
         mBlurredFbo.allocateBuffers(fboWidth, fboHeight);
@@ -94,27 +89,41 @@
 status_t BlurFilter::render() {
     ATRACE_NAME("BlurFilter::render");
 
-    // Now let's scale our blur up
-    mSimpleProgram.useProgram();
+    // Now let's scale our blur up. It will be interpolated with the larger composited
+    // texture for the first frames, to hide downscaling artifacts.
+    GLfloat mix = fmin(1.0, mRadius / kMaxCrossFadeRadius);
+    if (mix >= 1) {
+        mBlurredFbo.bindAsReadBuffer();
+        glBlitFramebuffer(0, 0, mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight(), 0, 0,
+                          mDisplayWidth, mDisplayHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
+        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+        return NO_ERROR;
+    }
+
+    mMixProgram.useProgram();
+    glUniform1f(mMMixLoc, mix);
     glActiveTexture(GL_TEXTURE0);
     glBindTexture(GL_TEXTURE_2D, mBlurredFbo.getTextureName());
-    glUniform1i(mSTextureLoc, 0);
+    glUniform1i(mMTextureLoc, 0);
+    glActiveTexture(GL_TEXTURE1);
+    glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
+    glUniform1i(mMCompositionTextureLoc, 1);
     mEngine.checkErrors("Setting final pass uniforms");
 
-    drawMesh(mSUvLoc, mSPosLoc);
+    drawMesh(mMUvLoc, mMPosLoc);
 
     glUseProgram(0);
+    glActiveTexture(GL_TEXTURE0);
     return NO_ERROR;
 }
 
 string BlurFilter::getVertexShader() const {
     return R"SHADER(
         #version 310 es
-        precision lowp float;
 
         in vec2 aPosition;
-        in mediump vec2 aUV;
-        out mediump vec2 vUV;
+        in highp vec2 aUV;
+        out highp vec2 vUV;
 
         void main() {
             vUV = aUV;
@@ -123,18 +132,22 @@
     )SHADER";
 }
 
-string BlurFilter::getSimpleFragShader() const {
+string BlurFilter::getMixFragShader() const {
     string shader = R"SHADER(
         #version 310 es
-        precision lowp float;
+        precision mediump float;
 
-        in mediump vec2 vUV;
+        in highp vec2 vUV;
         out vec4 fragColor;
 
+        uniform sampler2D uCompositionTexture;
         uniform sampler2D uTexture;
+        uniform float uMix;
 
         void main() {
-            fragColor = texture(uTexture, vUV);
+            vec4 blurred = texture(uTexture, vUV);
+            vec4 composition = texture(uCompositionTexture, vUV);
+            fragColor = mix(composition, blurred, uMix);
         }
     )SHADER";
     return shader;
diff --git a/libs/renderengine/gl/filters/BlurFilter.h b/libs/renderengine/gl/filters/BlurFilter.h
index e265b51..6889939 100644
--- a/libs/renderengine/gl/filters/BlurFilter.h
+++ b/libs/renderengine/gl/filters/BlurFilter.h
@@ -31,22 +31,25 @@
 public:
     // Downsample FBO to improve performance
     static constexpr float kFboScale = 0.25f;
+    // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
+    // image, up to this radius.
+    static constexpr float kMaxCrossFadeRadius = 15.0f;
 
     explicit BlurFilter(GLESRenderEngine& engine);
     virtual ~BlurFilter(){};
 
     // Set up render targets, redirecting output to offscreen texture.
-    status_t setAsDrawTarget(const DisplaySettings&);
+    status_t setAsDrawTarget(const DisplaySettings&, uint32_t radius);
     // Allocate any textures needed for the filter.
     virtual void allocateTextures() = 0;
     // Execute blur passes, rendering to offscreen texture.
-    virtual status_t prepare(uint32_t radius) = 0;
+    virtual status_t prepare() = 0;
     // Render blur to the bound framebuffer (screen).
     status_t render();
 
 protected:
+    uint32_t mRadius;
     void drawMesh(GLuint uv, GLuint position);
-    string getSimpleFragShader() const;
     string getVertexShader() const;
 
     GLESRenderEngine& mEngine;
@@ -58,12 +61,15 @@
     uint32_t mDisplayHeight;
 
 private:
+    string getMixFragShader() const;
     bool mTexturesAllocated = false;
 
-    GenericProgram mSimpleProgram;
-    GLuint mSPosLoc;
-    GLuint mSUvLoc;
-    GLuint mSTextureLoc;
+    GenericProgram mMixProgram;
+    GLuint mMPosLoc;
+    GLuint mMUvLoc;
+    GLuint mMMixLoc;
+    GLuint mMTextureLoc;
+    GLuint mMCompositionTextureLoc;
 };
 
 } // namespace gl
diff --git a/libs/renderengine/gl/filters/GaussianBlurFilter.cpp b/libs/renderengine/gl/filters/GaussianBlurFilter.cpp
index f5ba02a..4d7bf44 100644
--- a/libs/renderengine/gl/filters/GaussianBlurFilter.cpp
+++ b/libs/renderengine/gl/filters/GaussianBlurFilter.cpp
@@ -26,6 +26,10 @@
 
 #include <utils/Trace.h>
 
+#define PI 3.14159265359
+#define THETA 0.352
+#define K 1.0 / (2.0 * THETA * THETA)
+
 namespace android {
 namespace renderengine {
 namespace gl {
@@ -39,22 +43,24 @@
     mVPosLoc = mVerticalProgram.getAttributeLocation("aPosition");
     mVUvLoc = mVerticalProgram.getAttributeLocation("aUV");
     mVTextureLoc = mVerticalProgram.getUniformLocation("uTexture");
-    mVSizeLoc = mVerticalProgram.getUniformLocation("uSize");
-    mVRadiusLoc = mVerticalProgram.getUniformLocation("uRadius");
+    mVIncrementLoc = mVerticalProgram.getUniformLocation("uIncrement");
+    mVNumSamplesLoc = mVerticalProgram.getUniformLocation("uSamples");
+    mVGaussianWeightLoc = mVerticalProgram.getUniformLocation("uGaussianWeights");
 
     mHorizontalProgram.compile(getVertexShader(), getFragmentShader(true));
     mHPosLoc = mHorizontalProgram.getAttributeLocation("aPosition");
     mHUvLoc = mHorizontalProgram.getAttributeLocation("aUV");
     mHTextureLoc = mHorizontalProgram.getUniformLocation("uTexture");
-    mHSizeLoc = mHorizontalProgram.getUniformLocation("uSize");
-    mHRadiusLoc = mHorizontalProgram.getUniformLocation("uRadius");
+    mHIncrementLoc = mHorizontalProgram.getUniformLocation("uIncrement");
+    mHNumSamplesLoc = mHorizontalProgram.getUniformLocation("uSamples");
+    mHGaussianWeightLoc = mHorizontalProgram.getUniformLocation("uGaussianWeights");
 }
 
 void GaussianBlurFilter::allocateTextures() {
     mVerticalPassFbo.allocateBuffers(mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight());
 }
 
-status_t GaussianBlurFilter::prepare(uint32_t radius) {
+status_t GaussianBlurFilter::prepare() {
     ATRACE_NAME("GaussianBlurFilter::prepare");
 
     if (mVerticalPassFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
@@ -70,21 +76,38 @@
         return GL_INVALID_OPERATION;
     }
 
+    mCompositionFbo.bindAsReadBuffer();
+    mBlurredFbo.bindAsDrawBuffer();
+    glBlitFramebuffer(0, 0, mCompositionFbo.getBufferWidth(), mCompositionFbo.getBufferHeight(), 0,
+                      0, mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight(),
+                      GL_COLOR_BUFFER_BIT, GL_LINEAR);
+    glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+
     // First, we'll apply the vertical pass, that receives the flattened background layers.
     mVerticalPassFbo.bind();
     mVerticalProgram.useProgram();
 
+    // Precompute gaussian bell curve, and send it to the shader to avoid
+    // unnecessary computations.
+    auto samples = min(mRadius, kNumSamples);
+    GLfloat gaussianWeights[kNumSamples] = {};
+    for (size_t i = 0; i < samples; i++) {
+        float normalized = float(i) / samples;
+        gaussianWeights[i] = (float)exp(-K * normalized * normalized);
+    }
+
     // set uniforms
     auto width = mVerticalPassFbo.getBufferWidth();
     auto height = mVerticalPassFbo.getBufferHeight();
-    auto radiusF = fmax(1.0f, radius * kFboScale);
+    auto radiusF = fmax(1.0f, mRadius * kFboScale);
     glViewport(0, 0, width, height);
     glActiveTexture(GL_TEXTURE0);
-    glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
-    glGenerateMipmap(GL_TEXTURE_2D);
+    glBindTexture(GL_TEXTURE_2D, mBlurredFbo.getTextureName());
     glUniform1i(mVTextureLoc, 0);
-    glUniform2f(mVSizeLoc, width, height);
-    glUniform1f(mVRadiusLoc, radiusF);
+    glUniform2f(mVIncrementLoc, radiusF / (width * 2.0f), radiusF / (height * 2.0f));
+    glUniform1i(mVNumSamplesLoc, samples);
+    glUniform1fv(mVGaussianWeightLoc, kNumSamples, gaussianWeights);
     mEngine.checkErrors("Setting vertical-diagonal pass uniforms");
 
     drawMesh(mVUvLoc, mVPosLoc);
@@ -97,8 +120,9 @@
     glActiveTexture(GL_TEXTURE0);
     glBindTexture(GL_TEXTURE_2D, mVerticalPassFbo.getTextureName());
     glUniform1i(mHTextureLoc, 0);
-    glUniform2f(mHSizeLoc, width, height);
-    glUniform1f(mHRadiusLoc, radiusF);
+    glUniform2f(mHIncrementLoc, radiusF / (width * 2.0f), radiusF / (height * 2.0f));
+    glUniform1i(mHNumSamplesLoc, samples);
+    glUniform1fv(mHGaussianWeightLoc, kNumSamples, gaussianWeights);
     mEngine.checkErrors("Setting vertical pass uniforms");
 
     drawMesh(mHUvLoc, mHPosLoc);
@@ -115,42 +139,31 @@
 }
 
 string GaussianBlurFilter::getFragmentShader(bool horizontal) const {
-    string shader = "#version 310 es\n#define DIRECTION ";
-    shader += (horizontal ? "1" : "0");
-    shader += R"SHADER(
-        precision lowp float;
+    stringstream shader;
+    shader << "#version 310 es\n"
+           << "#define DIRECTION " << (horizontal ? "1" : "0") << "\n"
+           << "#define NUM_SAMPLES " << kNumSamples <<
+            R"SHADER(
+        precision mediump float;
 
         uniform sampler2D uTexture;
-        uniform vec2 uSize;
-        uniform float uRadius;
+        uniform vec2 uIncrement;
+        uniform float[NUM_SAMPLES] uGaussianWeights;
+        uniform int uSamples;
 
-        mediump in vec2 vUV;
+        highp in vec2 vUV;
         out vec4 fragColor;
 
-        #define PI 3.14159265359
-        #define THETA 0.352
-        #define MU 0.0
-        #define A 1.0 / (THETA * sqrt(2.0 * PI))
-        #define K 1.0 / (2.0 * THETA * THETA)
-        #define MAX_SAMPLES 10
-
-        float gaussianBellCurve(float x) {
-            float tmp = (x - MU);
-            return exp(-K * tmp * tmp);
-        }
-
-        vec3 gaussianBlur(sampler2D texture, mediump vec2 uv, float size,
-                          mediump vec2 direction, float radius) {
+        vec3 gaussianBlur(sampler2D texture, highp vec2 uv, float inc, vec2 direction) {
             float totalWeight = 0.0;
-            vec3 blurred = vec3(0.);
-            int samples = min(int(ceil(radius / 2.0)), MAX_SAMPLES);
-            float inc = radius / (size * 2.0);
+            vec3 blurred = vec3(0.0);
+            float fSamples = 1.0 / float(uSamples);
 
-            for (int i = -samples; i <= samples; i++) {
-                float normalized = float(i) / float(samples);
-                float weight = gaussianBellCurve(normalized);
+            for (int i = -uSamples; i <= uSamples; i++) {
+                float weight = uGaussianWeights[abs(i)];
+                float normalized = float(i) * fSamples;
                 float radInc = inc * normalized;
-                blurred += weight * (texture(texture, uv + radInc * direction)).rgb;;
+                blurred += weight * (texture(texture, radInc * direction + uv, 0.0)).rgb;
                 totalWeight += weight;
             }
 
@@ -159,15 +172,15 @@
 
         void main() {
             #if DIRECTION == 1
-            vec3 color = gaussianBlur(uTexture, vUV, uSize.x, vec2(1.0, 0.0), uRadius);
+            vec3 color = gaussianBlur(uTexture, vUV, uIncrement.x, vec2(1.0, 0.0));
             #else
-            vec3 color = gaussianBlur(uTexture, vUV, uSize.y, vec2(0.0, 1.0), uRadius);
+            vec3 color = gaussianBlur(uTexture, vUV, uIncrement.y, vec2(0.0, 1.0));
             #endif
             fragColor = vec4(color, 1.0);
         }
 
     )SHADER";
-    return shader;
+    return shader.str();
 }
 
 } // namespace gl
diff --git a/libs/renderengine/gl/filters/GaussianBlurFilter.h b/libs/renderengine/gl/filters/GaussianBlurFilter.h
index acf0f07..8580522 100644
--- a/libs/renderengine/gl/filters/GaussianBlurFilter.h
+++ b/libs/renderengine/gl/filters/GaussianBlurFilter.h
@@ -30,8 +30,10 @@
 
 class GaussianBlurFilter : public BlurFilter {
 public:
+    static constexpr uint32_t kNumSamples = 12;
+
     explicit GaussianBlurFilter(GLESRenderEngine& engine);
-    status_t prepare(uint32_t radius) override;
+    status_t prepare() override;
     void allocateTextures() override;
 
 private:
@@ -45,16 +47,18 @@
     GLuint mVPosLoc;
     GLuint mVUvLoc;
     GLuint mVTextureLoc;
-    GLuint mVSizeLoc;
-    GLuint mVRadiusLoc;
+    GLuint mVIncrementLoc;
+    GLuint mVNumSamplesLoc;
+    GLuint mVGaussianWeightLoc;
 
     // Horizontal pass and its uniforms
     GenericProgram mHorizontalProgram;
     GLuint mHPosLoc;
     GLuint mHUvLoc;
     GLuint mHTextureLoc;
-    GLuint mHSizeLoc;
-    GLuint mHRadiusLoc;
+    GLuint mHIncrementLoc;
+    GLuint mHNumSamplesLoc;
+    GLuint mHGaussianWeightLoc;
 };
 
 } // namespace gl
diff --git a/libs/renderengine/gl/filters/LensBlurFilter.cpp b/libs/renderengine/gl/filters/LensBlurFilter.cpp
index 799deac..fb29fbb 100644
--- a/libs/renderengine/gl/filters/LensBlurFilter.cpp
+++ b/libs/renderengine/gl/filters/LensBlurFilter.cpp
@@ -62,7 +62,7 @@
                                              mBlurredFbo.getBufferHeight());
 }
 
-status_t LensBlurFilter::prepare(uint32_t radius) {
+status_t LensBlurFilter::prepare() {
     ATRACE_NAME("LensBlurFilter::prepare");
 
     if (mVerticalDiagonalPassFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
@@ -86,11 +86,10 @@
     // set uniforms
     auto width = mVerticalDiagonalPassFbo.getBufferWidth();
     auto height = mVerticalDiagonalPassFbo.getBufferHeight();
-    auto radiusF = fmax(1.0f, radius * kFboScale);
+    auto radiusF = fmax(1.0f, mRadius * kFboScale);
     glViewport(0, 0, width, height);
     glActiveTexture(GL_TEXTURE0);
     glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
-    glGenerateMipmap(GL_TEXTURE_2D);
     glUniform1i(mVDTexture0Loc, 0);
     glUniform2f(mVDSizeLoc, mDisplayWidth, mDisplayHeight);
     glUniform1f(mVDRadiusLoc, radiusF);
@@ -134,8 +133,7 @@
     string shader = "#version 310 es\n#define DIRECTION ";
     shader += (forComposition ? "1" : "0");
     shader += R"SHADER(
-        precision lowp float;
-
+        precision mediump float;
         #define PI 3.14159265359
 
         uniform sampler2D uTexture0;
@@ -143,7 +141,7 @@
         uniform float uRadius;
         uniform int uNumSamples;
 
-        mediump in vec2 vUV;
+        highp in vec2 vUV;
 
         #if DIRECTION == 0
         layout(location = 0) out vec4 fragColor0;
diff --git a/libs/renderengine/gl/filters/LensBlurFilter.h b/libs/renderengine/gl/filters/LensBlurFilter.h
index 8543f0d..1620c5a 100644
--- a/libs/renderengine/gl/filters/LensBlurFilter.h
+++ b/libs/renderengine/gl/filters/LensBlurFilter.h
@@ -31,7 +31,7 @@
 class LensBlurFilter : public BlurFilter {
 public:
     explicit LensBlurFilter(GLESRenderEngine& engine);
-    status_t prepare(uint32_t radius) override;
+    status_t prepare() override;
     void allocateTextures() override;
 
 private:
diff --git a/libs/ui/include/ui/DisplayConfig.h b/libs/ui/include/ui/DisplayConfig.h
new file mode 100644
index 0000000..09b8211
--- /dev/null
+++ b/libs/ui/include/ui/DisplayConfig.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <type_traits>
+
+#include <ui/Size.h>
+#include <utils/Timers.h>
+
+namespace android {
+
+// Configuration supported by physical display.
+struct DisplayConfig {
+    ui::Size resolution;
+    float xDpi = 0;
+    float yDpi = 0;
+
+    float refreshRate = 0;
+    nsecs_t appVsyncOffset = 0;
+    nsecs_t sfVsyncOffset = 0;
+    nsecs_t presentationDeadline = 0;
+};
+
+static_assert(std::is_trivially_copyable_v<DisplayConfig>);
+
+} // namespace android
diff --git a/libs/ui/include/ui/DisplayInfo.h b/libs/ui/include/ui/DisplayInfo.h
index 38f8d6b..7773319 100644
--- a/libs/ui/include/ui/DisplayInfo.h
+++ b/libs/ui/include/ui/DisplayInfo.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007 The Android Open Source Project
+ * Copyright 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,35 +14,18 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_DISPLAY_INFO_H
-#define ANDROID_UI_DISPLAY_INFO_H
+#pragma once
 
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <ui/Rotation.h>
-#include <utils/Timers.h>
+#include <type_traits>
 
 namespace android {
 
-constexpr uint32_t NO_LAYER_STACK = static_cast<uint32_t>(-1);
-
+// Immutable information about physical display.
 struct DisplayInfo {
-    uint32_t w{0};
-    uint32_t h{0};
-    float xdpi{0};
-    float ydpi{0};
-    float fps{0};
-    float density{0};
-    ui::Rotation orientation{ui::ROTATION_0};
-    bool secure{false};
-    nsecs_t appVsyncOffset{0};
-    nsecs_t presentationDeadline{0};
-    uint32_t viewportW{0};
-    uint32_t viewportH{0};
-    uint32_t layerStack{NO_LAYER_STACK};
+    float density = 0.f;
+    bool secure = false;
 };
 
-} // namespace android
+static_assert(std::is_trivially_copyable_v<DisplayInfo>);
 
-#endif // ANDROID_COMPOSER_DISPLAY_INFO_H
+} // namespace android
diff --git a/libs/ui/include/ui/DisplayState.h b/libs/ui/include/ui/DisplayState.h
new file mode 100644
index 0000000..64efc84
--- /dev/null
+++ b/libs/ui/include/ui/DisplayState.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <ui/Rotation.h>
+#include <ui/Size.h>
+
+#include <cstdint>
+#include <type_traits>
+
+namespace android::ui {
+
+using LayerStack = uint32_t;
+constexpr LayerStack NO_LAYER_STACK = static_cast<LayerStack>(-1);
+
+// Transactional state of physical or virtual display. Note that libgui defines
+// android::DisplayState as a superset of android::ui::DisplayState.
+struct DisplayState {
+    LayerStack layerStack = NO_LAYER_STACK;
+    Rotation orientation = ROTATION_0;
+    Size viewport;
+};
+
+static_assert(std::is_trivially_copyable_v<DisplayState>);
+
+} // namespace android::ui
diff --git a/libs/ui/include_vndk/ui/DisplayConfig.h b/libs/ui/include_vndk/ui/DisplayConfig.h
new file mode 120000
index 0000000..1450319
--- /dev/null
+++ b/libs/ui/include_vndk/ui/DisplayConfig.h
@@ -0,0 +1 @@
+../../include/ui/DisplayConfig.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/DisplayState.h b/libs/ui/include_vndk/ui/DisplayState.h
new file mode 120000
index 0000000..4e92849
--- /dev/null
+++ b/libs/ui/include_vndk/ui/DisplayState.h
@@ -0,0 +1 @@
+../../include/ui/DisplayState.h
\ No newline at end of file
diff --git a/opengl/tests/lib/WindowSurface.cpp b/opengl/tests/lib/WindowSurface.cpp
index 4dcc1ca..dfb9c92 100644
--- a/opengl/tests/lib/WindowSurface.cpp
+++ b/opengl/tests/lib/WindowSurface.cpp
@@ -16,10 +16,13 @@
 
 #include <WindowSurface.h>
 
-#include <gui/SurfaceComposerClient.h>
+#include <utility>
+
 #include <gui/ISurfaceComposer.h>
 #include <gui/Surface.h>
-#include <ui/DisplayInfo.h>
+#include <gui/SurfaceComposerClient.h>
+#include <ui/DisplayConfig.h>
+#include <ui/DisplayState.h>
 
 using namespace android;
 
@@ -33,28 +36,33 @@
         return;
     }
 
-    // Get main display parameters.
-    const auto mainDpy = SurfaceComposerClient::getInternalDisplayToken();
-    if (mainDpy == nullptr) {
+    const auto displayToken = SurfaceComposerClient::getInternalDisplayToken();
+    if (displayToken == nullptr) {
         fprintf(stderr, "ERROR: no display\n");
         return;
     }
 
-    DisplayInfo mainDpyInfo;
-    err = SurfaceComposerClient::getDisplayInfo(mainDpy, &mainDpyInfo);
+    DisplayConfig displayConfig;
+    err = SurfaceComposerClient::getActiveDisplayConfig(displayToken, &displayConfig);
     if (err != NO_ERROR) {
-        fprintf(stderr, "ERROR: unable to get display characteristics\n");
+        fprintf(stderr, "ERROR: unable to get active display config\n");
         return;
     }
 
-    uint32_t width, height;
-    if (mainDpyInfo.orientation != ui::ROTATION_0 && mainDpyInfo.orientation != ui::ROTATION_180) {
-        // rotated
-        width = mainDpyInfo.h;
-        height = mainDpyInfo.w;
-    } else {
-        width = mainDpyInfo.w;
-        height = mainDpyInfo.h;
+    ui::DisplayState displayState;
+    err = SurfaceComposerClient::getDisplayState(displayToken, &displayState);
+    if (err != NO_ERROR) {
+        fprintf(stderr, "ERROR: unable to get display state\n");
+        return;
+    }
+
+    const ui::Size& resolution = displayConfig.resolution;
+    auto width = resolution.getWidth();
+    auto height = resolution.getHeight();
+
+    if (displayState.orientation == ui::ROTATION_90 ||
+        displayState.orientation == ui::ROTATION_270) {
+        std::swap(width, height);
     }
 
     sp<SurfaceControl> sc = surfaceComposerClient->createSurface(
diff --git a/services/automotive/display/CarWindowService.cpp b/services/automotive/display/CarWindowService.cpp
index e95c9e1..09ae34a 100644
--- a/services/automotive/display/CarWindowService.cpp
+++ b/services/automotive/display/CarWindowService.cpp
@@ -13,7 +13,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 //
-#include <ui/DisplayInfo.h>
+
+#include <utility>
+
 #include <gui/bufferqueue/2.0/B2HGraphicBufferProducer.h>
 
 #include "CarWindowService.h"
@@ -38,31 +40,35 @@
             return nullptr;
         }
 
-        // Get main display parameters.
-        sp<IBinder> mainDpy = SurfaceComposerClient::getInternalDisplayToken();
-        if (mainDpy == nullptr) {
+        const auto displayToken = SurfaceComposerClient::getInternalDisplayToken();
+        if (displayToken == nullptr) {
             ALOGE("Failed to get internal display ");
             return nullptr;
         }
-        DisplayInfo mainDpyInfo;
-        err = SurfaceComposerClient::getDisplayInfo(mainDpy, &mainDpyInfo);
+
+        err = SurfaceComposerClient::getActiveDisplayConfig(displayToken, &mDpyConfig);
         if (err != NO_ERROR) {
-            ALOGE("Failed to get display characteristics");
+            ALOGE("Failed to get active display config");
             return nullptr;
         }
-        unsigned int mWidth, mHeight;
-        if (mainDpyInfo.orientation != ui::ROTATION_0 &&
-            mainDpyInfo.orientation != ui::ROTATION_180) {
-            // rotated
-            mWidth = mainDpyInfo.h;
-            mHeight = mainDpyInfo.w;
-        } else {
-            mWidth = mainDpyInfo.w;
-            mHeight = mainDpyInfo.h;
+
+        err = SurfaceComposerClient::getDisplayState(displayToken, &mDpyState);
+        if (err != NO_ERROR) {
+            ALOGE("Failed to get display state");
+            return nullptr;
+        }
+
+        const ui::Size& resolution = mDpyConfig.resolution;
+        auto width = resolution.getWidth();
+        auto height = resolution.getHeight();
+
+        if (mDpyState.orientation == ui::ROTATION_90 ||
+            mDpyState.orientation == ui::ROTATION_270) {
+            std::swap(width, height);
         }
 
         mSurfaceControl = mSurfaceComposerClient->createSurface(
-                String8("Automotive Display"), mWidth, mHeight,
+                String8("Automotive Display"), width, height,
                 PIXEL_FORMAT_RGBX_8888, ISurfaceComposerClient::eOpaque);
         if (mSurfaceControl == nullptr || !mSurfaceControl->isValid()) {
             ALOGE("Failed to create SurfaceControl");
diff --git a/services/automotive/display/include/CarWindowService.h b/services/automotive/display/include/CarWindowService.h
index 3290cc7..a32ed7c 100644
--- a/services/automotive/display/include/CarWindowService.h
+++ b/services/automotive/display/include/CarWindowService.h
@@ -20,6 +20,8 @@
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
+#include <ui/DisplayConfig.h>
+#include <ui/DisplayState.h>
 
 namespace android {
 namespace frameworks {
@@ -37,11 +39,23 @@
     Return<sp<IGraphicBufferProducer>> getIGraphicBufferProducer() override;
     Return<bool> showWindow() override;
     Return<bool> hideWindow() override;
+    Return<void> getDisplayInfo(getDisplayInfo_cb _info_cb) override {
+        HwDisplayConfig cfg;
+        cfg.setToExternal((uint8_t*)&mDpyConfig, sizeof(DisplayConfig));
+
+        HwDisplayState state;
+        state.setToExternal((uint8_t*)&mDpyState, sizeof(DisplayState));
+
+       _info_cb(cfg, state);
+        return hardware::Void();
+    }
 
 private:
     sp<android::Surface> mSurface;
     sp<android::SurfaceComposerClient> mSurfaceComposerClient;
     sp<android::SurfaceControl> mSurfaceControl;
+    DisplayConfig mDpyConfig;
+    ui::DisplayState mDpyState;
 };
 }  // namespace implementation
 }  // namespace V1_0
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index baba64f..6eed24a 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -20,6 +20,7 @@
         "libbase",
         "libbinder",
         "libcutils",
+        "libgfxstats",
         "libgraphicsenv",
         "liblog",
         "libutils",
@@ -52,7 +53,6 @@
     name: "libgpuservice_sources",
     srcs: [
         "GpuService.cpp",
-        "gpustats/GpuStats.cpp"
     ],
 }
 
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index be4a462..91a76f1 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -24,14 +24,13 @@
 #include <binder/Parcel.h>
 #include <binder/PermissionCache.h>
 #include <cutils/properties.h>
+#include <gpustats/GpuStats.h>
 #include <private/android_filesystem_config.h>
 #include <utils/String8.h>
 #include <utils/Trace.h>
 
 #include <vkjson.h>
 
-#include "gpustats/GpuStats.h"
-
 namespace android {
 
 using base::StringAppendF;
@@ -53,8 +52,9 @@
                              int64_t driverBuildTime, const std::string& appPackageName,
                              const int32_t vulkanVersion, GpuStatsInfo::Driver driver,
                              bool isDriverLoaded, int64_t driverLoadingTime) {
-    mGpuStats->insert(driverPackageName, driverVersionName, driverVersionCode, driverBuildTime,
-                      appPackageName, vulkanVersion, driver, isDriverLoaded, driverLoadingTime);
+    mGpuStats->insertDriverStats(driverPackageName, driverVersionName, driverVersionCode,
+                                 driverBuildTime, appPackageName, vulkanVersion, driver,
+                                 isDriverLoaded, driverLoadingTime);
 }
 
 status_t GpuService::getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const {
diff --git a/services/gpuservice/TEST_MAPPING b/services/gpuservice/TEST_MAPPING
new file mode 100644
index 0000000..b345355
--- /dev/null
+++ b/services/gpuservice/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "gpuservice_unittest"
+    }
+  ]
+}
diff --git a/services/gpuservice/gpustats/Android.bp b/services/gpuservice/gpustats/Android.bp
new file mode 100644
index 0000000..49a98cc
--- /dev/null
+++ b/services/gpuservice/gpustats/Android.bp
@@ -0,0 +1,21 @@
+cc_library_shared {
+    name: "libgfxstats",
+    srcs: [
+        "GpuStats.cpp",
+    ],
+    shared_libs: [
+        "libcutils",
+        "libgraphicsenv",
+        "liblog",
+        "libutils",
+    ],
+    export_include_dirs: ["include"],
+    cppflags: [
+        "-Wall",
+        "-Werror",
+        "-Wformat",
+        "-Wthread-safety",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+}
diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp
index 7fff6ed..71e6b97 100644
--- a/services/gpuservice/gpustats/GpuStats.cpp
+++ b/services/gpuservice/gpustats/GpuStats.cpp
@@ -17,7 +17,7 @@
 #define LOG_TAG "GpuStats"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include "GpuStats.h"
+#include <gpustats/GpuStats.h>
 
 #include <cutils/properties.h>
 #include <log/log.h>
@@ -74,10 +74,11 @@
     }
 }
 
-void GpuStats::insert(const std::string& driverPackageName, const std::string& driverVersionName,
-                      uint64_t driverVersionCode, int64_t driverBuildTime,
-                      const std::string& appPackageName, const int32_t vulkanVersion,
-                      GpuStatsInfo::Driver driver, bool isDriverLoaded, int64_t driverLoadingTime) {
+void GpuStats::insertDriverStats(const std::string& driverPackageName,
+                                 const std::string& driverVersionName, uint64_t driverVersionCode,
+                                 int64_t driverBuildTime, const std::string& appPackageName,
+                                 const int32_t vulkanVersion, GpuStatsInfo::Driver driver,
+                                 bool isDriverLoaded, int64_t driverLoadingTime) {
     ATRACE_CALL();
 
     std::lock_guard<std::mutex> lock(mLock);
@@ -191,6 +192,11 @@
         dumpAll = false;
     }
 
+    if (dumpAll) {
+        dumpGlobalLocked(result);
+        dumpAppLocked(result);
+    }
+
     if (argsSet.count("--clear")) {
         bool clearAll = true;
 
@@ -208,13 +214,6 @@
             mGlobalStats.clear();
             mAppStats.clear();
         }
-
-        dumpAll = false;
-    }
-
-    if (dumpAll) {
-        dumpGlobalLocked(result);
-        dumpAppLocked(result);
     }
 }
 
diff --git a/services/gpuservice/gpustats/GpuStats.h b/services/gpuservice/gpustats/include/gpustats/GpuStats.h
similarity index 83%
rename from services/gpuservice/gpustats/GpuStats.h
rename to services/gpuservice/gpustats/include/gpustats/GpuStats.h
index 656b181..bcb9e0d 100644
--- a/services/gpuservice/gpustats/GpuStats.h
+++ b/services/gpuservice/gpustats/include/gpustats/GpuStats.h
@@ -16,15 +16,15 @@
 
 #pragma once
 
-#include <mutex>
-#include <unordered_map>
-#include <vector>
-
 #include <graphicsenv/GpuStatsInfo.h>
 #include <graphicsenv/GraphicsEnv.h>
 #include <utils/String16.h>
 #include <utils/Vector.h>
 
+#include <mutex>
+#include <unordered_map>
+#include <vector>
+
 namespace android {
 
 class GpuStats {
@@ -32,11 +32,12 @@
     GpuStats() = default;
     ~GpuStats() = default;
 
-    // Insert new gpu stats into global stats and app stats.
-    void insert(const std::string& driverPackageName, const std::string& driverVersionName,
-                uint64_t driverVersionCode, int64_t driverBuildTime,
-                const std::string& appPackageName, const int32_t vulkanVersion,
-                GpuStatsInfo::Driver driver, bool isDriverLoaded, int64_t driverLoadingTime);
+    // Insert new gpu driver stats into global stats and app stats.
+    void insertDriverStats(const std::string& driverPackageName,
+                           const std::string& driverVersionName, uint64_t driverVersionCode,
+                           int64_t driverBuildTime, const std::string& appPackageName,
+                           const int32_t vulkanVersion, GpuStatsInfo::Driver driver,
+                           bool isDriverLoaded, int64_t driverLoadingTime);
     // Insert target stats into app stats or potentially global stats as well.
     void insertTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
                            const GpuStatsInfo::Stats stats, const uint64_t value);
diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp
new file mode 100644
index 0000000..fee5bd4
--- /dev/null
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -0,0 +1,34 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test {
+    name: "gpuservice_unittest",
+    test_suites: ["device-tests"],
+    sanitize: {
+        address: true,
+    },
+    srcs: [
+        "GpuStatsTest.cpp",
+    ],
+    shared_libs: [
+        "libcutils",
+        "libgfxstats",
+        "libgraphicsenv",
+        "liblog",
+        "libutils",
+    ],
+    static_libs: [
+        "libgmock",
+    ],
+}
diff --git a/services/gpuservice/tests/unittests/AndroidTest.xml b/services/gpuservice/tests/unittests/AndroidTest.xml
new file mode 100644
index 0000000..66f51c7
--- /dev/null
+++ b/services/gpuservice/tests/unittests/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for gpuservice_unittest">
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="gpuservice_unittest->/data/local/tmp/gpuservice_unittest" />
+    </target_preparer>
+    <option name="test-suite-tag" value="apct" />
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="gpuservice_unittest" />
+    </test>
+</configuration>
diff --git a/services/gpuservice/tests/unittests/GpuStatsTest.cpp b/services/gpuservice/tests/unittests/GpuStatsTest.cpp
new file mode 100644
index 0000000..276a334
--- /dev/null
+++ b/services/gpuservice/tests/unittests/GpuStatsTest.cpp
@@ -0,0 +1,257 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "gpuservice_unittest"
+
+#include <cutils/properties.h>
+#include <gmock/gmock.h>
+#include <gpustats/GpuStats.h>
+#include <gtest/gtest.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+namespace android {
+namespace {
+
+using testing::HasSubstr;
+
+// clang-format off
+#define BUILTIN_DRIVER_PKG_NAME   "system"
+#define BUILTIN_DRIVER_VER_NAME   "0"
+#define BUILTIN_DRIVER_VER_CODE   0
+#define BUILTIN_DRIVER_BUILD_TIME 123
+#define UPDATED_DRIVER_PKG_NAME   "updated"
+#define UPDATED_DRIVER_VER_NAME   "1"
+#define UPDATED_DRIVER_VER_CODE   1
+#define UPDATED_DRIVER_BUILD_TIME 234
+#define VULKAN_VERSION            345
+#define CPU_VULKAN_VERSION        456
+#define OPENGLES_VERSION          567
+#define APP_PKG_NAME_1            "testapp1"
+#define APP_PKG_NAME_2            "testapp2"
+#define DRIVER_LOADING_TIME_1     678
+#define DRIVER_LOADING_TIME_2     789
+#define DRIVER_LOADING_TIME_3     891
+
+enum InputCommand : int32_t {
+    DUMP_ALL               = 0,
+    DUMP_GLOBAL            = 1,
+    DUMP_APP               = 2,
+    DUMP_ALL_THEN_CLEAR    = 3,
+    DUMP_GLOBAL_THEN_CLEAR = 4,
+    DUMP_APP_THEN_CLEAR    = 5,
+};
+// clang-format on
+
+class GpuStatsTest : public testing::Test {
+public:
+    GpuStatsTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+    }
+
+    ~GpuStatsTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+    }
+
+    std::string inputCommand(InputCommand cmd);
+
+    void SetUp() override {
+        property_set("ro.cpuvulkan.version", std::to_string(CPU_VULKAN_VERSION).c_str());
+        property_set("ro.opengles.version", std::to_string(OPENGLES_VERSION).c_str());
+        mCpuVulkanVersion = property_get_int32("ro.cpuvulkan.version", CPU_VULKAN_VERSION);
+        mGlesVersion = property_get_int32("ro.opengles.version", OPENGLES_VERSION);
+    }
+
+    std::unique_ptr<GpuStats> mGpuStats = std::make_unique<GpuStats>();
+    int32_t mCpuVulkanVersion = 0;
+    int32_t mGlesVersion = 0;
+};
+
+std::string GpuStatsTest::inputCommand(InputCommand cmd) {
+    std::string result;
+    Vector<String16> args;
+
+    switch (cmd) {
+        case InputCommand::DUMP_ALL:
+            break;
+        case InputCommand::DUMP_GLOBAL:
+            args.push_back(String16("--global"));
+            break;
+        case InputCommand::DUMP_APP:
+            args.push_back(String16("--app"));
+            break;
+        case InputCommand::DUMP_ALL_THEN_CLEAR:
+            args.push_back(String16("--clear"));
+            break;
+        case InputCommand::DUMP_GLOBAL_THEN_CLEAR:
+            args.push_back(String16("--global"));
+            args.push_back(String16("--clear"));
+            break;
+        case InputCommand::DUMP_APP_THEN_CLEAR:
+            args.push_back(String16("--app"));
+            args.push_back(String16("--clear"));
+            break;
+    }
+
+    mGpuStats->dump(args, &result);
+    return result;
+}
+
+TEST_F(GpuStatsTest, statsEmptyByDefault) {
+    ASSERT_TRUE(inputCommand(InputCommand::DUMP_ALL).empty());
+}
+
+TEST_F(GpuStatsTest, canInsertBuiltinDriverStats) {
+    mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+                                 BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+                                 DRIVER_LOADING_TIME_1);
+
+    std::string expectedResult = "driverPackageName = " + std::string(BUILTIN_DRIVER_PKG_NAME);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    expectedResult = "driverVersionName = " + std::string(BUILTIN_DRIVER_VER_NAME);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    expectedResult = "driverVersionCode = " + std::to_string(BUILTIN_DRIVER_VER_CODE);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    expectedResult = "driverBuildTime = " + std::to_string(BUILTIN_DRIVER_BUILD_TIME);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr("glLoadingCount = 1"));
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr("glLoadingFailureCount = 0"));
+    expectedResult = "appPackageName = " + std::string(APP_PKG_NAME_1);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+    expectedResult = "driverVersionCode = " + std::to_string(BUILTIN_DRIVER_VER_CODE);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+    expectedResult = "glDriverLoadingTime: " + std::to_string(DRIVER_LOADING_TIME_1);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+}
+
+TEST_F(GpuStatsTest, canInsertUpdatedDriverStats) {
+    mGpuStats->insertDriverStats(UPDATED_DRIVER_PKG_NAME, UPDATED_DRIVER_VER_NAME,
+                                 UPDATED_DRIVER_VER_CODE, UPDATED_DRIVER_BUILD_TIME, APP_PKG_NAME_2,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::VULKAN_UPDATED, false,
+                                 DRIVER_LOADING_TIME_2);
+
+    std::string expectedResult = "driverPackageName = " + std::string(UPDATED_DRIVER_PKG_NAME);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    expectedResult = "driverVersionName = " + std::string(UPDATED_DRIVER_VER_NAME);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    expectedResult = "driverVersionCode = " + std::to_string(UPDATED_DRIVER_VER_CODE);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    expectedResult = "driverBuildTime = " + std::to_string(UPDATED_DRIVER_BUILD_TIME);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr("vkLoadingCount = 1"));
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr("vkLoadingFailureCount = 1"));
+    expectedResult = "appPackageName = " + std::string(APP_PKG_NAME_2);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+    expectedResult = "driverVersionCode = " + std::to_string(UPDATED_DRIVER_VER_CODE);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+    expectedResult = "vkDriverLoadingTime: " + std::to_string(DRIVER_LOADING_TIME_2);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+}
+
+TEST_F(GpuStatsTest, canInsertAngleDriverStats) {
+    mGpuStats->insertDriverStats(UPDATED_DRIVER_PKG_NAME, UPDATED_DRIVER_VER_NAME,
+                                 UPDATED_DRIVER_VER_CODE, UPDATED_DRIVER_BUILD_TIME, APP_PKG_NAME_2,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::ANGLE, true,
+                                 DRIVER_LOADING_TIME_3);
+
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr("angleLoadingCount = 1"));
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr("angleLoadingFailureCount = 0"));
+    std::string expectedResult = "angleDriverLoadingTime: " + std::to_string(DRIVER_LOADING_TIME_3);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+}
+
+TEST_F(GpuStatsTest, canDump3dApiVersion) {
+    mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+                                 BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+                                 DRIVER_LOADING_TIME_1);
+
+    std::string expectedResult = "vulkanVersion = " + std::to_string(VULKAN_VERSION);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    expectedResult = "cpuVulkanVersion = " + std::to_string(mCpuVulkanVersion);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    expectedResult = "glesVersion = " + std::to_string(mGlesVersion);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+}
+
+TEST_F(GpuStatsTest, canNotInsertTargetStatsBeforeProperSetup) {
+    mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+                                 GpuStatsInfo::Stats::CPU_VULKAN_IN_USE, 0);
+    mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+                                 GpuStatsInfo::Stats::FALSE_PREROTATION, 0);
+    mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+                                 GpuStatsInfo::Stats::GLES_1_IN_USE, 0);
+
+    EXPECT_TRUE(inputCommand(InputCommand::DUMP_APP).empty());
+}
+
+TEST_F(GpuStatsTest, canInsertTargetStatsAfterProperSetup) {
+    mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+                                 BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+                                 DRIVER_LOADING_TIME_1);
+    mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+                                 GpuStatsInfo::Stats::CPU_VULKAN_IN_USE, 0);
+    mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+                                 GpuStatsInfo::Stats::FALSE_PREROTATION, 0);
+    mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+                                 GpuStatsInfo::Stats::GLES_1_IN_USE, 0);
+
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("cpuVulkanInUse = 1"));
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("falsePrerotation = 1"));
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("gles1InUse = 1"));
+}
+
+TEST_F(GpuStatsTest, canDumpAllBeforeClearAll) {
+    mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+                                 BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+                                 DRIVER_LOADING_TIME_1);
+
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_ALL_THEN_CLEAR).empty());
+    EXPECT_TRUE(inputCommand(InputCommand::DUMP_ALL).empty());
+}
+
+TEST_F(GpuStatsTest, canDumpGlobalBeforeClearGlobal) {
+    mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+                                 BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+                                 DRIVER_LOADING_TIME_1);
+
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_GLOBAL_THEN_CLEAR).empty());
+    EXPECT_TRUE(inputCommand(InputCommand::DUMP_GLOBAL).empty());
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_APP).empty());
+}
+
+TEST_F(GpuStatsTest, canDumpAppBeforeClearApp) {
+    mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+                                 BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+                                 DRIVER_LOADING_TIME_1);
+
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_APP_THEN_CLEAR).empty());
+    EXPECT_TRUE(inputCommand(InputCommand::DUMP_APP).empty());
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_GLOBAL).empty());
+}
+
+} // namespace
+} // namespace android
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index f6b5935..308e93a 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -83,6 +83,7 @@
     srcs: [
         "InputListener.cpp",
         "InputReaderBase.cpp",
+        "InputThread.cpp",
     ],
 
     shared_libs: [
diff --git a/services/inputflinger/InputThread.cpp b/services/inputflinger/InputThread.cpp
new file mode 100644
index 0000000..b87f7a1
--- /dev/null
+++ b/services/inputflinger/InputThread.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InputThread.h"
+
+namespace android {
+
+namespace {
+
+// Implementation of Thread from libutils.
+class InputThreadImpl : public Thread {
+public:
+    explicit InputThreadImpl(std::function<void()> loop)
+          : Thread(/* canCallJava */ true), mThreadLoop(loop) {}
+
+    ~InputThreadImpl() {}
+
+private:
+    std::function<void()> mThreadLoop;
+
+    bool threadLoop() override {
+        mThreadLoop();
+        return true;
+    }
+};
+
+} // namespace
+
+InputThread::InputThread(std::string name, std::function<void()> loop, std::function<void()> wake)
+      : mName(name), mThreadWake(wake) {
+    mThread = new InputThreadImpl(loop);
+    mThread->run(mName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY);
+}
+
+InputThread::~InputThread() {
+    mThread->requestExit();
+    if (mThreadWake) {
+        mThreadWake();
+    }
+    mThread->requestExitAndWait();
+}
+
+bool InputThread::isCallingThread() {
+    return gettid() == mThread->getTid();
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 246e735..9a6ef21 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -203,9 +203,10 @@
     const nsecs_t currentTime = now();
 
     MotionEvent event;
-    event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+    event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_DOWN, /* actionButton */ 0, /* flags */ 0,
                      /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
+                     1 /* xScale */, 1 /* yScale */,
                      /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0,
                      /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
                      AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, currentTime,
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 30fdf90..b2b5145 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -325,24 +325,6 @@
     return dispatchEntry;
 }
 
-// --- InputDispatcherThread ---
-
-class InputDispatcher::InputDispatcherThread : public Thread {
-public:
-    explicit InputDispatcherThread(InputDispatcher* dispatcher)
-          : Thread(/* canCallJava */ true), mDispatcher(dispatcher) {}
-
-    ~InputDispatcherThread() {}
-
-private:
-    InputDispatcher* mDispatcher;
-
-    virtual bool threadLoop() override {
-        mDispatcher->dispatchOnce();
-        return true;
-    }
-};
-
 // --- InputDispatcher ---
 
 InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
@@ -367,8 +349,6 @@
     mKeyRepeatState.lastKeyEntry = nullptr;
 
     policy->getDispatcherConfiguration(&mConfig);
-
-    mThread = new InputDispatcherThread(this);
 }
 
 InputDispatcher::~InputDispatcher() {
@@ -387,25 +367,21 @@
 }
 
 status_t InputDispatcher::start() {
-    if (mThread->isRunning()) {
+    if (mThread) {
         return ALREADY_EXISTS;
     }
-    return mThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
+    mThread = std::make_unique<InputThread>(
+            "InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });
+    return OK;
 }
 
 status_t InputDispatcher::stop() {
-    if (!mThread->isRunning()) {
-        return OK;
-    }
-    if (gettid() == mThread->getTid()) {
-        ALOGE("InputDispatcher can only be stopped from outside of the InputDispatcherThread!");
+    if (mThread && mThread->isCallingThread()) {
+        ALOGE("InputDispatcher cannot be stopped from its own thread!");
         return INVALID_OPERATION;
     }
-    // Directly calling requestExitAndWait() causes the thread to not exit
-    // if mLooper is waiting for a long timeout.
-    mThread->requestExit();
-    mLooper->wake();
-    return mThread->requestExitAndWait();
+    mThread.reset();
+    return OK;
 }
 
 void InputDispatcher::dispatchOnce() {
@@ -2410,7 +2386,7 @@
                 status = connection->inputPublisher
                                  .publishKeyEvent(dispatchEntry->seq, keyEntry->deviceId,
                                                   keyEntry->source, keyEntry->displayId,
-                                                  dispatchEntry->resolvedAction,
+                                                  INVALID_HMAC, dispatchEntry->resolvedAction,
                                                   dispatchEntry->resolvedFlags, keyEntry->keyCode,
                                                   keyEntry->scanCode, keyEntry->metaState,
                                                   keyEntry->repeatCount, keyEntry->downTime,
@@ -2424,26 +2400,28 @@
                 PointerCoords scaledCoords[MAX_POINTERS];
                 const PointerCoords* usingCoords = motionEntry->pointerCoords;
 
-                // Set the X and Y offset depending on the input source.
-                float xOffset, yOffset;
+                // Set the X and Y offset and X and Y scale depending on the input source.
+                float xOffset = 0.0f, yOffset = 0.0f;
+                float xScale = 1.0f, yScale = 1.0f;
                 if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) &&
                     !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
                     float globalScaleFactor = dispatchEntry->globalScaleFactor;
-                    float wxs = dispatchEntry->windowXScale;
-                    float wys = dispatchEntry->windowYScale;
-                    xOffset = dispatchEntry->xOffset * wxs;
-                    yOffset = dispatchEntry->yOffset * wys;
-                    if (wxs != 1.0f || wys != 1.0f || globalScaleFactor != 1.0f) {
+                    xScale = dispatchEntry->windowXScale;
+                    yScale = dispatchEntry->windowYScale;
+                    xOffset = dispatchEntry->xOffset * xScale;
+                    yOffset = dispatchEntry->yOffset * yScale;
+                    if (globalScaleFactor != 1.0f) {
                         for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
                             scaledCoords[i] = motionEntry->pointerCoords[i];
-                            scaledCoords[i].scale(globalScaleFactor, wxs, wys);
+                            // Don't apply window scale here since we don't want scale to affect raw
+                            // coordinates. The scale will be sent back to the client and applied
+                            // later when requesting relative coordinates.
+                            scaledCoords[i].scale(globalScaleFactor, 1 /* windowXScale */,
+                                                  1 /* windowYScale */);
                         }
                         usingCoords = scaledCoords;
                     }
                 } else {
-                    xOffset = 0.0f;
-                    yOffset = 0.0f;
-
                     // We don't want the dispatch target to know.
                     if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) {
                         for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
@@ -2457,13 +2435,13 @@
                 status = connection->inputPublisher
                                  .publishMotionEvent(dispatchEntry->seq, motionEntry->deviceId,
                                                      motionEntry->source, motionEntry->displayId,
-                                                     dispatchEntry->resolvedAction,
+                                                     INVALID_HMAC, dispatchEntry->resolvedAction,
                                                      motionEntry->actionButton,
                                                      dispatchEntry->resolvedFlags,
                                                      motionEntry->edgeFlags, motionEntry->metaState,
                                                      motionEntry->buttonState,
-                                                     motionEntry->classification, xOffset, yOffset,
-                                                     motionEntry->xPrecision,
+                                                     motionEntry->classification, xScale, yScale,
+                                                     xOffset, yOffset, motionEntry->xPrecision,
                                                      motionEntry->yPrecision,
                                                      motionEntry->xCursorPosition,
                                                      motionEntry->yCursorPosition,
@@ -2699,59 +2677,59 @@
     std::vector<EventEntry*> cancelationEvents =
             connection->inputState.synthesizeCancelationEvents(currentTime, options);
 
-    if (!cancelationEvents.empty()) {
+    if (cancelationEvents.empty()) {
+        return;
+    }
 #if DEBUG_OUTBOUND_EVENT_DETAILS
-        ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync "
-              "with reality: %s, mode=%d.",
-              connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason,
-              options.mode);
+    ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync "
+          "with reality: %s, mode=%d.",
+          connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason,
+          options.mode);
 #endif
-        for (size_t i = 0; i < cancelationEvents.size(); i++) {
-            EventEntry* cancelationEventEntry = cancelationEvents[i];
-            switch (cancelationEventEntry->type) {
-                case EventEntry::Type::KEY: {
-                    logOutboundKeyDetails("cancel - ",
-                                          static_cast<const KeyEntry&>(*cancelationEventEntry));
-                    break;
-                }
-                case EventEntry::Type::MOTION: {
-                    logOutboundMotionDetails("cancel - ",
-                                             static_cast<const MotionEntry&>(
-                                                     *cancelationEventEntry));
-                    break;
-                }
-                case EventEntry::Type::FOCUS: {
-                    LOG_ALWAYS_FATAL("Canceling focus events is not supported");
-                    break;
-                }
-                case EventEntry::Type::CONFIGURATION_CHANGED:
-                case EventEntry::Type::DEVICE_RESET: {
-                    LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
-                                     EventEntry::typeToString(cancelationEventEntry->type));
-                    break;
-                }
+    for (size_t i = 0; i < cancelationEvents.size(); i++) {
+        EventEntry* cancelationEventEntry = cancelationEvents[i];
+        switch (cancelationEventEntry->type) {
+            case EventEntry::Type::KEY: {
+                logOutboundKeyDetails("cancel - ",
+                                      static_cast<const KeyEntry&>(*cancelationEventEntry));
+                break;
             }
-
-            InputTarget target;
-            sp<InputWindowHandle> windowHandle =
-                    getWindowHandleLocked(connection->inputChannel->getConnectionToken());
-            if (windowHandle != nullptr) {
-                const InputWindowInfo* windowInfo = windowHandle->getInfo();
-                target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop,
-                                             windowInfo->windowXScale, windowInfo->windowYScale);
-                target.globalScaleFactor = windowInfo->globalScaleFactor;
+            case EventEntry::Type::MOTION: {
+                logOutboundMotionDetails("cancel - ",
+                                         static_cast<const MotionEntry&>(*cancelationEventEntry));
+                break;
             }
-            target.inputChannel = connection->inputChannel;
-            target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
-
-            enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
-                                       target, InputTarget::FLAG_DISPATCH_AS_IS);
-
-            cancelationEventEntry->release();
+            case EventEntry::Type::FOCUS: {
+                LOG_ALWAYS_FATAL("Canceling focus events is not supported");
+                break;
+            }
+            case EventEntry::Type::CONFIGURATION_CHANGED:
+            case EventEntry::Type::DEVICE_RESET: {
+                LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
+                                 EventEntry::typeToString(cancelationEventEntry->type));
+                break;
+            }
         }
 
-        startDispatchCycleLocked(currentTime, connection);
+        InputTarget target;
+        sp<InputWindowHandle> windowHandle =
+                getWindowHandleLocked(connection->inputChannel->getConnectionToken());
+        if (windowHandle != nullptr) {
+            const InputWindowInfo* windowInfo = windowHandle->getInfo();
+            target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop,
+                                         windowInfo->windowXScale, windowInfo->windowYScale);
+            target.globalScaleFactor = windowInfo->globalScaleFactor;
+        }
+        target.inputChannel = connection->inputChannel;
+        target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+
+        enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
+                                   target, InputTarget::FLAG_DISPATCH_AS_IS);
+
+        cancelationEventEntry->release();
     }
+
+    startDispatchCycleLocked(currentTime, connection);
 }
 
 MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry& originalMotionEntry,
@@ -2930,8 +2908,9 @@
     accelerateMetaShortcuts(args->deviceId, args->action, keyCode, metaState);
 
     KeyEvent event;
-    event.initialize(args->deviceId, args->source, args->displayId, args->action, flags, keyCode,
-                     args->scanCode, metaState, repeatCount, args->downTime, args->eventTime);
+    event.initialize(args->deviceId, args->source, args->displayId, INVALID_HMAC, args->action,
+                     flags, keyCode, args->scanCode, metaState, repeatCount, args->downTime,
+                     args->eventTime);
 
     android::base::Timer t;
     mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
@@ -3024,9 +3003,10 @@
             mLock.unlock();
 
             MotionEvent event;
-            event.initialize(args->deviceId, args->source, args->displayId, args->action,
-                             args->actionButton, args->flags, args->edgeFlags, args->metaState,
-                             args->buttonState, args->classification, 0, 0, args->xPrecision,
+            event.initialize(args->deviceId, args->source, args->displayId, INVALID_HMAC,
+                             args->action, args->actionButton, args->flags, args->edgeFlags,
+                             args->metaState, args->buttonState, args->classification, 1 /*xScale*/,
+                             1 /*yScale*/, 0 /* xOffset */, 0 /* yOffset */, args->xPrecision,
                              args->yPrecision, args->xCursorPosition, args->yCursorPosition,
                              args->downTime, args->eventTime, args->pointerCount,
                              args->pointerProperties, args->pointerCoords);
@@ -3126,7 +3106,7 @@
             accelerateMetaShortcuts(keyEvent.getDeviceId(), action,
                                     /*byref*/ keyCode, /*byref*/ metaState);
             keyEvent.initialize(keyEvent.getDeviceId(), keyEvent.getSource(),
-                                keyEvent.getDisplayId(), action, flags, keyCode,
+                                keyEvent.getDisplayId(), INVALID_HMAC, action, flags, keyCode,
                                 keyEvent.getScanCode(), metaState, keyEvent.getRepeatCount(),
                                 keyEvent.getDownTime(), keyEvent.getEventTime());
 
@@ -4682,8 +4662,8 @@
 
 KeyEvent InputDispatcher::createKeyEvent(const KeyEntry& entry) {
     KeyEvent event;
-    event.initialize(entry.deviceId, entry.source, entry.displayId, entry.action, entry.flags,
-                     entry.keyCode, entry.scanCode, entry.metaState, entry.repeatCount,
+    event.initialize(entry.deviceId, entry.source, entry.displayId, INVALID_HMAC, entry.action,
+                     entry.flags, entry.keyCode, entry.scanCode, entry.metaState, entry.repeatCount,
                      entry.downTime, entry.eventTime);
     return event;
 }
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index a4ba0de..93de18d 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -25,6 +25,7 @@
 #include "InputDispatcherPolicyInterface.h"
 #include "InputState.h"
 #include "InputTarget.h"
+#include "InputThread.h"
 #include "Monitor.h"
 #include "TouchState.h"
 #include "TouchedWindow.h"
@@ -124,8 +125,7 @@
         STALE,
     };
 
-    class InputDispatcherThread;
-    sp<InputDispatcherThread> mThread;
+    std::unique_ptr<InputThread> mThread;
 
     sp<InputDispatcherPolicyInterface> mPolicy;
     android::InputDispatcherConfiguration mConfig;
diff --git a/services/inputflinger/include/InputThread.h b/services/inputflinger/include/InputThread.h
new file mode 100644
index 0000000..407365a
--- /dev/null
+++ b/services/inputflinger/include/InputThread.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_THREAD_H
+#define _UI_INPUT_THREAD_H
+
+#include <utils/Thread.h>
+
+namespace android {
+
+/* A thread that loops continuously until destructed to process input events.
+ *
+ * Creating the InputThread starts it immediately. The thread begins looping the loop
+ * function until the InputThread is destroyed. The wake function is used to wake anything
+ * that sleeps in the loop when it is time for the thread to be destroyed.
+ */
+class InputThread {
+public:
+    explicit InputThread(std::string name, std::function<void()> loop,
+                         std::function<void()> wake = nullptr);
+    virtual ~InputThread();
+
+    bool isCallingThread();
+
+private:
+    std::string mName;
+    std::function<void()> mThreadWake;
+    sp<Thread> mThread;
+};
+
+} // namespace android
+
+#endif // _UI_INPUT_THREAD_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 05f0db1..2023c6e 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -49,25 +49,6 @@
 
 namespace android {
 
-// --- InputReader::InputReaderThread ---
-
-/* Thread that reads raw events from the event hub and processes them, endlessly. */
-class InputReader::InputReaderThread : public Thread {
-public:
-    explicit InputReaderThread(InputReader* reader)
-          : Thread(/* canCallJava */ true), mReader(reader) {}
-
-    ~InputReaderThread() {}
-
-private:
-    InputReader* mReader;
-
-    bool threadLoop() override {
-        mReader->loopOnce();
-        return true;
-    }
-};
-
 // --- InputReader ---
 
 InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
@@ -83,7 +64,6 @@
         mNextTimeout(LLONG_MAX),
         mConfigurationChangesToRefresh(0) {
     mQueuedListener = new QueuedInputListener(listener);
-    mThread = new InputReaderThread(this);
 
     { // acquire lock
         AutoMutex _l(mLock);
@@ -100,25 +80,21 @@
 }
 
 status_t InputReader::start() {
-    if (mThread->isRunning()) {
+    if (mThread) {
         return ALREADY_EXISTS;
     }
-    return mThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
+    mThread = std::make_unique<InputThread>(
+            "InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
+    return OK;
 }
 
 status_t InputReader::stop() {
-    if (!mThread->isRunning()) {
-        return OK;
-    }
-    if (gettid() == mThread->getTid()) {
-        ALOGE("InputReader can only be stopped from outside of the InputReaderThread!");
+    if (mThread && mThread->isCallingThread()) {
+        ALOGE("InputReader cannot be stopped from its own thread!");
         return INVALID_OPERATION;
     }
-    // Directly calling requestExitAndWait() causes the thread to not exit
-    // if mEventHub is waiting for a long timeout.
-    mThread->requestExit();
-    mEventHub->wake();
-    return mThread->requestExitAndWait();
+    mThread.reset();
+    return OK;
 }
 
 void InputReader::loopOnce() {
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 5024906..02957cd 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -21,6 +21,7 @@
 #include "InputListener.h"
 #include "InputReaderBase.h"
 #include "InputReaderContext.h"
+#include "InputThread.h"
 
 #include <utils/Condition.h>
 #include <utils/Mutex.h>
@@ -116,8 +117,7 @@
     friend class ContextImpl;
 
 private:
-    class InputReaderThread;
-    sp<InputReaderThread> mThread;
+    std::unique_ptr<InputThread> mThread;
 
     Mutex mLock;
 
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 98ebf50..094452a 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -225,18 +225,18 @@
     KeyEvent event;
 
     // Rejects undefined key actions.
-    event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
-            /*action*/ -1, 0,
-            AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME);
+    event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC,
+                     /*action*/ -1, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME,
+                     ARBITRARY_TIME);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
             &event,
             INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject key events with undefined action.";
 
     // Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API.
-    event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
-            AKEY_EVENT_ACTION_MULTIPLE, 0,
-            AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME);
+    event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC,
+                     AKEY_EVENT_ACTION_MULTIPLE, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0,
+                     ARBITRARY_TIME, ARBITRARY_TIME);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
             &event,
             INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
@@ -260,10 +260,10 @@
     constexpr MotionClassification classification = MotionClassification::NONE;
 
     // Rejects undefined motion actions.
-    event.initialize(DEVICE_ID, source, DISPLAY_ID,
-                     /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     ARBITRARY_TIME, ARBITRARY_TIME,
+    event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
+                     /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */,
+                     1 /* yScale */, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
             &event,
@@ -271,24 +271,24 @@
             << "Should reject motion events with undefined action.";
 
     // Rejects pointer down with invalid index.
-    event.initialize(DEVICE_ID, source, DISPLAY_ID,
+    event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_POINTER_DOWN |
                              (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-                     0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     ARBITRARY_TIME, ARBITRARY_TIME,
+                     0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
+                     0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
             &event,
             INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject motion events with pointer down index too large.";
 
-    event.initialize(DEVICE_ID, source, DISPLAY_ID,
+    event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_POINTER_DOWN |
                              (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-                     0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     ARBITRARY_TIME, ARBITRARY_TIME,
+                     0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
+                     0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
             &event,
@@ -296,24 +296,24 @@
             << "Should reject motion events with pointer down index too small.";
 
     // Rejects pointer up with invalid index.
-    event.initialize(DEVICE_ID, source, DISPLAY_ID,
+    event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_POINTER_UP |
                              (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-                     0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     ARBITRARY_TIME, ARBITRARY_TIME,
+                     0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
+                     0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
             &event,
             INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject motion events with pointer up index too large.";
 
-    event.initialize(DEVICE_ID, source, DISPLAY_ID,
+    event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_POINTER_UP |
                              (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-                     0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     ARBITRARY_TIME, ARBITRARY_TIME,
+                     0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
+                     0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
             &event,
@@ -321,20 +321,20 @@
             << "Should reject motion events with pointer up index too small.";
 
     // Rejects motion events with invalid number of pointers.
-    event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags,
-                     metaState, 0, classification, 0, 0, 0, 0,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     ARBITRARY_TIME, ARBITRARY_TIME,
+    event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
+                     edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0,
+                     0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 0, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
             &event,
             INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject motion events with 0 pointers.";
 
-    event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags,
-                     metaState, 0, classification, 0, 0, 0, 0,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     ARBITRARY_TIME, ARBITRARY_TIME,
+    event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
+                     edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0,
+                     0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
             &event,
@@ -343,10 +343,10 @@
 
     // Rejects motion events with invalid pointer ids.
     pointerProperties[0].id = -1;
-    event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags,
-                     metaState, 0, classification, 0, 0, 0, 0,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     ARBITRARY_TIME, ARBITRARY_TIME,
+    event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
+                     edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0,
+                     0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
             &event,
@@ -354,10 +354,10 @@
             << "Should reject motion events with pointer ids less than 0.";
 
     pointerProperties[0].id = MAX_POINTER_ID + 1;
-    event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags,
-                     metaState, 0, classification, 0, 0, 0, 0,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     ARBITRARY_TIME, ARBITRARY_TIME,
+    event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
+                     edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0,
+                     0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
             &event,
@@ -367,10 +367,10 @@
     // Rejects motion events with duplicate pointer ids.
     pointerProperties[0].id = 1;
     pointerProperties[1].id = 1;
-    event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags,
-                     metaState, 0, classification, 0, 0, 0, 0,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     ARBITRARY_TIME, ARBITRARY_TIME,
+    event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
+                     edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0,
+                     0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 2, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
             &event,
@@ -645,9 +645,9 @@
     nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // Define a valid key down event.
-    event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId,
-            AKEY_EVENT_ACTION_DOWN, /* flags */ 0,
-            AKEYCODE_A, KEY_A, AMETA_NONE, /* repeatCount */ 0, currentTime, currentTime);
+    event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId, INVALID_HMAC,
+                     AKEY_EVENT_ACTION_DOWN, /* flags */ 0, AKEYCODE_A, KEY_A, AMETA_NONE,
+                     /* repeatCount */ 0, currentTime, currentTime);
 
     // Inject event until dispatch out.
     return dispatcher->injectInputEvent(
@@ -674,10 +674,12 @@
 
     nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
     // Define a valid motion down event.
-    event.initialize(DEVICE_ID, source, displayId, action, /* actionButton */ 0, /* flags */ 0,
+    event.initialize(DEVICE_ID, source, displayId, INVALID_HMAC, action, /* actionButton */ 0,
+                     /* flags */ 0,
                      /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
-                     /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0,
-                     /* yPrecision */ 0, xCursorPosition, yCursorPosition, currentTime, currentTime,
+                     /* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0,
+                     /* xPrecision */ 0, /* yPrecision */ 0, xCursorPosition, yCursorPosition,
+                     currentTime, currentTime,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
 
     // Inject event until dispatch out.
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 1287393..3cc803e 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -116,23 +116,24 @@
              "relative to expectedPresent %" PRId64,
              getDebugName(), addedTime, expectedPresentTime);
 
+    if (!isPlausible) {
+        mFlinger->mTimeStats->incrementBadDesiredPresent(getSequence());
+    }
+
     const bool isDue = addedTime < expectedPresentTime;
     return isDue || !isPlausible;
 }
 
-bool BufferQueueLayer::setFrameRate(float frameRate) {
+bool BufferQueueLayer::setFrameRate(FrameRate frameRate) {
     float oldFrameRate = 0.f;
     status_t result = mConsumer->getFrameRate(&oldFrameRate);
-    bool frameRateChanged = result < 0 || frameRate != oldFrameRate;
-    mConsumer->setFrameRate(frameRate);
+    bool frameRateChanged = result < 0 || frameRate.rate != oldFrameRate;
+    mConsumer->setFrameRate(frameRate.rate);
     return frameRateChanged;
 }
 
-std::optional<float> BufferQueueLayer::getFrameRate() const {
-    if (mLatchedFrameRate > 0.f || mLatchedFrameRate == FRAME_RATE_NO_VOTE)
-        return mLatchedFrameRate;
-
-    return {};
+Layer::FrameRate BufferQueueLayer::getFrameRate() const {
+    return FrameRate(mLatchedFrameRate, Layer::FrameRateCompatibility::Default);
 }
 
 // -----------------------------------------------------------------------
@@ -156,7 +157,14 @@
         // able to be latched. To avoid this, grab this buffer anyway.
         return true;
     }
-    return mQueueItems[0].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+    const bool fenceSignaled =
+            mQueueItems[0].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+    if (!fenceSignaled) {
+        mFlinger->mTimeStats->incrementLatchSkipped(getSequence(),
+                                                    TimeStats::LatchSkipReason::LateAcquire);
+    }
+
+    return fenceSignaled;
 }
 
 bool BufferQueueLayer::framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const {
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 486c1bc..626af4b 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -56,8 +56,8 @@
 
     bool shouldPresentNow(nsecs_t expectedPresentTime) const override;
 
-    bool setFrameRate(float frameRate) override;
-    std::optional<float> getFrameRate() const override;
+    bool setFrameRate(FrameRate frameRate) override;
+    FrameRate getFrameRate() const override;
 
     // -----------------------------------------------------------------------
 
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 664c199..29688da 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -97,6 +97,8 @@
         }
     }
 
+    mPreviousReleaseFence = releaseFence;
+
     // Prevent tracing the same release multiple times.
     if (mPreviousFrameNumber != mPreviousReleasedFrameNumber) {
         mFlinger->mFrameTracer->traceFence(getSequence(), mPreviousBufferId, mPreviousFrameNumber,
@@ -110,7 +112,7 @@
     mTransformHint = orientation;
 }
 
-void BufferStateLayer::releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) {
+void BufferStateLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
     for (const auto& handle : mDrawingState.callbackHandles) {
         handle->transformHint = mTransformHint;
     }
@@ -119,6 +121,16 @@
             mDrawingState.callbackHandles);
 
     mDrawingState.callbackHandles = {};
+
+    const sp<Fence>& releaseFence(mPreviousReleaseFence);
+    std::shared_ptr<FenceTime> releaseFenceTime = std::make_shared<FenceTime>(releaseFence);
+    {
+        Mutex::Autolock lock(mFrameEventHistoryMutex);
+        if (mPreviousFrameNumber != 0) {
+            mFrameEventHistory.addRelease(mPreviousFrameNumber, dequeueReadyTime,
+                                          std::move(releaseFenceTime));
+        }
+    }
 }
 
 bool BufferStateLayer::shouldPresentNow(nsecs_t /*expectedPresentTime*/) const {
@@ -232,13 +244,26 @@
     return true;
 }
 
-bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, nsecs_t postTime,
-                                 nsecs_t desiredPresentTime, const client_cache_t& clientCacheId) {
+bool BufferStateLayer::updateFrameEventHistory(const sp<Fence>& acquireFence, nsecs_t postedTime,
+                                               nsecs_t desiredPresentTime) {
+    Mutex::Autolock lock(mFrameEventHistoryMutex);
+    mAcquireTimeline.updateSignalTimes();
+    std::shared_ptr<FenceTime> acquireFenceTime =
+            std::make_shared<FenceTime>((acquireFence ? acquireFence : Fence::NO_FENCE));
+    NewFrameEventsEntry newTimestamps = {mCurrentState.frameNumber, postedTime, desiredPresentTime,
+                                         acquireFenceTime};
+    mFrameEventHistory.addQueue(newTimestamps);
+    return true;
+}
+
+bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence,
+                                 nsecs_t postTime, nsecs_t desiredPresentTime,
+                                 const client_cache_t& clientCacheId) {
     if (mCurrentState.buffer) {
         mReleasePreviousBuffer = true;
     }
 
-    mFrameCounter++;
+    mCurrentState.frameNumber++;
 
     mCurrentState.buffer = buffer;
     mCurrentState.clientCacheId = clientCacheId;
@@ -246,15 +271,17 @@
     setTransactionFlags(eTransactionNeeded);
 
     const int32_t layerId = getSequence();
-    mFlinger->mTimeStats->setPostTime(layerId, mFrameNumber, getName().c_str(), postTime);
+    mFlinger->mTimeStats->setPostTime(layerId, mCurrentState.frameNumber, getName().c_str(),
+                                      postTime);
     mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
-    mFlinger->mFrameTracer->traceTimestamp(layerId, buffer->getId(), mFrameNumber, postTime,
-                                           FrameTracer::FrameEvent::POST);
+    mFlinger->mFrameTracer->traceTimestamp(layerId, buffer->getId(), mCurrentState.frameNumber,
+                                           postTime, FrameTracer::FrameEvent::POST);
     mCurrentState.desiredPresentTime = desiredPresentTime;
 
     mFlinger->mScheduler->recordLayerHistory(this,
                                              desiredPresentTime <= 0 ? 0 : desiredPresentTime);
 
+    updateFrameEventHistory(acquireFence, postTime, desiredPresentTime);
     return true;
 }
 
@@ -401,7 +428,14 @@
         return true;
     }
 
-    return getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled;
+    const bool fenceSignaled =
+            getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled;
+    if (!fenceSignaled) {
+        mFlinger->mTimeStats->incrementLatchSkipped(getSequence(),
+                                                    TimeStats::LatchSkipReason::LateAcquire);
+    }
+
+    return fenceSignaled;
 }
 
 bool BufferStateLayer::framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const {
@@ -413,7 +447,7 @@
 }
 
 uint64_t BufferStateLayer::getFrameNumber(nsecs_t /*expectedPresentTime*/) const {
-    return mFrameNumber;
+    return mDrawingState.frameNumber;
 }
 
 bool BufferStateLayer::getAutoRefresh() const {
@@ -489,7 +523,7 @@
         ALOGE("[%s] rejecting buffer: "
               "bufferWidth=%d, bufferHeight=%d, front.active.{w=%d, h=%d}",
               getDebugName(), bufferWidth, bufferHeight, s.active.w, s.active.h);
-        mFlinger->mTimeStats->removeTimeRecord(layerId, mFrameNumber);
+        mFlinger->mTimeStats->removeTimeRecord(layerId, mDrawingState.frameNumber);
         return BAD_VALUE;
     }
 
@@ -497,8 +531,6 @@
         handle->latchTime = latchTime;
     }
 
-    mFrameNumber = mFrameCounter;
-
     if (!SyncFeatures::getInstance().useNativeFenceSync()) {
         // Bind the new buffer to the GL texture.
         //
@@ -515,11 +547,13 @@
     }
 
     const uint64_t bufferID = getCurrentBufferId();
-    mFlinger->mTimeStats->setAcquireFence(layerId, mFrameNumber, mBufferInfo.mFenceTime);
-    mFlinger->mFrameTracer->traceFence(layerId, bufferID, mFrameNumber, mBufferInfo.mFenceTime,
+    mFlinger->mTimeStats->setAcquireFence(layerId, mDrawingState.frameNumber,
+                                          mBufferInfo.mFenceTime);
+    mFlinger->mFrameTracer->traceFence(layerId, bufferID, mDrawingState.frameNumber,
+                                       mBufferInfo.mFenceTime,
                                        FrameTracer::FrameEvent::ACQUIRE_FENCE);
-    mFlinger->mTimeStats->setLatchTime(layerId, mFrameNumber, latchTime);
-    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferID, mFrameNumber, latchTime,
+    mFlinger->mTimeStats->setLatchTime(layerId, mDrawingState.frameNumber, latchTime);
+    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferID, mDrawingState.frameNumber, latchTime,
                                            FrameTracer::FrameEvent::LATCH);
 
     mCurrentStateModified = false;
@@ -542,10 +576,14 @@
     return NO_ERROR;
 }
 
-status_t BufferStateLayer::updateFrameNumber(nsecs_t /*latchTime*/) {
+status_t BufferStateLayer::updateFrameNumber(nsecs_t latchTime) {
     // TODO(marissaw): support frame history events
     mPreviousFrameNumber = mCurrentFrameNumber;
-    mCurrentFrameNumber = mFrameNumber;
+    mCurrentFrameNumber = mDrawingState.frameNumber;
+    {
+        Mutex::Autolock lock(mFrameEventHistoryMutex);
+        mFrameEventHistory.addLatch(mCurrentFrameNumber, latchTime);
+    }
     return NO_ERROR;
 }
 
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 020c1b3..57ff8bc 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -68,8 +68,8 @@
     bool setTransformToDisplayInverse(bool transformToDisplayInverse) override;
     bool setCrop(const Rect& crop) override;
     bool setFrame(const Rect& frame) override;
-    bool setBuffer(const sp<GraphicBuffer>& buffer, nsecs_t postTime, nsecs_t desiredPresentTime,
-                   const client_cache_t& clientCacheId) override;
+    bool setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence, nsecs_t postTime,
+                   nsecs_t desiredPresentTime, const client_cache_t& clientCacheId) override;
     bool setAcquireFence(const sp<Fence>& fence) override;
     bool setDataspace(ui::Dataspace dataspace) override;
     bool setHdrMetadata(const HdrMetadata& hdrMetadata) override;
@@ -109,6 +109,9 @@
     void gatherBufferInfo() override;
 
 private:
+    bool updateFrameEventHistory(const sp<Fence>& acquireFence, nsecs_t postedTime,
+                                 nsecs_t requestedPresentTime);
+
     uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override;
 
     bool getAutoRefresh() const override;
diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp
index 6a81817..83050c4 100644
--- a/services/surfaceflinger/ColorLayer.cpp
+++ b/services/surfaceflinger/ColorLayer.cpp
@@ -58,7 +58,7 @@
 }
 
 bool ColorLayer::isVisible() const {
-    return !isHiddenByPolicy() && getAlpha() > 0.0f;
+    return !isHiddenByPolicy() && getAlpha() > 0.0_hf;
 }
 
 bool ColorLayer::setColor(const half3& color) {
@@ -109,7 +109,9 @@
 }
 
 bool ColorLayer::isOpaque(const Layer::State& s) const {
-    return (s.flags & layer_state_t::eLayerOpaque) != 0;
+    // Consider the layer to be opaque if its opaque flag is set or its effective
+    // alpha (considering the alpha of its parents as well) is 1.0;
+    return (s.flags & layer_state_t::eLayerOpaque) != 0 || getAlpha() == 1.0_hf;
 }
 
 ui::Dataspace ColorLayer::getDataSpace() const {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index c04aff5..9622e78 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -162,7 +162,8 @@
 
     // Sets the projection state to use
     virtual void setProjection(const ui::Transform&, uint32_t orientation, const Rect& frame,
-                               const Rect& viewport, const Rect& scissor, bool needsFiltering) = 0;
+                               const Rect& viewport, const Rect& sourceClip,
+                               const Rect& destinationClip, bool needsFiltering) = 0;
     // Sets the bounds to use
     virtual void setBounds(const ui::Size&) = 0;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index cc31e5f..d41337c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -39,7 +39,8 @@
     std::optional<DisplayId> getDisplayId() const override;
     void setCompositionEnabled(bool) override;
     void setProjection(const ui::Transform&, uint32_t orientation, const Rect& frame,
-                       const Rect& viewport, const Rect& scissor, bool needsFiltering) override;
+                       const Rect& viewport, const Rect& sourceClip, const Rect& destinationClip,
+                       bool needsFiltering) override;
     void setBounds(const ui::Size&) override;
     void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index e700b76..66ed2b6 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -79,8 +79,11 @@
     // The logical space user viewport rectangle
     Rect viewport;
 
-    // The physical space scissor rectangle
-    Rect scissor;
+    // The physical space source clip rectangle
+    Rect sourceClip;
+
+    // The physical space destination clip rectangle
+    Rect destinationClip;
 
     // If true, RenderEngine filtering should be enabled
     bool needsFiltering{false};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 2608b91..346c2d1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -36,8 +36,9 @@
     MOCK_CONST_METHOD0(getDisplayId, std::optional<DisplayId>());
 
     MOCK_METHOD1(setCompositionEnabled, void(bool));
-    MOCK_METHOD6(setProjection,
-                 void(const ui::Transform&, uint32_t, const Rect&, const Rect&, const Rect&, bool));
+    MOCK_METHOD7(setProjection,
+                 void(const ui::Transform&, uint32_t, const Rect&, const Rect&, const Rect&,
+                      const Rect&, bool));
     MOCK_METHOD1(setBounds, void(const ui::Size&));
     MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
 
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 47704ec..a389bf3 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -106,11 +106,13 @@
 }
 
 void Output::setProjection(const ui::Transform& transform, uint32_t orientation, const Rect& frame,
-                           const Rect& viewport, const Rect& scissor, bool needsFiltering) {
+                           const Rect& viewport, const Rect& sourceClip,
+                           const Rect& destinationClip, bool needsFiltering) {
     auto& outputState = editState();
     outputState.transform = transform;
     outputState.orientation = orientation;
-    outputState.scissor = scissor;
+    outputState.sourceClip = sourceClip;
+    outputState.destinationClip = destinationClip;
     outputState.frame = frame;
     outputState.viewport = viewport;
     outputState.needsFiltering = needsFiltering;
@@ -824,8 +826,8 @@
     const bool supportsProtectedContent = renderEngine.supportsProtectedContent();
 
     renderengine::DisplaySettings clientCompositionDisplay;
-    clientCompositionDisplay.physicalDisplay = outputState.scissor;
-    clientCompositionDisplay.clip = outputState.scissor;
+    clientCompositionDisplay.physicalDisplay = outputState.destinationClip;
+    clientCompositionDisplay.clip = outputState.sourceClip;
     clientCompositionDisplay.globalTransform = outputState.transform.asMatrix4();
     clientCompositionDisplay.orientation = outputState.orientation;
     clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut()
@@ -893,8 +895,7 @@
     // GPU composition can finish in time. We must reset GPU frequency afterwards,
     // because high frequency consumes extra battery.
     const bool expensiveRenderingExpected =
-            clientCompositionDisplay.outputDataspace == ui::Dataspace::DISPLAY_P3 ||
-            mLayerRequestingBackgroundBlur != nullptr;
+            clientCompositionDisplay.outputDataspace == ui::Dataspace::DISPLAY_P3;
     if (expensiveRenderingExpected) {
         setExpensiveRenderingExpected(true);
     }
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index 84d79f7..ca5be48 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -40,7 +40,8 @@
 
     dumpVal(out, "frame", frame);
     dumpVal(out, "viewport", viewport);
-    dumpVal(out, "scissor", scissor);
+    dumpVal(out, "sourceClip", sourceClip);
+    dumpVal(out, "destinationClip", destinationClip);
     dumpVal(out, "needsFiltering", needsFiltering);
 
     out.append("\n   ");
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 767faa5..2b45046 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -240,16 +240,19 @@
     const int32_t orientation = 123;
     const Rect frame{1, 2, 3, 4};
     const Rect viewport{5, 6, 7, 8};
-    const Rect scissor{9, 10, 11, 12};
+    const Rect sourceClip{9, 10, 11, 12};
+    const Rect destinationClip{13, 14, 15, 16};
     const bool needsFiltering = true;
 
-    mOutput->setProjection(transform, orientation, frame, viewport, scissor, needsFiltering);
+    mOutput->setProjection(transform, orientation, frame, viewport, sourceClip, destinationClip,
+                           needsFiltering);
 
     EXPECT_THAT(mOutput->getState().transform, transform);
     EXPECT_EQ(orientation, mOutput->getState().orientation);
     EXPECT_EQ(frame, mOutput->getState().frame);
     EXPECT_EQ(viewport, mOutput->getState().viewport);
-    EXPECT_EQ(scissor, mOutput->getState().scissor);
+    EXPECT_EQ(sourceClip, mOutput->getState().sourceClip);
+    EXPECT_EQ(destinationClip, mOutput->getState().destinationClip);
     EXPECT_EQ(needsFiltering, mOutput->getState().needsFiltering);
 }
 
@@ -2779,7 +2782,8 @@
 
         mOutput.mState.frame = kDefaultOutputFrame;
         mOutput.mState.viewport = kDefaultOutputViewport;
-        mOutput.mState.scissor = kDefaultOutputScissor;
+        mOutput.mState.sourceClip = kDefaultOutputSourceClip;
+        mOutput.mState.destinationClip = kDefaultOutputDestinationClip;
         mOutput.mState.transform = ui::Transform{kDefaultOutputOrientation};
         mOutput.mState.orientation = kDefaultOutputOrientation;
         mOutput.mState.dataspace = kDefaultOutputDataspace;
@@ -2823,7 +2827,8 @@
 
     static const Rect kDefaultOutputFrame;
     static const Rect kDefaultOutputViewport;
-    static const Rect kDefaultOutputScissor;
+    static const Rect kDefaultOutputSourceClip;
+    static const Rect kDefaultOutputDestinationClip;
     static const mat4 kDefaultColorTransformMat;
 
     static const Region kDebugRegion;
@@ -2843,7 +2848,8 @@
 
 const Rect OutputComposeSurfacesTest::kDefaultOutputFrame{1001, 1002, 1003, 1004};
 const Rect OutputComposeSurfacesTest::kDefaultOutputViewport{1005, 1006, 1007, 1008};
-const Rect OutputComposeSurfacesTest::kDefaultOutputScissor{1009, 1010, 1011, 1012};
+const Rect OutputComposeSurfacesTest::kDefaultOutputSourceClip{1009, 1010, 1011, 1012};
+const Rect OutputComposeSurfacesTest::kDefaultOutputDestinationClip{1013, 1014, 1015, 1016};
 const mat4 OutputComposeSurfacesTest::kDefaultColorTransformMat{mat4() * 0.5f};
 const Region OutputComposeSurfacesTest::kDebugRegion{Rect{100, 101, 102, 103}};
 const HdrCapabilities OutputComposeSurfacesTest::
@@ -3086,9 +3092,10 @@
     verify().ifMixedCompositionIs(true)
             .andIfUsesHdr(true)
             .andIfSkipColorTransform(false)
-            .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
-                                            kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
-                                            Region::INVALID_REGION, kDefaultOutputOrientation})
+            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+                                            mat4(), kDefaultMaxLuminance, kDefaultOutputDataspace,
+                                            mat4(), Region::INVALID_REGION,
+                                            kDefaultOutputOrientation})
             .execute()
             .expectAFenceWasReturned();
 }
@@ -3097,9 +3104,10 @@
     verify().ifMixedCompositionIs(true)
             .andIfUsesHdr(false)
             .andIfSkipColorTransform(false)
-            .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
-                                            kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
-                                            Region::INVALID_REGION, kDefaultOutputOrientation})
+            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+                                            mat4(), kDefaultMaxLuminance, kDefaultOutputDataspace,
+                                            mat4(), Region::INVALID_REGION,
+                                            kDefaultOutputOrientation})
             .execute()
             .expectAFenceWasReturned();
 }
@@ -3108,8 +3116,8 @@
     verify().ifMixedCompositionIs(false)
             .andIfUsesHdr(true)
             .andIfSkipColorTransform(false)
-            .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
-                                            kDefaultMaxLuminance, kDefaultOutputDataspace,
+            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+                                            mat4(), kDefaultMaxLuminance, kDefaultOutputDataspace,
                                             kDefaultColorTransformMat, Region::INVALID_REGION,
                                             kDefaultOutputOrientation})
             .execute()
@@ -3120,8 +3128,8 @@
     verify().ifMixedCompositionIs(false)
             .andIfUsesHdr(false)
             .andIfSkipColorTransform(false)
-            .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
-                                            kDefaultMaxLuminance, kDefaultOutputDataspace,
+            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+                                            mat4(), kDefaultMaxLuminance, kDefaultOutputDataspace,
                                             kDefaultColorTransformMat, Region::INVALID_REGION,
                                             kDefaultOutputOrientation})
             .execute()
@@ -3133,9 +3141,10 @@
     verify().ifMixedCompositionIs(false)
             .andIfUsesHdr(true)
             .andIfSkipColorTransform(true)
-            .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
-                                            kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
-                                            Region::INVALID_REGION, kDefaultOutputOrientation})
+            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+                                            mat4(), kDefaultMaxLuminance, kDefaultOutputDataspace,
+                                            mat4(), Region::INVALID_REGION,
+                                            kDefaultOutputOrientation})
             .execute()
             .expectAFenceWasReturned();
 }
@@ -3344,7 +3353,8 @@
     GenerateClientCompositionRequestsTest_ThreeLayers() {
         mOutput.mState.frame = kDisplayFrame;
         mOutput.mState.viewport = kDisplayViewport;
-        mOutput.mState.scissor = kDisplayScissor;
+        mOutput.mState.sourceClip = kDisplaySourceClip;
+        mOutput.mState.destinationClip = kDisplayDestinationClip;
         mOutput.mState.transform = ui::Transform{kDisplayOrientation};
         mOutput.mState.orientation = kDisplayOrientation;
         mOutput.mState.needsFiltering = false;
@@ -3375,14 +3385,17 @@
 
     static const Rect kDisplayFrame;
     static const Rect kDisplayViewport;
-    static const Rect kDisplayScissor;
+    static const Rect kDisplaySourceClip;
+    static const Rect kDisplayDestinationClip;
 
     std::array<Layer, 3> mLayers;
 };
 
 const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayFrame(0, 0, 100, 200);
 const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayViewport(0, 0, 101, 201);
-const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayScissor(0, 0, 102, 202);
+const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplaySourceClip(0, 0, 102, 202);
+const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayDestinationClip(0, 0, 103,
+                                                                                      203);
 
 TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, handlesNoClientCompostionLayers) {
     EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
@@ -3792,13 +3805,15 @@
 
     const Rect kPortraitFrame(0, 0, 1000, 2000);
     const Rect kPortraitViewport(0, 0, 2000, 1000);
-    const Rect kPortraitScissor(0, 0, 1000, 2000);
+    const Rect kPortraitSourceClip(0, 0, 1000, 2000);
+    const Rect kPortraitDestinationClip(0, 0, 1000, 2000);
     const uint32_t kPortraitOrientation = TR_ROT_90;
     constexpr ui::Dataspace kOutputDataspace = ui::Dataspace::DISPLAY_P3;
 
     mOutput.mState.frame = kPortraitFrame;
     mOutput.mState.viewport = kPortraitViewport;
-    mOutput.mState.scissor = kPortraitScissor;
+    mOutput.mState.sourceClip = kPortraitSourceClip;
+    mOutput.mState.destinationClip = kPortraitDestinationClip;
     mOutput.mState.transform = ui::Transform{kPortraitOrientation};
     mOutput.mState.orientation = kPortraitOrientation;
     mOutput.mState.needsFiltering = false;
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 4ae6dad..6ff39b4 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -146,12 +146,12 @@
     return mCompositionDisplay->getState().dataspace;
 }
 
-void DisplayDevice::setLayerStack(uint32_t stack) {
+void DisplayDevice::setLayerStack(ui::LayerStack stack) {
     mCompositionDisplay->setLayerStackFilter(stack, isPrimary());
 }
 
-void DisplayDevice::setDisplaySize(const int newWidth, const int newHeight) {
-    mCompositionDisplay->setBounds(ui::Size(newWidth, newHeight));
+void DisplayDevice::setDisplaySize(int width, int height) {
+    mCompositionDisplay->setBounds(ui::Size(width, height));
 }
 
 void DisplayDevice::setProjection(ui::Rotation orientation, Rect viewport, Rect frame) {
@@ -222,10 +222,13 @@
     const bool needsFiltering =
             (!globalTransform.preserveRects() || (type >= ui::Transform::SCALE));
 
-    Rect scissor = globalTransform.transform(viewport);
-    if (scissor.isEmpty()) {
-        scissor = displayBounds;
+    Rect sourceClip = globalTransform.transform(viewport);
+    if (sourceClip.isEmpty()) {
+        sourceClip = displayBounds;
     }
+    // For normal display use we always set the source and destination clip
+    // rectangles to the same values.
+    const Rect& destinationClip = sourceClip;
 
     uint32_t transformOrientation;
 
@@ -236,8 +239,8 @@
         transformOrientation = ui::Transform::toRotationFlags(orientation);
     }
 
-    getCompositionDisplay()->setProjection(globalTransform, transformOrientation,
-                                           frame, viewport, scissor, needsFiltering);
+    getCompositionDisplay()->setProjection(globalTransform, transformOrientation, frame, viewport,
+                                           sourceClip, destinationClip, needsFiltering);
 }
 
 ui::Transform::RotationFlags DisplayDevice::getPrimaryDisplayRotationFlags() {
@@ -286,7 +289,7 @@
     return mCompositionDisplay->getState().needsFiltering;
 }
 
-uint32_t DisplayDevice::getLayerStack() const {
+ui::LayerStack DisplayDevice::getLayerStack() const {
     return mCompositionDisplay->getState().layerStackId;
 }
 
@@ -302,8 +305,8 @@
     return mCompositionDisplay->getState().frame;
 }
 
-const Rect& DisplayDevice::getScissor() const {
-    return mCompositionDisplay->getState().scissor;
+const Rect& DisplayDevice::getSourceClip() const {
+    return mCompositionDisplay->getState().sourceClip;
 }
 
 bool DisplayDevice::hasWideColorGamut() const {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index ff48ecd..f45feae 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -16,8 +16,6 @@
 
 #pragma once
 
-#include <stdlib.h>
-
 #include <memory>
 #include <optional>
 #include <string>
@@ -30,7 +28,7 @@
 #include <math/mat4.h>
 #include <renderengine/RenderEngine.h>
 #include <system/window.h>
-#include <ui/DisplayInfo.h>
+#include <ui/DisplayState.h>
 #include <ui/GraphicTypes.h>
 #include <ui/HdrCapabilities.h>
 #include <ui/Region.h>
@@ -80,12 +78,12 @@
     // secure surfaces.
     bool isSecure() const;
 
-    int         getWidth() const;
-    int         getHeight() const;
+    int getWidth() const;
+    int getHeight() const;
+    ui::Size getSize() const { return {getWidth(), getHeight()}; }
 
-    void                    setLayerStack(uint32_t stack);
-    void                    setDisplaySize(const int newWidth, const int newHeight);
-
+    void setLayerStack(ui::LayerStack);
+    void setDisplaySize(int width, int height);
     void setProjection(ui::Rotation orientation, Rect viewport, Rect frame);
 
     ui::Rotation getPhysicalOrientation() const { return mPhysicalOrientation; }
@@ -96,9 +94,9 @@
     const ui::Transform& getTransform() const;
     const Rect& getViewport() const;
     const Rect& getFrame() const;
-    const Rect& getScissor() const;
+    const Rect& getSourceClip() const;
     bool needsFiltering() const;
-    uint32_t getLayerStack() const;
+    ui::LayerStack getLayerStack() const;
 
     const std::optional<DisplayId>& getId() const;
     const wp<IBinder>& getDisplayToken() const { return mDisplayToken; }
@@ -185,7 +183,7 @@
     int32_t sequenceId = sNextSequenceId++;
     std::optional<DisplayId> displayId;
     sp<IGraphicBufferProducer> surface;
-    uint32_t layerStack = NO_LAYER_STACK;
+    ui::LayerStack layerStack = ui::NO_LAYER_STACK;
     Rect viewport;
     Rect frame;
     ui::Rotation orientation = ui::ROTATION_0;
@@ -271,7 +269,7 @@
     Rect getSourceCrop() const override {
         // use the projected display viewport by default.
         if (mSourceCrop.isEmpty()) {
-            return mDisplay->getScissor();
+            return mDisplay->getSourceClip();
         }
 
         // Recompute the device transformation for the source crop.
@@ -280,14 +278,14 @@
         ui::Transform translateLogical;
         ui::Transform scale;
         const Rect& viewport = mDisplay->getViewport();
-        const Rect& scissor = mDisplay->getScissor();
+        const Rect& sourceClip = mDisplay->getSourceClip();
         const Rect& frame = mDisplay->getFrame();
 
         const auto flags = ui::Transform::toRotationFlags(mDisplay->getPhysicalOrientation());
         rotation.set(flags, getWidth(), getHeight());
 
         translateLogical.set(-viewport.left, -viewport.top);
-        translatePhysical.set(scissor.left, scissor.top);
+        translatePhysical.set(sourceClip.left, sourceClip.top);
         scale.set(frame.getWidth() / float(viewport.getWidth()), 0, 0,
                   frame.getHeight() / float(viewport.getHeight()));
         const ui::Transform finalTransform =
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 28cd667..effbed6 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -100,6 +100,7 @@
     mCurrentState.active.w = UINT32_MAX;
     mCurrentState.active.h = UINT32_MAX;
     mCurrentState.active.transform.set(0, 0);
+    mCurrentState.frameNumber = 0;
     mCurrentState.transform = 0;
     mCurrentState.transformToDisplayInverse = false;
     mCurrentState.crop.makeInvalid();
@@ -115,7 +116,6 @@
     mCurrentState.frameRateSelectionPriority = PRIORITY_UNSET;
     mCurrentState.metadata = args.metadata;
     mCurrentState.shadowRadius = 0.f;
-    mCurrentState.frameRate = 0.f;
 
     // drawing state & current state are identical
     mDrawingState = mCurrentState;
@@ -1263,7 +1263,7 @@
     return true;
 }
 
-bool Layer::setFrameRate(float frameRate) {
+bool Layer::setFrameRate(FrameRate frameRate) {
     if (mCurrentState.frameRate == frameRate) {
         return false;
     }
@@ -1275,11 +1275,8 @@
     return true;
 }
 
-std::optional<float> Layer::getFrameRate() const {
-    const auto frameRate = getDrawingState().frameRate;
-    if (frameRate > 0.f || frameRate == FRAME_RATE_NO_VOTE) return frameRate;
-
-    return {};
+Layer::FrameRate Layer::getFrameRate() const {
+    return getDrawingState().frameRate;
 }
 
 void Layer::deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber) {
@@ -1819,6 +1816,15 @@
     }
 }
 
+void Layer::traverse(LayerVector::StateSet state, const LayerVector::Visitor& visitor) {
+    visitor(this);
+    const LayerVector& children =
+            state == LayerVector::StateSet::Drawing ? mDrawingChildren : mCurrentChildren;
+    for (const sp<Layer>& child : children) {
+        child->traverse(state, visitor);
+    }
+}
+
 LayerVector Layer::makeChildrenTraversalList(LayerVector::StateSet stateSet,
                                              const std::vector<Layer*>& layersInTree) {
     LOG_ALWAYS_FATAL_IF(stateSet == LayerVector::StateSet::Invalid,
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index e7d057b..810e0af 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -135,6 +135,34 @@
         float radius = 0.0f;
     };
 
+    // FrameRateCompatibility specifies how we should interpret the frame rate associated with
+    // the layer.
+    enum class FrameRateCompatibility {
+        Default, // Layer didn't specify any specific handling strategy
+
+        ExactOrMultiple, // Layer needs the exact frame rate (or a multiple of it) to present the
+                         // content properly. Any other value will result in a pull down.
+
+        NoVote, // Layer doesn't have any requirements for the refresh rate and
+                // should not be considered when the display refresh rate is determined.
+    };
+
+    // Encapsulates the frame rate and compatibility of the layer. This information will be used
+    // when the display refresh rate is determined.
+    struct FrameRate {
+        float rate;
+        FrameRateCompatibility type;
+
+        FrameRate() : rate(0), type(FrameRateCompatibility::Default) {}
+        FrameRate(float rate, FrameRateCompatibility type) : rate(rate), type(type) {}
+
+        bool operator==(const FrameRate& other) const {
+            return rate == other.rate && type == other.type;
+        }
+
+        bool operator!=(const FrameRate& other) const { return !(*this == other); }
+    };
+
     struct State {
         Geometry active_legacy;
         Geometry requested_legacy;
@@ -188,6 +216,7 @@
         ui::Dataspace dataspace;
 
         // The fields below this point are only used by BufferStateLayer
+        uint64_t frameNumber;
         Geometry active;
 
         uint32_t transform;
@@ -225,7 +254,7 @@
         // Priority of the layer assigned by Window Manager.
         int32_t frameRateSelectionPriority;
 
-        float frameRate;
+        FrameRate frameRate;
     };
 
     explicit Layer(const LayerCreationArgs& args);
@@ -327,8 +356,8 @@
     virtual bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/) { return false; };
     virtual bool setCrop(const Rect& /*crop*/) { return false; };
     virtual bool setFrame(const Rect& /*frame*/) { return false; };
-    virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/, nsecs_t /*postTime*/,
-                           nsecs_t /*desiredPresentTime*/,
+    virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/, const sp<Fence>& /*acquireFence*/,
+                           nsecs_t /*postTime*/, nsecs_t /*desiredPresentTime*/,
                            const client_cache_t& /*clientCacheId*/) {
         return false;
     };
@@ -679,6 +708,15 @@
 
     renderengine::ShadowSettings getShadowSettings(const Rect& viewport) const;
 
+    /**
+     * Traverse this layer and it's hierarchy of children directly. Unlike traverseInZOrder
+     * which will not emit children who have relativeZOrder to another layer, this method
+     * just directly emits all children. It also emits them in no particular order.
+     * So this method is not suitable for graphical operations, as it doesn't represent
+     * the scene state, but it's also more efficient than traverseInZOrder and so useful for
+     * book-keeping.
+     */
+    void traverse(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
     void traverseInReverseZOrder(LayerVector::StateSet stateSet,
                                  const LayerVector::Visitor& visitor);
     void traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
@@ -745,9 +783,8 @@
      */
     Rect getCroppedBufferSize(const Layer::State& s) const;
 
-    constexpr static auto FRAME_RATE_NO_VOTE = -1.0f;
-    virtual bool setFrameRate(float frameRate);
-    virtual std::optional<float> getFrameRate() const;
+    virtual bool setFrameRate(FrameRate frameRate);
+    virtual FrameRate getFrameRate() const;
 
 protected:
     // constant
diff --git a/services/surfaceflinger/LayerVector.cpp b/services/surfaceflinger/LayerVector.cpp
index 7c959b9..9b94920 100644
--- a/services/surfaceflinger/LayerVector.cpp
+++ b/services/surfaceflinger/LayerVector.cpp
@@ -86,6 +86,14 @@
         layer->traverseInReverseZOrder(stateSet, visitor);
      }
 }
+
+void LayerVector::traverse(const Visitor& visitor) const {
+    for (auto i = static_cast<int64_t>(size()) - 1; i >= 0; i--) {
+        const auto& layer = (*this)[i];
+        layer->traverse(mStateSet, visitor);
+    }
+}
+
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/LayerVector.h b/services/surfaceflinger/LayerVector.h
index 88d7711..a531f4f 100644
--- a/services/surfaceflinger/LayerVector.h
+++ b/services/surfaceflinger/LayerVector.h
@@ -50,7 +50,7 @@
     using Visitor = std::function<void(Layer*)>;
     void traverseInReverseZOrder(StateSet stateSet, const Visitor& visitor) const;
     void traverseInZOrder(StateSet stateSet, const Visitor& visitor) const;
-
+    void traverse(const Visitor& visitor) const;
 private:
     const StateSet mStateSet;
 };
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index d3d9d3a..682679c 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -155,7 +155,7 @@
 
     Mutex::Autolock _l(mFlinger.mStateLock);
     mLayer = mClient->getLayerUser(mIBinder);
-    mLayer->setFrameRate(Layer::FRAME_RATE_NO_VOTE);
+    mLayer->setFrameRate(Layer::FrameRate(0, Layer::FrameRateCompatibility::NoVote));
 
     // setting Layer's Z requires resorting layersSortedByZ
     ssize_t idx = mFlinger.mCurrentState.layersSortedByZ.indexOf(mLayer);
@@ -208,7 +208,7 @@
     const int32_t buttom = top + display->getHeight() / 32;
 
     auto buffer = mBufferCache[refreshRate.fps];
-    mLayer->setBuffer(buffer, 0, 0, {});
+    mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {});
 
     mLayer->setFrame(Rect(left, top, right, buttom));
 
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index b976523..9aada11 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -39,7 +39,7 @@
 namespace {
 
 bool isLayerActive(const Layer& layer, const LayerInfo& info, nsecs_t threshold) {
-    if (layer.getFrameRate().has_value()) {
+    if (layer.getFrameRate().rate > 0) {
         return layer.isVisible();
     }
     return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
@@ -126,18 +126,27 @@
         // Only use the layer if the reference still exists.
         if (layer || CC_UNLIKELY(mTraceEnabled)) {
             // Check if frame rate was set on layer.
-            auto frameRate = layer->getFrameRate();
-            if (frameRate.has_value() && frameRate.value() > 0.f) {
-                summary.push_back(
-                        {layer->getName(), LayerVoteType::Explicit, *frameRate, /* weight */ 1.0f});
+            const auto frameRate = layer->getFrameRate();
+            if (frameRate.rate > 0.f) {
+                const auto voteType = [&]() {
+                    switch (frameRate.type) {
+                        case Layer::FrameRateCompatibility::Default:
+                            return LayerVoteType::ExplicitDefault;
+                        case Layer::FrameRateCompatibility::ExactOrMultiple:
+                            return LayerVoteType::ExplicitExactOrMultiple;
+                        case Layer::FrameRateCompatibility::NoVote:
+                            return LayerVoteType::NoVote;
+                    }
+                }();
+                summary.push_back({layer->getName(), voteType, frameRate.rate, /* weight */ 1.0f});
             } else if (recent) {
-                frameRate = info->getRefreshRate(now);
-                summary.push_back({layer->getName(), LayerVoteType::Heuristic, *frameRate,
+                summary.push_back({layer->getName(), LayerVoteType::Heuristic,
+                                   info->getRefreshRate(now),
                                    /* weight */ 1.0f});
             }
 
             if (CC_UNLIKELY(mTraceEnabled)) {
-                trace(weakLayer, round<int>(*frameRate));
+                trace(weakLayer, round<int>(frameRate.rate));
             }
         }
     }
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
index a6d2c74..ce085f4 100644
--- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
@@ -40,7 +40,7 @@
 namespace {
 
 bool isLayerActive(const Layer& layer, const LayerInfoV2& info, nsecs_t threshold) {
-    if (layer.getFrameRate().has_value()) {
+    if (layer.getFrameRate().rate > 0) {
         return layer.isVisible();
     }
     return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
@@ -63,13 +63,17 @@
     const auto& name = layer->getName();
     const auto noVoteTag = "LFPS NoVote " + name;
     const auto heuristicVoteTag = "LFPS Heuristic " + name;
-    const auto explicitVoteTag = "LFPS Explicit " + name;
+    const auto explicitDefaultVoteTag = "LFPS ExplicitDefault" + name;
+    const auto explicitExactOrMultipleVoteTag = "LFPS ExplicitExactOrMultiple" + name;
     const auto minVoteTag = "LFPS Min " + name;
     const auto maxVoteTag = "LFPS Max " + name;
 
     ATRACE_INT(noVoteTag.c_str(), type == LayerHistory::LayerVoteType::NoVote ? 1 : 0);
     ATRACE_INT(heuristicVoteTag.c_str(), type == LayerHistory::LayerVoteType::Heuristic ? fps : 0);
-    ATRACE_INT(explicitVoteTag.c_str(), type == LayerHistory::LayerVoteType::Explicit ? fps : 0);
+    ATRACE_INT(explicitDefaultVoteTag.c_str(),
+               type == LayerHistory::LayerVoteType::ExplicitDefault ? fps : 0);
+    ATRACE_INT(explicitExactOrMultipleVoteTag.c_str(),
+               type == LayerHistory::LayerVoteType::ExplicitExactOrMultiple ? fps : 0);
     ATRACE_INT(minVoteTag.c_str(), type == LayerHistory::LayerVoteType::Min ? 1 : 0);
     ATRACE_INT(maxVoteTag.c_str(), type == LayerHistory::LayerVoteType::Max ? 1 : 0);
 
@@ -160,12 +164,18 @@
             i++;
             // Set layer vote if set
             const auto frameRate = layer->getFrameRate();
-            if (frameRate.has_value()) {
-                if (*frameRate == Layer::FRAME_RATE_NO_VOTE) {
-                    info->setLayerVote(LayerVoteType::NoVote, 0.f);
-                } else {
-                    info->setLayerVote(LayerVoteType::Explicit, *frameRate);
+            const auto voteType = [&]() {
+                switch (frameRate.type) {
+                    case Layer::FrameRateCompatibility::Default:
+                        return LayerVoteType::ExplicitDefault;
+                    case Layer::FrameRateCompatibility::ExactOrMultiple:
+                        return LayerVoteType::ExplicitExactOrMultiple;
+                    case Layer::FrameRateCompatibility::NoVote:
+                        return LayerVoteType::NoVote;
                 }
+            }();
+            if (frameRate.rate > 0 || voteType == LayerVoteType::NoVote) {
+                info->setLayerVote(voteType, frameRate.rate);
             } else {
                 info->resetLayerVote();
             }
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
index d94d758..345b8f9 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
@@ -54,8 +54,15 @@
     return mFrameTimes.back().queueTime >= getActiveLayerThreshold(now);
 }
 
+bool LayerInfoV2::isFrameTimeValid(const FrameTimeData& frameTime) const {
+    return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>(
+                                          mFrameTimeValidSince.time_since_epoch())
+                                          .count();
+}
+
 bool LayerInfoV2::isFrequent(nsecs_t now) const {
-    // Assume layer is infrequent if too few present times have been recorded.
+    // If we know nothing about this layer we consider it as frequent as it might be the start
+    // of an animation.
     if (mFrameTimes.size() < FREQUENT_LAYER_WINDOW_SIZE) {
         return true;
     }
@@ -63,12 +70,24 @@
     // Layer is frequent if the earliest value in the window of most recent present times is
     // within threshold.
     const auto it = mFrameTimes.end() - FREQUENT_LAYER_WINDOW_SIZE;
+    if (!isFrameTimeValid(*it)) {
+        return true;
+    }
+
     const nsecs_t threshold = now - MAX_FREQUENT_LAYER_PERIOD_NS.count();
     return it->queueTime >= threshold;
 }
 
 bool LayerInfoV2::hasEnoughDataForHeuristic() const {
     // The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates
+    if (mFrameTimes.size() < 2) {
+        return false;
+    }
+
+    if (!isFrameTimeValid(mFrameTimes.front())) {
+        return false;
+    }
+
     if (mFrameTimes.size() < HISTORY_SIZE &&
         mFrameTimes.back().queueTime - mFrameTimes.front().queueTime < HISTORY_TIME.count()) {
         return false;
@@ -100,8 +119,7 @@
             static_cast<float>(totalPresentTimeDeltas) / (mFrameTimes.size() - 1);
 
     // Now once we calculated the refresh rate we need to make sure that all the frames we captured
-    // are evenly distrubuted and we don't calculate the average across some burst of frames.
-
+    // are evenly distributed and we don't calculate the average across some burst of frames.
     for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
         const nsecs_t presentTimeDeltas =
                 std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.h b/services/surfaceflinger/Scheduler/LayerInfoV2.h
index 564f05e..90f6310 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.h
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.h
@@ -82,12 +82,25 @@
     // updated time, the updated time is the present time.
     nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }
 
-    void clearHistory() { mFrameTimes.clear(); }
+    void clearHistory() {
+        // Mark mFrameTimeValidSince to now to ignore all previous frame times.
+        // We are not deleting the old frame to keep track of whether we should treat the first
+        // buffer as Max as we don't know anything about this layer or Min as this layer is
+        // posting infrequent updates.
+        mFrameTimeValidSince = std::chrono::steady_clock::now();
+    }
 
 private:
+    // Used to store the layer timestamps
+    struct FrameTimeData {
+        nsecs_t presetTime; // desiredPresentTime, if provided
+        nsecs_t queueTime;  // buffer queue time
+    };
+
     bool isFrequent(nsecs_t now) const;
     bool hasEnoughDataForHeuristic() const;
     std::optional<float> calculateRefreshRateIfPossible();
+    bool isFrameTimeValid(const FrameTimeData&) const;
 
     // Used for sanitizing the heuristic data
     const nsecs_t mHighRefreshRatePeriod;
@@ -103,12 +116,9 @@
         float fps;
     } mLayerVote;
 
-    // Used to store the layer timestamps
-    struct FrameTimeData {
-        nsecs_t presetTime; // desiredPresentTime, if provided
-        nsecs_t queueTime;  // buffer queue time
-    };
     std::deque<FrameTimeData> mFrameTimes;
+    std::chrono::time_point<std::chrono::steady_clock> mFrameTimeValidSince =
+            std::chrono::steady_clock::now();
     static constexpr size_t HISTORY_SIZE = 90;
     static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s;
 };
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index c187049..c73e825 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -37,7 +37,8 @@
     int explicitContentFramerate = 0;
     for (const auto& layer : layers) {
         const auto desiredRefreshRateRound = round<int>(layer.desiredRefreshRate);
-        if (layer.vote == LayerVoteType::Explicit) {
+        if (layer.vote == LayerVoteType::ExplicitDefault ||
+            layer.vote == LayerVoteType::ExplicitExactOrMultiple) {
             if (desiredRefreshRateRound > explicitContentFramerate) {
                 explicitContentFramerate = desiredRefreshRateRound;
             }
@@ -94,7 +95,8 @@
     int noVoteLayers = 0;
     int minVoteLayers = 0;
     int maxVoteLayers = 0;
-    int explicitVoteLayers = 0;
+    int explicitDefaultVoteLayers = 0;
+    int explicitExactOrMultipleVoteLayers = 0;
     for (const auto& layer : layers) {
         if (layer.vote == LayerVoteType::NoVote)
             noVoteLayers++;
@@ -102,8 +104,10 @@
             minVoteLayers++;
         else if (layer.vote == LayerVoteType::Max)
             maxVoteLayers++;
-        else if (layer.vote == LayerVoteType::Explicit)
-            explicitVoteLayers++;
+        else if (layer.vote == LayerVoteType::ExplicitDefault)
+            explicitDefaultVoteLayers++;
+        else if (layer.vote == LayerVoteType::ExplicitExactOrMultiple)
+            explicitExactOrMultipleVoteLayers++;
     }
 
     // Only if all layers want Min we should return Min
@@ -112,7 +116,7 @@
     }
 
     // If we have some Max layers and no Explicit we should return Max
-    if (maxVoteLayers > 0 && explicitVoteLayers == 0) {
+    if (maxVoteLayers > 0 && explicitDefaultVoteLayers + explicitExactOrMultipleVoteLayers == 0) {
         return *mAvailableRefreshRates.back();
     }
 
@@ -125,14 +129,28 @@
     }
 
     for (const auto& layer : layers) {
+        ALOGV("Calculating score for %s (type: %d)", layer.name.c_str(), layer.vote);
         if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min ||
             layer.vote == LayerVoteType::Max) {
             continue;
         }
 
-        // If we have Explicit layers, ignore the Huristic ones
-        if (explicitVoteLayers > 0 && layer.vote == LayerVoteType::Heuristic) {
-            continue;
+        // Adjust the weight in case we have explicit layers. The priority is:
+        //  - ExplicitExactOrMultiple
+        //  - ExplicitDefault
+        //  - Heuristic
+        auto weight = layer.weight;
+        if (explicitExactOrMultipleVoteLayers + explicitDefaultVoteLayers > 0) {
+            if (layer.vote == LayerVoteType::Heuristic) {
+                weight /= 2.f;
+            }
+        }
+
+        if (explicitExactOrMultipleVoteLayers > 0) {
+            if (layer.vote == LayerVoteType::Heuristic ||
+                layer.vote == LayerVoteType::ExplicitDefault) {
+                weight /= 2.f;
+            }
         }
 
         for (auto& [refreshRate, overallScore] : scores) {
@@ -148,28 +166,30 @@
             }
 
             float layerScore;
+            static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1
             if (displayFramesRem == 0) {
                 // Layer desired refresh rate matches the display rate.
-                layerScore = layer.weight * 1.0f;
+                layerScore = weight * 1.0f;
             } else if (displayFramesQuot == 0) {
                 // Layer desired refresh rate is higher the display rate.
-                layerScore = layer.weight * layerPeriod / displayPeriod;
+                layerScore = weight *
+                        (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) *
+                        (1.0f / (MAX_FRAMES_TO_FIT + 1));
             } else {
                 // Layer desired refresh rate is lower the display rate. Check how well it fits the
                 // cadence
                 auto diff = std::abs(displayFramesRem - (displayPeriod - displayFramesRem));
                 int iter = 2;
-                static constexpr size_t MAX_ITERATOR = 10; // Stop calculating when score < 0.1
-                while (diff > MARGIN && iter < MAX_ITERATOR) {
+                while (diff > MARGIN && iter < MAX_FRAMES_TO_FIT) {
                     diff = diff - (displayPeriod - diff);
                     iter++;
                 }
 
-                layerScore = layer.weight * 1.0f / iter;
+                layerScore = weight * (1.0f / iter);
             }
 
-            ALOGV("%s (weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(),
-                  layer.weight, 1e9f / layerPeriod, refreshRate->name.c_str(), layerScore);
+            ALOGV("%s (weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(), weight,
+                  1e9f / layerPeriod, refreshRate->name.c_str(), layerScore);
             overallScore += layerScore;
         }
     }
@@ -196,20 +216,12 @@
 
 const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicy() const {
     std::lock_guard lock(mLock);
-    if (!mRefreshRateSwitching) {
-        return *mCurrentRefreshRate;
-    } else {
-        return *mAvailableRefreshRates.front();
-    }
+    return *mAvailableRefreshRates.front();
 }
 
 const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicy() const {
     std::lock_guard lock(mLock);
-    if (!mRefreshRateSwitching) {
-        return *mCurrentRefreshRate;
-    } else {
         return *mAvailableRefreshRates.back();
-    }
 }
 
 const RefreshRate& RefreshRateConfigs::getCurrentRefreshRate() const {
@@ -222,18 +234,14 @@
     mCurrentRefreshRate = &mRefreshRates.at(configId);
 }
 
-RefreshRateConfigs::RefreshRateConfigs(bool refreshRateSwitching,
-                                       const std::vector<InputConfig>& configs,
-                                       HwcConfigIndexType currentHwcConfig)
-      : mRefreshRateSwitching(refreshRateSwitching) {
+RefreshRateConfigs::RefreshRateConfigs(const std::vector<InputConfig>& configs,
+                                       HwcConfigIndexType currentHwcConfig) {
     init(configs, currentHwcConfig);
 }
 
 RefreshRateConfigs::RefreshRateConfigs(
-        bool refreshRateSwitching,
         const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
-        HwcConfigIndexType currentConfigId)
-      : mRefreshRateSwitching(refreshRateSwitching) {
+        HwcConfigIndexType currentConfigId) {
     std::vector<InputConfig> inputConfigs;
     for (size_t configId = 0; configId < configs.size(); ++configId) {
         auto configGroup = HwcConfigGroupType(configs[configId]->getConfigGroup());
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 80d42cc..fc95959 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -94,16 +94,16 @@
     // Returns true if config is allowed by the current policy.
     bool isConfigAllowed(HwcConfigIndexType config) const EXCLUDES(mLock);
 
-    // Returns true if this device is doing refresh rate switching. This won't change at runtime.
-    bool refreshRateSwitchingSupported() const { return mRefreshRateSwitching; }
-
     // Describes the different options the layer voted for refresh rate
     enum class LayerVoteType {
-        NoVote,    // Doesn't care about the refresh rate
-        Min,       // Minimal refresh rate available
-        Max,       // Maximal refresh rate available
-        Heuristic, // Specific refresh rate that was calculated by platform using a heuristic
-        Explicit,  // Specific refresh rate that was provided by the app
+        NoVote,          // Doesn't care about the refresh rate
+        Min,             // Minimal refresh rate available
+        Max,             // Maximal refresh rate available
+        Heuristic,       // Specific refresh rate that was calculated by platform using a heuristic
+        ExplicitDefault, // Specific refresh rate that was provided by the app with Default
+                         // compatibility
+        ExplicitExactOrMultiple // Specific refresh rate that was provided by the app with
+                                // ExactOrMultiple compatibility
     };
 
     // Captures the layer requirements for a refresh rate. This will be used to determine the
@@ -164,10 +164,9 @@
         nsecs_t vsyncPeriod = 0;
     };
 
-    RefreshRateConfigs(bool refreshRateSwitching, const std::vector<InputConfig>& configs,
+    RefreshRateConfigs(const std::vector<InputConfig>& configs,
                        HwcConfigIndexType currentHwcConfig);
-    RefreshRateConfigs(bool refreshRateSwitching,
-                       const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
+    RefreshRateConfigs(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
                        HwcConfigIndexType currentConfigId);
 
 private:
@@ -205,8 +204,6 @@
     const RefreshRate* mMinSupportedRefreshRate;
     const RefreshRate* mMaxSupportedRefreshRate;
 
-    const bool mRefreshRateSwitching;
-
     mutable std::mutex mLock;
 };
 
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 7de35af..56d1581 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -64,7 +64,7 @@
 
 std::unique_ptr<DispSync> createDispSync() {
     // TODO (140302863) remove this and use the vsync_reactor system.
-    if (property_get_bool("debug.sf.vsync_reactor", false)) {
+    if (property_get_bool("debug.sf.vsync_reactor", true)) {
         // TODO (144707443) tune Predictor tunables.
         static constexpr int default_rate = 60;
         static constexpr auto initial_period =
@@ -408,7 +408,8 @@
                         "SurfaceView - "
                         "com.google.android.youtube/"
                         "com.google.android.apps.youtube.app.WatchWhileActivity#0") {
-                layer->setFrameRate(vote);
+                layer->setFrameRate(
+                        Layer::FrameRate(vote, Layer::FrameRateCompatibility::ExactOrMultiple));
             }
         }
     }
@@ -529,8 +530,6 @@
     using base::StringAppendF;
     const char* const states[] = {"off", "on"};
 
-    const bool supported = mRefreshRateConfigs.refreshRateSwitchingSupported();
-    StringAppendF(&result, "+  Refresh rate switching: %s\n", states[supported]);
     StringAppendF(&result, "+  Content detection: %s\n", states[mLayerHistory != nullptr]);
 
     StringAppendF(&result, "+  Idle timer: %s\n",
@@ -564,7 +563,8 @@
 
 bool Scheduler::layerHistoryHasClientSpecifiedFrameRate() {
     for (const auto& layer : mFeatures.contentRequirements) {
-        if (layer.vote == scheduler::RefreshRateConfigs::LayerVoteType::Explicit) {
+        if (layer.vote == scheduler::RefreshRateConfigs::LayerVoteType::ExplicitDefault ||
+            layer.vote == scheduler::RefreshRateConfigs::LayerVoteType::ExplicitExactOrMultiple) {
             return true;
         }
     }
@@ -573,35 +573,44 @@
 }
 
 HwcConfigIndexType Scheduler::calculateRefreshRateType() {
-    if (!mRefreshRateConfigs.refreshRateSwitchingSupported()) {
-        return mRefreshRateConfigs.getCurrentRefreshRate().configId;
+    // This block of the code checks whether any layers used the SetFrameRate API. If they have,
+    // their request should be honored regardless of whether the device has refresh rate switching
+    // turned off.
+    if (layerHistoryHasClientSpecifiedFrameRate()) {
+        if (!mUseContentDetectionV2) {
+            return mRefreshRateConfigs.getRefreshRateForContent(mFeatures.contentRequirements)
+                    .configId;
+        } else {
+            return mRefreshRateConfigs.getRefreshRateForContentV2(mFeatures.contentRequirements)
+                    .configId;
+        }
     }
 
     // If the layer history doesn't have the frame rate specified, use the old path. NOTE:
     // if we remove the kernel idle timer, and use our internal idle timer, this code will have to
     // be refactored.
-    if (!layerHistoryHasClientSpecifiedFrameRate()) {
-        // If Display Power is not in normal operation we want to be in performance mode.
-        // When coming back to normal mode, a grace period is given with DisplayPowerTimer
-        if (!mFeatures.isDisplayPowerStateNormal ||
-            mFeatures.displayPowerTimer == TimerState::Reset) {
-            return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
-        }
+    // If Display Power is not in normal operation we want to be in performance mode.
+    // When coming back to normal mode, a grace period is given with DisplayPowerTimer
+    if (mDisplayPowerTimer &&
+        (!mFeatures.isDisplayPowerStateNormal ||
+         mFeatures.displayPowerTimer == TimerState::Reset)) {
+        return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
+    }
 
-        // As long as touch is active we want to be in performance mode
-        if (mFeatures.touch == TouchState::Active) {
-            return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
-        }
+    // As long as touch is active we want to be in performance mode
+    if (mTouchTimer && mFeatures.touch == TouchState::Active) {
+        return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
+    }
 
-        // If timer has expired as it means there is no new content on the screen
-        if (mFeatures.idleTimer == TimerState::Expired) {
-            return mRefreshRateConfigs.getMinRefreshRateByPolicy().configId;
-        }
+    // If timer has expired as it means there is no new content on the screen
+    if (mIdleTimer && mFeatures.idleTimer == TimerState::Expired) {
+        return mRefreshRateConfigs.getMinRefreshRateByPolicy().configId;
     }
 
     if (!mUseContentDetectionV2) {
-        // If content detection is off we choose performance as we don't know the content fps
+        // If content detection is off we choose performance as we don't know the content fps.
         if (mFeatures.contentDetection == ContentDetectionState::Off) {
+            // TODO(b/148428554): Be careful to not always call this.
             return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
         }
 
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index ac32633..b467f24 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -38,7 +38,7 @@
 
 VSyncPredictor::VSyncPredictor(nsecs_t idealPeriod, size_t historySize,
                                size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent)
-      : mTraceOn(property_get_bool("debug.sf.vsp_trace", false)),
+      : mTraceOn(property_get_bool("debug.sf.vsp_trace", true)),
         kHistorySize(historySize),
         kMinimumSamplesForPrediction(minimumSamplesForPrediction),
         kOutlierTolerancePercent(std::min(outlierTolerancePercent, kMaxPercent)),
@@ -71,12 +71,12 @@
     return std::get<0>(mRateMap.find(mIdealPeriod)->second);
 }
 
-void VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
+bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
     std::lock_guard<std::mutex> lk(mMutex);
 
     if (!validate(timestamp)) {
         ALOGV("timestamp was too far off the last known timestamp");
-        return;
+        return false;
     }
 
     if (timestamps.size() != kHistorySize) {
@@ -89,7 +89,7 @@
 
     if (timestamps.size() < kMinimumSamplesForPrediction) {
         mRateMap[mIdealPeriod] = {mIdealPeriod, 0};
-        return;
+        return true;
     }
 
     // This is a 'simple linear regression' calculation of Y over X, with Y being the
@@ -143,7 +143,7 @@
 
     if (CC_UNLIKELY(bottom == 0)) {
         it->second = {mIdealPeriod, 0};
-        return;
+        return false;
     }
 
     nsecs_t const anticipatedPeriod = top / bottom * kScalingFactor;
@@ -156,6 +156,7 @@
 
     ALOGV("model update ts: %" PRId64 " slope: %" PRId64 " intercept: %" PRId64, timestamp,
           anticipatedPeriod, intercept);
+    return true;
 }
 
 nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index e366555..532fe9e 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -38,7 +38,7 @@
                    uint32_t outlierTolerancePercent);
     ~VSyncPredictor();
 
-    void addVsyncTimestamp(nsecs_t timestamp) final;
+    bool addVsyncTimestamp(nsecs_t timestamp) final;
     nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final;
     nsecs_t currentPeriod() const final;
     void resetModel() final;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 53fa212..70e4760 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -130,10 +130,11 @@
     }
 
     std::lock_guard<std::mutex> lk(mMutex);
-    if (mIgnorePresentFences) {
+    if (mExternalIgnoreFences || mInternalIgnoreFences) {
         return true;
     }
 
+    bool timestampAccepted = true;
     for (auto it = mUnfiredFences.begin(); it != mUnfiredFences.end();) {
         auto const time = (*it)->getCachedSignalTime();
         if (time == Fence::SIGNAL_TIME_PENDING) {
@@ -141,7 +142,8 @@
         } else if (time == Fence::SIGNAL_TIME_INVALID) {
             it = mUnfiredFences.erase(it);
         } else {
-            mTracker->addVsyncTimestamp(time);
+            timestampAccepted &= mTracker->addVsyncTimestamp(time);
+
             it = mUnfiredFences.erase(it);
         }
     }
@@ -152,7 +154,13 @@
         }
         mUnfiredFences.push_back(fence);
     } else {
-        mTracker->addVsyncTimestamp(signalTime);
+        timestampAccepted &= mTracker->addVsyncTimestamp(signalTime);
+    }
+
+    if (!timestampAccepted) {
+        mMoreSamplesNeeded = true;
+        setIgnorePresentFencesInternal(true);
+        mPeriodConfirmationInProgress = true;
     }
 
     return mMoreSamplesNeeded;
@@ -160,8 +168,17 @@
 
 void VSyncReactor::setIgnorePresentFences(bool ignoration) {
     std::lock_guard<std::mutex> lk(mMutex);
-    mIgnorePresentFences = ignoration;
-    if (mIgnorePresentFences == true) {
+    mExternalIgnoreFences = ignoration;
+    updateIgnorePresentFencesInternal();
+}
+
+void VSyncReactor::setIgnorePresentFencesInternal(bool ignoration) {
+    mInternalIgnoreFences = ignoration;
+    updateIgnorePresentFencesInternal();
+}
+
+void VSyncReactor::updateIgnorePresentFencesInternal() {
+    if (mExternalIgnoreFences || mInternalIgnoreFences) {
         mUnfiredFences.clear();
     }
 }
@@ -177,14 +194,18 @@
 }
 
 void VSyncReactor::startPeriodTransition(nsecs_t newPeriod) {
+    mPeriodConfirmationInProgress = true;
     mPeriodTransitioningTo = newPeriod;
     mMoreSamplesNeeded = true;
+    setIgnorePresentFencesInternal(true);
 }
 
 void VSyncReactor::endPeriodTransition() {
-    mPeriodTransitioningTo.reset();
-    mLastHwVsync.reset();
+    setIgnorePresentFencesInternal(false);
     mMoreSamplesNeeded = false;
+    mPeriodTransitioningTo.reset();
+    mPeriodConfirmationInProgress = false;
+    mLastHwVsync.reset();
 }
 
 void VSyncReactor::setPeriod(nsecs_t period) {
@@ -208,27 +229,33 @@
 
 void VSyncReactor::endResync() {}
 
-bool VSyncReactor::periodChangeDetected(nsecs_t vsync_timestamp) {
-    if (!mLastHwVsync || !mPeriodTransitioningTo) {
+bool VSyncReactor::periodConfirmed(nsecs_t vsync_timestamp) {
+    if (!mLastHwVsync || !mPeriodConfirmationInProgress) {
         return false;
     }
+    auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : getPeriod();
+
+    static constexpr int allowancePercent = 10;
+    static constexpr std::ratio<allowancePercent, 100> allowancePercentRatio;
+    auto const allowance = period * allowancePercentRatio.num / allowancePercentRatio.den;
     auto const distance = vsync_timestamp - *mLastHwVsync;
-    return std::abs(distance - *mPeriodTransitioningTo) < std::abs(distance - getPeriod());
+    return std::abs(distance - period) < allowance;
 }
 
 bool VSyncReactor::addResyncSample(nsecs_t timestamp, bool* periodFlushed) {
     assert(periodFlushed);
 
     std::lock_guard<std::mutex> lk(mMutex);
-    if (periodChangeDetected(timestamp)) {
-        mTracker->setPeriod(*mPeriodTransitioningTo);
-        for (auto& entry : mCallbacks) {
-            entry.second->setPeriod(*mPeriodTransitioningTo);
+    if (periodConfirmed(timestamp)) {
+        if (mPeriodTransitioningTo) {
+            mTracker->setPeriod(*mPeriodTransitioningTo);
+            for (auto& entry : mCallbacks) {
+                entry.second->setPeriod(*mPeriodTransitioningTo);
+            }
+            *periodFlushed = true;
         }
-
         endPeriodTransition();
-        *periodFlushed = true;
-    } else if (mPeriodTransitioningTo) {
+    } else if (mPeriodConfirmationInProgress) {
         mLastHwVsync = timestamp;
         mMoreSamplesNeeded = true;
         *periodFlushed = false;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index f318dcb..5b79f35 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -61,9 +61,11 @@
     void reset() final;
 
 private:
+    void setIgnorePresentFencesInternal(bool ignoration) REQUIRES(mMutex);
+    void updateIgnorePresentFencesInternal() REQUIRES(mMutex);
     void startPeriodTransition(nsecs_t newPeriod) REQUIRES(mMutex);
     void endPeriodTransition() REQUIRES(mMutex);
-    bool periodChangeDetected(nsecs_t vsync_timestamp) REQUIRES(mMutex);
+    bool periodConfirmed(nsecs_t vsync_timestamp) REQUIRES(mMutex);
 
     std::unique_ptr<Clock> const mClock;
     std::unique_ptr<VSyncTracker> const mTracker;
@@ -71,10 +73,12 @@
     size_t const mPendingLimit;
 
     std::mutex mMutex;
-    bool mIgnorePresentFences GUARDED_BY(mMutex) = false;
+    bool mInternalIgnoreFences GUARDED_BY(mMutex) = false;
+    bool mExternalIgnoreFences GUARDED_BY(mMutex) = false;
     std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
 
     bool mMoreSamplesNeeded GUARDED_BY(mMutex) = false;
+    bool mPeriodConfirmationInProgress GUARDED_BY(mMutex) = false;
     std::optional<nsecs_t> mPeriodTransitioningTo GUARDED_BY(mMutex);
     std::optional<nsecs_t> mLastHwVsync GUARDED_BY(mMutex);
 
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 2b27884..a25b8a9 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -33,8 +33,10 @@
      * to the model.
      *
      * \param [in] timestamp    The timestamp when the vsync signal was.
+     * \return                  True if the timestamp was consistent with the internal model,
+     *                          False otherwise
      */
-    virtual void addVsyncTimestamp(nsecs_t timestamp) = 0;
+    virtual bool addVsyncTimestamp(nsecs_t timestamp) = 0;
 
     /*
      * Access the next anticipated vsync time such that the anticipated time >= timePoint.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 727d901..e0c3372 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -62,8 +62,10 @@
 #include <renderengine/RenderEngine.h>
 #include <ui/ColorSpace.h>
 #include <ui/DebugUtils.h>
+#include <ui/DisplayConfig.h>
 #include <ui/DisplayInfo.h>
 #include <ui/DisplayStatInfo.h>
+#include <ui/DisplayState.h>
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/PixelFormat.h>
 #include <ui/UiConfig.h>
@@ -116,6 +118,7 @@
 #include "android-base/parseint.h"
 #include "android-base/stringprintf.h"
 
+#include <android/configuration.h>
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/types.h>
@@ -183,6 +186,19 @@
     bool mLocked;
 };
 
+// TODO(b/141333600): Consolidate with HWC2::Display::Config::Builder::getDefaultDensity.
+constexpr float FALLBACK_DENSITY = ACONFIGURATION_DENSITY_TV / 160.f;
+
+float getDensityFromProperty(const char* property, bool required) {
+    char value[PROPERTY_VALUE_MAX];
+    const float density = property_get(property, value, nullptr) > 0 ? std::atof(value) : 0.f;
+    if (!density && required) {
+        ALOGE("%s must be defined as a build property", property);
+        return FALLBACK_DENSITY;
+    }
+    return density / 160.f;
+}
+
 // Currently we only support V0_SRGB and DISPLAY_P3 as composition preference.
 bool validateCompositionDataspace(Dataspace dataspace) {
     return dataspace == Dataspace::V0_SRGB || dataspace == Dataspace::DISPLAY_P3;
@@ -250,7 +266,8 @@
         mFrameTracer(std::make_unique<FrameTracer>()),
         mEventQueue(mFactory.createMessageQueue()),
         mCompositionEngine(mFactory.createCompositionEngine()),
-        mPendingSyncInputWindows(false) {}
+        mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
+        mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)) {}
 
 SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) {
     ALOGI("SurfaceFlinger is starting");
@@ -539,12 +556,6 @@
         readPersistentProperties();
         mBootStage = BootStage::FINISHED;
 
-        if (mRefreshRateConfigs->refreshRateSwitchingSupported()) {
-            // set the refresh rate according to the policy
-            const auto& performanceRefreshRate = mRefreshRateConfigs->getMaxRefreshRateByPolicy();
-            changeRefreshRateLocked(performanceRefreshRate, Scheduler::ConfigEvent::None);
-        }
-
         if (property_get_bool("sf.debug.show_refresh_rate_overlay", false)) {
             mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(*this);
             mRefreshRateOverlay->changeRefreshRate(mRefreshRateConfigs->getCurrentRefreshRate());
@@ -735,8 +746,56 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState* state) {
+    if (!displayToken || !state) {
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock lock(mStateLock);
+
+    const auto display = getDisplayDeviceLocked(displayToken);
+    if (!display) {
+        return NAME_NOT_FOUND;
+    }
+
+    state->layerStack = display->getLayerStack();
+    state->orientation = display->getOrientation();
+
+    const Rect viewport = display->getViewport();
+    state->viewport = viewport.isValid() ? viewport.getSize() : display->getSize();
+
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::getDisplayInfo(const sp<IBinder>& displayToken, DisplayInfo* info) {
+    if (!displayToken || !info) {
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock lock(mStateLock);
+
+    const auto display = getDisplayDeviceLocked(displayToken);
+    if (!display) {
+        return NAME_NOT_FOUND;
+    }
+
+    if (display->isVirtual()) {
+        return INVALID_OPERATION;
+    }
+
+    if (mEmulatedDisplayDensity) {
+        info->density = mEmulatedDisplayDensity;
+    } else {
+        info->density = display->isPrimary() ? mInternalDisplayDensity : FALLBACK_DENSITY;
+    }
+
+    info->secure = display->isSecure();
+
+    return NO_ERROR;
+}
+
 status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& displayToken,
-                                           Vector<DisplayInfo>* configs) {
+                                           Vector<DisplayConfig>* configs) {
     if (!displayToken || !configs) {
         return BAD_VALUE;
     }
@@ -748,78 +807,42 @@
         return NAME_NOT_FOUND;
     }
 
-    // TODO: Not sure if display density should handled by SF any longer
-    class Density {
-        static float getDensityFromProperty(char const* propName) {
-            char property[PROPERTY_VALUE_MAX];
-            float density = 0.0f;
-            if (property_get(propName, property, nullptr) > 0) {
-                density = strtof(property, nullptr);
-            }
-            return density;
-        }
-    public:
-        static float getEmuDensity() {
-            return getDensityFromProperty("qemu.sf.lcd_density"); }
-        static float getBuildDensity()  {
-            return getDensityFromProperty("ro.sf.lcd_density"); }
-    };
+    const bool isInternal = (displayId == getInternalDisplayIdLocked());
 
     configs->clear();
 
     for (const auto& hwConfig : getHwComposer().getConfigs(*displayId)) {
-        DisplayInfo info = DisplayInfo();
+        DisplayConfig config;
 
-        float xdpi = hwConfig->getDpiX();
-        float ydpi = hwConfig->getDpiY();
+        auto width = hwConfig->getWidth();
+        auto height = hwConfig->getHeight();
 
-        info.w = hwConfig->getWidth();
-        info.h = hwConfig->getHeight();
-        // Default display viewport to display width and height
-        info.viewportW = info.w;
-        info.viewportH = info.h;
+        auto xDpi = hwConfig->getDpiX();
+        auto yDpi = hwConfig->getDpiY();
 
-        if (displayId == getInternalDisplayIdLocked()) {
-            // The density of the device is provided by a build property
-            float density = Density::getBuildDensity() / 160.0f;
-            if (density == 0) {
-                // the build doesn't provide a density -- this is wrong!
-                // use xdpi instead
-                ALOGE("ro.sf.lcd_density must be defined as a build property");
-                density = xdpi / 160.0f;
-            }
-            if (Density::getEmuDensity()) {
-                // if "qemu.sf.lcd_density" is specified, it overrides everything
-                xdpi = ydpi = density = Density::getEmuDensity();
-                density /= 160.0f;
-            }
-            info.density = density;
-
-            const auto display = getDefaultDisplayDeviceLocked();
-            info.orientation = display->getOrientation();
-
-            // This is for screenrecord
-            const Rect viewport = display->getViewport();
-            if (viewport.isValid()) {
-                info.viewportW = uint32_t(viewport.getWidth());
-                info.viewportH = uint32_t(viewport.getHeight());
-            }
-            info.layerStack = display->getLayerStack();
-        } else {
-            // TODO: where should this value come from?
-            static const int TV_DENSITY = 213;
-            info.density = TV_DENSITY / 160.0f;
-
-            const auto display = getDisplayDeviceLocked(displayToken);
-            info.layerStack = display->getLayerStack();
+        if (isInternal &&
+            (internalDisplayOrientation == ui::ROTATION_90 ||
+             internalDisplayOrientation == ui::ROTATION_270)) {
+            std::swap(width, height);
+            std::swap(xDpi, yDpi);
         }
 
-        info.xdpi = xdpi;
-        info.ydpi = ydpi;
-        info.fps = 1e9 / hwConfig->getVsyncPeriod();
+        config.resolution = ui::Size(width, height);
 
-        const auto offset = mPhaseConfiguration->getOffsetsForRefreshRate(info.fps);
-        info.appVsyncOffset = offset.late.app;
+        if (mEmulatedDisplayDensity) {
+            config.xDpi = mEmulatedDisplayDensity;
+            config.yDpi = mEmulatedDisplayDensity;
+        } else {
+            config.xDpi = xDpi;
+            config.yDpi = yDpi;
+        }
+
+        const nsecs_t period = hwConfig->getVsyncPeriod();
+        config.refreshRate = 1e9f / period;
+
+        const auto offsets = mPhaseConfiguration->getOffsetsForRefreshRate(config.refreshRate);
+        config.appVsyncOffset = offsets.late.app;
+        config.sfVsyncOffset = offsets.late.sf;
 
         // This is how far in advance a buffer must be queued for
         // presentation at a given time.  If you want a buffer to appear
@@ -833,18 +856,9 @@
         //
         // We add an additional 1ms to allow for processing time and
         // differences between the ideal and actual refresh rate.
-        info.presentationDeadline = hwConfig->getVsyncPeriod() - offset.late.sf + 1000000;
+        config.presentationDeadline = period - config.sfVsyncOffset + 1000000;
 
-        // All non-virtual displays are currently considered secure.
-        info.secure = true;
-
-        if (displayId == getInternalDisplayIdLocked() &&
-            (internalDisplayOrientation == ui::ROTATION_90 ||
-             internalDisplayOrientation == ui::ROTATION_270)) {
-            std::swap(info.w, info.h);
-        }
-
-        configs->push_back(info);
+        configs->push_back(config);
     }
 
     return NO_ERROR;
@@ -2060,7 +2074,7 @@
         compositorTiming = getBE().mCompositorTiming;
     }
 
-    mDrawingState.traverseInZOrder([&](Layer* layer) {
+    mDrawingState.traverse([&](Layer* layer) {
         bool frameLatched = layer->onPostComposition(displayDevice, glCompositionDoneFenceTime,
                                                      presentFenceTime, compositorTiming);
         if (frameLatched) {
@@ -2497,7 +2511,7 @@
     const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
 
     // Notify all layers of available frames
-    mCurrentState.traverseInZOrder([expectedPresentTime](Layer* layer) {
+    mCurrentState.traverse([expectedPresentTime](Layer* layer) {
         layer->notifyAvailableFrames(expectedPresentTime);
     });
 
@@ -2507,7 +2521,7 @@
      */
 
     if ((transactionFlags & eTraversalNeeded) || mTraversalNeededMainThread) {
-        mCurrentState.traverseInZOrder([&](Layer* layer) {
+        mCurrentState.traverse([&](Layer* layer) {
             uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
             if (!trFlags) return;
 
@@ -2554,7 +2568,7 @@
         sp<const DisplayDevice> hintDisplay;
         uint32_t currentlayerStack = 0;
         bool first = true;
-        mCurrentState.traverseInZOrder([&](Layer* layer) {
+        mCurrentState.traverse([&](Layer* layer) {
             // NOTE: we rely on the fact that layers are sorted by
             // layerStack first (so we don't have to traverse the list
             // of displays for every layer).
@@ -2700,8 +2714,7 @@
 
     auto currentConfig = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(primaryDisplayId));
     mRefreshRateConfigs =
-            std::make_unique<scheduler::RefreshRateConfigs>(refresh_rate_switching(false),
-                                                            getHwComposer().getConfigs(
+            std::make_unique<scheduler::RefreshRateConfigs>(getHwComposer().getConfigs(
                                                                     primaryDisplayId),
                                                             currentConfig);
     mRefreshRateStats =
@@ -2782,7 +2795,7 @@
     // clear the "changed" flags in current state
     mCurrentState.colorMatrixChanged = false;
 
-    mDrawingState.traverseInZOrder([&](Layer* layer) {
+    mDrawingState.traverse([&](Layer* layer) {
         layer->commitChildList();
 
         // If the layer can be reached when traversing mDrawingState, then the layer is no
@@ -2793,7 +2806,7 @@
     });
 
     commitOffscreenLayers();
-    mDrawingState.traverseInZOrder([&](Layer* layer) { layer->updateMirrorInfo(); });
+    mDrawingState.traverse([&](Layer* layer) { layer->updateMirrorInfo(); });
 }
 
 void SurfaceFlinger::withTracingLock(std::function<void()> lockedOperation) {
@@ -2818,7 +2831,7 @@
 
 void SurfaceFlinger::commitOffscreenLayers() {
     for (Layer* offscreenLayer : mOffscreenLayers) {
-        offscreenLayer->traverseInZOrder(LayerVector::StateSet::Drawing, [](Layer* layer) {
+        offscreenLayer->traverse(LayerVector::StateSet::Drawing, [](Layer* layer) {
             uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
             if (!trFlags) return;
 
@@ -2859,7 +2872,7 @@
     // 3.) Layer 1 is latched.
     // Display is now waiting on Layer 1's frame, which is behind layer 0's
     // second frame. But layer 0's second frame could be waiting on display.
-    mDrawingState.traverseInZOrder([&](Layer* layer) {
+    mDrawingState.traverse([&](Layer* layer) {
         if (layer->hasReadyFrame()) {
             frameQueued = true;
             if (layer->shouldPresentNow(expectedPresentTime)) {
@@ -2877,7 +2890,7 @@
     // be shown on screen. Therefore, we need to latch and release buffers of offscreen
     // layers to ensure dequeueBuffer doesn't block indefinitely.
     for (Layer* offscreenLayer : mOffscreenLayers) {
-        offscreenLayer->traverseInZOrder(LayerVector::StateSet::Drawing,
+        offscreenLayer->traverse(LayerVector::StateSet::Drawing,
                                          [&](Layer* l) { l->latchAndReleaseBuffer(); });
     }
 
@@ -2912,7 +2925,7 @@
         mBootStage = BootStage::BOOTANIMATION;
     }
 
-    mDrawingState.traverseInZOrder([&](Layer* layer) { layer->updateCloneBufferInfo(); });
+    mDrawingState.traverse([&](Layer* layer) { layer->updateCloneBufferInfo(); });
 
     // Only continue with the refresh if there is actually new work to do
     return !mLayersWithQueuedFrames.empty() && newDataLatched;
@@ -3536,7 +3549,9 @@
         }
     }
     if (what & layer_state_t::eFrameRateChanged) {
-        if (layer->setFrameRate(s.frameRate)) flags |= eTraversalNeeded;
+        if (layer->setFrameRate(
+                    Layer::FrameRate(s.frameRate, Layer::FrameRateCompatibility::Default)))
+            flags |= eTraversalNeeded;
     }
     // This has to happen after we reparent children because when we reparent to null we remove
     // child layers from current state and remove its relative z. If the children are reparented in
@@ -3578,7 +3593,8 @@
         buffer = s.buffer;
     }
     if (buffer) {
-        if (layer->setBuffer(buffer, postTime, desiredPresentTime, s.cachedBuffer)) {
+        if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime,
+                             s.cachedBuffer)) {
             flags |= eTraversalNeeded;
         }
     }
@@ -3734,7 +3750,7 @@
     bool matchFound = true;
     while (matchFound) {
         matchFound = false;
-        mCurrentState.traverseInZOrder([&](Layer* layer) {
+        mCurrentState.traverse([&](Layer* layer) {
             if (layer->getName() == uniqueName) {
                 matchFound = true;
                 uniqueName = base::StringPrintf("%s#%u", name, ++dupeCounter);
@@ -4105,7 +4121,7 @@
     const bool clearAll = args.size() < 2;
     const auto name = clearAll ? String8() : String8(args[1]);
 
-    mCurrentState.traverseInZOrder([&](Layer* layer) {
+    mCurrentState.traverse([&](Layer* layer) {
         if (clearAll || layer->getName() == name.string()) {
             layer->clearFrameStats();
         }
@@ -4121,7 +4137,7 @@
 // This should only be called from the main thread.  Otherwise it would need
 // the lock and should use mCurrentState rather than mDrawingState.
 void SurfaceFlinger::logFrameStats() {
-    mDrawingState.traverseInZOrder([&](Layer* layer) {
+    mDrawingState.traverse([&](Layer* layer) {
         layer->logFrameStats();
     });
 
@@ -4356,7 +4372,7 @@
     result.append("Offscreen Layers:\n");
     postMessageSync(new LambdaMessage([&]() {
         for (Layer* offscreenLayer : mOffscreenLayers) {
-            offscreenLayer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
+            offscreenLayer->traverse(LayerVector::StateSet::Drawing, [&](Layer* layer) {
                 layer->dumpCallingUidPid(result);
             });
         }
@@ -4620,7 +4636,9 @@
         case GET_PHYSICAL_DISPLAY_TOKEN:
         case GET_DISPLAY_COLOR_MODES:
         case GET_DISPLAY_NATIVE_PRIMARIES:
+        case GET_DISPLAY_INFO:
         case GET_DISPLAY_CONFIGS:
+        case GET_DISPLAY_STATE:
         case GET_DISPLAY_STATS:
         case GET_SUPPORTED_FRAME_TIMESTAMPS:
         // Calling setTransactionState is safe, because you need to have been
@@ -4650,12 +4668,6 @@
             }
             return OK;
         }
-        // The following codes are deprecated and should never be allowed to access SF.
-        case CONNECT_DISPLAY_UNUSED:
-        case CREATE_GRAPHIC_BUFFER_ALLOC_UNUSED: {
-            ALOGE("Attempting to access SurfaceFlinger with unused code: %u", code);
-            return PERMISSION_DENIED;
-        }
         case CAPTURE_SCREEN_BY_ID: {
             IPCThreadState* ipc = IPCThreadState::self();
             const int uid = ipc->getCallingUid();
@@ -5594,6 +5606,10 @@
 
 // ---------------------------------------------------------------------------
 
+void SurfaceFlinger::State::traverse(const LayerVector::Visitor& visitor) const {
+    layersSortedByZ.traverse(visitor);
+}
+
 void SurfaceFlinger::State::traverseInZOrder(const LayerVector::Visitor& visitor) const {
     layersSortedByZ.traverseInZOrder(stateSet, visitor);
 }
@@ -5683,26 +5699,19 @@
     mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
                                 display->getActiveConfig(), vsyncPeriod);
 
-    if (mRefreshRateConfigs->refreshRateSwitchingSupported()) {
-        auto configId = mScheduler->getPreferredConfigId();
-        auto preferredRefreshRate = configId
-                ? mRefreshRateConfigs->getRefreshRateFromConfigId(*configId)
-                : mRefreshRateConfigs->getMinRefreshRateByPolicy();
-        ALOGV("trying to switch to Scheduler preferred config %d (%s)",
-              preferredRefreshRate.configId.value(), preferredRefreshRate.name.c_str());
-        if (isDisplayConfigAllowed(preferredRefreshRate.configId)) {
-            ALOGV("switching to Scheduler preferred config %d",
-                  preferredRefreshRate.configId.value());
-            setDesiredActiveConfig(
-                    {preferredRefreshRate.configId, Scheduler::ConfigEvent::Changed});
-        } else {
-            // Set the highest allowed config
-            setDesiredActiveConfig({mRefreshRateConfigs->getMaxRefreshRateByPolicy().configId,
-                                    Scheduler::ConfigEvent::Changed});
-        }
+    auto configId = mScheduler->getPreferredConfigId();
+    auto preferredRefreshRate = configId
+            ? mRefreshRateConfigs->getRefreshRateFromConfigId(*configId)
+            // NOTE: Choose the default config ID, if Scheduler doesn't have one in mind.
+            : mRefreshRateConfigs->getRefreshRateFromConfigId(defaultConfig);
+    ALOGV("trying to switch to Scheduler preferred config %d (%s)",
+          preferredRefreshRate.configId.value(), preferredRefreshRate.name.c_str());
+
+    if (isDisplayConfigAllowed(preferredRefreshRate.configId)) {
+        ALOGV("switching to Scheduler preferred config %d", preferredRefreshRate.configId.value());
+        setDesiredActiveConfig({preferredRefreshRate.configId, Scheduler::ConfigEvent::Changed});
     } else {
-        ALOGV("switching to config %d", defaultConfig.value());
-        setDesiredActiveConfig({defaultConfig, Scheduler::ConfigEvent::Changed});
+        LOG_ALWAYS_FATAL("Desired config not allowed: %d", preferredRefreshRate.configId.value());
     }
 
     return NO_ERROR;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index f8980a5..ccf5794 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -384,6 +384,7 @@
 
         renderengine::ShadowSettings globalShadowSettings;
 
+        void traverse(const LayerVector::Visitor& visitor) const;
         void traverseInZOrder(const LayerVector::Visitor& visitor) const;
         void traverseInReverseZOrder(const LayerVector::Visitor& visitor) const;
     };
@@ -433,13 +434,13 @@
             float frameScale, bool childrenOnly) override;
 
     status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats) override;
-    status_t getDisplayConfigs(const sp<IBinder>& displayToken,
-                               Vector<DisplayInfo>* configs) override;
+    status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*) override;
+    status_t getDisplayInfo(const sp<IBinder>& displayToken, DisplayInfo*) override;
+    status_t getDisplayConfigs(const sp<IBinder>& displayToken, Vector<DisplayConfig>*) override;
     int getActiveConfig(const sp<IBinder>& displayToken) override;
-    status_t getDisplayColorModes(const sp<IBinder>& displayToken,
-                                  Vector<ui::ColorMode>* configs) override;
+    status_t getDisplayColorModes(const sp<IBinder>& displayToken, Vector<ui::ColorMode>*) override;
     status_t getDisplayNativePrimaries(const sp<IBinder>& displayToken,
-                                       ui::DisplayPrimaries &primaries);
+                                       ui::DisplayPrimaries&) override;
     ui::ColorMode getActiveColorMode(const sp<IBinder>& displayToken) override;
     status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode) override;
     status_t getAutoLowLatencyModeSupport(const sp<IBinder>& displayToken,
@@ -1164,6 +1165,9 @@
     sp<RegionSamplingThread> mRegionSamplingThread;
     ui::DisplayPrimaries mInternalDisplayPrimaries;
 
+    const float mInternalDisplayDensity;
+    const float mEmulatedDisplayDensity;
+
     sp<IInputFlinger> mInputFlinger;
     InputWindowCommands mPendingInputWindowCommands GUARDED_BY(mStateLock);
     // Should only be accessed by the main thread.
@@ -1180,7 +1184,7 @@
 
     const sp<SetInputWindowsListener> mSetInputWindowsListener = new SetInputWindowsListener(this);
 
-    bool mPendingSyncInputWindows GUARDED_BY(mStateLock);
+    bool mPendingSyncInputWindows GUARDED_BY(mStateLock) = false;
     Hwc2::impl::PowerAdvisor mPowerAdvisor;
 
     // This should only be accessed on the main thread.
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index b4716eb..768074a 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -226,14 +226,6 @@
     return static_cast<int64_t>(defaultValue);
 }
 
-bool refresh_rate_switching(bool defaultValue) {
-    auto temp = SurfaceFlingerProperties::refresh_rate_switching();
-    if (temp.has_value()) {
-        return *temp;
-    }
-    return defaultValue;
-}
-
 int32_t set_idle_timer_ms(int32_t defaultValue) {
     auto temp = SurfaceFlingerProperties::set_idle_timer_ms();
     if (temp.has_value()) {
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index e394cca..5f88322 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -73,8 +73,6 @@
 int64_t color_space_agnostic_dataspace(
         android::hardware::graphics::common::V1_2::Dataspace defaultValue);
 
-bool refresh_rate_switching(bool defaultValue);
-
 int32_t set_idle_timer_ms(int32_t defaultValue);
 
 int32_t set_touch_timer_ms(int32_t defaultValue);
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 12c98da..fdf8a41 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -40,13 +40,18 @@
 status_pull_atom_return_t TimeStats::pullAtomCallback(int32_t atom_tag,
                                                       pulled_stats_event_list* data, void* cookie) {
     impl::TimeStats* timeStats = reinterpret_cast<impl::TimeStats*>(cookie);
+    status_pull_atom_return_t result = STATS_PULL_SKIP;
     if (atom_tag == android::util::SURFACEFLINGER_STATS_GLOBAL_INFO) {
-        return timeStats->populateGlobalAtom(data);
+        result = timeStats->populateGlobalAtom(data);
     } else if (atom_tag == android::util::SURFACEFLINGER_STATS_LAYER_INFO) {
-        return timeStats->populateLayerAtom(data);
+        result = timeStats->populateLayerAtom(data);
     }
 
-    return STATS_PULL_SKIP;
+    // Enable timestats now. The first full pull for a given build is expected to
+    // have empty or very little stats, as stats are first enabled after the
+    // first pull is completed for either the global or layer stats.
+    timeStats->enable();
+    return result;
 }
 
 status_pull_atom_return_t TimeStats::populateGlobalAtom(pulled_stats_event_list* data) {
@@ -167,13 +172,19 @@
     }
 }
 
+TimeStats::~TimeStats() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mStatsDelegate->unregisterStatsPullAtomCallback(
+            android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
+    mStatsDelegate->unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO);
+}
+
 void TimeStats::onBootFinished() {
-    // Temporarily enable TimeStats by default. Telemetry is disabled while
-    // we move onto statsd, so TimeStats is currently not exercised at all
-    // during testing without enabling by default.
-    // TODO: remove this, as we should only be paying this overhead on devices
-    // where statsd exists.
-    enable();
+    std::lock_guard<std::mutex> lock(mMutex);
+    mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+                                                  TimeStats::pullAtomCallback, nullptr, this);
+    mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+                                                  TimeStats::pullAtomCallback, nullptr, this);
 }
 
 void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::string& result) {
@@ -352,7 +363,12 @@
             TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[layerName];
             timeStatsLayer.totalFrames++;
             timeStatsLayer.droppedFrames += layerRecord.droppedFrames;
+            timeStatsLayer.lateAcquireFrames += layerRecord.lateAcquireFrames;
+            timeStatsLayer.badDesiredPresentFrames += layerRecord.badDesiredPresentFrames;
+
             layerRecord.droppedFrames = 0;
+            layerRecord.lateAcquireFrames = 0;
+            layerRecord.badDesiredPresentFrames = 0;
 
             const int32_t postToAcquireMs = msBetween(timeRecords[0].frameTime.postTime,
                                                       timeRecords[0].frameTime.acquireTime);
@@ -466,6 +482,36 @@
     }
 }
 
+void TimeStats::incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) {
+    if (!mEnabled.load()) return;
+
+    ATRACE_CALL();
+    ALOGV("[%d]-LatchSkipped-Reason[%d]", layerId,
+          static_cast<std::underlying_type<LatchSkipReason>::type>(reason));
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
+
+    switch (reason) {
+        case LatchSkipReason::LateAcquire:
+            layerRecord.lateAcquireFrames++;
+            break;
+    }
+}
+
+void TimeStats::incrementBadDesiredPresent(int32_t layerId) {
+    if (!mEnabled.load()) return;
+
+    ATRACE_CALL();
+    ALOGV("[%d]-BadDesiredPresent", layerId);
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
+    layerRecord.badDesiredPresentFrames++;
+}
+
 void TimeStats::setDesiredTime(int32_t layerId, uint64_t frameNumber, nsecs_t desiredTime) {
     if (!mEnabled.load()) return;
 
@@ -735,10 +781,6 @@
     mEnabled.store(true);
     mTimeStats.statsStart = static_cast<int64_t>(std::time(0));
     mPowerTime.prevTime = systemTime();
-    mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
-                                                  TimeStats::pullAtomCallback, nullptr, this);
-    mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
-                                                  TimeStats::pullAtomCallback, nullptr, this);
     ALOGD("Enabled");
 }
 
@@ -751,9 +793,6 @@
     flushPowerTimeLocked();
     mEnabled.store(false);
     mTimeStats.statsEnd = static_cast<int64_t>(std::time(0));
-    mStatsDelegate->unregisterStatsPullAtomCallback(
-            android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
-    mStatsDelegate->unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO);
     ALOGD("Disabled");
 }
 
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 71f06af..7f58725 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -70,6 +70,18 @@
     virtual void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
                              nsecs_t postTime) = 0;
     virtual void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) = 0;
+    // Reasons why latching a particular buffer may be skipped
+    enum class LatchSkipReason {
+        // If the acquire fence did not fire on some devices we skip latching
+        // the buffer until the fence fires.
+        LateAcquire,
+    };
+    // Increments the counter of skipped latch buffers.
+    virtual void incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) = 0;
+    // Increments the counter of bad desired present times for this layer.
+    // Bad desired present times are "implausible" and cause SurfaceFlinger to
+    // latch a buffer immediately to avoid stalling.
+    virtual void incrementBadDesiredPresent(int32_t layerId) = 0;
     virtual void setDesiredTime(int32_t layerId, uint64_t frameNumber, nsecs_t desiredTime) = 0;
     virtual void setAcquireTime(int32_t layerId, uint64_t frameNumber, nsecs_t acquireTime) = 0;
     virtual void setAcquireFence(int32_t layerId, uint64_t frameNumber,
@@ -116,6 +128,8 @@
         // fences to signal, but rather waiting to receive those fences/timestamps.
         int32_t waitData = -1;
         uint32_t droppedFrames = 0;
+        uint32_t lateAcquireFrames = 0;
+        uint32_t badDesiredPresentFrames = 0;
         TimeRecord prevTimeRecord;
         std::deque<TimeRecord> timeRecords;
     };
@@ -182,6 +196,8 @@
               std::optional<size_t> maxPulledLayers,
               std::optional<size_t> maxPulledHistogramBuckets);
 
+    ~TimeStats() override;
+
     void onBootFinished() override;
     void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override;
     bool isEnabled() override;
@@ -200,6 +216,8 @@
     void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
                      nsecs_t postTime) override;
     void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) override;
+    void incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) override;
+    void incrementBadDesiredPresent(int32_t layerId) override;
     void setDesiredTime(int32_t layerId, uint64_t frameNumber, nsecs_t desiredTime) override;
     void setAcquireTime(int32_t layerId, uint64_t frameNumber, nsecs_t acquireTime) override;
     void setAcquireFence(int32_t layerId, uint64_t frameNumber,
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index 0ba90e2..e2f85cc 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -83,6 +83,8 @@
     StringAppendF(&result, "packageName = %s\n", packageName.c_str());
     StringAppendF(&result, "totalFrames = %d\n", totalFrames);
     StringAppendF(&result, "droppedFrames = %d\n", droppedFrames);
+    StringAppendF(&result, "lateAcquireFrames = %d\n", lateAcquireFrames);
+    StringAppendF(&result, "badDesiredPresentFrames = %d\n", badDesiredPresentFrames);
     const auto iter = deltas.find("present2present");
     if (iter != deltas.end()) {
         StringAppendF(&result, "averageFPS = %.3f\n", 1000.0 / iter->second.averageTime());
@@ -112,8 +114,14 @@
     StringAppendF(&result, "totalP2PTime = %" PRId64 " ms\n", presentToPresent.totalTime());
     StringAppendF(&result, "presentToPresent histogram is as below:\n");
     result.append(presentToPresent.toString());
+    const float averageFrameDuration = frameDuration.averageTime();
+    StringAppendF(&result, "averageFrameDuration = %.3f ms\n",
+                  std::isnan(averageFrameDuration) ? 0.0f : averageFrameDuration);
     StringAppendF(&result, "frameDuration histogram is as below:\n");
     result.append(frameDuration.toString());
+    const float averageRenderEngineTiming = renderEngineTiming.averageTime();
+    StringAppendF(&result, "averageRenderEngineTiming = %.3f ms\n",
+                  std::isnan(averageRenderEngineTiming) ? 0.0f : averageRenderEngineTiming);
     StringAppendF(&result, "renderEngineTiming histogram is as below:\n");
     result.append(renderEngineTiming.toString());
     const auto dumpStats = generateDumpStats(maxLayers);
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 702c50e..e374b73 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -46,6 +46,8 @@
         std::string packageName;
         int32_t totalFrames = 0;
         int32_t droppedFrames = 0;
+        int32_t lateAcquireFrames = 0;
+        int32_t badDesiredPresentFrames = 0;
         std::unordered_map<std::string, Histogram> deltas;
 
         std::string toString() const;
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index ed2b220..0653959 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -301,18 +301,6 @@
     prop_name: "ro.surface_flinger.display_primary_white"
 }
 
-# refreshRateSwitching indicates whether SurfaceFlinger should use refresh rate
-# switching on the device, e.g. to switch between 60 and 90 Hz. The settings
-# below that are related to refresh rate switching will only have an effect if
-# refresh_rate_switching is enabled.
-prop {
-    api_name: "refresh_rate_switching"
-    type: Boolean
-    scope: System
-    access: Readonly
-    prop_name: "ro.surface_flinger.refresh_rate_switching"
-}
-
 prop {
     api_name: "set_idle_timer_ms"
     type: Integer
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index d24ad18..1adab84 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -73,10 +73,6 @@
     enum_values: "ORIENTATION_0|ORIENTATION_90|ORIENTATION_180|ORIENTATION_270"
   }
   prop {
-    api_name: "refresh_rate_switching"
-    prop_name: "ro.surface_flinger.refresh_rate_switching"
-  }
-  prop {
     api_name: "running_without_sync_framework"
     prop_name: "ro.surface_flinger.running_without_sync_framework"
   }
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index f339ab0..507d28b 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -1,20 +1,15 @@
-#include <algorithm>
-#include <functional>
-#include <limits>
-#include <ostream>
-
 #include <gtest/gtest.h>
-
 #include <gui/ISurfaceComposer.h>
 #include <gui/LayerDebugInfo.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
-
 #include <private/android_filesystem_config.h>
 #include <private/gui/ComposerService.h>
-#include <ui/DisplayInfo.h>
+#include <ui/DisplayConfig.h>
 #include <utils/String8.h>
 
+#include <functional>
+
 namespace android {
 
 using Transaction = SurfaceComposerClient::Transaction;
@@ -67,14 +62,13 @@
         mDisplay = SurfaceComposerClient::getInternalDisplayToken();
         ASSERT_FALSE(mDisplay == nullptr);
 
-        DisplayInfo info;
-        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(mDisplay, &info));
-        const ssize_t displayWidth = info.w;
-        const ssize_t displayHeight = info.h;
+        DisplayConfig config;
+        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(mDisplay, &config));
 
         // Background surface
         mBGSurfaceControl =
-                mComposerClient->createSurface(SURFACE_NAME, displayWidth, displayHeight,
+                mComposerClient->createSurface(SURFACE_NAME, config.resolution.getWidth(),
+                                               config.resolution.getHeight(),
                                                PIXEL_FORMAT_RGBA_8888, 0);
         ASSERT_TRUE(mBGSurfaceControl != nullptr);
         ASSERT_TRUE(mBGSurfaceControl->isValid());
@@ -188,10 +182,10 @@
     const auto display = SurfaceComposerClient::getInternalDisplayToken();
     ASSERT_TRUE(display != nullptr);
 
-    DisplayInfo info;
-    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
+    DisplayConfig config;
+    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
 
-    Vector<DisplayInfo> configs;
+    Vector<DisplayConfig> configs;
     ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
 
     ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveConfig(display));
@@ -255,17 +249,6 @@
     ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, false));
 }
 
-TEST_F(CredentialsTest, DISABLED_DestroyDisplayTest) {
-    setupVirtualDisplay();
-
-    DisplayInfo info;
-    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(mVirtualDisplay, &info));
-    SurfaceComposerClient::destroyDisplay(mVirtualDisplay);
-    // This test currently fails. TODO(b/112002626): Find a way to properly create
-    // a display in the test environment, so that destroy display can remove it.
-    ASSERT_EQ(NAME_NOT_FOUND, SurfaceComposerClient::getDisplayInfo(mVirtualDisplay, &info));
-}
-
 TEST_F(CredentialsTest, CaptureTest) {
     const auto display = SurfaceComposerClient::getInternalDisplayToken();
     std::function<status_t()> condition = [=]() {
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
index 3aa4474..0ed2ffb 100644
--- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -46,24 +46,25 @@
                                                                        &initialMin, &initialMax);
     ASSERT_EQ(res, NO_ERROR);
 
-    Vector<DisplayInfo> configs;
+    Vector<DisplayConfig> configs;
     res = SurfaceComposerClient::getDisplayConfigs(mDisplayToken, &configs);
     ASSERT_EQ(res, NO_ERROR);
 
     for (size_t i = 0; i < configs.size(); i++) {
-        res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, i, configs[i].fps,
-                                                                  configs[i].fps);
+        res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, i,
+                                                                  configs[i].refreshRate,
+                                                                  configs[i].refreshRate);
         ASSERT_EQ(res, NO_ERROR);
 
         int defaultConfig;
-        float minFps;
-        float maxFps;
+        float minRefreshRate;
+        float maxRefreshRate;
         res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfig,
-                                                                  &minFps, &maxFps);
+                                                                  &minRefreshRate, &maxRefreshRate);
         ASSERT_EQ(res, NO_ERROR);
         ASSERT_EQ(defaultConfig, i);
-        ASSERT_EQ(minFps, configs[i].fps);
-        ASSERT_EQ(maxFps, configs[i].fps);
+        ASSERT_EQ(minRefreshRate, configs[i].refreshRate);
+        ASSERT_EQ(maxRefreshRate, configs[i].refreshRate);
     }
 
     res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, initialDefaultConfig,
diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp
index 8a756a6..4023c66 100644
--- a/services/surfaceflinger/tests/IPC_test.cpp
+++ b/services/surfaceflinger/tests/IPC_test.cpp
@@ -14,24 +14,20 @@
  * limitations under the License.
  */
 
-#include <gtest/gtest.h>
-
 #include <binder/IInterface.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/ProcessState.h>
-
+#include <gtest/gtest.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/LayerState.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
+#include <ui/DisplayConfig.h>
+#include <utils/String8.h>
 
 #include <limits>
 
-#include <ui/DisplayInfo.h>
-
-#include <utils/String8.h>
-
 #include "BufferGenerator.h"
 #include "utils/CallbackUtils.h"
 #include "utils/ColorUtils.h"
@@ -231,10 +227,10 @@
         ASSERT_EQ(NO_ERROR, mClient->initCheck());
 
         mPrimaryDisplay = mClient->getInternalDisplayToken();
-        DisplayInfo info;
-        mClient->getDisplayInfo(mPrimaryDisplay, &info);
-        mDisplayWidth = info.w;
-        mDisplayHeight = info.h;
+        DisplayConfig config;
+        mClient->getActiveDisplayConfig(mPrimaryDisplay, &config);
+        mDisplayWidth = config.resolution.getWidth();
+        mDisplayHeight = config.resolution.getHeight();
 
         Transaction setupTransaction;
         setupTransaction.setDisplayLayerStack(mPrimaryDisplay, 0);
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index f7a6d96..5eb1739 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -13,17 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#ifndef ANDROID_LAYER_TRANSACTION_TEST_H
-#define ANDROID_LAYER_TRANSACTION_TEST_H
+
+#pragma once
 
 #include <gtest/gtest.h>
-
 #include <gui/ISurfaceComposer.h>
 #include <gui/SurfaceComposerClient.h>
 #include <hardware/hwcomposer_defs.h>
 #include <private/gui/ComposerService.h>
-
-#include <ui/DisplayInfo.h>
+#include <ui/DisplayConfig.h>
 
 #include "BufferGenerator.h"
 #include "utils/ScreenshotUtils.h"
@@ -255,18 +253,16 @@
         mDisplay = mClient->getInternalDisplayToken();
         ASSERT_FALSE(mDisplay == nullptr) << "failed to get display";
 
-        // get display width/height
-        DisplayInfo info;
-        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(mDisplay, &info));
-        mDisplayWidth = info.w;
-        mDisplayHeight = info.h;
-        mDisplayRect =
-                Rect(static_cast<int32_t>(mDisplayWidth), static_cast<int32_t>(mDisplayHeight));
+        DisplayConfig config;
+        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(mDisplay, &config));
+        mDisplayRect = Rect(config.resolution);
+        mDisplayWidth = mDisplayRect.getWidth();
+        mDisplayHeight = mDisplayRect.getHeight();
 
         // After a new buffer is queued, SurfaceFlinger is notified and will
         // latch the new buffer on next vsync.  Let's heuristically wait for 3
         // vsyncs.
-        mBufferPostDelay = int32_t(1e6 / info.fps) * 3;
+        mBufferPostDelay = static_cast<int32_t>(1e6 / config.refreshRate) * 3;
 
         mDisplayLayerStack = 0;
 
@@ -295,6 +291,5 @@
 
     friend class LayerRenderPathTestHarness;
 };
-} // namespace android
 
-#endif
+} // namespace android
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index 0459386..a1c4128 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -36,14 +36,13 @@
         const auto display = SurfaceComposerClient::getInternalDisplayToken();
         ASSERT_FALSE(display == nullptr);
 
-        DisplayInfo info;
-        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
-
-        ssize_t displayWidth = info.w;
-        ssize_t displayHeight = info.h;
+        DisplayConfig config;
+        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        const ui::Size& resolution = config.resolution;
 
         // Background surface
-        mBGSurfaceControl = createLayer(String8("BG Test Surface"), displayWidth, displayHeight, 0);
+        mBGSurfaceControl = createLayer(String8("BG Test Surface"), resolution.getWidth(),
+                                        resolution.getHeight(), 0);
         ASSERT_TRUE(mBGSurfaceControl != nullptr);
         ASSERT_TRUE(mBGSurfaceControl->isValid());
         TransactionUtils::fillSurfaceRGBA8(mBGSurfaceControl, 63, 63, 195);
@@ -73,7 +72,8 @@
                     .show(mFGSurfaceControl);
 
             t.setLayer(mSyncSurfaceControl, INT32_MAX - 1)
-                    .setPosition(mSyncSurfaceControl, displayWidth - 2, displayHeight - 2)
+                    .setPosition(mSyncSurfaceControl, resolution.getWidth() - 2,
+                                 resolution.getHeight() - 2)
                     .show(mSyncSurfaceControl);
         });
     }
diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
index e525e2a..c9fdc3b 100644
--- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
+++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
@@ -18,6 +18,8 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
 
+#include <ui/DisplayState.h>
+
 #include "LayerTransactionTest.h"
 
 namespace android {
@@ -34,12 +36,14 @@
         ASSERT_EQ(NO_ERROR, mClient->initCheck());
 
         mMainDisplay = SurfaceComposerClient::getInternalDisplayToken();
-        SurfaceComposerClient::getDisplayInfo(mMainDisplay, &mMainDisplayInfo);
+        SurfaceComposerClient::getDisplayState(mMainDisplay, &mMainDisplayState);
+        SurfaceComposerClient::getActiveDisplayConfig(mMainDisplay, &mMainDisplayConfig);
 
         sp<IGraphicBufferConsumer> consumer;
         BufferQueue::createBufferQueue(&mProducer, &consumer);
         consumer->setConsumerName(String8("Virtual disp consumer"));
-        consumer->setDefaultBufferSize(mMainDisplayInfo.w, mMainDisplayInfo.h);
+        consumer->setDefaultBufferSize(mMainDisplayConfig.resolution.getWidth(),
+                                       mMainDisplayConfig.resolution.getHeight());
     }
 
     virtual void TearDown() {
@@ -48,14 +52,14 @@
         mColorLayer = 0;
     }
 
-    void createDisplay(const Rect& layerStackRect, uint32_t layerStack) {
+    void createDisplay(const ui::Size& layerStackSize, 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));
+            t.setDisplayProjection(mVirtualDisplay, mMainDisplayState.orientation,
+                                   Rect(layerStackSize), Rect(mMainDisplayConfig.resolution));
         });
     }
 
@@ -76,7 +80,8 @@
         });
     }
 
-    DisplayInfo mMainDisplayInfo;
+    ui::DisplayState mMainDisplayState;
+    DisplayConfig mMainDisplayConfig;
     sp<IBinder> mMainDisplay;
     sp<IBinder> mVirtualDisplay;
     sp<IGraphicBufferProducer> mProducer;
@@ -85,7 +90,7 @@
 };
 
 TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInVirtualDisplay) {
-    createDisplay({mMainDisplayInfo.viewportW, mMainDisplayInfo.viewportH}, 1 /* layerStack */);
+    createDisplay(mMainDisplayState.viewport, 1 /* layerStack */);
     createColorLayer(1 /* layerStack */);
 
     asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
@@ -108,7 +113,7 @@
 
     // 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 */);
+    createDisplay(mMainDisplayState.viewport, 0 /* layerStack */);
     createColorLayer(0 /* layerStack */);
 
     asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index 4a2ab7c..0e7eba8 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -20,18 +20,13 @@
 
 #include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
 #include <google/protobuf/io/zero_copy_stream_impl.h>
-
 #include <gtest/gtest.h>
-
-#include <android/native_window.h>
-
 #include <gui/ISurfaceComposer.h>
 #include <gui/LayerState.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
-
 #include <private/gui/ComposerService.h>
-#include <ui/DisplayInfo.h>
+#include <ui/DisplayConfig.h>
 
 #include <fstream>
 #include <random>
@@ -271,21 +266,21 @@
     const auto display = SurfaceComposerClient::getInternalDisplayToken();
     ASSERT_FALSE(display == nullptr);
 
-    DisplayInfo info;
-    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
-
-    ssize_t displayWidth = info.w;
-    ssize_t displayHeight = info.h;
+    DisplayConfig config;
+    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+    const ui::Size& resolution = config.resolution;
 
     // Background surface
-    mBGSurfaceControl = mComposerClient->createSurface(String8(TEST_BG_SURFACE_NAME), displayWidth,
-                                                       displayHeight, PIXEL_FORMAT_RGBA_8888, 0);
+    mBGSurfaceControl =
+            mComposerClient->createSurface(String8(TEST_BG_SURFACE_NAME), resolution.getWidth(),
+                                           resolution.getHeight(), PIXEL_FORMAT_RGBA_8888, 0);
     ASSERT_TRUE(mBGSurfaceControl != nullptr);
     ASSERT_TRUE(mBGSurfaceControl->isValid());
 
     // Foreground surface
-    mFGSurfaceControl = mComposerClient->createSurface(String8(TEST_FG_SURFACE_NAME), displayWidth,
-                                                       displayHeight, PIXEL_FORMAT_RGBA_8888, 0);
+    mFGSurfaceControl =
+            mComposerClient->createSurface(String8(TEST_FG_SURFACE_NAME), resolution.getWidth(),
+                                           resolution.getHeight(), PIXEL_FORMAT_RGBA_8888, 0);
     ASSERT_TRUE(mFGSurfaceControl != nullptr);
     ASSERT_TRUE(mFGSurfaceControl->isValid());
 
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index 5612bb2..040852f 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -16,41 +16,10 @@
 #ifndef ANDROID_TRANSACTION_TEST_HARNESSES
 #define ANDROID_TRANSACTION_TEST_HARNESSES
 
-/*#include <algorithm>
-#include <chrono>
-#include <cinttypes>
-#include <functional>
-#include <limits>
-#include <ostream>
+#include <ui/DisplayState.h>
 
-#include <android/native_window.h>
-
-#include <binder/ProcessState.h>
-#include <gui/BufferItemConsumer.h>
-#include <gui/IProducerListener.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/LayerState.h>
-#include <gui/Surface.h>
-#include <gui/SurfaceComposerClient.h>
-#include <hardware/hwcomposer_defs.h>
-#include <private/android_filesystem_config.h>
-#include <private/gui/ComposerService.h>
-
-#include <ui/DisplayInfo.h>
-
-#include <math.h>
-#include <math/vec3.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "BufferGenerator.h"
-*/
 #include "LayerTransactionTest.h"
-/*#include "utils/CallbackUtils.h"
-#include "utils/ColorUtils.h"
-#include "utils/ScreenshotUtils.h"
-#include "utils/TransactionUtils.h"
-*/
+
 namespace android {
 
 using android::hardware::graphics::common::V1_1::BufferUsage;
@@ -66,9 +35,14 @@
                 return mDelegate->screenshot();
             case RenderPath::VIRTUAL_DISPLAY:
 
-                const auto mainDisplay = SurfaceComposerClient::getInternalDisplayToken();
-                DisplayInfo mainDisplayInfo;
-                SurfaceComposerClient::getDisplayInfo(mainDisplay, &mainDisplayInfo);
+                const auto displayToken = SurfaceComposerClient::getInternalDisplayToken();
+
+                ui::DisplayState displayState;
+                SurfaceComposerClient::getDisplayState(displayToken, &displayState);
+
+                DisplayConfig displayConfig;
+                SurfaceComposerClient::getActiveDisplayConfig(displayToken, &displayConfig);
+                const ui::Size& resolution = displayConfig.resolution;
 
                 sp<IBinder> vDisplay;
                 sp<IGraphicBufferProducer> producer;
@@ -77,7 +51,7 @@
                 BufferQueue::createBufferQueue(&producer, &consumer);
 
                 consumer->setConsumerName(String8("Virtual disp consumer"));
-                consumer->setDefaultBufferSize(mainDisplayInfo.w, mainDisplayInfo.h);
+                consumer->setDefaultBufferSize(resolution.getWidth(), resolution.getHeight());
 
                 itemConsumer = new BufferItemConsumer(consumer,
                                                       // Sample usage bits from screenrecord
@@ -90,9 +64,8 @@
                 SurfaceComposerClient::Transaction t;
                 t.setDisplaySurface(vDisplay, producer);
                 t.setDisplayLayerStack(vDisplay, 0);
-                t.setDisplayProjection(vDisplay, mainDisplayInfo.orientation,
-                                       Rect(mainDisplayInfo.viewportW, mainDisplayInfo.viewportH),
-                                       Rect(mainDisplayInfo.w, mainDisplayInfo.h));
+                t.setDisplayProjection(vDisplay, displayState.orientation,
+                                       Rect(displayState.viewport), Rect(resolution));
                 t.apply();
                 SurfaceComposerClient::Transaction().apply(true);
                 BufferItem item;
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 6874f6f..e751496 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -41,7 +41,7 @@
 #include <hwbinder/ProcessState.h>
 #include <log/log.h>
 #include <private/gui/ComposerService.h>
-#include <ui/DisplayInfo.h>
+#include <ui/DisplayConfig.h>
 #include <utils/Looper.h>
 
 #include <gmock/gmock.h>
@@ -338,15 +338,16 @@
             const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
             EXPECT_FALSE(display == nullptr);
 
-            DisplayInfo info;
-            EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
-            EXPECT_EQ(200u, info.w);
-            EXPECT_EQ(400u, info.h);
-            EXPECT_EQ(1e9f / 16'666'666, info.fps);
+            DisplayConfig config;
+            EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+            const ui::Size& resolution = config.resolution;
+            EXPECT_EQ(ui::Size(200, 400), resolution);
+            EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
 
             auto surfaceControl =
-                    mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w,
-                                                   info.h, PIXEL_FORMAT_RGBA_8888, 0);
+                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
+                                                   resolution.getWidth(), resolution.getHeight(),
+                                                   PIXEL_FORMAT_RGBA_8888, 0);
             EXPECT_TRUE(surfaceControl != nullptr);
             EXPECT_TRUE(surfaceControl->isValid());
             fillSurfaceRGBA8(surfaceControl, BLUE);
@@ -369,8 +370,8 @@
             const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
             EXPECT_TRUE(display == nullptr);
 
-            DisplayInfo info;
-            EXPECT_NE(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
+            DisplayConfig config;
+            EXPECT_NE(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
         }
     }
 
@@ -398,17 +399,18 @@
         const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
         EXPECT_FALSE(display == nullptr);
 
-        DisplayInfo info;
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
-        EXPECT_EQ(200u, info.w);
-        EXPECT_EQ(400u, info.h);
-        EXPECT_EQ(1e9f / 16'666'666, info.fps);
+        DisplayConfig config;
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        EXPECT_EQ(ui::Size(200, 400), config.resolution);
+        EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
 
         mFakeComposerClient->clearFrames();
         {
+            const ui::Size& resolution = config.resolution;
             auto surfaceControl =
-                    mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w,
-                                                   info.h, PIXEL_FORMAT_RGBA_8888, 0);
+                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
+                                                   resolution.getWidth(), resolution.getHeight(),
+                                                   PIXEL_FORMAT_RGBA_8888, 0);
             EXPECT_TRUE(surfaceControl != nullptr);
             EXPECT_TRUE(surfaceControl->isValid());
             fillSurfaceRGBA8(surfaceControl, BLUE);
@@ -421,7 +423,7 @@
             }
         }
 
-        Vector<DisplayInfo> configs;
+        Vector<DisplayConfig> configs;
         EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
         EXPECT_EQ(configs.size(), 2);
 
@@ -436,27 +438,29 @@
         }
 
         for (int i = 0; i < configs.size(); i++) {
-            if (configs[i].w == 800u) {
+            const auto& config = configs[i];
+            if (config.resolution.getWidth() == 800) {
                 EXPECT_EQ(NO_ERROR,
                           SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
-                                                                              configs[i].fps,
-                                                                              configs[i].fps));
+                                                                              config.refreshRate,
+                                                                              config.refreshRate));
                 waitForDisplayTransaction();
                 EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
                 break;
             }
         }
 
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
-        EXPECT_EQ(800u, info.w);
-        EXPECT_EQ(1600u, info.h);
-        EXPECT_EQ(1e9f / 11'111'111, info.fps);
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        EXPECT_EQ(ui::Size(800, 1600), config.resolution);
+        EXPECT_EQ(1e9f / 11'111'111, config.refreshRate);
 
         mFakeComposerClient->clearFrames();
         {
+            const ui::Size& resolution = config.resolution;
             auto surfaceControl =
-                    mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w,
-                                                   info.h, PIXEL_FORMAT_RGBA_8888, 0);
+                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
+                                                   resolution.getWidth(), resolution.getHeight(),
+                                                   PIXEL_FORMAT_RGBA_8888, 0);
             EXPECT_TRUE(surfaceControl != nullptr);
             EXPECT_TRUE(surfaceControl->isValid());
             fillSurfaceRGBA8(surfaceControl, BLUE);
@@ -500,17 +504,18 @@
         const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
         EXPECT_FALSE(display == nullptr);
 
-        DisplayInfo info;
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
-        EXPECT_EQ(800u, info.w);
-        EXPECT_EQ(1600u, info.h);
-        EXPECT_EQ(1e9f / 16'666'666, info.fps);
+        DisplayConfig config;
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        EXPECT_EQ(ui::Size(800, 1600), config.resolution);
+        EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
 
         mFakeComposerClient->clearFrames();
         {
+            const ui::Size& resolution = config.resolution;
             auto surfaceControl =
-                    mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w,
-                                                   info.h, PIXEL_FORMAT_RGBA_8888, 0);
+                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
+                                                   resolution.getWidth(), resolution.getHeight(),
+                                                   PIXEL_FORMAT_RGBA_8888, 0);
             EXPECT_TRUE(surfaceControl != nullptr);
             EXPECT_TRUE(surfaceControl->isValid());
             fillSurfaceRGBA8(surfaceControl, BLUE);
@@ -523,7 +528,7 @@
             }
         }
 
-        Vector<DisplayInfo> configs;
+        Vector<DisplayConfig> configs;
         EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
         EXPECT_EQ(configs.size(), 2);
 
@@ -537,27 +542,29 @@
         }
 
         for (int i = 0; i < configs.size(); i++) {
-            if (configs[i].fps == 1e9f / 11'111'111) {
+            const auto& config = configs[i];
+            if (config.refreshRate == 1e9f / 11'111'111) {
                 EXPECT_EQ(NO_ERROR,
                           SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
-                                                                              configs[i].fps,
-                                                                              configs[i].fps));
+                                                                              config.refreshRate,
+                                                                              config.refreshRate));
                 waitForDisplayTransaction();
                 EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
                 break;
             }
         }
 
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
-        EXPECT_EQ(800u, info.w);
-        EXPECT_EQ(1600u, info.h);
-        EXPECT_EQ(1e9f / 11'111'111, info.fps);
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        EXPECT_EQ(ui::Size(800, 1600), config.resolution);
+        EXPECT_EQ(1e9f / 11'111'111, config.refreshRate);
 
         mFakeComposerClient->clearFrames();
         {
+            const ui::Size& resolution = config.resolution;
             auto surfaceControl =
-                    mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w,
-                                                   info.h, PIXEL_FORMAT_RGBA_8888, 0);
+                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
+                                                   resolution.getWidth(), resolution.getHeight(),
+                                                   PIXEL_FORMAT_RGBA_8888, 0);
             EXPECT_TRUE(surfaceControl != nullptr);
             EXPECT_TRUE(surfaceControl->isValid());
             fillSurfaceRGBA8(surfaceControl, BLUE);
@@ -611,17 +618,18 @@
         const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
         EXPECT_FALSE(display == nullptr);
 
-        DisplayInfo info;
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
-        EXPECT_EQ(800u, info.w);
-        EXPECT_EQ(1600u, info.h);
-        EXPECT_EQ(1e9f / 16'666'666, info.fps);
+        DisplayConfig config;
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        EXPECT_EQ(ui::Size(800, 1600), config.resolution);
+        EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
 
         mFakeComposerClient->clearFrames();
         {
+            const ui::Size& resolution = config.resolution;
             auto surfaceControl =
-                    mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w,
-                                                   info.h, PIXEL_FORMAT_RGBA_8888, 0);
+                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
+                                                   resolution.getWidth(), resolution.getHeight(),
+                                                   PIXEL_FORMAT_RGBA_8888, 0);
             EXPECT_TRUE(surfaceControl != nullptr);
             EXPECT_TRUE(surfaceControl->isValid());
             fillSurfaceRGBA8(surfaceControl, BLUE);
@@ -634,7 +642,7 @@
             }
         }
 
-        Vector<DisplayInfo> configs;
+        Vector<DisplayConfig> configs;
         EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
         EXPECT_EQ(configs.size(), 4);
 
@@ -648,27 +656,29 @@
         }
 
         for (int i = 0; i < configs.size(); i++) {
-            if (configs[i].w == 800u && configs[i].fps == 1e9f / 11'111'111) {
+            const auto& config = configs[i];
+            if (config.resolution.getWidth() == 800 && config.refreshRate == 1e9f / 11'111'111) {
                 EXPECT_EQ(NO_ERROR,
                           SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
-                                                                              configs[i].fps,
-                                                                              configs[i].fps));
+                                                                              configs[i].refreshRate,
+                                                                              configs[i].refreshRate));
                 waitForDisplayTransaction();
                 EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
                 break;
             }
         }
 
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
-        EXPECT_EQ(800u, info.w);
-        EXPECT_EQ(1600u, info.h);
-        EXPECT_EQ(1e9f / 11'111'111, info.fps);
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        EXPECT_EQ(ui::Size(800, 1600), config.resolution);
+        EXPECT_EQ(1e9f / 11'111'111, config.refreshRate);
 
         mFakeComposerClient->clearFrames();
         {
+            const ui::Size& resolution = config.resolution;
             auto surfaceControl =
-                    mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w,
-                                                   info.h, PIXEL_FORMAT_RGBA_8888, 0);
+                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
+                                                   resolution.getWidth(), resolution.getHeight(),
+                                                   PIXEL_FORMAT_RGBA_8888, 0);
             EXPECT_TRUE(surfaceControl != nullptr);
             EXPECT_TRUE(surfaceControl->isValid());
             fillSurfaceRGBA8(surfaceControl, BLUE);
@@ -691,27 +701,29 @@
         }
 
         for (int i = 0; i < configs.size(); i++) {
-            if (configs[i].fps == 1e9f / 8'333'333) {
+            const auto& config = configs[i];
+            if (config.refreshRate == 1e9f / 8'333'333) {
                 EXPECT_EQ(NO_ERROR,
                           SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
-                                                                              configs[i].fps,
-                                                                              configs[i].fps));
+                                                                              config.refreshRate,
+                                                                              config.refreshRate));
                 waitForDisplayTransaction();
                 EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
                 break;
             }
         }
 
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
-        EXPECT_EQ(1600u, info.w);
-        EXPECT_EQ(3200u, info.h);
-        EXPECT_EQ(1e9f / 8'333'333, info.fps);
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        EXPECT_EQ(ui::Size(1600, 3200), config.resolution);
+        EXPECT_EQ(1e9f / 8'333'333, config.refreshRate);
 
         mFakeComposerClient->clearFrames();
         {
+            const ui::Size& resolution = config.resolution;
             auto surfaceControl =
-                    mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w,
-                                                   info.h, PIXEL_FORMAT_RGBA_8888, 0);
+                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
+                                                   resolution.getWidth(), resolution.getHeight(),
+                                                   PIXEL_FORMAT_RGBA_8888, 0);
             EXPECT_TRUE(surfaceControl != nullptr);
             EXPECT_TRUE(surfaceControl->isValid());
             fillSurfaceRGBA8(surfaceControl, BLUE);
@@ -734,27 +746,29 @@
         }
 
         for (int i = 0; i < configs.size(); i++) {
-            if (configs[i].w == 1600 && configs[i].fps == 1e9f / 11'111'111) {
+            const auto& config = configs[i];
+            if (config.resolution.getWidth() == 1600 && config.refreshRate == 1e9f / 11'111'111) {
                 EXPECT_EQ(NO_ERROR,
                           SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
-                                                                              configs[i].fps,
-                                                                              configs[i].fps));
+                                                                              config.refreshRate,
+                                                                              config.refreshRate));
                 waitForDisplayTransaction();
                 EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
                 break;
             }
         }
 
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
-        EXPECT_EQ(1600u, info.w);
-        EXPECT_EQ(3200u, info.h);
-        EXPECT_EQ(1e9f / 11'111'111, info.fps);
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        EXPECT_EQ(ui::Size(1600, 3200), config.resolution);
+        EXPECT_EQ(1e9f / 11'111'111, config.refreshRate);
 
         mFakeComposerClient->clearFrames();
         {
+            const ui::Size& resolution = config.resolution;
             auto surfaceControl =
-                    mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w,
-                                                   info.h, PIXEL_FORMAT_RGBA_8888, 0);
+                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
+                                                   resolution.getWidth(), resolution.getHeight(),
+                                                   PIXEL_FORMAT_RGBA_8888, 0);
             EXPECT_TRUE(surfaceControl != nullptr);
             EXPECT_TRUE(surfaceControl->isValid());
             fillSurfaceRGBA8(surfaceControl, BLUE);
@@ -787,8 +801,8 @@
             const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
             EXPECT_TRUE(display == nullptr);
 
-            DisplayInfo info;
-            auto result = SurfaceComposerClient::getDisplayInfo(display, &info);
+            DisplayConfig config;
+            auto result = SurfaceComposerClient::getActiveDisplayConfig(display, &config);
             EXPECT_NE(NO_ERROR, result);
         }
 
@@ -813,12 +827,11 @@
             const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
             EXPECT_FALSE(display == nullptr);
 
-            DisplayInfo info;
-            auto result = SurfaceComposerClient::getDisplayInfo(display, &info);
+            DisplayConfig config;
+            auto result = SurfaceComposerClient::getActiveDisplayConfig(display, &config);
             EXPECT_EQ(NO_ERROR, result);
-            ASSERT_EQ(400u, info.w);
-            ASSERT_EQ(200u, info.h);
-            EXPECT_EQ(1e9f / 16'666'666, info.fps);
+            ASSERT_EQ(ui::Size(400, 200), config.resolution);
+            EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
         }
     }
 
@@ -968,11 +981,12 @@
         const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
         ASSERT_FALSE(display == nullptr);
 
-        DisplayInfo info;
-        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
+        DisplayConfig config;
+        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
 
-        mDisplayWidth = info.w;
-        mDisplayHeight = info.h;
+        const ui::Size& resolution = config.resolution;
+        mDisplayWidth = resolution.getWidth();
+        mDisplayHeight = resolution.getHeight();
 
         // Background surface
         mBGSurfaceControl =
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 3f29558..888e009 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -713,7 +713,9 @@
 
 struct DefaultLayerProperties : public BaseLayerProperties<DefaultLayerProperties> {};
 
-struct ColorLayerProperties : public BaseLayerProperties<ColorLayerProperties> {};
+struct ColorLayerProperties : public BaseLayerProperties<ColorLayerProperties> {
+    static constexpr IComposerClient::BlendMode BLENDMODE = IComposerClient::BlendMode::NONE;
+};
 
 struct SidebandLayerProperties : public BaseLayerProperties<SidebandLayerProperties> {
     using Base = BaseLayerProperties<SidebandLayerProperties>;
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 9680a17..dddad92 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -1357,7 +1357,8 @@
                                 mHardwareDisplaySize.height),
                   compositionState.transform);
         EXPECT_EQ(TRANSFORM_FLAGS_ROT_0, compositionState.orientation);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.scissor);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.sourceClip);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
         EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.frame);
         EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.viewport);
         EXPECT_EQ(false, compositionState.needsFiltering);
@@ -1369,7 +1370,8 @@
                                 mHardwareDisplaySize.height),
                   compositionState.transform);
         EXPECT_EQ(TRANSFORM_FLAGS_ROT_90, compositionState.orientation);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.scissor);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.sourceClip);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
         // For 90, the frame and viewport have the hardware display size width and height swapped
         EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.frame);
         EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.viewport);
@@ -1382,7 +1384,8 @@
                                 mHardwareDisplaySize.height),
                   compositionState.transform);
         EXPECT_EQ(TRANSFORM_FLAGS_ROT_180, compositionState.orientation);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.scissor);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.sourceClip);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
         EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.frame);
         EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.viewport);
         EXPECT_EQ(false, compositionState.needsFiltering);
@@ -1394,7 +1397,8 @@
                                 mHardwareDisplaySize.height),
                   compositionState.transform);
         EXPECT_EQ(TRANSFORM_FLAGS_ROT_270, compositionState.orientation);
-        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.scissor);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.sourceClip);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
         // For 270, the frame and viewport have the hardware display size width and height swapped
         EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.frame);
         EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.viewport);
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 9ca1b70..18e9941 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -63,8 +63,7 @@
 
     auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); }
 
-    RefreshRateConfigs mConfigs{true,
-                                {
+    RefreshRateConfigs mConfigs{{
                                         RefreshRateConfigs::InputConfig{HwcConfigIndexType(0),
                                                                         HwcConfigGroupType(0),
                                                                         LO_FPS_PERIOD},
@@ -85,7 +84,7 @@
     const auto layer = createLayer();
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
     EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
-    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
 
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(0, activeLayerCount());
@@ -114,7 +113,7 @@
     const auto layer = createLayer();
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
     EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
-    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
 
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(0, activeLayerCount());
@@ -138,15 +137,15 @@
 
     EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
     EXPECT_CALL(*layer1, getFrameSelectionPriority()).WillRepeatedly(Return(1));
-    EXPECT_CALL(*layer1, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+    EXPECT_CALL(*layer1, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
 
     EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
     EXPECT_CALL(*layer2, getFrameSelectionPriority()).WillRepeatedly(Return(1));
-    EXPECT_CALL(*layer2, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+    EXPECT_CALL(*layer2, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
 
     EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
     EXPECT_CALL(*layer3, getFrameSelectionPriority()).WillRepeatedly(Return(1));
-    EXPECT_CALL(*layer3, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+    EXPECT_CALL(*layer3, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
     nsecs_t time = mTime;
 
     EXPECT_EQ(3, layerCount());
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
index 11ace05..959c256 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
@@ -71,8 +71,7 @@
 
     auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); }
 
-    RefreshRateConfigs mConfigs{true,
-                                {
+    RefreshRateConfigs mConfigs{{
                                         RefreshRateConfigs::InputConfig{HwcConfigIndexType(0),
                                                                         HwcConfigGroupType(0),
                                                                         LO_FPS_PERIOD},
@@ -84,7 +83,6 @@
     TestableScheduler* const mScheduler{new TestableScheduler(mConfigs, true)};
     TestableSurfaceFlinger mFlinger;
 
-    const nsecs_t mTime = systemTime();
 };
 
 namespace {
@@ -92,28 +90,30 @@
 TEST_F(LayerHistoryTestV2, oneLayer) {
     const auto layer = createLayer();
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
 
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(0, activeLayerCount());
 
+    const nsecs_t time = systemTime();
+
     // No layers returned if no layers are active.
-    EXPECT_TRUE(history().summarize(mTime).empty());
+    EXPECT_TRUE(history().summarize(time).empty());
     EXPECT_EQ(0, activeLayerCount());
 
     // Max returned if active layers have insufficient history.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
-        history().record(layer.get(), 0, mTime);
-        ASSERT_EQ(1, history().summarize(mTime).size());
-        EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(mTime)[0].vote);
+        history().record(layer.get(), 0, time);
+        ASSERT_EQ(1, history().summarize(time).size());
+        EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
         EXPECT_EQ(1, activeLayerCount());
     }
 
     // Max is returned since we have enough history but there is no timestamp votes.
     for (int i = 0; i < 10; i++) {
-        history().record(layer.get(), 0, mTime);
-        ASSERT_EQ(1, history().summarize(mTime).size());
-        EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(mTime)[0].vote);
+        history().record(layer.get(), 0, time);
+        ASSERT_EQ(1, history().summarize(time).size());
+        EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
         EXPECT_EQ(1, activeLayerCount());
     }
 }
@@ -121,33 +121,36 @@
 TEST_F(LayerHistoryTestV2, oneInvisibleLayer) {
     const auto layer = createLayer();
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
 
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(0, activeLayerCount());
 
-    history().record(layer.get(), 0, mTime);
-    auto summary = history().summarize(mTime);
-    ASSERT_EQ(1, history().summarize(mTime).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(mTime)[0].vote);
+    nsecs_t time = systemTime();
+
+    history().record(layer.get(), 0, time);
+    auto summary = history().summarize(time);
+    ASSERT_EQ(1, history().summarize(time).size());
+    // Layer is still considered inactive so we expect to get Min
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
     EXPECT_EQ(1, activeLayerCount());
 
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
 
-    summary = history().summarize(mTime);
-    EXPECT_TRUE(history().summarize(mTime).empty());
+    summary = history().summarize(time);
+    EXPECT_TRUE(history().summarize(time).empty());
     EXPECT_EQ(0, activeLayerCount());
 }
 
 TEST_F(LayerHistoryTestV2, explicitTimestamp) {
     const auto layer = createLayer();
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
 
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(0, activeLayerCount());
 
-    nsecs_t time = mTime;
+    nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
         history().record(layer.get(), time, time);
         time += LO_FPS_PERIOD;
@@ -163,14 +166,14 @@
 TEST_F(LayerHistoryTestV2, oneLayerNoVote) {
     const auto layer = createLayer();
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
 
     setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::NoVote);
 
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(0, activeLayerCount());
 
-    nsecs_t time = mTime;
+    nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
         history().record(layer.get(), time, time);
         time += HI_FPS_PERIOD;
@@ -190,14 +193,14 @@
 TEST_F(LayerHistoryTestV2, oneLayerMinVote) {
     const auto layer = createLayer();
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
 
     setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Min);
 
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(0, activeLayerCount());
 
-    nsecs_t time = mTime;
+    nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
         history().record(layer.get(), time, time);
         time += HI_FPS_PERIOD;
@@ -218,14 +221,14 @@
 TEST_F(LayerHistoryTestV2, oneLayerMaxVote) {
     const auto layer = createLayer();
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
 
     setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Max);
 
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(0, activeLayerCount());
 
-    nsecs_t time = mTime;
+    nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
         history().record(layer.get(), time, time);
         time += LO_FPS_PERIOD;
@@ -246,19 +249,53 @@
 TEST_F(LayerHistoryTestV2, oneLayerExplicitVote) {
     auto layer = createLayer();
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(73.4f));
+    EXPECT_CALL(*layer, getFrameRate())
+            .WillRepeatedly(
+                    Return(Layer::FrameRate(73.4f, Layer::FrameRateCompatibility::Default)));
 
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(0, activeLayerCount());
 
-    nsecs_t time = mTime;
+    nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
         history().record(layer.get(), time, time);
         time += HI_FPS_PERIOD;
     }
 
     ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Explicit, history().summarize(time)[0].vote);
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, history().summarize(time)[0].vote);
+    EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer became inactive
+    setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_TRUE(history().summarize(time).empty());
+    // TODO: activeLayerCount() should be 0 but it is 1 since getFrameRate() returns a value > 0
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTestV2, oneLayerExplicitExactVote) {
+    auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRate())
+            .WillRepeatedly(Return(
+                    Layer::FrameRate(73.4f, Layer::FrameRateCompatibility::ExactOrMultiple)));
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer.get(), time, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+              history().summarize(time)[0].vote);
     EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(1, frequentLayerCount(time));
@@ -278,15 +315,15 @@
     auto layer3 = createLayer();
 
     EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer1, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+    EXPECT_CALL(*layer1, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
 
     EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer2, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+    EXPECT_CALL(*layer2, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
 
     EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer3, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+    EXPECT_CALL(*layer3, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
 
-    nsecs_t time = mTime;
+    nsecs_t time = systemTime();
 
     EXPECT_EQ(3, layerCount());
     EXPECT_EQ(0, activeLayerCount());
@@ -314,7 +351,7 @@
 
     ASSERT_EQ(2, history().summarize(time).size());
     EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[1].vote);
+    ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[1].vote);
     EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
     EXPECT_EQ(2, activeLayerCount());
     EXPECT_EQ(1, frequentLayerCount(time));
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 78009b8..841c624 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -74,26 +74,14 @@
     std::vector<RefreshRateConfigs::InputConfig> configs{
             {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60}}};
     auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
-    ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
-}
-
-TEST_F(RefreshRateConfigsTest, oneDeviceConfig_SwitchingNotSupported) {
-    std::vector<RefreshRateConfigs::InputConfig> configs{
-            {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60}}};
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
-    ASSERT_FALSE(refreshRateConfigs->refreshRateSwitchingSupported());
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
 }
 
 TEST_F(RefreshRateConfigsTest, invalidPolicy) {
     std::vector<RefreshRateConfigs::InputConfig> configs{
             {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60}}};
     auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
     ASSERT_LT(refreshRateConfigs->setPolicy(HwcConfigIndexType(10), 60, 60, nullptr), 0);
     ASSERT_LT(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 20, 40, nullptr), 0);
 }
@@ -103,10 +91,7 @@
             {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
              {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
     auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
-
-    ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
 
     const auto minRate = refreshRateConfigs->getMinRefreshRate();
     const auto performanceRate = refreshRateConfigs->getMaxRefreshRate();
@@ -128,10 +113,8 @@
             {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
              {HWC_CONFIG_ID_90, HWC_GROUP_ID_1, VSYNC_90}}};
     auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
     const auto minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
     const auto performanceRate = refreshRateConfigs->getMaxRefreshRate();
     const auto minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
@@ -145,7 +128,6 @@
     ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 60, 90, nullptr), 0);
     refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
 
-    ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
     const auto minRate90 = refreshRateConfigs->getMinRefreshRateByPolicy();
     const auto performanceRate90 = refreshRateConfigs->getMaxRefreshRateByPolicy();
 
@@ -161,9 +143,8 @@
             {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
              {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
     auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
-    ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
     auto minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
     auto performanceRate = refreshRateConfigs->getMaxRefreshRateByPolicy();
 
@@ -174,7 +155,6 @@
     ASSERT_EQ(expectedPerformanceConfig, performanceRate);
 
     ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60, nullptr), 0);
-    ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
 
     auto minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
     auto performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
@@ -187,8 +167,7 @@
             {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
              {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
     auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
     {
         auto current = refreshRateConfigs->getCurrentRefreshRate();
         EXPECT_EQ(current.configId, HWC_CONFIG_ID_60);
@@ -212,10 +191,7 @@
             {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
              {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
     auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
-
-    ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
 
     RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
     RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
@@ -276,10 +252,7 @@
             {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
              {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
     auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
-
-    ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
 
     RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
     RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
@@ -387,10 +360,7 @@
              {HWC_CONFIG_ID_72, HWC_GROUP_ID_0, VSYNC_72},
              {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
     auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
-
-    ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
 
     RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
     RefreshRate expected72Config = {HWC_CONFIG_ID_72, VSYNC_72, HWC_GROUP_ID_0, "72fps", 70};
@@ -430,10 +400,7 @@
              {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90},
              {HWC_CONFIG_ID_120, HWC_GROUP_ID_0, VSYNC_120}}};
     auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
-
-    ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
 
     RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
     RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
@@ -465,15 +432,83 @@
     EXPECT_EQ(expected72Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
 }
 
+TEST_F(RefreshRateConfigsTest,
+       twoDeviceConfigs_getRefreshRateForContentV2_30_60_90_120_DifferentTypes) {
+    std::vector<RefreshRateConfigs::InputConfig> configs{
+            {{HWC_CONFIG_ID_30, HWC_GROUP_ID_0, VSYNC_30},
+             {HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+             {HWC_CONFIG_ID_72, HWC_GROUP_ID_0, VSYNC_72},
+             {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90},
+             {HWC_CONFIG_ID_120, HWC_GROUP_ID_0, VSYNC_120}}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
+    RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+    RefreshRate expected72Config = {HWC_CONFIG_ID_72, VSYNC_72, HWC_GROUP_ID_0, "72fps", 72};
+    RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+    RefreshRate expected120Config = {HWC_CONFIG_ID_120, VSYNC_120, HWC_GROUP_ID_0, "120fps", 120};
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+                                                LayerRequirement{.weight = 1.0f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr2.desiredRefreshRate = 60.0f;
+    lr2.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(expected120Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 60.0f;
+    lr2.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(expected120Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 60.0f;
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    EXPECT_EQ(expected120Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 90.0f;
+    lr2.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(expected120Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr2.desiredRefreshRate = 90.0f;
+    lr2.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(expected120Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 90.0f;
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 90.0f;
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    EXPECT_EQ(expected120Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr2.desiredRefreshRate = 90.0f;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+}
+
 TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_30_60) {
     std::vector<RefreshRateConfigs::InputConfig> configs{
             {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
              {HWC_CONFIG_ID_30, HWC_GROUP_ID_0, VSYNC_30}}};
     auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
-
-    ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
 
     RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
     RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
@@ -495,7 +530,7 @@
     EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
 
     lr.desiredRefreshRate = 45.0f;
-    EXPECT_EQ(expected30Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
 
     lr.desiredRefreshRate = 30.0f;
     EXPECT_EQ(expected30Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
@@ -511,10 +546,7 @@
              {HWC_CONFIG_ID_72, HWC_GROUP_ID_0, VSYNC_72},
              {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
     auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
-
-    ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
 
     RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
     RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
@@ -553,10 +585,7 @@
              {HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
              {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
     auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
-
-    ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
 
     RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
     RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
@@ -577,7 +606,7 @@
     EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
 
     lr1.vote = LayerVoteType::Min;
-    lr2.vote = LayerVoteType::Explicit;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 24.0f;
     EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
 
@@ -587,7 +616,7 @@
     EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
 
     lr1.vote = LayerVoteType::Max;
-    lr2.vote = LayerVoteType::Explicit;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 60.0f;
     EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
 
@@ -599,7 +628,7 @@
 
     lr1.vote = LayerVoteType::Heuristic;
     lr1.desiredRefreshRate = 30.0f;
-    lr2.vote = LayerVoteType::Explicit;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 45.0f;
     EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
 }
@@ -609,10 +638,7 @@
             {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
              {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
     auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
-
-    ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
 
     RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
     RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
@@ -621,7 +647,7 @@
     auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
     auto& lr = layers[0];
 
-    lr.vote = LayerVoteType::Explicit;
+    lr.vote = LayerVoteType::ExplicitExactOrMultiple;
     for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
         lr.desiredRefreshRate = fps;
         const auto& refreshRate = refreshRateConfigs->getRefreshRateForContentV2(layers);
@@ -635,10 +661,7 @@
             {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
              {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
     auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
-
-    ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
 
     RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
     RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
@@ -650,13 +673,13 @@
 
     lr1.vote = LayerVoteType::Heuristic;
     lr1.desiredRefreshRate = 60.0f;
-    lr2.vote = LayerVoteType::Explicit;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 90.0f;
     EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(layers));
 
     lr1.vote = LayerVoteType::Heuristic;
     lr1.desiredRefreshRate = 90.0f;
-    lr2.vote = LayerVoteType::Explicit;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 60.0f;
     EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(layers));
 }
@@ -666,10 +689,7 @@
             {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
              {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
     auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
-
-    ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
 
     RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
     RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
@@ -681,13 +701,13 @@
 
     lr1.vote = LayerVoteType::Heuristic;
     lr1.desiredRefreshRate = 60.0f;
-    lr2.vote = LayerVoteType::Explicit;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 90.0f;
     EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
 
     lr1.vote = LayerVoteType::Heuristic;
     lr1.desiredRefreshRate = 90.0f;
-    lr2.vote = LayerVoteType::Explicit;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 60.0f;
     EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
 }
@@ -702,6 +722,29 @@
     ASSERT_FALSE(expectedDefaultConfig.inPolicy(50.0f, 59.998f));
 }
 
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_75HzContent) {
+    std::vector<RefreshRateConfigs::InputConfig> configs{
+            {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+             {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
+    RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+    RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+    for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) {
+        lr.desiredRefreshRate = fps;
+        const auto& refreshRate = refreshRateConfigs->getRefreshRateForContentV2(layers);
+        printf("%.2fHz chooses %s\n", fps, refreshRate.name.c_str());
+        EXPECT_EQ(expected90Config, refreshRate);
+    }
+}
+
 } // namespace
 } // namespace scheduler
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
index 8e07c79..18d6bd2 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -47,8 +47,8 @@
     ~RefreshRateStatsTest();
 
     void init(const std::vector<RefreshRateConfigs::InputConfig>& configs) {
-        mRefreshRateConfigs = std::make_unique<RefreshRateConfigs>(
-                /*refreshRateSwitching=*/true, configs, /*currentConfig=*/CONFIG_ID_0);
+        mRefreshRateConfigs =
+                std::make_unique<RefreshRateConfigs>(configs, /*currentConfig=*/CONFIG_ID_0);
         mRefreshRateStats =
                 std::make_unique<RefreshRateStats>(*mRefreshRateConfigs, mTimeStats,
                                                    /*currentConfigId=*/CONFIG_ID_0,
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 82a00ee..89002a8 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -73,8 +73,7 @@
     std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{
             {{HwcConfigIndexType(0), HwcConfigGroupType(0), 16666667}}};
     mRefreshRateConfigs = std::make_unique<
-            scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs,
-                                           /*currentConfig=*/HwcConfigIndexType(0));
+            scheduler::RefreshRateConfigs>(configs, /*currentConfig=*/HwcConfigIndexType(0));
 
     mScheduler = std::make_unique<TestableScheduler>(*mRefreshRateConfigs, false);
 
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 64838ca..798ba76 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -202,8 +202,7 @@
         std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{
                 {{HwcConfigIndexType(0), HwcConfigGroupType(0), 16666667}}};
         mFlinger->mRefreshRateConfigs = std::make_unique<
-                scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs,
-                                               /*currentConfig=*/HwcConfigIndexType(0));
+                scheduler::RefreshRateConfigs>(configs, /*currentConfig=*/HwcConfigIndexType(0));
         mFlinger->mRefreshRateStats = std::make_unique<
                 scheduler::RefreshRateStats>(*mFlinger->mRefreshRateConfigs, *mFlinger->mTimeStats,
                                              /*currentConfig=*/HwcConfigIndexType(0),
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 68e6697..30505b9 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -37,6 +37,7 @@
 
 using namespace android::surfaceflinger;
 using namespace google::protobuf;
+using namespace std::chrono_literals;
 
 namespace android {
 namespace {
@@ -258,22 +259,25 @@
     ASSERT_FALSE(mTimeStats->isEnabled());
 }
 
-TEST_F(TimeStatsTest, enabledAfterBoot) {
+TEST_F(TimeStatsTest, registersCallbacksAfterBoot) {
     mTimeStats->onBootFinished();
-    ASSERT_TRUE(mTimeStats->isEnabled());
+    EXPECT_THAT(mDelegate->mAtomTags,
+                UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+                                     android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+}
+
+TEST_F(TimeStatsTest, unregistersCallbacksOnDestruction) {
+    EXPECT_CALL(*mDelegate,
+                unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
+    EXPECT_CALL(*mDelegate,
+                unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+    mTimeStats.reset();
 }
 
 TEST_F(TimeStatsTest, canEnableAndDisableTimeStats) {
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
     ASSERT_TRUE(mTimeStats->isEnabled());
-    EXPECT_THAT(mDelegate->mAtomTags,
-                UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
-                                     android::util::SURFACEFLINGER_STATS_LAYER_INFO));
 
-    EXPECT_CALL(*mDelegate,
-                unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
-    EXPECT_CALL(*mDelegate,
-                unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO));
     EXPECT_TRUE(inputCommand(InputCommand::DISABLE, FMT_STRING).empty());
     ASSERT_FALSE(mTimeStats->isEnabled());
 }
@@ -306,6 +310,41 @@
     EXPECT_EQ(CLIENT_COMPOSITION_FRAMES, globalProto.client_composition_frames());
 }
 
+TEST_F(TimeStatsTest, canIncreaseLateAcquireFrames) {
+    // this stat is not in the proto so verify by checking the string dump
+    constexpr size_t LATE_ACQUIRE_FRAMES = 2;
+
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    for (size_t i = 0; i < LATE_ACQUIRE_FRAMES; i++) {
+        mTimeStats->incrementLatchSkipped(LAYER_ID_0, TimeStats::LatchSkipReason::LateAcquire);
+    }
+    insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_0, 2, 2000000);
+
+    const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    const std::string expectedResult = "lateAcquireFrames = " + std::to_string(LATE_ACQUIRE_FRAMES);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
+TEST_F(TimeStatsTest, canIncreaseBadDesiredPresent) {
+    // this stat is not in the proto so verify by checking the string dump
+    constexpr size_t BAD_DESIRED_PRESENT_FRAMES = 2;
+
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    for (size_t i = 0; i < BAD_DESIRED_PRESENT_FRAMES; i++) {
+        mTimeStats->incrementBadDesiredPresent(LAYER_ID_0);
+    }
+    insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_0, 2, 2000000);
+
+    const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    const std::string expectedResult =
+            "badDesiredPresentFrames = " + std::to_string(BAD_DESIRED_PRESENT_FRAMES);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
 TEST_F(TimeStatsTest, canIncreaseClientCompositionReusedFrames) {
     // this stat is not in the proto so verify by checking the string dump
     constexpr size_t CLIENT_COMPOSITION_REUSED_FRAMES = 2;
@@ -321,6 +360,45 @@
     EXPECT_THAT(result, HasSubstr(expectedResult));
 }
 
+TEST_F(TimeStatsTest, canAverageFrameDuration) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+    mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+    mTimeStats
+            ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(),
+                                  std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
+                                          .count());
+    mTimeStats
+            ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(),
+                                  std::chrono::duration_cast<std::chrono::nanoseconds>(16ms)
+                                          .count());
+
+    const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    EXPECT_THAT(result, HasSubstr("averageFrameDuration = 10.000 ms"));
+}
+
+TEST_F(TimeStatsTest, canAverageRenderEngineTimings) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+    mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms)
+                                                   .count(),
+                                           std::make_shared<FenceTime>(
+                                                   std::chrono::duration_cast<
+                                                           std::chrono::nanoseconds>(3ms)
+                                                           .count()));
+
+    mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(4ms)
+                                                   .count(),
+                                           std::chrono::duration_cast<std::chrono::nanoseconds>(8ms)
+                                                   .count());
+
+    // Push a dummy present fence to trigger flushing the RenderEngine timings.
+    mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+    mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(
+            std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
+
+    const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    EXPECT_THAT(result, HasSubstr("averageRenderEngineTiming = 3.000 ms"));
+}
+
 TEST_F(TimeStatsTest, canInsertGlobalPresentToPresent) {
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
 
@@ -353,8 +431,6 @@
 TEST_F(TimeStatsTest, canInsertGlobalFrameDuration) {
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
 
-    using namespace std::chrono_literals;
-
     mTimeStats->setPowerMode(HWC_POWER_MODE_OFF);
     mTimeStats
             ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(),
@@ -378,8 +454,6 @@
 TEST_F(TimeStatsTest, canInsertGlobalRenderEngineTiming) {
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
 
-    using namespace std::chrono_literals;
-
     mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms)
                                                    .count(),
                                            std::make_shared<FenceTime>(
@@ -635,7 +709,6 @@
     ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementClientCompositionFrames());
     ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL));
 
-    using namespace std::chrono_literals;
     mTimeStats
             ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(3ms).count(),
                                   std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
@@ -665,14 +738,27 @@
     EXPECT_EQ(0, globalProto.stats_size());
 }
 
-TEST_F(TimeStatsTest, canClearClientCompositionSkippedFrames) {
-    // this stat is not in the proto so verify by checking the string dump
+TEST_F(TimeStatsTest, canClearDumpOnlyTimeStats) {
+    // These stats are not in the proto so verify by checking the string dump.
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
     ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementClientCompositionReusedFrames());
+    mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+    mTimeStats
+            ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(),
+                                  std::chrono::duration_cast<std::chrono::nanoseconds>(5ms)
+                                          .count());
+    mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(4ms)
+                                                   .count(),
+                                           std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
+                                                   .count());
+    mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(
+            std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
     EXPECT_TRUE(inputCommand(InputCommand::CLEAR, FMT_STRING).empty());
 
     const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
     EXPECT_THAT(result, HasSubstr("clientCompositionReusedFrames = 0"));
+    EXPECT_THAT(result, HasSubstr("averageFrameDuration = 0.000 ms"));
+    EXPECT_THAT(result, HasSubstr("averageRenderEngineTiming = 0.000 ms"));
 }
 
 TEST_F(TimeStatsTest, canDumpWithMaxLayers) {
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index acf852d..caac61d 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -37,7 +37,7 @@
 public:
     FixedRateIdealStubTracker() : mPeriod{toNs(3ms)} {}
 
-    void addVsyncTimestamp(nsecs_t) final {}
+    bool addVsyncTimestamp(nsecs_t) final { return true; }
 
     nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
         auto const floor = timePoint % mPeriod;
@@ -60,7 +60,7 @@
 public:
     VRRStubTracker(nsecs_t period) : mPeriod{period} {}
 
-    void addVsyncTimestamp(nsecs_t) final {}
+    bool addVsyncTimestamp(nsecs_t) final { return true; }
 
     nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point) const final {
         std::lock_guard<decltype(mMutex)> lk(mMutex);
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 70c9225..3ab38e4 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -39,9 +39,10 @@
     MockVSyncTracker(nsecs_t period) : mPeriod{period} {
         ON_CALL(*this, nextAnticipatedVSyncTimeFrom(_))
                 .WillByDefault(Invoke(this, &MockVSyncTracker::nextVSyncTime));
+        ON_CALL(*this, addVsyncTimestamp(_)).WillByDefault(Return(true));
     }
 
-    MOCK_METHOD1(addVsyncTimestamp, void(nsecs_t));
+    MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t));
     MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
     MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
     MOCK_METHOD1(setPeriod, void(nsecs_t));
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index ce1fafe..1de72b9 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -35,7 +35,8 @@
 
 class MockVSyncTracker : public VSyncTracker {
 public:
-    MOCK_METHOD1(addVsyncTimestamp, void(nsecs_t));
+    MockVSyncTracker() { ON_CALL(*this, addVsyncTimestamp(_)).WillByDefault(Return(true)); }
+    MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t));
     MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
     MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
     MOCK_METHOD1(setPeriod, void(nsecs_t));
@@ -46,7 +47,9 @@
 public:
     VSyncTrackerWrapper(std::shared_ptr<VSyncTracker> const& tracker) : mTracker(tracker) {}
 
-    void addVsyncTimestamp(nsecs_t timestamp) final { mTracker->addVsyncTimestamp(timestamp); }
+    bool addVsyncTimestamp(nsecs_t timestamp) final {
+        return mTracker->addVsyncTimestamp(timestamp);
+    }
     nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
         return mTracker->nextAnticipatedVSyncTimeFrom(timePoint);
     }
@@ -239,6 +242,22 @@
     EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime)));
 }
 
+TEST_F(VSyncReactorTest, ignoresProperlyAfterAPeriodConfirmation) {
+    bool periodFlushed = true;
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2);
+    mReactor.setIgnorePresentFences(true);
+
+    nsecs_t const newPeriod = 5000;
+    mReactor.setPeriod(newPeriod);
+
+    EXPECT_TRUE(mReactor.addResyncSample(0, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    EXPECT_FALSE(mReactor.addResyncSample(newPeriod, &periodFlushed));
+    EXPECT_TRUE(periodFlushed);
+
+    EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+}
+
 TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshNow) {
     nsecs_t const fakeTimestamp = 4839;
     EXPECT_CALL(*mMockTracker, currentPeriod()).Times(0);
@@ -330,6 +349,35 @@
     EXPECT_TRUE(periodFlushed);
 }
 
+TEST_F(VSyncReactorTest, reportedBadTimestampFromPredictorWillReactivateHwVSync) {
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_))
+            .WillOnce(Return(false))
+            .WillOnce(Return(true))
+            .WillOnce(Return(true));
+    EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+    EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+
+    nsecs_t skewyPeriod = period >> 1;
+    bool periodFlushed = false;
+    nsecs_t sampleTime = 0;
+    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += skewyPeriod, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    EXPECT_FALSE(mReactor.addResyncSample(sampleTime += period, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+}
+
+TEST_F(VSyncReactorTest, reportedBadTimestampFromPredictorWillReactivateHwVSyncPendingFence) {
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_))
+            .Times(2)
+            .WillOnce(Return(false))
+            .WillOnce(Return(true));
+
+    auto fence = generatePendingFence();
+    EXPECT_FALSE(mReactor.addPresentFence(fence));
+    signalFenceWithTime(fence, period >> 1);
+    EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+}
+
 TEST_F(VSyncReactorTest, presentFenceAdditionDoesNotInterruptConfirmationProcess) {
     nsecs_t const newPeriod = 5000;
     mReactor.setPeriod(newPeriod);
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 494e73d..e2f6abd 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -31,7 +31,7 @@
     MOCK_METHOD0(getFrameSelectionPriority, int32_t());
     MOCK_CONST_METHOD0(isVisible, bool());
     MOCK_METHOD0(createClone, sp<Layer>());
-    MOCK_CONST_METHOD0(getFrameRate, std::optional<float>());
+    MOCK_CONST_METHOD0(getFrameRate, FrameRate());
 };
 
 } // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index d1df08c..2720537 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -40,6 +40,8 @@
     MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, nsecs_t));
     MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, const std::shared_ptr<FenceTime>&));
     MOCK_METHOD4(setPostTime, void(int32_t, uint64_t, const std::string&, nsecs_t));
+    MOCK_METHOD2(incrementLatchSkipped, void(int32_t layerId, LatchSkipReason reason));
+    MOCK_METHOD1(incrementBadDesiredPresent, void(int32_t layerId));
     MOCK_METHOD3(setLatchTime, void(int32_t, uint64_t, nsecs_t));
     MOCK_METHOD3(setDesiredTime, void(int32_t, uint64_t, nsecs_t));
     MOCK_METHOD3(setAcquireTime, void(int32_t, uint64_t, nsecs_t));
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 90a73e2..a7ec4ae 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -164,6 +164,11 @@
     "ro.board.platform",
 }};
 
+// LoadDriver returns:
+// * 0 when succeed, or
+// * -ENOENT when fail to open binary libraries, or
+// * -EINVAL when fail to find HAL_MODULE_INFO_SYM_AS_STR or
+//   HWVULKAN_HARDWARE_MODULE_ID in the library.
 int LoadDriver(android_namespace_t* library_namespace,
                const hwvulkan_module_t** module) {
     ATRACE_CALL();
@@ -221,7 +226,13 @@
         return -ENOENT;
     android::GraphicsEnv::getInstance().setDriverToLoad(
         android::GpuStatsInfo::Driver::VULKAN_UPDATED);
-    return LoadDriver(ns, module);
+    int result = LoadDriver(ns, module);
+    if (result != 0) {
+        LOG_ALWAYS_FATAL(
+            "couldn't find an updated Vulkan implementation from %s",
+            android::GraphicsEnv::getInstance().getDriverPath().c_str());
+    }
+    return result;
 }
 
 bool Hal::Open() {